3

I'm trying to write simple parent-child classes, and trying to use smart pointers for it.

I heard that it's bad to use shared_from_this() in constructors, so i put it in connect() method. But when i call weak_from_this() in this method, it returns empty weak pointer.

I have no idea what i'm doing wrong, please help:)

#include <iostream>
#include <memory>
#include <list>
#include <sstream>

using namespace std;

class Base : protected enable_shared_from_this<Base> {
public:
    weak_ptr<Base> parent;

    virtual void connect() {}
    virtual void replaceChild(shared_ptr<Base> oldChild, shared_ptr<Base> newChild) {}

    void setParent(weak_ptr<Base> parent) {
        this->parent = parent; // <-- passed to here, but always empty
    }

    virtual string toString() { return "Base"; }
};

class Item : public Base {
public:
    string toString() { return "Item"; }
};

class Item2 : public Base {
public:
    void connect() {
        if (auto parent = this->parent.lock()) {
            parent->replaceChild(this->shared_from_this(), make_shared<Item>());
        }
    }

    string toString() { return "Item2"; }
};

class Container : public Base {
public:
    list<shared_ptr<Base>> items;

    void connect() {
        for (auto &item : items) {
            weak_ptr<Base> ptr = this->weak_from_this();
            item->setParent(ptr); // <-- ptr is empty here, according to debugger breakpoint
            item->connect();
        }
    }

    void replaceChild(shared_ptr<Base> oldChild, shared_ptr<Base> newChild) {
        for (auto &item : items) {
            if (item == oldChild) {
                item = newChild;
                item->setParent(this->weak_from_this());
                item->connect();
                break;
            }
        }
    }

    string toString() {
        ostringstream ss;
        ss << "Container(";
        for (auto item : items) {
            if (item != *items.begin()) ss << ", ";
            ss << item->toString();
        }
        ss << ")";
        return ss.str();
    }
};

int main(int argc, char ** argv) {
    shared_ptr<Base> base = make_shared<Container>();

    if (auto cont1 = dynamic_pointer_cast<Container>(base)) {
        cont1->items.push_back(make_shared<Item2>());
        cont1->items.push_back(make_shared<Base>());
        cont1->items.push_back(make_shared<Container>());
    }

    base->connect();

    cout << base->toString() << endl;

    return 0;
}

Expected output:

Container(Item, Base, Container())

Actual output:

Container(Item2, Base, Container())

because Item2 has empty parent.

  • 9
    Change `protected enable_shared_from_this` to `public enable_shared_from_this` and then you will see desired output. [LIVE DEMO](https://coliru.stacked-crooked.com/a/1a5cccf064c5a25c). – rafix07 Jun 02 '19 at 12:52
  • Turn on warnings; this code has lots of problems. You may want to use the "passkey idiom" to make the constructors effectively private, and provide a static factory function in the class (otherwise people may make non-shared objects on the stack or `new` up their own non-shared objects, and the object is self-aware it ought to be shared). https://stackoverflow.com/questions/52179063/make-shared-doesnt-play-along-with-enable-shared-from-this/52179200#52179200 – Eljay Jun 02 '19 at 14:49

1 Answers1

4

Thanks to @rafix07!!! Changed to protected to public, and it works!!!