GNU GCC (g++):为什么它生成多个博士

GNU GCC (g++): Why does it generate multiple dtors?

本文关键字:博士 为什么 GCC g++ GNU      更新时间:2023-10-16

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

当我试图研究如何在单元测试中增加"代码覆盖率——特别是功能覆盖率"时,我发现一些类博士似乎被生成了多次。你们中有人知道为什么吗?

我试着用下面的代码观察我上面提到的。

在"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 test),然后看看生成了什么类型的符号,如下所示,

nm—demangle试验

我可以看到如下输出:

==== 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)为什么生成了多个医生(BaseClass - 2, DerivedClass - 3)?

2)这些医生有什么不同?如何有选择地使用这些医生?

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

如果有人能给我上述问题的答复,我将非常感激。

首先,在Itanium c++ ABI中描述了这些函数的用途;参见"基对象析构函数"、"完全对象析构函数"answers"删除析构函数"中的定义。到变形名称的映射在5.1.4中给出。

基本上

:

  • D2是"基对象析构函数"。它会销毁对象本身,以及数据成员和非虚基类。
  • D1是"完全对象析构函数"。它还会销毁虚拟基类。
  • D0是"删除对象析构函数"。它做所有的对象析构函数做的事情,加上它调用operator delete来释放内存。

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

通常有两个构造函数(not-in-charge/in-charge)和三个析构函数(not-in-charge/in-charge/in-charge deleting)。

not-in-charge ctor和dtor在处理使用virtual关键字从另一个类继承的类的对象时使用,当该对象不是完整的对象(因此当前对象"不负责"构造或析构虚拟基对象)。这个函数接收一个指向虚基对象的指针并存储它。

负责 actor和doctors用于所有其他情况,即如果不涉及虚拟继承;如果类有虚析构函数,则负责删除 dtor指针将进入虚值表槽,而知道对象动态类型的作用域(即具有自动或静态存储时间的对象)将使用负责的 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, bazquux的每个变量中的dr表项指向各自的负责删除 dr。
  • b1b2baz() in-charge构造,调用foo(1) in-charge
  • q1q2是由quux() in-charge构造的,foo(2) in-chargebaz() not- charge有一个指针指向之前构造的foo对象
  • q2~auto_ptr() in-charge销毁,~quux() in-charge删除虚拟医生~baz() not-in-charge~foo() in-chargeoperator delete调用。q1~quux() in-charge销毁,~baz() not-in-charge~foo() in-charge
  • b2~auto_ptr() in-charge销毁,它调用虚拟医生~baz() in-charge删除~foo() in-chargeoperator delete
  • b1~baz() in-charge销毁,它调用~foo() in-charge

任何从quux派生的人都将使用它的不负责的 tor和tor,并承担创建foo对象的责任。

原则上,没有虚基的类永远不需要不负责的变体;在这种情况下,负责的变体有时被称为统一的,并且/或者负责的不负责的的符号被别名为单个实现。