Я использую 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?) игнорировать этот символ в своем отчете?
Я думаю, это потому, что у вас есть 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 — для уменьшения дублирования.
Вы не можете иметь родительские объекты, так что нет. Это недосмотр GCC, что эта ненужная функция генерируется. Оптимизатор действительно должен удалить его, так как он не используется, но я обнаружил, что у GCC есть проблемы и в этой области.
Как объяснил ikh, деструктор D0 генерируется излишне (и не может использоваться), когда чистый виртуальный родительский класс имеет виртуальный деструктор.
Однако, если чистый виртуальный родительский класс имеет невиртуальном деструктор, вы можете удалить указатель на родительский тип, и это будут вызвать деструктор родителя D0. Конечно, не виртуальные деструкторы в родительском классе редко желательны или предназначены, поэтому g ++ выдает предупреждение: [-Wdelete-non-virtual-dtor]
,