25

I have a function which takes a shared_ptr<MyClass>. In some member function memfun of MyClass, I need to pass this to that function. But if I write

void MyClass:memfun()
{
   func(shared_ptr<MyClass>(this))
}

I am assuming that after the call has ended the reference count will reach 0 and this will be attempted to be destroyed, which is bad.

Then I remembered that there this class enable_shared_from_this with the function shared_from_this.

So now I am going to use the following:

class MyClass: public enable_shared_from_this<MyClass>
{
    void MyClass:memfun()
    {
       func(shared_from_this());
    }
};

Questions are:

1) Is is absolutely impossible to use the functionality without deriving from enable_shared_from_this?
2) Does deriving from enable_shared_from_this mean that calling memfun on an object with automatic storage duration will result in something bad? E.g.

 int main()
 { 
    MyClass m;   //is this OK?
    m.memfun();  // what about this?
 }

3) If I derive from MyClass, will the enable_shared_from_this functionality be correctly inherited or do I need to derive again? That is,

class MyCoolClass: public Myclass
{
   void someCoolMember
   {
      someCoolFuncTakingSharedPtrToMyCoolClass(shared_from_this());
   }
}

Is this OK? Or correct is the following?

 class MyCoolClass: public Myclass, public enable_shared_from_this<MyCoolClass>
    {
       void someCoolMember
       {
          someCoolFuncTakingSharedPtrToMyCoolClass(enable_shared_from_this<MyCoolClass>::shared_from_this());
       }
    }   

Thanks very much in advance.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434

5 Answers5

27

1) It depends on what you mean by "do this" as to whether or not you can. You can always construct a shared_ptr from a raw pointer such as this, but it won't share the reference count with another shared_ptr instance that was separately constructed from a raw pointer. You will thus need to use a custom deleter on one or other instance to avoid double deletions, but unless you take great care then you may end up with dangling shared_ptr instances due to the object being deleted through one, but still accessible from another.

shared_from_this enables you to guarantee that if you have one shared_ptr instance to your object then you can construct another without copying the first, and that these instances will share the reference count. You could achieve this by storing a weak_ptr as a class member, and setting that value when you first allocate a shared_ptr to your object.

2) Calling shared_from_this() requires that there is at least one shared_ptr instance already pointing to your object. If you use it on an automatic object without a shared_ptr instance with a custom deleter then you will get bad stuff happening.

3) If you derive from your class then the enable_shared_from_this functionality will give you a shared_ptr to the base class (the one that derived from enable_shared_from_this). You could then use static_pointer_cast or dynamic_pointer_cast to cast the result of shared_from_this() to a pointer to the derived class.

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
12

The important question here is why does the function take the argument through a shared_ptr. Does it store the pointer internally for later use? Does it only use it for the duration of the call? Why is the ownership diluted among the caller and the callee?

Some answers suggest that you provide a no-op deleter if you are going to pass a stack allocated object into the function, but if the function is actually storing the shared_ptr for later use, it might be the case that by the time it gets around to it, the locally allocated object is no longer in the stack and you trigger UB. Having the no-op deleter shared_ptr will allow the call, but the semantics will not be correct.

If the function does not store the shared_ptr for later use, what was the design decision that led to that API? If you can change the function (and there is no impending reason), make it receive the argument by reference and you will have a friendlier interface that does not impose a shared_ptr for no reason.

If at the end you determine that you can guarantee that the object in the stack will be alive for the whole duration of the process triggered by that function call, then and only then use the no-op deleter.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
4

1) No, it's not impossible to do this without shared_from_this. You can simply construct a shared_ptr with a no-op deleter:

void do_nothing(MyClass*) {}

void MyClass:memfun()
{
    func(shared_ptr<MyClass>(this, do_nothing));
}

Seeing as you don't actually seem to need shared_from_this after all, I'm going to skip the next two parts of your question.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • boost already provide a no-op deleter although it is hidden away in boost::serialization for some reason – CashCow Mar 08 '11 at 13:14
  • Really? Where? We'd like to know here, and also on this question: http://stackoverflow.com/questions/2710765/no-op-deallocator-for-boostshared-ptr and on the Boost bug tracker: https://svn.boost.org/trac/boost/ticket/1913 – John Zwinck Mar 08 '11 at 13:15
  • 1
    "_a shared_ptr with a no-op deleter_" Every time I see a suggestion to use a no-op deleter, I ask: "What is the point of using a smart pointer here? Would a normal pointer be sufficient?" I am _not_ saying that any design involving a no-op deleter is absurd, but that any such design needs a specific justification. Of course, when the use of the smart pointer cannot be explained, a normal should be used instead. – curiousguy Oct 07 '11 at 10:35
  • Yes, there are instances when you may either need to create an object "on the fly" for which the holder will need to delete when it is finished with it, or pass it an already existing one, in which case you do not want the user to delete it. The person who supplies the pointer knows that should be done with it when finished. That logic is put into the "deleter". The other side just does what it's told and "invokes" the deleter, albeit implicitly. – CashCow Oct 09 '11 at 00:02
  • @CashCow You mean that the `shared_ptr` is used by the callee to tell the caller, before the callee returns, that the callee doesn't need the object any more? – curiousguy Oct 09 '11 at 00:29
  • 1
    @curiousguy The caller provides the object and knows it should continue to exist after the callee returns. The callee does not know that. – CashCow Oct 09 '11 at 18:38
  • A no-op deleter is a bad idea particularly because it almost certainly is being used to get around the semantic promise of shared_ptr that the destructor will not be called until all holders are done. Any client in the call stack which holds on to the ptr beyond the call will likely cause a crash. It's often impossible to know that no one in the call chain will hold on to it. – Catskul Feb 12 '20 at 22:18
2

If you have an object with automatic storage and a function that requires shared_ptr and you know that the lifetime of your object will be long enough for the duration of the function and that it does not store the shared_ptr anywhere, then you can pass it with a no-op deleter.

This is useful for static objects. If it really does have local automatic storage, you need to ask yourself why the function is taking shared_ptr. Does it store them?

There is another lesser-known constructor to shared_ptr for an object that is a member of another reference-counted object. You can actually create a shared_ptr with the shared_ptr from the outer object and the pointer from the inner object.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • "_you know that the lifetime of your object will be long enough for the duration of the function and that it does not store the shared_ptr anywhere_" Then what is the point of the "smart" pointer in this case? – curiousguy Oct 07 '11 at 10:48
  • 1
    The function you are calling requires one. Perhaps it is sometimes called with one that has a limited lifetime, i.e. it is not guaranteed that the callee will hold on to it. – CashCow Oct 08 '11 at 23:53
  • "_The function you are calling requires one._" I know, and I am wondering why. "_Perhaps it is sometimes called with one that has a limited lifetime, i.e. it is not guaranteed that the callee will hold on to it._" I don't understand. – curiousguy Oct 09 '11 at 00:24
-3

In addition with David Rodríguez - dribeas, shared pointer isn't recommended by google

It maintains reference count internally, so making it work correctly, InterlockedIncrement and InterlockedDecrement are used, these two functions are really slower than normal ++ and --.

You should check this object ownership truly need be shared with others, per my experience, shared pointer could be avoided in most cases.

Chang
  • 3,953
  • 2
  • 30
  • 43
  • You didn't answer the question being asked. And smart pointers shouldn't be avoided. – Yixing Liu Aug 03 '18 at 12:57
  • "isn't recommended" doesn't really hold up in CS. There's uses for everything, and there are situations where shared_ptr is not only a good choice, but the only one, or at least the most correct one. In addition to that - your link 404s – Folling Nov 29 '19 at 13:24