Home > Mobile >  Fail to make a shared pointer from a reference
Fail to make a shared pointer from a reference

Time:11-30

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.

  • Related