Reputation: 81
I have been reading around and am struggling to understand and get this working.
I have a base class Person, which Teacher & Student inherit from. I want to store them both inside of a vector of type "Person". I have tried a few things. However, I keep getting pages of errors, and I am struggling to understand them.
This current code compiled with g++ -std=c++17 test.cpp is giving me:
Undefined symbols for architecture x86_64:
"Person::~Person()", referenced from:
Teacher::~Teacher() in test-9423bf.o
Student::~Student() in test-9423bf.o
ld: symbol(s) not found for architecture x86_64
Would appreciate any tips and good references on c++ features written simply.
#include <iostream>
#include <vector>
#include <memory>
class Person {
public:
virtual void printName() = 0;
virtual ~Person() = 0;
};
class Teacher : public Person {
public:
void printName() {
std::cout << "Hello My Name is Teacher" << std::endl;
}
~Teacher() {}
};
class Student : public Person {
public:
void printName() {
std::cout << "Hello My Name Is Student" << std::endl;
}
~Student() {}
};
//Capturing the raw pointer and letting it go out of scope
template<typename Person, typename Teacher>
std::unique_ptr<Person> static_unique_pointer_cast (std::unique_ptr<Teacher>&& old){
return std::unique_ptr<Person>{static_cast<Person*>(old.release())};
//conversion: unique_ptr<FROM>->FROM*->TO*->unique_ptr<TO>
}
auto main() -> int {
auto t1 = std::make_unique<Teacher>();
auto t2 = std::make_unique<Teacher>();
auto t3 = std::make_unique<Teacher>();
auto s1 = std::make_unique<Student>();
auto s2 = std::make_unique<Student>();
auto s3 = std::make_unique<Student>();
std::vector<std::unique_ptr<Person>> v;
// v.push_back(static_unique_pointer_cast<Person>(std::move(s1)));
auto foo = static_unique_pointer_cast<Person>(std::move(s1));
// std::vector<std::unique_ptr<Person>> ve = {
// std::move(t1),
// std::move(t2),
// std::move(t3),
// std::move(s1),
// std::move(s2),
// std::move(s3)
// };
return 0;
}
Edit: I got it working by changing base class destructor to default.
I now have this:
std::vector<std::unique_ptr<Person>> v;
v.push_back(static_unique_pointer_cast<Person>(std::move(s1)));
v.push_back(static_unique_pointer_cast<Person>(std::move(s1)));
for (auto item: v) {
item->printName();
}
but I am getting the following error:
error: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<Person, std::__1::default_delete<Person> >'
for (auto item: v) {
edit 2:
The above works when I use:
for (auto &&item: v) {
item->printName();
}
Could someone explain this to me? The vector holds unique pointers (that were once an rvalue (specifically exrvalue) but now they aren't. Why do I need to use auto &&?
Upvotes: 0
Views: 918
Reputation: 2371
I've cleaned up the code an will try to explain the changes.
#include <iostream>
#include <vector>
#include <memory>
class Person {
public:
virtual ~Person() = default; // before this was a pure-virtual d'tor. Usually, you don't need it, but the linker told you it wasn't implemented
virtual void printName() = 0;
};
class Teacher : public Person {
public:
void printName() override {
std::cout << "Hello My Name is Teacher\n";
}
// removed d'tor since you already have a default virtual destructor here
};
class Student : public Person {
public:
void printName() override {
std::cout << "Hello My Name Is Student\n";
}
// removed d'tor since you already have a default virtual destructor here
};
// removed template this upcasting is almost straight forward.
auto main() -> int {
auto t1 = std::make_unique<Teacher>();
auto t2 = std::make_unique<Teacher>();
auto t3 = std::make_unique<Teacher>();
auto s1 = std::make_unique<Student>();
auto s2 = std::make_unique<Student>();
auto s3 = std::make_unique<Student>();
std::vector<std::unique_ptr<Person>> v;
v.push_back(std::move(t1));
v.push_back(std::move(t2));
v.push_back(std::move(t3)); // Easy to add Students and Teachers
v.push_back(std::move(s1));
v.push_back(std::move(s2));
v.push_back(std::move(s3));
for (auto &item: v) { // Taking a reference, `&`, the pointers in `v` still stay there after the loop and can be reused. Don't use `&&` unless you want the use the pointers once only.
item->printName();
}
return 0;
}
Upvotes: 3