[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: multiple destructors - which gets linked in?
From: |
Michael Elizabeth Chastain |
Subject: |
Re: multiple destructors - which gets linked in? |
Date: |
Fri, 9 Jul 2004 15:38:58 -0400 (EDT) |
Hi Andy,
A better mailling list for gcc questions is address@hidden .
But here's an answer.
gcc emits two or three versions of the object code for each
destructor.
They are:
_ZN7DerivedD0Ev Derived::~Derived [in-charge deleting]()
_ZN7DerivedD1Ev Derived::~Derived [in-charge]()
_ZN7DerivedD2Ev Derived::~Derived [not-in-charge]()
When the program destroys an object, the compiler generates code
to call one of the in-charge destructors for the object's complete
type. The in-charge destructor calls not-in-charge destructors
for each of the non-virtual base classes, then calls not-in-charge
destructors for each virtual base.
Consider the classic diamond-shaped hierarchy:
class A { };
class B1 : virtual public A { ... };
class B2 : virtual public A { ... };
class C : virtual public B1, virtual public B2 { ... };
When the program destroys an object of type A:
A::~A [in-charge]
When the program destroys an object of type B1:
B1::~B1 [in-charge]
A::~A [not-in-charge]
When the program destroys an object of type C:
C::~C [in-charge]
B2::B2 [not-in-charge]
B1::B1 [not-in-charge]
A::A [not-in-charge]
See, the destructor for the complete object is the only destructor
that can destroy the virtual base classes. If you destroy an object
of type B1, then you need B1::~B1 [in-charge], which calls one of
the flavors of A::~A. If you destroy an object of type C,
then C::~C [in-charge] calls B1::~B1 [not-in-charge], and
B1::~B1 [not-in-charge] does *not* call any of A::~A. It's the
responsibility of the complete object type, and *only* the complete
object type, to construct/destroy virtual bases.
That's what the in-charge/not-in-charge distinction is all about.
I don't know what the "in-charge" versus "in-charge deleting"
distinction is about.
Here is more info on the C++ multi-vendor ABI standard.
http://www.codesourcery.com/cxx-abi/
http://www.codesourcery.com/cxx-abi/abi.html
This was originally developed for Itanium, but g++ uses it for
all C++ architectures.
> I need to know which instance of DerivedD2Ev gets linked into the final
> executable (don't ask why...), so I modified the assembler files and
> continued the build. However my modifications (inserted instructions)
> never make it into the final build.
_ZN7DerivedD2Ev will be called by the destructor of any class
which has 'Derived' directly as a base class, or directly or
indirectly as a virtual base class.
class MoreDerived : public Derived // will call _ZN7DerivedD2Ev
class MoreMoreDerived : public MoreDerived // won't call _ZN7DerivedD2Ev
class V1 : public virtual Derived // will call _ZN7DerivedD2Ev
class V2 : public V1 // will call _ZN7DerivedD2Ev
Again, each destructor calls the destructors for its immediate
non-virtual base classes. And then the top-level destructor
calls the destructor for all the virtual base classes anywhere
in the inheritance hierarchy.
> Interestingly, there is no instance of it in Derived.S, just in
> ReallyDerived.S and in another file which references it.
Yup. The compiler synthesizes these functions where they are used.
Derived::~Derived [not-in-charge]() is not used by Derived;
it's used by ReallyDerived.
If you have a lot of classes that have Derived as an immediate
base class, or directly or indirectly as a virtual base, then you'll
get a lot of Derived::~Derived [not-in-charge]() in a lot of
different .o files. Depending on your platform, these might be
coalesced by the linker into a single function, or there might be
several copies in the object code.
That's what happens when you let the compiler synthesize methods.
It synthesizes them at point-of-use.
If you write class Derived like this:
// Derived.h
Derived::Derived
{
virtual ~Derived ();
}
// Derived.c
Derived::~Derived () { ; }
Then there will be one copy of Derived::~Derived [not-in-charge],
and it will be in Derived.o. That will make it easier for you
to mess with the assembly code.
Hope this helps,
Michael C