简体   繁体   中英

shared_ptr deletes the object

void ClassName::LocalMethod( )
{
    boost::shared_ptr<ClassName> classNamePtr( this );

    //some operation with classNamePtr
    return;
}

Here the object is getting released when it returns from LocalMethod() since classNamePtr is out of scope. Isn't the shared_ptr smart enough to know that the ClassName object is still in scope and not to delete it?

What does it mean to create a shared_ptr to an object? It means that the holder of the shared_ptr now assumes ownership over the object. Ownership meaning that the object will be deleted when he so desires. When the holder of the shared_ptr destroys its shared_ptr , that will cause the object to potentially be destroyed, assuming that there are no other shared_ptr s to that object.

When a shared_ptr is a member of a class, that means that the lifetime of the object pointed to by the shared_ptr is at least as long as the object that the shared_ptr is a member of. When a shared_ptr is on the stack, this means that the lifetime of the object that the shared_ptr is pointing to will be at least as long as the scope it was created in. Once the object falls off the stack, it may be deleted.

The only time you should ever take a pointer and wrap it into a shared_ptr is when you are allocating the object initially . Why? Because an object does not know whether it is in a shared_ptr or not. It can't know. This means that the person who creates the original shared_ptr now has the responsibility to pass it around to other people who need to share ownership of that memory. The only way shared ownership works is through the copy constructor of shared_ptr . For example:

shared_ptr<int> p1 = new int(12);
shared_ptr<int> p2 = p1.get();
shared_ptr<int> p3 = p1;

The copy constructor of shared_ptr creates shared ownership between p1 and p3 . Note that p2 does not share ownership with p1 . They both think they have ownership over the same memory, but that's not the same as sharing it. Because they both think that they have unique ownership of it.

Therefore, when the three pointers are destroyed, the following will happen. First, p3 will be destroyed. But since p3 and p1 share ownership of the integer, the integer will not be destroyed yet. Next, p2 will be destroyed. Since it thinks that it is the only holder of the integer, it will then destroy it.

At this point, p1 is pointing to deleted memory. When p1 is destroyed, it thinks that it is the only holder of the integer, so it will then destroy it. This is bad , since it was already destroyed.

Your problem is this. You are inside an instance of a class. And you need to call some functions of yours that take a shared_ptr . But all you have is this , which is a regular pointer. What do you do?

You're going to get some examples that suggest enable_shared_from_this . But consider a more relevant question: "why do those functions take a shared_ptr as an argument?"

The type of pointer a function takes is indicative of what that function does with its argument. If a function takes a shared_ptr , that means that it needs to own the pointer. It needs to take shared ownership of the memory. So, look at your code and ask whether those functions truly need to take ownership of the memory. Are they storing the shared_ptr somewhere long-term (ie: in an object), or are they just using them for the duration of the function call?

If it's the latter, then the functions should take a naked pointer, not a shared_ptr . That way, they cannot claim ownership. Your interface is then self-documenting: the pointer type explains ownership.

However, it is possible that you could be calling functions that truly do need to take shared ownership. Then you need to use enable_shared_from_this . First, your class needs to be derived from enable_shared_from_this . Then, in the function:

void ClassName::LocalMethod()
{
    boost::shared_ptr<ClassName> classNamePtr(shared_from_this());

    //some operation with classNamePtr
    return;
}

Note that there is a cost here. enable_shared_from_this puts a boost::weak_ptr in the class. But there is no virtual overhead or somesuch; it doesn't make the class virtual. enable_shared_from_this is a template, so you have to declare it like this:

class ClassName : public boost::enable_shared_from_this<ClassName>

Isn't the shared_ptr smart enough to know that the ClassName object is still in scope and not to delete it?

That's not how shared_ptr works. When you pass a pointer while constructing a shared_ptr , the shared_ptr will assume ownership of the pointee (in this case, *this ). In other words, the shared_ptr assumes total control over the lifetime of the pointee by virtue of the fact that the shared_ptr now owns it. Because of this, the last shared_ptr owning the pointee will delete it.

If there will be no copies of classNamePtr outside of ClassName::LocalMethod() , you can pass a deleter that does nothing while constructing classNamePtr . Here's an example of a custom deleter being used to prevent a shared_ptr from deleting its pointee . Adapting the example to your situation:

struct null_deleter // Does nothing
{
    void operator()(void const*) const {}
};

void ClassName::LocalMethod() 
{
    // Construct a shared_ptr to this, but make it so that it doesn't
    // delete the pointee.
    boost::shared_ptr<ClassName> classNamePtr(this, null_deleter()); 
    // Some operation with classNamePtr 

    // The only shared_ptr here will go away as the stack unwinds,
    // but because of the null deleter it won't delete this.
    return; 
}

You can also use enable_shared_from_this to obtain a shared_ptr from this . Note that the member function shared_from_this() only works if you have an existing shared_ptr already pointing to this .

class ClassName : public enable_shared_from_this<ClassName> 
{ 
public: 
    void LocalMethod()
    { 
        boost::shared_ptr<ClassName> classNamePtr = shared_from_this(); 
    } 
} 

// ...

// This must have been declared somewhere...
shared_ptr<ClassName> p(new ClassName);
// before you call this:
p->LocalMethod();

This is the more appropriate, "official" method and it's much less hackish than the null deleter method.

It could also be that you don't actually need to create a shared_ptr in the first place. What goes into the section commented //some operation with classNamePtr ? There might be an even better way than the first two ways.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM