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

发布于 2024-11-18 20:15:46 字数 1621 浏览 3 评论 0原文

开发环境: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 test)然后查看生成了什么样的符号,如下所示,

nm --demangle test

我可以看到以下内容输出。

==== 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。

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

Developing environment: GNU GCC (g++) 4.1.2

While I'm trying to investigate how to increase 'code coverage - particularly function coverage' in unit testing, I've found that some of class dtor seems to be generated multiple times. Does some of you have any idea on why, please?

I tried and observed what I mentioned the above by using the following code.

In "test.h"

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

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

In "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;
}

When I built the above code (g++ test.cpp -o test) and then see what kind of symbols have been generated as follows,

nm --demangle test

I could see the following output.

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

My questions are as follows.

1) Why multiple dtors have been generated (BaseClass - 2, DerivedClass - 3)?

2) What are the difference among these dtors? How those multiple dtors will be selectively used?

I now have a feeling that in order to achieve 100% function coverage for C++ project, we would need to understand this so that I can invoke all those dtors in my unit tests.

I would greately appreciate if someone could give me the reply on the above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贱贱哒 2024-11-25 20:15:46

首先,Itanium C++ ABI;请参阅“基本对象析构函数”、“完整对象析构函数”和“删除析构函数”下的定义。 5.1.4 中给出了到损坏名称的映射。

基本上:

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

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

First, the purposes of these functions are described in the Itanium C++ ABI; see definitions under "base object destructor", "complete object destructor", and "deleting destructor". The mapping to mangled names is given in 5.1.4.

Basically:

  • D2 is the "base object destructor". It destroys the object itself, as well as data members and non-virtual base classes.
  • D1 is the "complete object destructor". It additionally destroys virtual base classes.
  • D0 is the "deleting object destructor". It does everything the complete object destructor does, plus it calls operator delete to actually free the memory.

If you have no virtual base classes, D2 and D1 are identical; GCC will, on sufficient optimization levels, actually alias the symbols to the same code for both.

丶视觉 2024-11-25 20:15:46

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

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

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

结果:

  • foobazquux 的每个 vtable 中的 dtor 条目都指向相应的负责人删除dtor。
  • b1b2baz() in-charge 构造,它调用 foo(1) in-charge
  • q1q2quux() in-charge 构造,即 foo(2) in-chargebaz() not-in-charge 带有指向之前构造的 foo 对象的指针
  • q2~auto_ptr() in-charge 破坏,它调用虚拟 dtor ~quux() < em>负责删除,调用~baz() 非负责人~foo() 负责人操作员删除
  • q1~quux() in-charge 破坏,它调用 ~baz() 不-in-charge~foo() in-charge
  • b2~auto_ptr() in-charge,调用虚拟 dtor ~baz() 负责删除,它调用 ~foo() 负责 和 < code>operator delete
  • b1~baz() in-charge 破坏,它调用 ~foo() 负责

任何来自quux 将使用其not-in-charge ctor 和 dtor 并承担创建 foo 对象的责任。

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

There are usually two variants of the constructor (not-in-charge / in-charge) and three of the destructor (not-in-charge / in-charge / in-charge deleting).

The not-in-charge ctor and dtor are used when handling an object of a class that inherits from another class using the virtual keyword, when the object is not the complete object (so the current object is "not in charge" of constructing or destructing the virtual base object). This ctor receives a pointer to the virtual base object and stores it.

The in-charge ctor and dtors are for all the other cases, i.e. if there is no virtual inheritance involved; if the class has a virtual destructor, the in-charge deleting dtor pointer goes into the vtable slot, while a scope that knows the dynamic type of the object (i.e. for objects with automatic or static storage duration) will use the in-charge dtor (because this memory should not be freed).

Code example:

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

Results:

  • The dtor entry in each of the vtables for foo, baz and quux point at the respective in-charge deleting dtor.
  • b1 and b2 are constructed by baz() in-charge, which calls foo(1) in-charge
  • q1 and q2 are constructed by quux() in-charge, which falls foo(2) in-charge and baz() not-in-charge with a pointer to the foo object it constructed earlier
  • q2 is destructed by ~auto_ptr() in-charge, which calls the virtual dtor ~quux() in-charge deleting, which calls ~baz() not-in-charge, ~foo() in-charge and operator delete.
  • q1 is destructed by ~quux() in-charge, which calls ~baz() not-in-charge and ~foo() in-charge
  • b2 is destructed by ~auto_ptr() in-charge, which calls the virtual dtor ~baz() in-charge deleting, which calls ~foo() in-charge and operator delete
  • b1 is destructed by ~baz() in-charge, which calls ~foo() in-charge

Anyone deriving from quux would use its not-in-charge ctor and dtor and take on the responsibility of creating the foo object.

In principle, the not-in-charge variant is never needed for a class that has no virtual bases; in that case, the in-charge variant is then sometimes called unified, and/or the symbols for both in-charge and not-in-charge are aliased to a single implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文