GNU GCC(g ++):为什么它会产生多个dtors?

Smg*_*Smg 84 c++ destructor g++

开发环境:GNU GCC(g ++)4.1.2

虽然我正在尝试研究如何在单元测试中增加"代码覆盖率 - 特别是功能覆盖率",但我发现有些类dtor似乎是多次生成的.你们当中有些人知道为什么吗?

我通过使用以下代码尝试并观察了我上面提到的内容.

在"test.h"中

class BaseClass
{
public:
    ~BaseClass();
    void someMethod();
};

class DerivedClass : public BaseClass
{
public:
    virtual ~DerivedClass();
    virtual void someMethod();
};
Run Code Online (Sandbox Code Playgroud)

在"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;
}
Run Code Online (Sandbox Code Playgroud)

当我构建上面的代码(g ++ test.cpp -o test),然后看看生成了哪种符号,如下所示,

nm - 降级测试

我可以看到以下输出.

==== 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()
Run Code Online (Sandbox Code Playgroud)

我的问题如下.

1)为什么生成了多个dtors(BaseClass - 2,DerivedClass - 3)?

2)这些dtors有什么区别?如何有选择地使用这些多个dtors?

我现在有一种感觉,为了实现C++项目的100%功能覆盖,我们需要理解这一点,以便我可以在单元测试中调用所有这些dtors.

如果有人能给我上面的回复,我会非常感激.

bdo*_*lan 67

首先,这些功能的目的在Itanium C++ ABI中描述; 请参阅"base object析构函数","完整对象析构函数"和"删除析构函数"下的定义.5.1.4中给出了到损坏名称的映射.

基本上:

  • D2是"基础对象析构函数".它会破坏对象本身,以及数据成员和非虚拟基类.
  • D1是"完整对象析构函数".它还会破坏虚拟基类.
  • D0是"删除对象析构函数".它完成了完整的对象析构函数所做的一切,并且它调用operator delete实际释放内存.

如果没有虚拟基类,则D2和D1相同; 在足够的优化级别上,GCC实际上将符号别名为两者的相同代码.


Sim*_*ter 37

通常有两种构造函数(非充电/负责)和三种析构函数(非充电/充电/充电删除).

未在充电构造函数和析构函数被处理,使用从另一个类继承的类的对象时使用的virtual关键字,当对象是不完整的对象(因此当前对象是"不充电"构造或自毁的虚拟基础对象).该ctor接收指向虚拟基础对象的指针并存储它.

主管构造函数和dtors对于所有其他情况,也就是说,如果没有涉及虚拟继承; 如果类具有虚拟析构函数,则负责删除 dtor指针进入vtable槽,而知道对象动态类型的范围(即对于具有自动或静态存储持续时间的对象)将使用负责人 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);
Run Code Online (Sandbox Code Playgroud)

结果:

  • 每个vtable中的dtor条目foo,bazquux指向相应的主管删除 dtor.
  • b1b2由构成baz() 主管,它调用foo(1) 在充电
  • q1q2由构成quux() 主管,其落在foo(2) 在充电baz() 未在充电用的指针foo它前面构造的对象
  • q2~auto_ptr() 充电破坏,这称为虚拟dtor ~quux() 充电删除,其中称为~baz() 非充电,~foo() 负责operator delete.
  • q1被破坏的~quux() 主管,要求~baz() 未主管~foo() 主管
  • b2~auto_ptr() 充电破坏,它调用虚拟dtor ~baz() 充电删除,调用~foo() 负责operator delete
  • b1被破坏~baz() 的负责,这就要求~foo() 主管

任何人获得quux将使用其未主管构造函数和析构函数,并就创建的责任foo对象.

原则上,对于没有虚拟基础的类,从不需要非负责变体; 在这种情况下,有时将负责变体称为统一变量,和/或用于充电负责的符号别名为单个实现.