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!
CodePudding user response:
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.
CodePudding user response:
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 originalObject
.Store
Fields
as field of std::unique_ptr type, allocated in Heap.In original
Object
class make all original fields as references pointing to fields ofFields
heap allocated object.Add
destroyed_
bool flag that marks thatObject
was already destroyed. This flag becomestrue
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.Make destructor non-virtual! It is important because when object is destroyed its virtual table pointer becomes ill-formed, hence you can't have virtual destructor. Actually all virtual methods also can't be called. Maybe I'm wrong and modern compilers don't destroy virtual table anymore, some exports opinion is needed here.
Inside destructor on first call make all cleanup as usual. And mark
destroyed_
astrue
. 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.
#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...
destroyed_ = true;
}
private:
struct Fields {
int f0_ = 0;
std::string f1_;
};
std::unique_ptr<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.
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!