2014-09-04 137 views
10

我在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是。

答案Dual emission of constructor symbolsGNU GCC (g++): Why does it generate multiple dtors?報告_ZN6ParentD0Ev是刪除構造函數。

有沒有在Parent類上調用這個「刪除析構函數」的情況?

子公司的問題:如果沒有,是否有辦法讓gcov/lcov代碼覆蓋工具(在Detailed guide on using gcov with CMake/CDash?的回答下使用)在其報告中忽略該符號?

+0

答案也是這樣:「不,無法獲得該功能的覆蓋範圍?」 – RPGillespie 2017-04-20 02:09:11

+0

你有沒有想過如何讓gcov忽略那個符號? – RPGillespie 2017-04-20 02:14:14

+0

如果我沒記錯,我只是忽略了使用標準GCOV結構化註釋 – rcomblen 2017-04-20 08:37:47

回答

6

我認爲這是因爲你有Child對象,而不是Parent對象。

{ 
    Child o; 
    o.f(); 
} // 1 

{ 
    Parent * o = new Child; 
    delete o; 
} // 2 

{ 
    Child * o = new Child; 
    delete o; 
} // 3 

// 1o被破壞,完整的對象的析構函數Child被調用。由於Child繼承Parent,因此它會調用基礎對象析構函數,這是_ZN6ParentD2EvParent

// 2o是動態分配和刪除,並刪除析構函數的Child被調用。然後,它會調用Parent基礎對象析構函數。在這兩個中,都調用基礎對象析構函數。

// 3是相同的。它只相當於// 2,除了o的類型。


我測試它在Cygwin &克++ 4.8.3 &視窗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:} 

正如你所看到的,_ZN6ParentD2EvBase基本對象destructur,而Base別人不叫被調用。

然而,_ZN5ChildD0Ev,刪除的Child析構函數被調用了兩次和_ZN5ChildD1EvChild完整的對象的析構函數,被調用了三次,因爲有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的實現 - 爲了減少重複。

+0

這是否意味着當刪除一個對象時,被調用的唯一「刪除析構函數」是final/actual類型中的一個,而不是來自inherithance層次結構的任何其他類型?如果是這樣,那麼顯然它永遠不會被稱爲'Parent'。 – rcomblen 2014-09-04 10:11:04

+0

@rcomblen *當然*。 – ikh 2014-09-04 10:12:25

1

你不能有父對象,所以沒有。海灣合作委員會忽略了這種不必要的功能。優化器確實應該刪除它,因爲它沒有被使用,但是我發現GCC也有問題。

0

正如ikh所解釋的,當純虛擬父類具有虛擬析構函數時,D0析構函數是不必要地生成(並且不可用)。

但是,如果純虛父類有一個非虛析構函數,你可以刪除一個指向父類型,這調用父類的析構函數D0。當然,父類中的非虛擬析構函數很少需要或不打算,所以g ++會發出警告:[-Wdelete-non-virtual-dtor]

相關問題