[英]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 ++):為什么它會生成多個dtors? 報告_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
,它會調用基對象的析構 ,這是_ZN6ParentD2Ev
的Parent
。
在// 2
, o
被動態分配和刪除,並且調用Child
的刪除析構函數 。 然后,它將調用Parent
的基礎對象析構函數 。 在兩者中,都調用基礎對象析構函數。
// 3
是一樣的 它只等於// 2
,除了o
的類型。
我在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:}
如您所見, _ZN6ParentD2Ev
Base
的基礎對象析構函數_ZN6ParentD2Ev
,而不調用Base
的其他對象。
然而, _ZN5ChildD0Ev
,刪除Child
析構函數,被調用兩次, _ZN5ChildD1Ev
, Child
完整對象析構函數,被調用三次,因為它有delete o;
和Child o;
。
但根據我的解釋, _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也存在問題。
正如ikh所解釋的那樣,當純虛擬父類具有虛擬析構函數時,D0析構函數會不必要地生成(並且不可用)。
但是,如果純虛擬父類具有非虛擬析構函數,則可以刪除指向父類型的指針,這將調用父虛擬機的D0析構函數。 當然,父類中的非虛擬析構函數很少是可取的或預期的,因此g ++會發出警告: [-Wdelete-non-virtual-dtor]
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.