Should I mix up the "this" pointer and smart pointers?

How can I avoid using the "this" pointer in conjunction with smart pointers? Are there any design patterns / general guidelines for working with this?

I am guessing that combining the two is no-no, since:

  • you are passing your own pointer to a smart pointer object that hits the point of using smart pointers in the first place,
  • if you cast the "this" pointer to the smart pointer when using eg. "return CSmartPtr (this);", you have effectively set up multiple smart pointers manipulating the same object, so the first one that has a reference count will destroy the object from under the other, or
  • If you have a member variable holding the CSmartPtr (this) value to return in these cases, it will end up as a circular reference, resulting in the reference count always being one.

To give a little context, I recently learned about the negative consequences of combining STL containers with objects (repeated shallow copying, slicing when using base class containers, etc.), so I am replacing some of their usage in my code with smart pointers to objects. Several objects pass references to themselves using the "this" pointer, which I am stuck with.

I found smart pointers + "this", considered malicious , given for some similar problem, but the answer is not helpful since I am not using nudges.

Edit: A (very contrived) example of what I was doing would be

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

      

+2


source to share


3 answers


A combination of the two can be done, but you always need to be clear about your ownership issues. As a general rule of thumb, the rule I follow is to never convert a raw pointer to a smart pointer (with ownership), unless you are sure you will acquire ownership of the object at that point. The times when it is safe to do so should be obvious, but include things like:

  • you just created an object (via new

    )
  • you have been passed an object from some outer method call where the semantics are obviously property (e.g. add

    for a container class)
  • the object is passed to another thread

As long as you follow the rule and you do not have any ambiguous situations with the owner, then there should be no problems that arise.

In the examples above, I can look at them like this:

  • you are passing your own pointer to a smart pointer object that hits the point of using smart pointers in the first place.

In this case, since you are looping around your own pointer, you can assume by my rule that you do not transfer ownership so as not to convert it to a smart pointer



  1. if you cast the "this" pointer to the smart pointer when using eg. "return CSmartPtr (this);" you have effectively created multiple smart pointers manipulating the same object, so the first one that has a reference count will destroy the object from under the other

This is clearly illegal since you said the object already belongs to another smart pointer.

  1. If you have a member variable holding the CSmartPtr (this) value to return in these cases, it will end up as a circular reference, resulting in the reference count always being one.

This can actually be controlled if some external code implicitly owns the member variable - that code might call some method close()

at some point before the object is released. Obviously, when reflected, this outer code owns the object, so it should really have its own smart pointer.

The acceleration library (which I understand you said you weren't using) makes it easier to manage such problems as it divides the smart pointer library into different ownership types (scope, general, weak, etc.)).

+2


source


Most smart pointer frameworks provide a means around this. For example, Boost.SmartPtr provides a enable_shared_from_this<T>

CRTP class that you use as your base class, and then you can ensure that your shared pointer does not lead to two pointers to the same object.



+2


source


One fairly reliable solution to this problem is the use of intrusive smart pointers. To instantiate RefCountedPtr<T>

T should be inferred from RefCount. This allows you to build RefCountedPtr<T>

from this

because it this->RefCount::m_count

contains a single counter that determines the lifetime.

Downside: You have an unused RefCount when you push an object onto the stack.

+1


source







All Articles