2014-09-09 2 views
2

Я компилирую программу на C++ для ядра NIOS II с очень ограниченной памятью. Так как это встроенная система, мы также не используем кучу. Поскольку мы добавили наследование к нашему коду, мы увидели, что malloc и свободные (и связанные функции) были встроены в наш бинарный файл, увеличив его размер на несколько килобайт.Почему деструктор базового класса звонит бесплатно

У нас есть один чистый виртуальный базовый класс, StatisticsApi и один производный класс Statistics. Статистика, и оба они имеют виртуальные деструкторы.

Мы собираем с Alteras GCC с fno-rtti и -fno_expections и также определяем наш собственный __cxa_pure_virtual().

Вглядываясь в него, мы видим, что delete вызывается из деструкторов (и, следовательно, свободен). Почему это? Какую память он пытается освободить?

Вот ассемблер для деструкторов:

0000c6b8 <_ZN7Namespace12StatisticsD0Ev>: 
    c6b8: defffd04 addi sp,sp,-12 
    c6bc: dfc00215 stw ra,8(sp) 
    c6c0: df000115 stw fp,4(sp) 
    c6c4: df000104 addi fp,sp,4 
    c6c8: e13fff15 stw r4,-4(fp) 
    c6cc: 00c000b4 movhi r3,2 
    c6d0: 18c74b04 addi r3,r3,7468 
    c6d4: e0bfff17 ldw r2,-4(fp) 
    c6d8: 10c00015 stw r3,0(r2) 
    c6dc: e13fff17 ldw r4,-4(fp) 
    c6e0: 000c7f00 call c7f0 <_ZN7Namespace15StatisticsApiD2Ev> 
    c6e4: 00800044 movi r2,1 
    c6e8: 10803fcc andi r2,r2,255 
    c6ec: 1005003a cmpeq r2,r2,zero 
    c6f0: 1000021e bne r2,zero,c6fc <_ZN7LinCtrl12TxStatisticsD0Ev+0x44> 
    c6f4: e13fff17 ldw r4,-4(fp) 
    c6f8: 000c8e00 call c8e0 <_ZdlPv> 
    c6fc: e037883a mov sp,fp 
    c700: dfc00117 ldw ra,4(sp) 
    c704: df000017 ldw fp,0(sp) 
    c708: dec00204 addi sp,sp,8 
    c70c: f800283a ret 


0000c710 <_ZN7Namespace12StatisticsD1Ev>: 
    c710: defffd04 addi sp,sp,-12 
    c714: dfc00215 stw ra,8(sp) 
    c718: df000115 stw fp,4(sp) 
    c71c: df000104 addi fp,sp,4 
    c720: e13fff15 stw r4,-4(fp) 
    c724: 00c000b4 movhi r3,2 
    c728: 18c74b04 addi r3,r3,7468 
    c72c: e0bfff17 ldw r2,-4(fp) 
    c730: 10c00015 stw r3,0(r2) 
    c734: e13fff17 ldw r4,-4(fp) 
    c738: 000c7f00 call c7f0 <_ZN7Namespace15StatisticsApiD2Ev> 
    c73c: 0005883a mov r2,zero 
    c740: 10803fcc andi r2,r2,255 
    c744: 1005003a cmpeq r2,r2,zero 
    c748: 1000021e bne r2,zero,c754 <_ZN7Namespace12StatisticsD1Ev+0x44> 
    c74c: e13fff17 ldw r4,-4(fp) 
    c750: 000c8e00 call c8e0 <_ZdlPv> 
    c754: e037883a mov sp,fp 
    c758: dfc00117 ldw ra,4(sp) 
    c75c: df000017 ldw fp,0(sp) 
    c760: dec00204 addi sp,sp,8 
    c764: f800283a ret 


0000c768 <_ZN7Namespace12StatisticsD2Ev>: 
    c768: defffd04 addi sp,sp,-12 
    c76c: dfc00215 stw ra,8(sp) 
    c770: df000115 stw fp,4(sp) 
    c774: df000104 addi fp,sp,4 
    c778: e13fff15 stw r4,-4(fp) 
    c77c: 00c000b4 movhi r3,2 
    c780: 18c74b04 addi r3,r3,7468 
    c784: e0bfff17 ldw r2,-4(fp) 
    c788: 10c00015 stw r3,0(r2) 
    c78c: e13fff17 ldw r4,-4(fp) 
    c790: 000c7f00 call c7f0 <_ZN7Namespace15StatisticsApiD2Ev> 
    c794: 0005883a mov r2,zero 
    c798: 10803fcc andi r2,r2,255 
    c79c: 1005003a cmpeq r2,r2,zero 
    c7a0: 1000021e bne r2,zero,c7ac <_ZN7Namespace12StatisticsD2Ev+0x44> 
    c7a4: e13fff17 ldw r4,-4(fp) 
    c7a8: 000c8e00 call c8e0 <_ZdlPv> 
    c7ac: e037883a mov sp,fp 
    c7b0: dfc00117 ldw ra,4(sp) 
    c7b4: df000017 ldw fp,0(sp) 
    c7b8: dec00204 addi sp,sp,8 
    c7bc: f800283a ret 

А вот деструкторы базового класса:

0000c7f0 <_ZN7Namespace15StatisticsApiD2Ev>: 
    c7f0: defffd04 addi sp,sp,-12 
    c7f4: dfc00215 stw ra,8(sp) 
    c7f8: df000115 stw fp,4(sp) 
    c7fc: df000104 addi fp,sp,4 
    c800: e13fff15 stw r4,-4(fp) 
    c804: 00c000b4 movhi r3,2 
    c808: 18c76204 addi r3,r3,7560 
    c80c: e0bfff17 ldw r2,-4(fp) 
    c810: 10c00015 stw r3,0(r2) 
    c814: 0005883a mov r2,zero 
    c818: 10803fcc andi r2,r2,255 
    c81c: 1005003a cmpeq r2,r2,zero 
    c820: 1000021e bne r2,zero,c82c <_ZN7Namespace15StatisticsApiD2Ev+0x3c> 
    c824: e13fff17 ldw r4,-4(fp) 
    c828: 000c8e00 call c8e0 <_ZdlPv> 
    c82c: e037883a mov sp,fp 
    c830: dfc00117 ldw ra,4(sp) 
    c834: df000017 ldw fp,0(sp) 
    c838: dec00204 addi sp,sp,8 
    c83c: f800283a ret 


0000c840 <_ZN7LinCtrl15TxStatisticsApiD0Ev>: 
    c840: defffd04 addi sp,sp,-12 
    c844: dfc00215 stw ra,8(sp) 
    c848: df000115 stw fp,4(sp) 
    c84c: df000104 addi fp,sp,4 
    c850: e13fff15 stw r4,-4(fp) 
    c854: 00c000b4 movhi r3,2 
    c858: 18c76204 addi r3,r3,7560 
    c85c: e0bfff17 ldw r2,-4(fp) 
    c860: 10c00015 stw r3,0(r2) 
    c864: 00800044 movi r2,1 
    c868: 10803fcc andi r2,r2,255 
    c86c: 1005003a cmpeq r2,r2,zero 
    c870: 1000021e bne r2,zero,c87c <_ZN7Namespace15StatisticsApiD0Ev+0x3c> 
    c874: e13fff17 ldw r4,-4(fp) 
    c878: 000c8e00 call c8e0 <_ZdlPv> 
    c87c: e037883a mov sp,fp 
    c880: dfc00117 ldw ra,4(sp) 
    c884: df000017 ldw fp,0(sp) 
    c888: dec00204 addi sp,sp,8 
    c88c: f800283a ret 


0000c890 <_ZN7Namespace15StatisticsApiD1Ev>: 
    c890: defffd04 addi sp,sp,-12 
    c894: dfc00215 stw ra,8(sp) 
    c898: df000115 stw fp,4(sp) 
    c89c: df000104 addi fp,sp,4 
    c8a0: e13fff15 stw r4,-4(fp) 
    c8a4: 00c000b4 movhi r3,2 
    c8a8: 18c76204 addi r3,r3,7560 
    c8ac: e0bfff17 ldw r2,-4(fp) 
    c8b0: 10c00015 stw r3,0(r2) 
    c8b4: 0005883a mov r2,zero 
    c8b8: 10803fcc andi r2,r2,255 
    c8bc: 1005003a cmpeq r2,r2,zero 
    c8c0: 1000021e bne r2,zero,c8cc <_ZN7Namespace15StatisticsApiD1Ev+0x3c> 
    c8c4: e13fff17 ldw r4,-4(fp) 
    c8c8: 000c8e00 call c8e0 <_ZdlPv> 
    c8cc: e037883a mov sp,fp 
    c8d0: dfc00117 ldw ra,4(sp) 
    c8d4: df000017 ldw fp,0(sp) 
    c8d8: dec00204 addi sp,sp,8 
    c8dc: f800283a ret 

И, наконец, удалить:

0000c8e0 <_ZdlPv>: 
    c8e0: 20000126 beq r4,zero,c8e8 <_ZdlPv+0x8> 
    c8e4: 000ebf01 jmpi ebf0 <free> 
    c8e8: f800283a ret 

И вот ссылка на набор инструкций для NIOS II для справки: http://www.altera.com/literature/hb/nios2/n2cpu_nii51017.pdf

Два решения/обходные мы обнаружили, были:

  1. Переосмысление новые/удалить в пустые функции, таким образом, избегая вызова, чтобы освободить (без кучи, чтобы они не следует назвать в любом случае!).

  2. Оставляя деструкторы неопределенными в C++, что делает их не получающими , созданных в ассемблере. Хотя компилятор будет жаловаться , что виртуального деструктора нет.

Обходные пути или нет, почему деструкторы звонят бесплатно? Какая память она пытается освободить!?! У нас есть другие деструкторы, которые не являются ни базовыми классами, ни производными классами, и они не называются свободными. Это как такое деструктор выглядит следующим образом:

00005194 <_ZN7Namespace16OtherClassD2Ev>: 
    5194: defffe04 addi sp,sp,-8 
    5198: df000115 stw fp,4(sp) 
    519c: df000104 addi fp,sp,4 
    51a0: e13fff15 stw r4,-4(fp) 
    51a4: e037883a mov sp,fp 
    51a8: df000017 ldw fp,0(sp) 
    51ac: dec00104 addi sp,sp,4 
    51b0: f800283a ret 

000051b4 <_ZN7Namespace16OtherClassD1Ev>: 
    51b4: defffe04 addi sp,sp,-8 
    51b8: df000115 stw fp,4(sp) 
    51bc: df000104 addi fp,sp,4 
    51c0: e13fff15 stw r4,-4(fp) 
    51c4: e037883a mov sp,fp 
    51c8: df000017 ldw fp,0(sp) 
    51cc: dec00104 addi sp,sp,4 
    51d0: f800283a ret 

А также, в общем, почему существует несколько функций для каждого деструктора (D0, D1 и D2)?

+0

Вы оптимизируете ('gcc -O2')? Вы рассмотрели возможность обновления вашего компилятора GCC (до 4.9)? И, пожалуйста, покажите некоторые из ваших занятий! –

+0

Вы также можете посмотреть промежуточное представление gimple ('gcc -O -fdump-tree-gimple' или' -fdump-tree-all'), которое производит * много * дамп-файлов. –

+0

BTW, как только вы поймете оскорбительный прыжок, вы можете использовать [MELT] (http://gcc-melt.org/), чтобы обнаружить их с помощью [findgimple] (http://gcc-melt.org/tutousemelt. html). –

ответ

1

Когда вы вызываете delete на объект с виртуальным деструктором, он ищет функцию для вызова в виртуальной таблице. Эта функция вызывает деструктор, а также освобождает память, поскольку только на производном уровне он знает правильный указатель, чтобы перейти к свободной функции.

Проблема в том, что компилятор не знает, что вы никогда не вызываете delete. Он по-прежнему должен создавать код, даже если он никогда не будет использоваться, как и любая другая виртуальная функция.

Хотя, как правило, деструкторы должны быть виртуальными в классах с другими виртуальными функциями, это может быть допустимым исключением. Обеспечение защиты деструктора в базовом классе поможет избежать ошибок.

+0

Вон - Я думаю, что это ответ на вопрос. Но я не понимаю первое предложение! «Когда вы вызываете delete на объекте с виртуальным деструктором, он ищет функцию для вызова в виртуальной таблице». Вы имеете в виду, что delete просматривает его и вызывает специальный конструктор, который затем вызывает удаление других вещей? Что нужно для освобождения - ах - может быть, если вы назовете delete на базовом классе? Является ли этот материал стандартным где-то - я изо всех сил пытаюсь найти его документально зарегистрированным;) Спасибо! – JCx

+0

http://ehsanakhgari.org/blog/2012-12-11/c-deleting-destructors очень актуально. Может быть, у меня есть ответ :) – JCx

Смежные вопросы