Prevent a parent object's destructor from being called?

P

Pete C

Squeamz said:
If the answer to my above question is no, does anyone know of an
alternative simple solution? I'd prefer to not have to rewrite
auto_ptr when the only difference would be the destructor.

shared_ptr (part of the boost libraries) is a better alternative to
auto_ptr. It lets you specify a 'deleter' function - ie, a function
that is called when all references are released in order to delete the
object. It sounds like what you want:
http://www.boost.org/libs/smart_ptr/shared_ptr.htm
 
R

Ron Natalie

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
prevent Parent's destructor from being called when a Child object goes
out of scope?

The virtual-ness of a destructor has no bearing here. All the
destructors of the base classes and any non-static members are
called automatically. There's nothing you can do to stop this.
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
destructor just calls the 3rd-party "destructor" function.
Unfortunately the problem is that std::auto_ptr's destructor is not
virtual.

What does the virtualness of have to do with it. The only thing a
virtual destructor does for you is make destructing through base
class destructors. auto_ptr isn't appropriate here for the purpose
anyhow. Auto_ptr calls delete on a single object. Since delete
appears to be inappropriate, I don't know why you are using it.

Here:
class Wrapper {
public:
Wrapper() {
cstruct = C_ConstructorFunction();
}
~Wrapper() {
C_DestructorFunction(cstruct);
}
// you need to define or disable copy constructor
// and assignement operators as well.
private:
CStruct* cstruct;
};
 
S

Squeamz

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
prevent Parent's destructor from being called when a Child object goes
out of scope?

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
destructor just calls the 3rd-party "destructor" function.
Unfortunately the problem is that std::auto_ptr's destructor is not
virtual.

If the answer to my above question is no, does anyone know of an
alternative simple solution? I'd prefer to not have to rewrite
auto_ptr when the only difference would be the destructor.

Thanks,
Squeamz
 
S

Squeamz

The virtual-ness of a destructor has no bearing here. All the
destructors of the base classes and any non-static members are
called automatically. There's nothing you can do to stop this.

Ah, you're right. For some reason I was under the impression that
only one destructor gets called if the base destructor is virtual.
Thanks for the info.
What does the virtualness of have to do with it. The only thing a
virtual destructor does for you is make destructing through base
class destructors. auto_ptr isn't appropriate here for the purpose
anyhow. Auto_ptr calls delete on a single object. Since delete
appears to be inappropriate, I don't know why you are using it.

Well, I tried to explain my reasoning. Aside from its use of
'delete', auto_ptr does everything I need here. I wanted to override
the only part of auto_ptr that uses delete. That's why I was using
it. Obviously my reasoning was flawed :).

Your suggestion works, of course, but like I said I didn't want to
have to deal with writing the assignment operator and stuff.

Anyway I think I'll take a look at boost's shared_ptr (thanks Pete!).

Squeamz
 
K

Kaz Kylheku

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.
 
K

Kaz Kylheku

Squeamz said:
Ah, you're right. For some reason I was under the impression that
only one destructor gets called if the base destructor is virtual.
Thanks for the info.

The virtual destructor in the base ensures that the destructor call
through the base goes to the destructor of the most derived class, as
if that were called directly. From there, all the destructors are
called in the appropriate order over the entire inheritance lattice!
Well, I tried to explain my reasoning. Aside from its use of
'delete', auto_ptr does everything I need here. I wanted to override
the only part of auto_ptr that uses delete. That's why I was using
it. Obviously my reasoning was flawed :).

Normally, destructors just take care of clean-up at their level. The
reason destructors can be virtual is /not/ for the sake of overriding
base class behavior, as is the case with other virtual functions.

But you /can/ nervertheless override base class destructor behavior in
a derived class destructor. You just have to ensure that the base class
destructur somehow knows that it was done, so it does not try to
duplicate the job that was taken over, which would result in a
redundant or otherwise wrongful deallocation.
 
R

Ron Natalie

Squeamz said:
Well, I tried to explain my reasoning. Aside from its use of
'delete', auto_ptr does everything I need here. I wanted to override
the only part of auto_ptr that uses delete. That's why I was using
it. Obviously my reasoning was flawed :).

Other than delete, auto_ptr is just a pointer.
Your suggestion works, of course, but like I said I didn't want to
have to deal with writing the assignment operator and stuff.

All you need to do is:
private:
Wrapper(const Wrapper&); // Don't implement
Wrapper& operator=(const Wrapper&); // don't implement

This will make the object non-copyable in most cases, and should cause
a linker error for the missing functions in the few cases that don't

There's also a boost::non_copyable class you can mix in to do
the same thing (it's just a base class with the same construct that
"poisons" any object that contains it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,585
Members
45,082
Latest member
KetonaraKetoACV

Latest Threads

Top