8

OK, I started using shared-pointers and pass shared-pointers as much as possible. No conversion to raw pointers anymore. This works good, except in this specific case:

Suppose we have a class that also is an observer on another class, like this:

class MyClass : public IObserver
   {
   public:
      MyClass (std::shared_ptr<SomeOtherClass> otherClass);
      void DoSomethingImportant();
   private:
      std::shared_ptr<SomeOtherClass> m_otherClass;
   };

This class is used like this in my application:

std::shared_ptr<MyClass> myInstance(new MyClass(otherInstance));
...
myInstance->DoSomethingImportant();

MyClass gets a shared-pointer to another class and stores this in its m_otherClass data member. In the DoSomethingImportant method, the MyClass instance does lots of important things, including registering itself as an observer on m_otherClass, like this:

m_otherClass->registerObserver(this);

The problem is that the registerObserver method is defined like this:

void registerObserver (std::shared_ptr observer);

It expects a shared pointer, but 'this' is a raw pointer, not a shared one.

I see three ways of solving this:

  • Find a trick to convert a normal pointer to a shared pointer (see question convert pointer to shared_ptr), but the answers to that question only suggest to copy the shared-pointers, not on how to actually convert the pointer to a shared pointer.
  • Pass the shared-pointer to ourself to the method, like this: "myInstance->DoSomethingImportant(myInstance);" which seems a bit stupid.
  • Put the observer part into a separate class. This looks like some overkill, and might make the class harder to understand.

This problem makes it obvious that shared-pointers are just an add-on to C++ (I don't think you have the same problem in other languages/environments like C# (or .Net in general) and Java).

Any other suggestions or tricks on how to handle this situation?

Community
  • 1
  • 1
Patrick
  • 22,097
  • 9
  • 57
  • 125
  • 2
    Can't you use `enable_shared_from_this`? (See [Getting a Boost shared_ptr from this](http://stackoverflow.com/questions/142391/getting-a-boostshared-ptr-for-this)) – Luc Touraille Feb 02 '11 at 08:49
  • 3
    Who is downvoting this question and all the answers? Is this really such a stupid question? – Patrick Feb 02 '11 at 09:05
  • I sometimes wonder... if some people are not just simply trying to get a badge http://stackoverflow.com/badges/7/critic?userid=147192 – Matthieu M. Feb 02 '11 at 09:09
  • As other mentioned enable_shared_from_this will solve your problem. However one thing no one mentioned here is that you should be careful since what you are doing is creating a circular reference (i.e. the subject has a shared_ptr to its observer and the observer a shared_ptr to its subject. It is quite easy to create situations where both the subject and the observer become immortal :-). – ds27680 Feb 02 '11 at 09:16
  • @ds27680: you are right and to elaborate: only owners should maintain a shared_ptr, all other users should work with weak_ptr. For observers, you can choose: keep a shared_ptr which must be reset when unregistering or keep a weak_ptr and clean up when not valid anymore. – stefaanv Feb 02 '11 at 10:13

4 Answers4

8

What you need is probably the enable_shared_from_this and shared_from_this facilities. The docs are here

Note that you cannot use shared_from_this until the constructor has fully completed and the object is already owned by another shared_ptr.

struct test : boost::enabled_shared_from_this<test>
{
    test() {
       // shared_from_this(this); // error, still not owned by another shared_ptr
    }
    boost::shared_ptr<test> shared() {
       return shared_from_this(this);
    }
};
int main() {
   test * t = new test;
   // boost::shared_ptr<test> p = t->shared(); // error, not yet owned by other shared_ptr
   boost::shared_ptr<test> owner( t ); 
   boost::shared_ptr<test> p = t->shared();     // [*] ok, "owner" owns the object
}

[*] This part of the example is silly, you could just copy owner into p, instead of calling the method. It is just presented to note when it is ok or not to called shared_from_this inside the test methods.

David Rodríguez - dribeas
  • 192,922
  • 20
  • 275
  • 473
  • This is indeed what I'm looking for. Is there a similar solution for the C++0x shared_ptr? (I'm using Visual Studio 2010, not using Boost) Do you have an idea on the impact on memory footprint? Does it store a shared-pointer to itself or how does this work? – Patrick Feb 02 '11 at 09:01
  • Found it, there is indeed an enable_shared_from_this in Visual Studio 2010. Thanks. – Patrick Feb 02 '11 at 09:03
  • The boost and the upcoming standard libraries are quite similar, if not exactly the same. This goes for the smart pointers (`scoped_ptr` is present in boost, but dropped from the standard), and threading (some details on the semantics of the operations were changed from the original boost::thread...) – David Rodríguez - dribeas Feb 02 '11 at 10:00
4

For observer pattern, the observed object doesn't take the ownership of the observer, why not just using a raw pointer? The life cycle of the observer should be controlled by observer itself.

By using enable_shared_from_this, you introduce cycle dependency for the observer and its observed object. That means if not delete explicitly, the resource will never be released.

Shuo
  • 6,777
  • 3
  • 26
  • 34
0

How about making the constructor private and having a static construction method like this:

class MyClass : public IObserver
   {
   public:
      static std::shared_ptr<MyClass> createObserver(std::shared_ptr<SomeOtherClass> otherClass);
      void DoSomethingImportant();
   private:
      MyClass (std::shared_ptr<SomeOtherClass> otherClass);
      std::shared_ptr<SomeOtherClass> m_otherClass;
   };

Then you could instantiate the observer cleanly in the static method and not have to worry about the this pointer at all.

Rune Aamodt
  • 2,427
  • 1
  • 22
  • 27
  • 1
    This assumes that the observer (actually: all observers) needs to be attached at construction time (which is not always wanted). Also typo: you probably intended to make the createObserver method static. – Patrick Feb 02 '11 at 08:58
0


Can you move the registration step into a seperate method? :

shared_ptr<SomeOtherClass> other(new SomeOtherClass());
shared_ptr<MyClass> my(new MyClass());
// register myself to the observer  
other->registerObserver(my);
my->DoSomethingImportant();

A good design of observer pattern can be implemented with boost::signal and boost::bind libraries. I encourage you to have a look.

Best Regards,
Marcin

Marcin
  • 767
  • 1
  • 6
  • 17