The main question here is: Can I make a shared pointer using a reference?
In the following code, class AnimalList
implements a container that holds a vector of shared pointers to all types derived from Animal
. In order to push items into the container, class AnimalList
has two methods, one allows a reference and the other accepts a shared pointer.
Clearly, creating a share pointer via a reference fails to deliver the desired result. See Segmentation fault at line [5].
I am aware of the advice that creating a shared pointer out of a reference shall be avoided. But I don't quite understand why creating a shared pointer via reference in this way fails. What should be the proper approach if this is possible at all?
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Animal {
private:
std::string type = "Animal";
public:
Animal() {}
virtual std::string get_type() { return type; }
};
class Bird : public Animal {
private:
std::string type = "Bird";
public:
Bird() {}
std::string get_type() { return type; }
};
class Mammal : public Animal {
private:
std::string type = "Mammal";
public:
Mammal() {}
std::string get_type() { return type; }
};
class AnimalList {
public:
std::vector<std::shared_ptr<Animal> > list; // [1]
void push(Animal& ref) { // [2]
list.push_back(std::make_shared<Animal>(ref));
}
void push(std::shared_ptr<Animal> shared_ptr) {
list.push_back(shared_ptr);
}
};
int main() {
Bird magpie;
std::shared_ptr<Mammal> dog_ptr (new Mammal());
AnimalList list;
list.push(magpie); // [3]
list.push(dog_ptr); // [4]
//std::cout << std::dynamic_pointer_cast<Bird>(list.list[0])->get_type() << "\n"; //[5] Segmentation fault
std::cout << std::dynamic_pointer_cast<Mammal>(list.list[1])->get_type() << "\n"; //[6] Mammal
return 0;
}
CodePudding user response:
If you take a reference to an object in the stack and make a shared pointer out of it you tell the system “hey I want two owners to control this.” That means two things will try to kill the object, which is not ok. There should only be one owner for each object. If you want a shared pointer the object must be constructed in a way that nothing else owns it.
Either you need to pass on a shared pointer, make a copy, or create the object dynamically some other way. Then only one thing owns the object and handles its destruction.
CodePudding user response:
Given std::make_shared<Animal>(ref)
, you're creating an std::shared_ptr
pointing an Animal
, which is copied-constructed from ref
. Then the dynamic cast fails and std::dynamic_pointer_cast<Bird>(list.list[0])
returns a null pointer.
You need to create the std::shared_ptr
pointing to Bird
from the beginning. E.g.
void push(Bird& ref) { // [2]
list.push_back(std::make_shared<Bird>(ref));
}
BTW: The destructor of Animal
should be marked as virtual
.