[с++] виртуальный деструктор

erotic

 
#include <iostream>
using namespace std;
class Abstract1
{
public:
~Abstract1{}
};
class Abstract2
{
public:
virtual ~Abstract2{}
};
class Derive : public Abstract1, public Abstract2
{
~Derive{cout<<"Derive destructor";}
};
int main
{
Abstract1* p = new Derive;
delete p;
return 0;
}

При запуске вылетает ошибка
Debug Assertion Failed!
Program: ...
File: dbgdeI.cpp
Line: 52
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBIockUse)
For information on how your program can cause an assertion
failure, see the VisuaI C++ documentation on asserts.
(Press Retry to debug the application)

Причем если virtual поставить перед деструктором Abstract1 (неважно, стоит ли при этом virtual перед деструктором Abstract2 или нет то нормально вызывается деструктор Derive.
В чем дело?

ppplva

Abstract1* p = new Derive;
delete p;
Само собой, если в Abstact1 деструктор не виртуальный, то именно он и вызовется вместо деструктора потомка.

erotic

Хорошо, если было бы так!
Но вылетает Debug error, код я привел выше.

vladan67

ошибка с памятью.. какой-то объект залез на другой и его хвост был удален...

erotic

Бррр.... что еще за ошибка с памятью и откуда она могла возникнуть?
Приведен код полностью, больше в программе ничего нет.

okunek

Посмотри, какой указатель возвращает new (не Abstract1 *p, а именно что вылазит из самой реализации new) и что запихивается в delete
Сразу встанет все на свои места

erotic

Из new возвращается точно такой же указатель, который передается в delete. Если, конечно, я правильно тебя понял.

Slavaga

Возможно, немного оффтоп, но существует мнение, что множественное наследование - не очень хороший тон программирования и в большинстве случаев можно обойтись без него ("Совершенный код", Мак Коннелл).

okunek

Ну если из new вылез бы тот же самый указатель, что и залез в delete, то и ошибки никакой не было... что на самом деле хуже, если бы она была, т.к. по крайней мере, ты узнал, что у тебя косяк.

erotic

Я смотрел, что передается в реализацию delete и что возвращается из new. Дебаггером. Абсолютно одинаковые указатели...

stm8808080

Я не спец в плюсах, но казалось бы, если метод не виртуальный, то вставляется вызов, соответствующий статическому типу переменной (в данном случае Abstract1* а не динамическому (Derive*). То есть вызовется деструктор класса Abstract1, который неправильно почистит память, так как на самом деле там объект класса Derive.

erotic

Какую еще память он тебе должен почистить? Насколько я понимаю, delete действительно должна вызвать деструктор Abstract1, который ничего не делает, после чего пометить память как свободную, но не давать при этом сбоев.

stm8808080

хм. Определенно загадочный язык.
А как при уничтожении объекта определяется его тип?
Скомпилил gcc — никаких ассертов не печатает.

stm8823636

Собери под релизом. Думать будет легче. Действительно вызывается только деструктор ~Abstract1.

erotic

Если объект уничтожается, как статический, то его тип известен.
Если через указатель, то тип определяется через тип переменной, передаваемой оператору delete.
Обзовем его Class. Если при этом у какого-либо из предков Class был объявлен виртуальный деструктор, то перед освобождением памяти вызывается деструктор по таблице виртуальных функций, и он будет соответствовать фактическому типу объекта. Если виртуальных деструкторов не было, то вызывается деструтор типа Class.
А вот что происходит со множественным наследованием, я не совсем понимаю. Полистал Страуструпа, но пока не нашел (

stm8808080

Если через указатель, то тип определяется через тип переменной, передаваемой оператору delete.
То есть, в данном случае, Abstract1. Объект класса Derive убивается как если бы он был Abstract1.

erotic

Ну да, в общем-то. Т.ч. я не вижу никаких причин возникать ошибке памяти.

ppplva

Издеваешься ?
У тебя 1) ошибка в программе, 2) на нее ругается компилятор (или хз кто).
Что непонятно-то ?

stm8808080

Ну когда классы пустые — вроде как неоткуда (хотя фиг знает, как они там все реализовали).
А представь, Derive будет толстым, а удалится пустой Abstract1. Мож runtime перестраховывается

evgen5555

Дебажный delete "видит" неиспользуемые программой блоки (потипу GC) в таких тривиальных случаях и ругается.

erotic

Ты это сейчас от балды написал?
1. Ошибки нет.
2. Компилятор не ругается даже ворнингами.

erotic

Хз, может быть. По крайней мере, звучит правдоподобно.

ermsoft

Гм.

The C++ language standard is unusually clear on this topic. When you try to delete a derived class object through a base class pointer and the base class has a nonvirtual destructor (as EnemyTarget does the results are undefined. That means compilers may generate code to do whatever they like: reformat your disk, send suggestive mail to your boss, fax source code to your competitors, whatever.
Оставить комментарий
Имя или ник:
Комментарий: