37

Simple question here: are you allowed to explicitly delete a boost::shared_ptr yourself? Should you ever?

Clarifying, I don't mean delete the pointer held by the shared_ptr. I meant the actual shared_ptr itself. I know most people suggest to not do it, so I was just wondering if it's OK to explicitly do it.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
meteoritepanama
  • 6,092
  • 14
  • 42
  • 55
  • 1
    Certainly you are allowed to delete a (dynamically allocated) *shared_ptr*; deleting it's contents is an entirely different question ;-) – eq- Sep 07 '12 at 16:26
  • I think you are looking for `weak_ptr`. – Yakov Galka Sep 07 '12 at 16:35
  • 8
    Dynamically allocating a `shared_ptr` defeats the purpose of using a `shared_ptr`. – Dan Sep 07 '12 at 17:07
  • 6
    Why on earth would you allocate a `shared_ptr` on the heap? – mfontanini Sep 07 '12 at 17:20
  • 1
    Your question is no different from "If I allocate an object with new, can I explicitly delete it?" ... the answer, of course, is yes -- you can and you should. It doesn't matter what the type of the object is. – Jim Balter Nov 01 '14 at 00:28
  • @mfontanini Note that I'm sure you do that all the time, although in pretty much all cases those `shared_ptr` will be part of another object variable members. – Alexis Wilke Sep 23 '18 at 19:48

5 Answers5

41

Your question isn't clear. If you've allocated a shared_ptr dynamically then you're certainly allowed to delete it whenever you want.

But if you're asking whether you're allowed to delete whatever object is being managed by the shared_ptr, then the answer is ... it depends. If shared_ptr::unique returns true, then calling shared_ptr::reset will delete the managed object. However, if shared_ptr::unique returns false, it means there are more than one shared_ptrs sharing ownership of that object. In this case a call to reset will only result in the reference count being decremented by 1, actual deletion of the object will take place when the last shared_ptr managing that object either goes out of scope or is itself reset.

EDIT:
After your edit, it seems you are asking about deleting a dynamically allocated shared_ptr. Something like this:

auto sp = new boost::shared_ptr<int>( new int(42) );

// do something with sp

delete sp;

This is allowed and will work as expected, although it would be an unusual use case. The only caveat is that if in between the allocation and deletion of sp you create another shared_ptr that shares ownership of the object, deleting sp will not result in deletion of the object, that will only happen when the reference count for the object goes to 0.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
8

[Edit: you can delete a shared_ptr if and only if it was created with new, same as any other type. I can't think why you'd create a shared_ptr with new, but there's nothing stopping you.]

Well, you could write delete ptr.get();.

Doing so leads almost inevitably to undefined behavior either when the other shared owners use their shared_ptr to access the now-deleted object, or the last shared_ptr to the object is destroyed, and the object gets deleted again.

So no, you shouldn't.

The purpose of shared_ptr is to manage an object that no one "person" has the right or responsibility to delete, because there could be others sharing ownership. So you shouldn't ever want to, either.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
4

If you want to simulate the count decrement, you can do it manually on the heap like so:

int main(void) {
    std::shared_ptr<std::string>* sp = new std::shared_ptr<std::string>(std::make_shared<std::string>(std::string("test")));
    std::shared_ptr<std::string>* sp2 = new std::shared_ptr<std::string>(*sp);
    delete sp;

    std::cout << *(*sp2) << std::endl;    // test
    return 0;
}

Or on the stack using std::shared_ptr::reset() like so:

int main(void) {
    std::shared_ptr<std::string> p = std::make_shared<std::string>(std::string("test"));
    std::shared_ptr<std::string> p2 = p;
    p.reset();

    std::cout << *p2 << std::endl;    // test
    return 0;
} 

But it's not that useful.

wulfgarpro
  • 6,666
  • 12
  • 69
  • 110
3

You cannot force its reference count to zero, no.

Think about what would be required for that to work. You would need to go to each place the shared_ptr is used and clear it.

If you did force the shared pointer to delete and set it to NULL, it would be just like a weak_ptr. However, all those places in the code using that shared_ptr are not ready for that and expect to be holding a valid pointer. They have no reason to check for NULL, and so those bits of code would crash.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
2

Expliticly deleting comes in handy in some (very?) rare cases.

In addition to explicitly deleting, sometimes you HAVE to explicitly destruct a shared pointer when you are 'deleting' it!

Things can get weird when interfacing with C code, passing a shared_ptr as an opaque value.

For example I have the following for passing objects to and from the Lua scripting language which is written in C. (www.lua.org)

static void push( lua_State *L, std::shared_ptr<T> sp )
{
    if( sp == nullptr ) {
        lua_pushnil( L );
        return;
    }

    // This is basically malloc from C++ point of view.
    void *ud = lua_newuserdata( L, sizeof(std::shared_ptr<T>));

    // Copy constructor, bumps ref count.
    new(ud) std::shared_ptr<T>( sp );

    luaL_setmetatable( L, B::class_name );
}

So thats a shared_ptr in some malloc'd memory. The reverse is this... (setup to be called just before Lua garbage collects an object and 'free's it).

static int destroy( lua_State *L )
{
    // Grab opaque pointer
    void* ud = luaL_checkudata( L, 1, B::class_name );

    std::shared_ptr<T> *sp = static_cast<std::shared_ptr<T>*>(ud);

    // Explicitly called, as this was 'placement new'd
    // Decrements the ref count
    sp->~shared_ptr();

    return 0;
}
Nigel Atkinson
  • 660
  • 6
  • 11