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); 

结果:

  • foobazquux每个vtable中的dtor条目指向相应的负责删除的 dtor。
  • b1b2主pipe baz() 负责 ,负责pipe理foo(1)
  • q1q2quux() 负责负责负责 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对象的责任。

原则上,对于没有虚拟基础的类别来说,不需要这种不带负担的变体。 在这种情况下, 负责的变体有时被称为统一的 ,和/或负责 负责的符号被混淆到单个实现。