GNU GCC(g ++):为什么会产生多个dtors?
开发环境:GNU GCC(g ++)4.1.2
当我试图调查如何在unit testing中增加“代码覆盖率 – 特别是函数覆盖率”时,我发现一些类似于生成多次的代码。 请问你们有些人有什么想法吗?
我试过,并通过使用下面的代码观察了我上面提到的。
在“test.h”
class BaseClass { public: ~BaseClass(); void someMethod(); }; class DerivedClass : public BaseClass { public: virtual ~DerivedClass(); virtual void someMethod(); };
在“test.cpp”
#include <iostream> #include "test.h" BaseClass::~BaseClass() { std::cout << "BaseClass dtor invoked" << std::endl; } void BaseClass::someMethod() { std::cout << "Base class method" << std::endl; } DerivedClass::~DerivedClass() { std::cout << "DerivedClass dtor invoked" << std::endl; } void DerivedClass::someMethod() { std::cout << "Derived class method" << std::endl; } int main() { BaseClass* b_ptr = new BaseClass; b_ptr->someMethod(); delete b_ptr; }
当我build立上面的代码(g ++ test.cpp -o test),然后看看如下生成了什么样的符号,
nm –demangletesting
我可以看到下面的输出。
==== following is partial output ==== 08048816 T DerivedClass::someMethod() 08048922 T DerivedClass::~DerivedClass() 080489aa T DerivedClass::~DerivedClass() 08048a32 T DerivedClass::~DerivedClass() 08048842 T BaseClass::someMethod() 0804886e T BaseClass::~BaseClass() 080488f6 T BaseClass::~BaseClass()
我的问题如下。
1)为什么生成了多个dt(BaseClass – 2,DerivedClass – 3)?
2)这些dtors有什么区别? 如何select使用这些多个转换器?
我现在有一种感觉,为了达到C ++项目的100%函数覆盖率,我们需要理解这一点,以便我可以在我的unit testing中调用所有这些dtors。
如果有人能够给我上面的答复,我会很感激。
首先,这些函数的目的在Itanium C ++ ABI中描述; 请参阅“基础对象析构函数”,“完整对象析构函数”和“删除析构函数”下的定义。 在5.1.4中给出了对错位名称的映射。
基本上:
- D2是“基础对象析构函数”。 它破坏了对象本身,以及数据成员和非虚拟基类。
- D1是“完整的对象析构函数”。 它还会破坏虚拟基类。
- D0是“删除对象析构函数”。 它完成了全部对象析构函数的工作,并且调用
operator delete
来释放内存。
如果你没有虚拟基类,D2和D1是相同的; GCC将在足够的优化级别上实际上将这些符号别名为相同的代码。
构造函数通常有两种变体( 不在主pipe / 负责人 )和三个析构主体( 非 主pipe / 主pipe / 负责人删除 )。
使用virtual
关键字处理从另一个类inheritance的类的对象时,使用不负责的 ctor和dtor,当对象不是完整的对象时(因此当前对象是“构造中的”破坏虚拟基础对象)。 这个ctor接收一个指向虚拟基础对象的指针并存储它。
负责的 ctor和dtors是所有其他情况下,即如果没有虚拟inheritance涉及; 如果这个类有一个虚拟的析构函数,那么负责删除的指针就会进入vtable槽,而知道对象dynamictypes的范围(例如,具有自动或静态存储持续时间的对象)将使用负责的 dtor (因为这个内存不应该被释放)。
代码示例:
struct foo { foo(int); virtual ~foo(void); int bar; }; struct baz : virtual foo { baz(void); virtual ~baz(void); }; struct quux : baz { quux(void); virtual ~quux(void); }; foo::foo(int i) { bar = i; } foo::~foo(void) { return; } baz::baz(void) : foo(1) { return; } baz::~baz(void) { return; } quux::quux(void) : foo(2), baz() { return; } quux::~quux(void) { return; } baz b1; std::auto_ptr<foo> b2(new baz); quux q1; std::auto_ptr<foo> q2(new quux);
结果:
-
foo
,baz
和quux
每个vtable中的dtor条目指向相应的负责删除的 dtor。 -
b1
和b2
由主pipebaz()
负责 ,负责pipe理foo(1)
-
q1
和q2
由quux()
负责 , 负责负责foo(2)
,并且baz()
不负责指向它早先构造的foo
对象的指针 -
q2
被负责负责的~quux()
负责 ,负责调用虚拟的dtor〜quux~quux()
负责删除 ,调用~quux()
不负责 ,~quux()
负责和operator delete
。 -
q1
被负责处理的~quux()
所破坏,其调用~quux()
不负责和~quux()
负责 -
b2
被负责的〜auto_ptr~auto_ptr()
负责 ,负责调用虚拟的dtor〜baz~baz()
负责删除 ,调用〜foo~foo()
负责和operator delete
-
b1
被负责的~baz()
负责 ,负责调用~foo()
任何源自quux
人都将使用其不负责任的ctor和dtor,并承担创buildfoo
对象的责任。
原则上,对于没有虚拟基础的类别来说,不需要这种不带负担的变体。 在这种情况下, 负责的变体有时被称为统一的 ,和/或负责和不 负责的符号被混淆到单个实现。