1

Is there any way to destroy a std::shared_ptr from within a function? In the following example if I set the sptr to nullptr in main() it "destroys" the object so calling it's Print() fails. However, doing so inside the Delete() function doesn't do this. Guessing it increments it's ref count as it's passed in. I get that's the idea, BUT is there any way to override that and decrease that ref count inside Delete() so the obj = nullptr destroys it in this case?

I get this isn't normally what you'd want to do, but this is a case where an external scripting language needs to be able to call a function to actually destroy the pointed to object so having a Delete(obj) would be ideal.

#include <memory>
#include <string>
#include <iostream>

using namespace std;

class Test{
private:
    string name;
public:
    Test(string _name) { name = _name; }
    void Print() const { cout << name; }
};

void Delete(std::shared_ptr<Test> obj)
{
    obj = nullptr;
}

int main(){
    shared_ptr<Test> sptr(new Test("Test"));

    Delete(sptr);
    //sptr = nullptr;

    sptr.get()->Print();
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
user441521
  • 6,942
  • 23
  • 88
  • 160
  • 1
    Pass it by reference? – Amadeusz Mar 28 '18 at 12:49
  • Tried void Delete(shared_ptr* obj){} , Delete(&sptr);. didn't work – user441521 Mar 28 '18 at 12:50
  • `obj.reset()` instead of `obj = nullptr;` anyway no sense for such stupid things. – Victor Gubin Mar 28 '18 at 12:51
  • That's not a reference, that's a pointer, and you've set the pointer to the smart pointer to `nullptr`, not the smart pointer itself. Try `shared_ptr&` instead. (That *is* a reference type) And the idiomatic way is to use the `reset()` method rather than assigning null, but that's not a correctness problem as such. – pmdj Mar 28 '18 at 12:51
  • @pmdj Correct, that did work. Thanks. – user441521 Mar 28 '18 at 12:53
  • @VictorGubin obj.reset() didn't work instead of obj = nullptr. – user441521 Mar 28 '18 at 12:53
  • 1
    `obj->reset();` would have worked with the pointer type. Or `obj.reset()` with the reference type. Neither does what you want with passing the smart_ptr by value. – pmdj Mar 28 '18 at 12:54
  • 1
    @user441521 `void Delete(shared_ptr& obj) { obj.reset(); } ` – Victor Gubin Mar 28 '18 at 12:54
  • The whole point of a shared pointer is that you can't delete something otherś may still be using. If you **only** have the one in `main()` then why use a `shared_ptr`? Why not use a `unique_ptr`? – Galik Mar 28 '18 at 13:40
  • One of the other benefits is that you can check to see if it's still valid before calling anything on it to avoid a crash. When we are mixing C++ and a scripting language this can have benefits. Let's say 2 towers in a tower defense game are locked in on 1 enemy unit. They both have references to it. When one kills it and deletes the unit the other doesn't know this so it tries to call the units Hurt() function and the entire thing blows up. If we could check if the unit was null/nil first then we'd know if it was still there or not. These help in that situation. – user441521 Mar 28 '18 at 13:44

2 Answers2

2

Passing by value void Delete(shared_ptr<Test> obj) makes a copy, this increments counter of the sptr, so assigning of nullptr decrements counter, but doesn't not destroy underlying object. You can pass by reference instead void Delete(shared_ptr<Test>& obj).

Yola
  • 18,496
  • 11
  • 65
  • 106
  • This does work. Would it be frowned on to call ._Decref() as that seems to do it as well. I'm guessing they are sort of doing the same thing though? – user441521 Mar 28 '18 at 12:55
  • @user441521 Is `_Decref` an implementation defined helper function in `std::shared_ptr`? Don't use it. It is not intended to be used by you. Use the ways documented in the Standard. – Yola Mar 28 '18 at 12:58
  • @user441521 Though, you can try just out of interest, but think first what will happen when `sptr` in `main` will go out of scope? – Yola Mar 28 '18 at 12:59
  • Looks like .reset() is basically doing the same thing. Reduces 1 owner on the object (ref count). I supposed one could loop over how many references there are with use_count() (?) and call reset() that many times to truly remove the object. – user441521 Mar 28 '18 at 13:02
  • 2
    @user441521 don't do this. It is not supposed to be used like this. If you think you really need this, you should check architecture of your application. `shared_ptr` is called shared because ownership is shared. Maybe you can consider using `unique_ptr`. – Yola Mar 28 '18 at 13:05
  • Yep, just keeping the possibilities open. This is more for the idea of a scripting language that is C++ at it's core and how to remove an object from the scripting langauge itself. If a shared_ptr is created in C++ from a CreateObject() in the scripting language, then would need a way to delete that as well from the scripting language. – user441521 Mar 28 '18 at 13:11
1

If you want to desptroy the shared_ptr inside the Delete function. I guess using std::move is the better option.

void Delete(shared_ptr<Test>&& obj) //rvalue reference
{
    obj = nullptr;
}

and call the function using

Delete(std::move(sptr));

using std::move transfers the ownership of resource and cannot be used further inside main after this call.

nayab
  • 2,332
  • 1
  • 20
  • 34
  • *"using `std::move` transfers the ownership of resource"* Not exactly, `std::move` is "just" a cast. All the works happen in `obj = nullptr` (as `obj` is a (rvalue-)reference). – Jarod42 Mar 28 '18 at 13:22