簡體   English   中英

有沒有辦法調用純虛擬類的“刪除析構函數”?

[英]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

// 1o被銷毀,並且調用了Child完整對象析構函數 由於Child繼承Parent ,它會調用基對象的析構 ,這是_ZN6ParentD2EvParent

// 2o被動態分配和刪除,並且調用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析構函數,被調用兩次, _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的實現 - 減少重復。

你不能擁有Parent對象,所以沒有。 GCC監督是產生這種不必要的功能。 優化器真的應該刪除它,因為它沒有使用,但我發現GCC也存在問題。

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

但是,如果純虛擬父類具有非虛擬析構函數,則可以刪除指向父類型的指針,這調用父虛擬機的D0析構函數。 當然,父類中的非虛擬析構函數很少是可取的或預期的,因此g ++會發出警告: [-Wdelete-non-virtual-dtor]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM