Search code examples
c++pointersc++11shared

Crash with shared_ptr


Can someone explain to me why the crash here?

#include <memory>
#include <functional>

struct Teacher : std::enable_shared_from_this<Teacher> {
    struct Timetable;
    std::shared_ptr<Timetable> timetable;
    Teacher (int n) : timetable(std::make_shared<Timetable>(*this, n)) {}
};

struct Period {
    std::shared_ptr<Teacher> teacher;
    Period (const std::shared_ptr<Teacher>& t) : teacher(t) {}
};

struct Teacher::Timetable {
    Teacher& teacher;
    std::shared_ptr<Period> homeForm;
    Timetable (Teacher& t, int n) : teacher(t),  // Assume something is done with n.
        homeForm( std::make_shared<Period>(teacher.shared_from_this()) ) {}  // Crashes.
//      homeForm( std::make_shared<Period>(std::shared_ptr<Teacher>(&teacher)) ) {}  // Also crashes.
};

int main() {
    std::shared_ptr<Teacher> teacher = std::make_shared<Teacher>(3);
}

Isn't teacher.shared_from_this() allowed here since std::shared_ptr<Teacher> teacher is a shared_ptr already? If not, how do I initialize homeForm properly?


Solution

  • The problem is that your code calls shared_from_this before the std::shared_ptr<Teacher> is fully constructed. It somehow makes sense that the Teacher sub-object has to be constructed before the smart pointer is fully constructed.

    If you change the code like this,

    struct Teacher : std::enable_shared_from_this<Teacher> {
        struct Timetable;
        std::shared_ptr<Timetable> timetable;
        Teacher() {}
        void init(int n) { this->timetable = std::make_shared<Timetable>(*this, n); }
    };
    
    // Everything else unchanged
    
    int main() {
        std::shared_ptr<Teacher> teacher = std::make_shared<Teacher>();
        teacher->init(3);
    }
    

    it will run fine.

    Note that I'm not recommending this as a refactoring. Constructors should fully initialize objects whenever possible. Looking at your code, it seems to me that you might want to consider re-structuring it somewhat more radical. Do you really need all this cross-referencing shared pointers?