Есть ли способ вызвать «удаление деструктора»? чистого виртуального класса?

Я использую C ++ 11 и g ++ 4.8 на Ubuntu Trusty.

Посмотрите на этот фрагмент

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?) игнорировать этот символ в своем отчете?

10

Решение

Я думаю, это потому, что у вас есть 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, называется в то время как другие 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 — для уменьшения дублирования.

6

Другие решения

Вы не можете иметь родительские объекты, так что нет. Это недосмотр GCC, что эта ненужная функция генерируется. Оптимизатор действительно должен удалить его, так как он не используется, но я обнаружил, что у GCC есть проблемы и в этой области.

1

Как объяснил ikh, деструктор D0 генерируется излишне (и не может использоваться), когда чистый виртуальный родительский класс имеет виртуальный деструктор.

Однако, если чистый виртуальный родительский класс имеет невиртуальном деструктор, вы можете удалить указатель на родительский тип, и это будут вызвать деструктор родителя D0. Конечно, не виртуальные деструкторы в родительском классе редко желательны или предназначены, поэтому g ++ выдает предупреждение: [-Wdelete-non-virtual-dtor],

0
По вопросам рекламы [email protected]