views:

201

answers:

3

How should I avoid using the "this" pointer in conjunction with smart pointers? Are there any design patterns/general suggestions on working around this?

I'm assuming combining the two is a no-no since either:

  1. you're passing around a native pointer to a smart pointer-managed object which defeats the point of using the smart pointers in the first place,
  2. if you wrap the "this" pointer in a smart pointer at use, e.g. "return CSmartPtr(this);", you've effectively set up multiple smart pointers managing the same object so the first one to have a reference count of zero will destroy the object from under the other, or
  3. if you have a member variable holding the value of CSmartPtr(this) to return in these cases, it'll ultimately be a circular reference that results in the reference count always being one.

To give a bit of context, I recently learned about the negative implications of combining STL containers with objects (repeated shallow copying, slicing when using containers of a base class, etc), so I'm replacing some usage of these in my code with smart pointers to the objects. A few objects pass around references to themselves using the "this" pointer, which is where I'm stuck.

I've found smart pointers + “this” considered harmful? asked on a somewhat similar problem, but the answer isn't useful as I'm not using Boost.

Edit: A (very contrived) example of what I'd been doing would be

...::AddToProcessingList(vector<CSmartPtr> &vecPtrs)
{
    vecPtrs.push_back(CSmartPtr(this));
}
+2  A: 

Most smart pointer frameworks provide a means around this. For instance, Boost.SmartPtr provides an enable_shared_from_this<T> CRTP class which you use as a base class, and then you can make sure your shared pointer doesn't result in two pointers to the same object.

coppro
+2  A: 

Combining the two can be done, but you always need to keep clear in your mind about the ownership issues. Generally, the rule I follow is to never convert a raw pointer to a smart pointer (with ownership), unless you are sure that you are taking ownership of the object at that point. Times when this is safe to do should be obvious, but include things like:

  1. you just created the object (via new)
  2. you were passed the object from some external method call where the semantic is obviously one of ownership (such as an add to a container class)
  3. the object is being passed to another thread

As long as you follow the rule, and you don't have any ambiguous ownership situations, then there should be no arising issues.

In your examples above, I might look on them as follows:

  1. you're passing around a native pointer to a smart pointer-managed object which defeats the point of using the smart pointers in the first place

In this case, since you are passing around the native pointer, you can assume by my rule that you are not transferring ownership so you can not convert it to a smart pointer

  1. if you wrap the "this" pointer in a smart pointer at use, e.g. "return CSmartPtr(this);", you've effectively set up multiple smart pointers managing the same object so the first one to have a reference count of zero will destroy the object from under the other

This is obviously illegal, since you have said that the object is already owned by some other smart pointer.

  1. if you have a member variable holding the value of CSmartPtr(this) to return in these cases, it'll ultimately be a circular reference that results in the reference count always being one.

This can actually be managed, if some external code implicitly owns the member variable - this code can call some kind of close() method at some point prior to the object being released. Obviously on reflection, that external code owns the object so it should really have a smart pointer itself.

The boost library (which I accept you have said you are not using) makes these kind of issues easier to manage, because it divides up the smart pointer library along the different types of ownership (scoped, shared, weak and so on).

1800 INFORMATION
+1  A: 

One fairly robust solution to this problem is the use of intrusive smart pointers. To instantiate RefCountedPtr<T>, T should derive from RefCount. This allows construction of RefCountedPtr<T> from this, because this->RefCount::m_count holds the single counter that determines the lifetime.

Downside: you have an unused RefCount when you put the object on the stack.

MSalters