Squeamz said:
Hello,
Say I create a class ("Child") that inherits from another class
("Parent"). Parent's destructor is not virtual. Is there a way I can
Irrelevant; you aren't deleting a dynamic object through a base class
pointer.
prevent Parent's destructor from being called when a Child object goes
out of scope?
A Child object is a kind of Parent object. So a Parent is going out of
scope.
The destructor calls are linked, and that is not under programmer
control. The only way to break out of it would be to throw an exception
out of Child::~Child(). That's a bad idea; it would screw up other
things; don't do that.
I think you have to use aggregation here or composition rather than
inheritance. The Child class should have a reference to a Parent.
Specifically, I am dealing with a C library that provides a function
that must be called to "destruct" a particular struct (this struct is
dynamically allocated by another provided function). To avoid memory
leaks, I created a class that inherits from std::auto_ptr<[the C
struct]>, and I gave it just a constructor and a destructor.
The problem with this is that auto_ptr destructor will want to call
delete on whatever pointer is inside it. Inheritance won't fix this
behavior. The only way to prevent its destructor from doing that is to
steal that pointer!
In your own destructor, you take the managed pointer out of the
auto_ptr base class and replace it with null. Then if the pointer you
pulled out is non-null, you call the C library's destruction routine.
If the pointer is null, then the auto_ptr doesn't own any object; you
do nothing. But if you do this, what's the point of using auto_ptr? You
are rewriting half of its logic in your own derived class, which
defeats the point of inheritance.
Th eproblem
Here is how to do this silliness:
class MyClass : public auto_ptr<Whatever> {
// ...
public:
// ...
~MyClass() {
try {
Whatever *purloined = release();
if (purloined != 0)
CLibraryDeleteFunction(purloined);
} catch (...) {
// release() threw because there is no pointer!
}
}
};
Some implementations of auto_ptr throw an exception in release() if the
object doesn't own any pointer; others just return null, that's why
there is the try/catch.
Get it? Your destructor is called first. It calls release() in the base
class, which takes ownership of the pointer out of auto_ptr and
replaces it it with null, and returns the original pointer. If the
return value is non-null it means that the object really did own
something, and so the C library routine can be called.
But with all that work, you might as well just complete the above class
to be a smart pointer in itself, and then get rid of the inheritance
from auto_ptr.
The right way with auto_ptr to take advantage of the second template
parameter in the smart pointer class! That template parameter lets you
specify a custom allocator class. There is a default value for that
parameter, so you don't have to deal with it most of the time: the
default value specifies a class that uses new/delete. You can write
your own allocator class that uses the C library's functions. There is
probably code somewhere you can cut and paste for this.
Another way to deal with this problem is to wrap a C++ management class
around the object. The C++ class's constructor and destructor call into
the C library's creation and deletion functions, respectively. A
pointer to the C object is kept inside this C++ object.
You can dynamically allocate the C++ object also, and use smart
pointers to manage that. Of course, there is no need for a special
allocator because you are using new/delete on the C++ object.