Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 有没有一种方法可以称之为;删除析构函数";一个纯粹的虚拟类?_C++_C++11_Destructor_Gcov_Lcov - Fatal编程技术网

C++ 有没有一种方法可以称之为;删除析构函数";一个纯粹的虚拟类?

C++ 有没有一种方法可以称之为;删除析构函数";一个纯粹的虚拟类?,c++,c++11,destructor,gcov,lcov,C++,C++11,Destructor,Gcov,Lcov,我在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; d

我在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生成代码覆盖率报告。它报告从未调用符号为
\u ZN6ParentD0Ev
的析构函数,而调用符号为
\u ZN6ParentD2Ev
的析构函数

回答并报告
\u ZN6ParentD0Ev
是删除构造函数

是否有在
父类上调用此“删除析构函数”的情况?


附属问题:如果没有,是否有办法让gcov/lcov代码覆盖工具(在的回答后使用)忽略其报告中的符号?

您不能有父对象,因此没有。生成此不必要的函数是GCC的疏忽。优化器确实应该删除它,因为它没有使用,但我发现GCC在这方面也有问题。

我认为这是因为您有
子对象,而不是
父对象

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

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

{
    Child * o  = new Child;
    delete o;
} // 3
//1
中,销毁
o
,并调用
子对象的完整对象析构函数。由于
Child
继承了
Parent
,它将调用基本对象析构函数,即
\u ZN6ParentD2Ev
,属于
Parent

//2
中,动态分配和删除
o
,并调用
的删除析构函数。然后,它将调用父对象的基本对象析构函数。在这两种情况下,都会调用基本对象析构函数

//3
是相同的。除了
o
的类型外,它只与
/2
相同


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

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:}
如您所见,
\u ZN6ParentD2Ev
,即
base
的基本对象分解结构,被调用,而
base
的其他对象分解结构则未被调用

但是,删除
子对象的析构函数的
\u ZN5ChildD0Ev
被调用两次,而
\u ZN5ChildD1Ev
子对象的完整对象析构函数的
被调用三次,因为有
删除o
子对象o

但是根据我的解释,
\u zn5child40ev
应该被调用两次,而
\u zn5child1ev
应该被调用一次,不是吗?为了找出原因,我做了以下工作:

$ 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
00403c88:
403c88:55推力%ebp
403c89:89 e5移动百分比esp,%ebp
403c8b:83 ec 18子$0x18,%esp
403c8e:a1 20 80 40 00 mov 0x408020,%eax
403c93:8b 15 24 80 40 00 mov 0x408024%edx
403c99:83 C001添加$0x1,%eax
403c9c:83 D200 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
403加元:89 04 24 mov%eax,(%esp)
403cb0:e8 47 00 00呼叫403cfc
403cb5:a1 28 80 40 00 mov 0x408028,%eax
403cba:8b 15 2c 80 40 00 mov 0x40802c,%edx
403cc0:83 C001添加$0x1,%eax
403cc3:83 D200 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呼叫403680
403cdc:a1 30 80 40 00 mov 0x408030,%eax
403ce1:8b 15 34 80 40 00 mov 0x408034%edx
403ce7:83 C001添加$0x1,%eax
403cea:83 D200 adc$0x0,%edx
403ced:a3 30 80 40 00 mov%eax,0x408030
403cf2:89 15 34 80 40 00 mov%edx,0x408034
403cf8:c9离开
403cf9:c3 ret
403cfa:90无
403循环流化床:90无

是的,因为
\u ZN5ChildD0Ev
调用了
\u ZN5ChildD1Ev
\u ZN5ChildD1Ev
被调用了三次。(1+2)我想这只是GCC的实现——为了减少重复。

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


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

这是否意味着在删除对象时,调用的唯一“deleting destructor”是最终/实际类型之一,而不是继承层次结构中的任何其他类型?如果是这样,那么显然它永远不会在
父级上调用。答案也是“不,没有办法覆盖该函数?”你有没有想过如何让gcov忽略该符号?如果我记得清楚的话,我只是用标准的GCOV结构化注释忽略了析构函数的覆盖范围。你是说LCOV排除标记吗?好的,是的,我可以在派生类周围使用
LCOV\u EXCL\u START
LCOV\u EXCL\u STOP
来抑制它。