有没有办法调用纯虚拟类的"deleting destructor"?
Is there a way to call the "deleting destructor" of a pure virtual class?
我在Ubuntu Trusty上使用C++11和g++4.8。
考虑这个片段
class Parent {
public:
virtual ~Parent() = default;
virtual void f() = 0;
};
class Child: public Parent {
public:
void f(){}
};
使用调用
{
Child o;
o.f();
}
{
Parent * o = new Child;
delete o;
}
{
Child * o = new Child;
delete o;
}
我使用gcov生成代码覆盖率报告。它报告具有符号_ZN6ParentD0Ev
的析构函数从未被调用,而_ZN6ParentD2Ev
是.
答:构造函数符号和GNU GCC(g++)的双重发射:为什么它会生成多个dtor?报告_ZN6ParentD0Ev
是正在删除的构造函数。
是否存在在Parent
类上调用此"删除析构函数"的情况
附属问题:如果没有,是否有办法让gcov/lcov代码覆盖工具(在以下关于将gcov与CMake/CDash一起使用的详细指南的答案中使用)忽略其报告中的符号?
我认为这是因为您有Child
对象,而不是Parent
对象。
{
Child o;
o.f();
} // 1
{
Parent * o = new Child;
delete o;
} // 2
{
Child * o = new Child;
delete o;
} // 3
在// 1
中,o
被破坏,并且Child
的完全对象析构函数被调用。由于Child
继承了Parent
,它将调用Parent
的基对象析构函数,即_ZN6ParentD2Ev
。
在// 2
中,o
被动态分配和删除,并调用Child
的删除析构函数。然后,它将调用Parent
的基对象析构函数。在两者中,都调用了基对象析构函数。
CCD_ 18相同。除了o
的类型外,它刚好等于// 2
。
我已经在cygwin&g++4.8.3&windows 7 x86 SP1。这是我的测试代码。
class Parent
{
public:
virtual ~Parent() { }
virtual void f() = 0;
};
class Child : public Parent
{
public:
void f() { }
};
int main()
{
{
Child o;
o.f();
}
{
Parent * o = new Child;
delete o;
}
{
Child * o = new Child;
delete o;
}
}
并编译&gcov选项:
$ g++ -std=c++11 -fprofile-arcs -ftest-coverage -O0 test.cpp -o test
$ ./test
$ gcov -b -f test.cpp
结果如下。
-: 0:Source:test.cpp
-: 0:Graph:test.gcno
-: 0:Data:test.gcda
-: 0:Runs:1
-: 0:Programs:1
function _ZN6ParentC2Ev called 2 returned 100% blocks executed 100%
2: 1:class Parent
-: 2:{
-: 3:public:
function _ZN6ParentD0Ev called 0 returned 0% blocks executed 0%
function _ZN6ParentD1Ev called 0 returned 0% blocks executed 0%
function _ZN6ParentD2Ev called 3 returned 100% blocks executed 75%
3: 4: virtual ~Parent() = default;
call 0 never executed
call 1 never executed
branch 2 never executed
branch 3 never executed
call 4 never executed
branch 5 taken 0% (fallthrough)
branch 6 taken 100%
call 7 never executed
-: 5: virtual void f() = 0;
-: 6:};
-: 7:
function _ZN5ChildD0Ev called 2 returned 100% blocks executed 100%
function _ZN5ChildD1Ev called 3 returned 100% blocks executed 75%
function _ZN5ChildC1Ev called 2 returned 100% blocks executed 100%
7: 8:class Child : public Parent
call 0 returned 100%
call 1 returned 100%
call 2 returned 100%
branch 3 taken 0% (fallthrough)
branch 4 taken 100%
call 5 never executed
call 6 returned 100%
-: 9:{
-: 10:public:
function _ZN5Child1fEv called 1 returned 100% blocks executed 100%
1: 11: void f() { }
-: 12:};
-: 13:
function main called 1 returned 100% blocks executed 100%
1: 14:int main()
-: 15:{
-: 16: {
1: 17: Child o;
1: 18: o.f();
call 0 returned 100%
call 1 returned 100%
-: 19: }
-: 20: {
1: 21: Parent * o = new Child;
call 0 returned 100%
call 1 returned 100%
1: 22: delete o;
branch 0 taken 100% (fallthrough)
branch 1 taken 0%
call 2 returned 100%
-: 23: }
-: 24: {
1: 25: Child * o = new Child;
call 0 returned 100%
call 1 returned 100%
1: 26: delete o;
branch 0 taken 100% (fallthrough)
branch 1 taken 0%
call 2 returned 100%
-: 27: }
1: 28:}
如您所见,调用了Base
的基本对象析构函数_ZN6ParentD2Ev
,而不调用Base
的其他析构函数。
然而,由于有delete o;
和Child o;
,删除Child
的析构函数_ZN5ChildD0Ev
被调用两次,Child
的完整对象析构函数_ZN5ChildD1Ev
被调用三次。
但根据我的解释,_ZN5ChildD0Ev
应该被调用两次,_ZN5ChildD1Ev
应该被调用一次,不是吗?为了找出原因,我这样做了:
$ objdump -d test > test.dmp
结果:
00403c88 <__ZN5ChildD0Ev>:
403c88: 55 push %ebp
403c89: 89 e5 mov %esp,%ebp
403c8b: 83 ec 18 sub $0x18,%esp
403c8e: a1 20 80 40 00 mov 0x408020,%eax
403c93: 8b 15 24 80 40 00 mov 0x408024,%edx
403c99: 83 c0 01 add $0x1,%eax
403c9c: 83 d2 00 adc $0x0,%edx
403c9f: a3 20 80 40 00 mov %eax,0x408020
403ca4: 89 15 24 80 40 00 mov %edx,0x408024
403caa: 8b 45 08 mov 0x8(%ebp),%eax
403cad: 89 04 24 mov %eax,(%esp)
403cb0: e8 47 00 00 00 call 403cfc <__ZN5ChildD1Ev>
403cb5: a1 28 80 40 00 mov 0x408028,%eax
403cba: 8b 15 2c 80 40 00 mov 0x40802c,%edx
403cc0: 83 c0 01 add $0x1,%eax
403cc3: 83 d2 00 adc $0x0,%edx
403cc6: a3 28 80 40 00 mov %eax,0x408028
403ccb: 89 15 2c 80 40 00 mov %edx,0x40802c
403cd1: 8b 45 08 mov 0x8(%ebp),%eax
403cd4: 89 04 24 mov %eax,(%esp)
403cd7: e8 a4 f9 ff ff call 403680 <___wrap__ZdlPv>
403cdc: a1 30 80 40 00 mov 0x408030,%eax
403ce1: 8b 15 34 80 40 00 mov 0x408034,%edx
403ce7: 83 c0 01 add $0x1,%eax
403cea: 83 d2 00 adc $0x0,%edx
403ced: a3 30 80 40 00 mov %eax,0x408030
403cf2: 89 15 34 80 40 00 mov %edx,0x408034
403cf8: c9 leave
403cf9: c3 ret
403cfa: 90 nop
403cfb: 90 nop
是的,由于_ZN5ChildD0Ev
调用_ZN5ChildD1Ev
,_ZN5ChildD1Ev
被调用了三次。(1+2)我想这只是GCC的实现——为了减少重复。
不能有Parent对象,所以不能。生成这个不必要的函数是GCC的疏忽。优化器确实应该删除它,因为它是未使用的,但我发现GCC在这方面也有问题。
但是,如果纯虚拟父类具有非虚拟析构函数,则可以删除指向父类型的指针,并且该将调用父类的D0析构函数。当然,父类中的非虚拟析构函数很少是可取的,因此g++会发出警告:[-Wdelete-non-virtual-dtor]
。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- SegFault deleting QTreeWidgetItem
- 防止复制构造函数实例化 C++11 类"deleting"
- 合法的方式将destructor信息存储到void*
- 检查destructor是否正在破坏所有动态内存
- 无法删除在destructor中的成员指针的课程
- 简单链接列表程序中的Destructor segfault
- Deleting qwtsymbol
- IS类型INFO自动更新了以前调用Destructor
- 如何解决"expected constructor, destructor, or type conversion before ‘(’ token"错误?
- {app.exe!_com_error::'vector delete destructor'(unsigned int)}
- 我应该重置Destructor中的原始成员变量
- 从Q开始关闭QDialog时,请致电Destructor
- 为什么构造函数的数量与Destructor调用的数量不匹配
- 用const_iterator成员调用类destructor时堆积损坏
- 当我要退出应用程序时,如何在QT中自动调用destructor
- 在MSVC上的数组初始化期间,destructor在不复制或移动构造方的情况下调用
- 为什么不在C++中调用Destructor
- "Destructor already defined"专用析构函数
- 有没有办法调用纯虚拟类的"deleting destructor"?