views:

64

answers:

2

Hi!

I'm working on a couple of classes and I'm wondering how I can use a normal member in my application class, where the member needs to use shared_from_this()?

Here is some code to clarify what I mean (see comments)

class Observable {
public:
    void addObserver(boost::shared_ptr<Observer> observer) {
        // add to a list
    }
};

class Observer {
public:
    virtual void onUpdate() = 0;
};

class MyObservableType : public Observable {
};

class ApplicationModel : public Observer {
private:
    MyObservableType mot;
public:
    void setup() {
    // how do I pass this as a boost::shared_ptr, as ApplicationModel is not 
    // a boost::shared_ptr in the Application class this using a call to 
    // "shared_from_this()" (and inheriting public shared_from_this<ApplicationModel>
        mot.addObserver([shared_from_this]) 
    }
};

class Application {
private:
    ApplicationModel model;
public:
    void setup() {
        model.
    }
};
A: 

You can't. shared_from_this() requires that your object be allocated dynamically via a shared_ptr.

See this page of the documentation, which states:

Requires: enable_shared_from_this must be an accessible base class of T. *this must be a subobject of an instance t of type T . There must exist at least one shared_ptr instance p that owns t.

So you would need to alter your code to have any instances of ApplicationModel be "owned" by a shared_ptr. For example:

class ApplicationModel : 
    public Observer,
    public boost::enable_shared_from_this<ApplicationModel>
{
    //...
    void setup() {
        mot.addObserver(shared_from_this());
    }
};

class Application {
private:
    // Application object must initialize this somewhere
    boost::shared_ptr<ApplicationModel> model;  
    //...
};
bjlaub
+1  A: 

You have three solutions to this problem:

First solution: force the application to create a shared_ptr by making its constructor private. This is what I would recommend to do for any class that derivates from enable_shared_from_this

class ApplicationModel : public Observer, public boost::enable_shared_from_this<ApplicationModel> {
private:
    ApplicationModel(); // private constructor

    MyObservableType mot;
public:
    // an instance of this class can only be created using this function
    static boost::shared_ptr<ApplicationModel> buildApplicationModel() {
        return boost::make_shared<ApplicationModel>();
    }

    void setup() {
        mot.addObserver(shared_from_this()) ;
    }
};

Second solution: change your code design. You should not ask the ApplicationModel to register itself to the Observable, but do it yourself. This way the ApplicationModel doesn't enforce anything, but if its owner wants to call addObservable, it has to create a shared_ptr. This is more or less what is called dependency injection.

class Application {
private:
    boost::shared_ptr<ApplicationModel> model;
    MyObservableType mot;
public:
    void setup() {
        model = boost::make_shared<ApplicationModel>();
        mot.addObserver(model);
    }
};

EDIT: Third solution: use a dummy shared_ptr, like this:

class ApplicationModel : public Observer {
private:
    boost::shared_ptr<ApplicationModel> myself;

    MyObservableType mot;

public:
    void setup() {
        mot.addObserver(myself) ;
    }

    ApplicationModel() {
        myself = boost::shared_ptr<ApplicationModel>(this, [](ApplicationModel*) {});
    }

    ~ApplicationModel() {
        mot.removeObserver(myself);
        assert(myself.unique());
    }
};

The idea is to create a shared_ptr to this and to tell shared_ptr not to call the destructor (here I use an empty lambda function but you can easily create an inline structure). This is a hack and you shouldn't do so.

Tomaka17