Automatically adding and removing an object from a list - c++

I have a class. When this class is instantiated, I want the instance added to a list. When the object is deleted, I want it removed from the list.
So I give the object a shared pointer to itself. I then have a list of weak pointers to those shared pointers. When an object is created, it creates a shared pointer to itself, makes a weak pointer to that, and puts the weak pointer in a list.
When the object is destroyed, the shared pointer is as well. Whenever I try to access a member in the list, I ensure that it hasn't expired and that its use count isn't 0. Despite this, I still crash when the list member is destroyed. Why? Can I get around it? Here's my SSCCE:
#include <iostream>
#include <memory>
#include <vector>
class test
{
private:
std::shared_ptr<test> self;
public:
int val;
test(int set);
test(test &copy) = delete; // making sure there weren't issues
// with a wrong instance being deleted
};
std::vector<std::weak_ptr<test>> tests;
test::test(int set):
val(set)
{
this->self = std::shared_ptr<test>(this);
tests.push_back(std::weak_ptr<test>(this->self));
}
void printTests()
{
for (auto i = tests.begin(); i != tests.end(); i++)
{
if (i->use_count() == 0 || i->expired())
{
tests.erase(i);
continue;
}
std::cout << i->lock()->val << std::endl;
}
std::cout << std::endl;
}
int main(int argc, char **argv)
{
{
test t(3);
std::cout << "First tests printing: " << std::endl;
printTests();
} // SEGFAULTS HERE
std::cout << "Second tests printing: " << std::endl;
printTests();
return 0;
}
The output of this program is as follows:
First tests printing:
3
Segmentation fault (core dumped)

Your issue is with how you are creating the self pointer:
this->self = std::shared_ptr<test>(this);
When a shared_ptr is created with this constructor, according to the documentation,
When T is not an array type, constructs a shared_ptr that owns the pointer p.
...
p must be a pointer to an object that was allocated via a C++ new expression or be 0
So the issue is that the shared_ptr is taking ownership of your stack object, so when the object gets destructed (and the shared_ptr along with it), shared_ptr is trying to delete your object that is on the stack. This is not valid.
For your use case, if you expect tests to outlive your vector, then you might be able to just store this.

I think the OP is interested in a solution to his original problem even if it uses a different method than the one he attempted. Here is a simple example of how to add an object to a global list when it is constructed, and remove it when it is deleted. One thing to remember: you must call AddList in every constructor you add to your base class. I didn't know whether you want the list to be accessible outside the class or not, so I added getter functions to return non-const iterators to the list.
class MyClass
{
private:
static std::list<MyClass*> mylist;
std::list<MyClass*>::iterator mylink;
// disable copy constructor and assignment operator
MyClass(const MyClass& other);
MyClass& operator = (const MyClass& other);
void AddList()
{
mylink = mylist.insert(mylist.end(), this);
}
void RemoveList()
{
mylist.erase(mylink);
}
public:
MyClass()
{
AddList();
}
virtual ~MyClass()
{
RemoveList();
}
static std::list<MyClass*>::iterator GetAllObjects_Begin()
{
return mylist.begin();
}
static std::list<MyClass*>::iterator GetAllObjects_End()
{
return mylist.end();
}
virtual std::string ToString() const
{
return "MyClass";
}
};
class Derived : public MyClass
{
virtual std::string ToString() const
{
return "Derived";
}
};
std::list<MyClass*> MyClass::mylist;
int main()
{
std::vector<MyClass*> objects;
objects.push_back(new MyClass);
objects.push_back(new MyClass);
objects.push_back(new Derived);
objects.push_back(new MyClass);
for (std::list<MyClass*>::const_iterator it = MyClass::GetAllObjects_Begin(), end_it = MyClass::GetAllObjects_End(); it != end_it; ++it)
{
const MyClass& obj = **it;
std::cout << obj.ToString() << "\n";
}
while (! objects.empty())
{
delete objects.back();
objects.pop_back();
}
}

This line is trouble:
tests.erase(i);
An iterator pointing to the erased element is invalid, and you can't increment it any longer. Luckily, erase returns a new iterator you can use:
auto i = tests.begin();
while (i != tests.end())
{
if (i->use_count() == 0 || i->expired())
{
i = tests.erase(i);
}
else {
std::cout << i->lock()->val << std::endl;
++i;
}
}

Related

How to delete an Object whose associating use_count is bigger than 1?

I have an Object with multiple shared_ptrs pointing to it, and its reference count use_count in the associating control block is bigger than 1.
Now, I want to deconstruct the Object, but I do not know where are all those shared_ptrs, so I cannot find and deconstruct them before I deconstruct the Object.
If I just deconstruct the Object, it will make those shared_ptrs become dangling. Therefore, under this situation, how to delete the Object with use_count bigger than 1 but have no idea about all its shared_ptrs?
Thanks for any suggestions!
If you have access to code of Object class and can modify it then you can do following steps (you may jump straight away to final code afterwards):
Create special structure Fields that contains all fields of original Object.
Store Fields as heap-allocated pointer field p_.
In original Object class make all original fields as references pointing to fields of Fields heap allocated object.
Add destroyed_ bool flag that marks that Object was already destroyed. This flag becomes true after first call of destructor.
In every method check that destroyed_ is not true, otherwise throw an exception. Because NONE of methods can be used when object is already destroyed. You may also just show a message with some error instead of throwing exception and return from method without doing anything. Up to you how to handle this error.
Inside destructor on first call make all cleanup as usual. And mark destroyed_ as true. Second call to destructor should just silently exit due to destroyed_ being already true.
All copy constructors and assignment operators should be implemented as usual. Example in code below.
To delete object before all shared pointers are freed just call ptr->~Object(); destructor, here ptr is any shared pointer, or use a convenience function std::destroy_at, like this std::destroy_at(ptr.get());.
In code below if last DoSomething() is not called (try to comment it out) then program finishes without exception, although warning is shown about calling destructor second time.
Try it online!
#include <iostream>
#include <memory>
class Object {
public:
Object()
: p_(new Fields{}), f0_(p_->f0_), f1_(p_->f1_) {}
Object(Object const & o)
: p_(new Fields{}), f0_(o.f0_), f1_(o.f1_) {}
Object & operator = (Object const & o) {
f0_ = o.f0_;
f1_ = o.f1_;
return *this;
}
void DoSomething() {
if (destroyed_)
throw std::runtime_error("Object already destroyed!");
f0_ += 1;
f1_ += std::to_string(f0_) + " ";
std::cout << "DoSomething: '" << f1_ << "'" << std::endl;
}
~Object() {
if (destroyed_) {
std::cout << "Called destructor of destroyed object..."
<< std::endl;
return;
}
// Process fields cleanup here...
delete p_;
p_ = nullptr;
destroyed_ = true;
}
private:
struct Fields {
int f0_ = 0;
std::string f1_;
};
Fields * p_ = nullptr;
bool destroyed_ = false;
int & f0_;
std::string & f1_;
};
int main() {
try {
std::shared_ptr<Object> o0 = std::make_shared<Object>();
{
std::shared_ptr<Object> o1 = o0;
o1->DoSomething();
o1->DoSomething();
// Call destructor when you don't need object.
// Even if some shared_ptrs still use it.
o1->~Object();
}
o0->DoSomething();
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Output:
DoSomething: '1 '
DoSomething: '1 2 '
Called destructor of destroyed object...
Exception: Object already destroyed!
Similar code to above can be also implemented using C++17 std::optional instead of heap pointer, this solution is even better because it doesn't use any heap allocation, all fields are located inside object's body (i.e. stack allocation).
Try it online!
#include <iostream>
#include <memory>
#include <optional>
class Object {
public:
Object()
: p_(Fields{}), f0_(p_->f0_), f1_(p_->f1_) {}
Object(Object const & o)
: p_(Fields{}), f0_(o.f0_), f1_(o.f1_) {}
Object & operator = (Object const & o) {
f0_ = o.f0_;
f1_ = o.f1_;
return *this;
}
void DoSomething() {
if (destroyed_)
throw std::runtime_error("Object already destroyed!");
f0_ += 1;
f1_ += std::to_string(f0_) + " ";
std::cout << "DoSomething: '" << f1_ << "'" << std::endl;
}
~Object() {
if (destroyed_) {
std::cout << "Called destructor of destroyed object..."
<< std::endl;
return;
}
// Process fields cleanup here...
p_ = std::nullopt;
destroyed_ = true;
}
private:
struct Fields {
int f0_ = 0;
std::string f1_;
};
std::optional<Fields> p_;
bool destroyed_ = false;
int & f0_;
std::string & f1_;
};
int main() {
try {
std::shared_ptr<Object> o0 = std::make_shared<Object>();
{
std::shared_ptr<Object> o1 = o0;
o1->DoSomething();
o1->DoSomething();
// Call destructor when you don't need object.
// Even if some shared_ptrs still use it.
o1->~Object();
}
o0->DoSomething();
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Output:
DoSomething: '1 '
DoSomething: '1 2 '
Called destructor of destroyed object...
Exception: Object already destroyed!
How to delete an Object whose associating use_count is bigger than 1?
By reducing the use count to 0. This can be achieved by destroying or reassigning the shared pointers that currently point to the object.
but I do not know where are all those shared_ptrs
Then you must solve the problem that prevents you from knowing all those pointers.
Or, you could consider that perhaps there is a reason why those pointers exist, and that perhaps the object isn't supposed to be deleted yet, since it's apparently being used by some part of the program.

Implicit Conversion of Class

#include <iostream>
using namespace std;
class temp
{
public:
temp()
{
std::cout << "Constructor created." << std::endl;
}
~temp()
{
std::cout << "Deconstructor called." << std::endl;
}
};
class scopedptr
{
private:
temp* ptr;
public:
scopedptr(temp* p)
{
ptr = p;
std::cout << "scoped" << std::endl;
}
~scopedptr()
{
delete ptr;
}
};
int main()
{
{
scopedptr a = new temp();
}
}
Most of the code above is not relevant but I wrote it so that there is a background to my question.
My question resides inside the main function, I know the line "scopedptr a = new temp();" will work, my doubt is the what is happening with the object a, is it associated with the class scopedptr or the temp class.
This code does not have many variables but what if there are multiple variables inside both the classes, will object a be able to call methods from class scopedptr or class temp or both.
I'm literally confused, what's happening with the object? which class is its type???
In the line
scopedptr a = new temp();
type of a is explicitly specified as scopedptr. Any usage of a must correspond to the type scopedptr.
When in doubt, simplify.
temp* temp_ptr = new temp();
scopedptr a{temp_ptr};
Then, there is less scope for confusion.
a is a scopedptr with an owning pointer to a temp.
will object a be able to call methods from class scopedptr or class temp or both
To be able to treat it as a smart pointer, you'd usually add member functions, deferencing the owned pointer:
class scopedptr {
public:
temp* operator->() { return ptr; }
temp& operator*() { return *ptr; }
};
You can now do a->func(); to call a function in the temp object.
Also look into the Rule of 5 for safe handling of the owned pointer.
Demo

C++ Why emplacing object in vector segfaults?

I want to create a vector of "Act" objects that contain pointers to either "Eat" or "Drink" dynamically allocated objects. The new objects are being emplaced like so:
action_vector.emplace_back(Act::BehaviorType::eat);
However, it is seg-faulting and I can't figure out why. I thought that emplace_back would implicitly call the move constructor, not the destructor, but for some reason it is, which (I think) is what is screwing everything up.
Is there any way to successfully create a vector of such objects?
Here is the rest of the code along with its output. Sorry if it's a little verbose, but basically it's just a strategy pattern.
#include <iostream>
#include <vector>
class IBehavior
{
public:
IBehavior() = default;
virtual ~IBehavior() = default;
virtual void execute() = 0;
};
class Drink : public IBehavior
{
public:
Drink(): IBehavior() {}
~Drink() {}
void execute() { std::cout << "Drinking" << std::endl; }
};
class Eat : public IBehavior
{
public:
Eat(): IBehavior() {}
~Eat() {}
void execute() { std::cout << "Eating" << std::endl; }
};
class Act
{
IBehavior * b;
public:
enum class BehaviorType { eat = 0, drink = 1 };
Act() = default;
~Act()
{
std::cout << "Calling the destructor" << std::endl;
delete b;
}
Act(BehaviorType b_type) { SetBehavior(b_type); }
Act(Act&& act)
{
std::cout << "Calling the move constructor" << std::endl;
this->b = act.b;
}
void SetBehavior(BehaviorType b_type)
{
if(b_type == BehaviorType::eat) b = new Eat();
if(b_type == BehaviorType::drink) b = new Drink();
}
void execute() { b->execute(); }
};
int main(int argc, char * argv[])
{
std::vector<Act> action_vector;
for(int i = 0; i < 10; ++i)
{
action_vector.emplace_back(Act::BehaviorType::eat);
action_vector[i].execute();
}
return 0;
}
output:
Eating
Calling the move constructor
Calling the destructor
Eating
Calling the move constructor
Calling the move constructor
Calling the destructor
Calling the destructor
Segmentation fault: 11
Your move constructor copies b, and destructor deletes b, so if you move construct an instance then the same pointer value will be deleted twice which has undefined behaviour.
General solution: Use a smart pointer.
Another bug: Default constructor leaves b uninitialised. When a default constructed object is destroyed, the uninitialised pointer is deleted and behaviour is undefined. Smart pointer fixes this also.

Polymorphism and Dynamic Casting

So I'm working on a text-based RPG, and I've run into an issue. I am currently working on equipping weapons from the character's inventory. I am trying to make it so that my program can tell if the item they want to equip is of class Weapon or not. Here is the clip of relevant code:
Item tempChosenWeapon = myInventory.chooseItem();
cout << tempChosenWeapon.getName() << endl;
Item *chosenWeapon = &tempChosenWeapon;
cout << chosenWeapon->getName() << endl;//THE CODE WORKS UP TO HERE
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
cout << maybeWeapon->getName() << endl;
Now, Weapon is a child class of Item, which is why I am using dynamic cast -- in an attempt to change chosenWeapon, which is of type Item, to type Weapon in order to compare the two classes. (I am using these cout<<s in or to test whether or not calling a function from these objects works).
My program compiles, and everything runs fine until we come to maybeWeapon->getName(), in which the program crashes. I've researched quite a bit, but I just don't understand what I am doing wrong. Any answer or alternative suggestion is much appreciated! Thanks!
The problem
The problem is that you try to make a dynamic cast to a Weapon but in reality the object pointed to is a true copy constructed Item and not a subclass. This is results in a nullptr and UB when you dereference it !
Why ?
Let's suppose that you have only Weapon objects in your inventory. The first instruction in your snippet is the root of your evil:
Item tempChosenWeapon = myInventory.chooseItem();
This is statement is a copy construction of an Item object. If the source object was a Weapon, it will be sliced.
Later you take a pointer to this object:
Item *chosenWeapon = &tempChosenWeapon;
But this Item* doesn't point to a Weapon object as you think. It points to a real crude Item object ! So when you do the dynamic cast here:
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
the code will find out that choosenWeapon is not a Weapon*, and the result of dynamic_cast will be a nullptr. Until now it's not necessarily a catastrophe. But when you then derefence this pointer you get UB:
maybeWeapon->getName() // OUCH !!!!!!
Solution
Checking if the dynamic_cast was successful (i.e. result not nullptr) is a protection against the crash, but will not solve your root problem.
It is even possible that the problem is even deeper than expected: what type does the myInventory.chooseItem() return in reality ? Is it a plain Item ? Then you might have the slicing problem already in the inventory !
If you want to use polymorphism:
you have to work with pointers (preferably smart pointers) or with references, in order not to loose the original type of an object, like it happened here.
If you need to copy polymorphic objects, you can't just use an assignment with an Item: you'd need to invoke a polymorphic clone() function and ensure that the target of this cloning has a compatible type.
To start with a solution, it's something like this:
Item* chosenWeapon = myInventory.chooseItem(); // refactor choosItem() to return a pointer.
cout << chosenWeapon->getName() << endl;
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
if (maybeWeapon)
cout << maybeWeapon->getName() << endl;
else cout << "Oops the chosen item was not a weapon" <<endl;
If this still not work, then your inventory container would be flawed. In this case, look at this question before opening a separate question with the code of your container
dynamic_cast will return nullptr if the pointer cast cannot be performed (for reference casts it will throw an exception), so your code should read something like:
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
if ( maybeWeapon ) {
cout << maybeWeapon->getName() << endl;
else {
// it's not a weapon
}
If you don't perform that test, and try to dereference the pointer containing nullptr, you are off in Undefined Behaviour Land.
Item tempChosenWeapon = myInventory.chooseItem();
this is an Item. Not a type descended from Item. It is an Item.
Values in C++ have known types.
cout << tempChosenWeapon.getName() << endl;
all good, but please stop using namespace std;
Item *chosenWeapon = &tempChosenWeapon;
This is a pointer to an Item. I can prove it is not polymorphic, because it is a pointer to a instance of type Item. The compiler can probably prove it to.
cout << chosenWeapon->getName() << endl;//THE CODE WORKS UP TO HERE
ok, this repeats the previous call.
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
This deterministically returns nullptr. chosenWeapon is an Item* that we know points to an Item, and an Item is not a Weapon.
cout << maybeWeapon->getName() << endl;
this dereferences nullptr.
There are a number of ways to handle polymorphism in C++. But you have to think about it.
First, do you want value semantics? Value sematnics means that a copy of something is a copy of it. Things don't refer to other things; they are those things.
You can do value semantics with polymorphic values, but it takes a bit of work. You write two classes; the value wrapper, and the internal pImpl.
The internal pImpl has a std::unique_ptr<Impl> Impl->clone() const method, and the value wrapper calls it when you copy it.
You write your interface like this:
template<class D>
struct clonable {
std::unique_ptr<D> clone() const = 0;
};
struct ITarget;
struct IItem:clonable<IItem> {
virtual std::string get_name() const = 0;
virtual bool can_equip( ITarget const& ) const = 0;
~virtual IItem() {}
};
struct Target;
struct Item {
using Impl = IItem;
explicit operator bool() const { return (bool)pImpl; }
IItem* get_impl() { return pImpl.get(); }
IItem const* get_impl() const { return pImpl.get(); }
template<class D>
D copy_and_downcast() const& {
auto* ptr = dynamic_cast<typename D::Impl const*>( pImpl.get() );
if (!ptr) return {};
return D(ptr->clone());
}
template<class D>
D copy_and_downcast() && {
auto* ptr = dynamic_cast<typename D::Impl*>( pImpl.get() );
if (!ptr) return {};
pImpl.release();
return D(std::unique_ptr<typename D::Impl>(ptr));
}
std::string get_name() const {
if (!*this) return {};
return pImpl->get_name();
}
bool can_equip(Target const& target)const{
if (!*this) return false;
if (!target) return false;
return pImpl->can_equip( *target.get_impl() );
}
Item() = default;
Item(Item&&) = default;
Item& operator=(Item&&) = default;
Item(std::unique_ptr<IItem> o):pImpl(std::move(o)) {}
Item(Item const& o):
Item( o?Item(o.pImpl->clone()):Item{} )
{}
Item& operator=( Item const& o ) {
Item tmp(o);
std::swap(pImpl, tmp.pImpl);
return *this;
}
private:
std::unique_ptr<IItem> pImpl;
};
which probably has bugs and is maybe too complex for you.
Second, you can go with reference semantics.
In this case, you want to return shared_ptr<const T> or shared_ptr<T> from your data. Or you can go half way and return a unique_ptr<T> copy from your chooseItem functions.
Reference semantics is really hard to get right. But you do get to use dynamic_cast or dynamic_pointer_cast directly.
std::shared_ptr<Item> chosenWeapon = myInventory.chooseItem();
if (!chosenWeapon) return;
std::cout << chosenWeapon->getName() << std::endl;
auto maybeWeapon = dynamic_pointer_cast<Weapon>(chosenWeapon);
if (maybeWeapon)
std::cout << maybeWeapon->getName() << std::endl;
else
std::cout << "Not a weapon" << std::endl;
You cannot cast an object of type Item to an object of a subclass of Item.
Note that with Item tempChosenWeapon = myInventory.chooseItem(), you will get an Item-object, even if chooseItem might return a Weapon-object. This is called "slicing" and cuts out an Item-subobject of any Weapon-object. Note that variables that are not references or pointers are not polymorphic:
struct A {
int a = 0;
virtual void print() const {
std::cout << "a:" << a << std::endl;
}
};
struct B : public A {
int b = 1;
void print() const override {
std::cout << "a:" << a << "; b:" << b << std::endl;
}
};
B b;
A get_b() { // will slice b;
return b;
}
A& getRefTo_b() { // reference to b; polymorphic
return b;
}
A* getPointerTo_b() { // pointer to b; polymorphic.
return &b;
}
int main() {
A a1 = get_b(); // copy of A-subobject of b; not polymorphic
a1.print();
// a:0
A a2 = getRefTo_b(); // copy of A-subobject of referenced b-object; not polymorphic
a2.print();
// a:0
A &a3 = getRefTo_b(); // storing reference to b-object; polymorphic
a3.print();
// a:0; b:1
A *a4 = getPointerTo_b(); // pointer to b-object; polymorphic
a4->print();
// a:0; b:1
B* b1 = dynamic_cast<B*>(&a1); // fails (nullptr); a1 is not a B
B* b2 = dynamic_cast<B*>(&a2); // fails (nullptr); a2 is not a B
B* b3 = dynamic_cast<B*>(&a3); // OK; a3 refers to a B-object
B* b4 = dynamic_cast<B*>(a4); // OK; a4 points to a B-object
return 0;
}
So your signature should probably be
Item &Inventory::chooseItem() {
static Weapon weapon;
...
return weapon;
};
int main() {
Item &myWeapon = myInventory.chooseItem();
Weapon* w = dynamic_cast<Weapon*>(&myWeapon);
...
}

How to wrap a raw pointer into a shared_ptr and prevent shared_ptr from deleting the object?

I need to wrap a raw pointer into a shared_ptr in order to pass it to a function. The function doesn't hold any reference to the input object once it returns.
{
MyClass i;
shared_ptr<MyClass> p(&i);
f(p);
// BAD: shared_ptr will delete i.
}
How to prevent shared_ptr from deleting the referenced object?
As chris alluded to in the comments, write an empty deleter:
#include <type_traits>
template <typename T>
struct empty_delete
{
empty_delete() /* noexcept */
{
}
template <typename U>
empty_delete(const empty_delete<U>&,
typename std::enable_if<
std::is_convertible<U*, T*>::value
>::type* = nullptr) /* noexcept */
{
}
void operator()(T* const) const /* noexcept */
{
// do nothing
}
};
Example of use:
#include <iostream>
#include <memory>
struct noisy
{
noisy() { std::cout << "alive" << std::endl; }
~noisy() { std::cout << "dead" << std::endl; }
noisy(const noisy&);
noisy& operator=(const noisy&);
};
template <typename T>
void take(T& yours)
{
std::cout << "Taking..." << std::endl;
{
auto mine = std::move(yours);
}
std::cout << "Took." << std::endl;
}
int main()
{
std::unique_ptr<noisy> a(new noisy());
std::shared_ptr<noisy> b(new noisy());
std::unique_ptr<noisy, empty_delete<noisy>> c(new noisy());
std::shared_ptr<noisy> d(new noisy(), empty_delete<noisy>());
take(a);
take(b);
take(c);
take(d);
}
Output:
alive
alive
alive
alive
Taking...
dead
Took.
Taking...
dead
Took.
Taking...
Took.
Taking...
Took.
Of course, this example leaks memory.
In case of .NET/clr as mentioned in the comments, you should implement ref class, pass the managed handle ^ and let the garbage collector manage the lifetime.
ref class MyClassManaged : public Pen
{
public:
MyClassManaged() : Pen{}, native_{ new MyClass{} } { }
~MyClassManaged() { this->!MyClassManaged(); }
!MyClassManaged() { delete native_; }
private:
MyClass* native_;
};
//...
{
MyClassManaged^ i = gcnew MyClassManaged{};
fManaged(i);
}
TL;DR Simply keep another copy of the shared_ptr<MyClass> alive, and allocate i on the heap instead of the stack if you still need it.
Scenario 1
{
shared_ptr<MyClass> another_p;
{
MyClass i;
shared_ptr<MyClass> p(&i);
f(p);
// p has not deleted i yet
another_p = p; // increment reference count
// p will decrement the reference count
// i will be deleted due to stack unwinding
}
// another_p still holds a reference to where i was on the stack
if (another_p)
something(); // yes, something() will be invoked
else
nothing(); // no, nothing() won't run here
// another_p will attempt to delete again if not the shared_ptr is not copied elsewhere
}
Scenario 2
{
shared_ptr<MyClass> another_p;
{
auto p = make_shared<MyClass>();
auto& i = *p;
f(p);
// p has not deleted i
another_p = p; // increment reference count
// p will decrement the reference count
// i will be NOT deleted
}
// another_p still holds a reference to where i was on the heap
if (another_p)
something(); // yes, something() will be invoked
else
nothing(); // no, nothing() won't run here
// another_p will attempt to delete again if not the shared_ptr is not copied elsewhere
}
// it will finally be deleted now
Simply keep another copy of the shared_ptr alive; no need to mess with an empty deleter alike.