首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否有一种方法来调用纯虚拟类的“删除析构函数”?

是否有一种方法来调用纯虚拟类的“删除析构函数”?
EN

Stack Overflow用户
提问于 2014-09-04 09:42:23
回答 3查看 2.5K关注 0票数 11

我在Ubuntu上使用了C++11和g++4.8。

考虑一下这个片段

代码语言:javascript
复制
class Parent {
public:
    virtual ~Parent() =  default;
    virtual void f() = 0;
};

class Child: public Parent {
public:
    void f(){}
};

调用使用

代码语言:javascript
复制
{
    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?的答案后面使用)在其报告中忽略该符号?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-09-04 09:55:56

我认为这是因为您有Child对象,而不是Parent对象。

代码语言:javascript
复制
{
    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的基本对象析构函数。在这两种情况下,都调用基对象析构函数。

// 3是一样的。它只是等于// 2,除了o的类型。

我已经在cygwin & g++ 4.8.3和windows 7 x86 SP1上测试了它。这是我的测试代码。

代码语言:javascript
复制
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选项:

代码语言:javascript
复制
$ g++ -std=c++11 -fprofile-arcs -ftest-coverage -O0 test.cpp -o test
$ ./test
$ gcov -b -f test.cpp

结果是这样的。

代码语言:javascript
复制
        -:    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的基本对象destructur )被调用,而Base的其他对象则没有被调用。

然而,_ZN5ChildD0Ev,删除Child的析构函数,被调用两次,_ZN5ChildD1EvChild的完整对象析构函数,被调用三次,因为有delete o;Child o;

但是根据我的解释,_ZN5ChildD0Ev应该被叫两次,_ZN5ChildD1Ev应该叫做一次,不是吗?为了找出原因,我做了这个:

代码语言:javascript
复制
$ objdump -d test > test.dmp

结果:

代码语言:javascript
复制
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的实施--为了减少重复。

票数 6
EN

Stack Overflow用户

发布于 2014-09-04 09:54:45

你不能有父对象,所以没有。这是GCC的疏忽,这种不必要的功能产生了。优化器确实应该删除它,因为它是未使用的,但我发现GCC也有问题。

票数 2
EN

Stack Overflow用户

发布于 2015-09-06 17:31:25

正如ikh所解释的,当纯虚拟父类有一个虚拟析构函数时,就会不必要地生成(且不可用) D0析构函数。

但是,如果纯虚拟父类具有非虚拟析构函数,则可以删除指向父类型的指针,这将调用父类的D0析构函数。当然,父类中的非虚拟析构函数很少是理想的或有意的,因此g++发出警告:[-Wdelete-non-virtual-dtor]

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25662174

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档