Consider the case where a function foo(MyClass* mc) is supposed to retain a copy of mc into an internal data structure, and guarantee that the object will be deleted when no longer used.
void foo(MyClass* mc) // acquires ownership of mc; may throw
{
// code that may throw
bar(mc); // acquires mc; may also throw
}
The problem arises when this function executes code that might throw (for instance, an OutOfMemory exception). If the exception is raised before the pointer was saved to the data structure, the object should obviously be freed before the function unwinds since the caller is no more responsible for it (the caller doesn’t even know if the pointer was actually saved in the data structure or not).
One could use RAII to handle this with scope guards, but it seems very clumsy, and produces some overhead (it has to be done in every single function that acquires a pointer).
Would one really need to do this every time a dynamically-allocated object is acquired, or is there a neater way to do it?!
template <class T>
struct VerifyAcq {
T* ptr;
bool done;
VerifyAcq(T* ptr):ptr(ptr) { done = false; }
~VerifyAcq() {
if (!done) delete ptr;
}
};
void foo(MyClass* mc) // acquires mc; may throw
{
VerifyAcq<MyClass> va(mc);
// code that may throw
bar(mc); // acquires mc; may throw; must implement the same mechanism!
va.done = true;
}
// Note: there might be no public way of "undoing" what bar has done (no rollbak)
// and even if there was, what if it could also throw?...
The exception cannot be caught by the caller in order to delete the pointer, because before throwing an exception, the function may have successfully added the pointer to the data structure, and freeing the object would make the data structure unsound (dangling pointer).
When I started reading the code, I stopped at this point:
A comment is not the best way to document ownership acquisition. The best way to do that is to use the type system.
Fixing the interfaces also ends up solving the problem.
(If you don’t have unique_ptr in your standard library, there are alternatives like for example, Howard Hinnant’s emulation in C++03).