GNU GCC(g ++):为什么会生成多个dtor?

问题描述 投票:83回答:2

开发环境: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();
};

在“ 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;
}

当我构建上面的代码(g ++ test.cpp -o测试),然后查看生成了以下哪种类型的符号时,

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

我的问题如下。

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

2)这些dtor之间有什么区别?如何选择使用这些多个dtor?

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

如果有人能给我以上的答复,我将不胜感激。

c++ g++ destructor
2个回答
66
投票

首先,这些功能的目的在Itanium C++ ABI中进行了说明;请参见“基础对象析构函数”,“完整对象析构函数”和“删除析构函数”下的定义。在5.1.4中给出了到错误名称的映射。

基本:

  • D2是“基础对象析构函数”。它会破坏对象本身以及数据成员和非虚拟基类。
  • D1是“完整对象析构函数”。此外,它还会破坏虚拟基类。
  • D0是“删除对象析构函数”。它完成了完整的对象析构函数所做的所有事情,而且它调用operator delete来实际释放内存。

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


36
投票

通常有构造函数的两个变体(in-in-charge / in-charge)和三个析构函数(not-in-charge / in-charge / 负责删除)。

当对象不是完整对象(因此当前对象是完整对象)时,使用

not-in-charge ctor和dtor来处理使用virtual关键字从另一个类继承的类的对象。 “不负责”构建或销毁虚拟基础对象)。此ctor接收指向虚拟基础对象的指针并将其存储。

in-charge ctor和dtor用于所有其他情况,即,不涉及虚拟继承;如果该类具有虚拟析构函数,则in-charge delete 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);

结果:

  • [v0,foobaz的每个vtable中的dtor条目指向相应的in-charge删除 dtor。
  • quuxb1b2 in-charge
  • 构成,其称为baz() in-charge
  • foo(1)q1q2 in-charge
  • 构成,其中quux() in-chargefoo(2) not-in-charge落在指向它先前构建的baz()对象的指针
  • fooq2 in-charge
  • 破坏,它调用虚拟dtor~auto_ptr()in-charge delete,将其称为~quux() not-in-charge~baz() 主管~foo()
  • operator deleteq1 in-charge
  • 破坏,它称为~quux() not-in-charge~baz() in-charge
  • ~foo()b2 in-charge
  • 破坏,它调用虚拟dtor~auto_ptr()in-charge delete,后者将~baz() in-charge和[ C0]
  • ~foo()operator delete in-charge
  • 破坏,它称为b1 in-charge

    ~baz()派生的任何人都将使用其非负责人

ctor和dtor,并负责创建~foo()对象。

原则上,对于没有虚拟基础的类,永远不需要not-in-charge

变体;在那种情况下,in-charge变体有时称为unified,和/或in-chargenot-in-charge的符号都别名为单个实现。
© www.soinside.com 2019 - 2024. All rights reserved.