virtual деструктор
От: yaser Украина  
Дата: 09.10.07 20:11
Оценка:
возникла какая-то проблема, но не могу для себя обьяснить ее. прошу помощи

class a {
public:
std::string g,t,r;
private:
...
};

class b: virtual public a {
public:
...
private:
....
};

a* _var = dynamic_cast<a*>(new b());
delete _var;

Деструкторов в классе а и в как вы видите нет. Кто как считает, должен ли я определить деструктор пустой или нет?
Если да, то почему?
Re: virtual деструктор
От: VoidEx  
Дата: 09.10.07 20:32
Оценка:
Здравствуйте, yaser, Вы писали:


Y>Деструкторов в классе а и в как вы видите нет. Кто как считает, должен ли я определить деструктор пустой или нет?

Y>Если да, то почему?
Более того, еще и виртуальный.
Строка delete _var вызовет деструктор a::~a(), и если он невиртуален, то корректного разрушения объекта не будет,
если он будет виртуальным, то на деле вызовется b::~b() (как и для обычных виртуальных функций) и все будет нормально.
Re: Потому, что С++
От: Erop Россия  
Дата: 09.10.07 21:40
Оценка:
Здравствуйте, yaser, Вы писали:

Y>Если да, то почему?

Если коротко, то "птому что С++"

А если длинно, то удалять можно только то, что ты получил из new. Тот же тип и значение указателя!

Иначе можно нарваться. Никто ведь не обещал, например, что указатель на A будет указтелм на начало блока...
Попробуй так, например:
B b;
B* pB = &b;
A* pA = pB;
void* p1 = pb;
void* p2 = pa;
std::cout << "pB = " << (unsigned int)p1 << "pA = " << (unsigned unt)p2;


Кстати, пользуйся тэгами [c] тут код [/c], так намного удобнее читать твои посты будет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Потому, что С++
От: yaser Украина  
Дата: 10.10.07 06:59
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, yaser, Вы писали:


Y>>Если да, то почему?

E>Если коротко, то "птому что С++"

E>А если длинно, то удалять можно только то, что ты получил из new. Тот же тип и значение указателя!


E>Иначе можно нарваться. Никто ведь не обещал, например, что указатель на A будет указтелм на начало блока...

E>Попробуй так, например:
B b;
E>B* pB = &b;
E>A* pA = pB;
E>void* p1 = pb;
E>void* p2 = pa;
E>std::cout << "pB = " << (unsigned int)p1 << "pA = " << (unsigned unt)p2;


E>Кстати, пользуйся тэгами [c] тут код [/c], так намного удобнее читать твои посты будет...


А почему так важно чтобы отработал деструктор дочернего класса? ведь мне он не нужен. В деструкторе дочернего класса что-то разрушается?
Если указатель класса А указывает не на начало, то я лишь потеряю несколько байт класса ( утечка памяти ), но не сбой же программы должен быть.
Re: virtual деструктор
От: dvg  
Дата: 10.10.07 07:09
Оценка: -1
В данном случае можно не обьявлять, если все, что есть из членов класса — это указанный std::string g,t,r или подобные интегральный или простые типы. Однако при появлении в составе класса казателей на динамически создаваемые объекты без деструктора лучше не обходиться

Ответ на вопрос о том, виртуальным ли его делать для базового класса зависит от содержания базового и производных классов.

И, кстати, зачем у тебя виртуальное наследование class b : virtual class a ? Ты планируешь множественное наследование?вмп
Re[3]: Потому, что С++
От: Erop Россия  
Дата: 10.10.07 07:53
Оценка: 1 (1)
Здравствуйте, yaser, Вы писали:

Y>А почему так важно чтобы отработал деструктор дочернего класса? ведь мне он не нужен. В деструкторе дочернего класса что-то разрушается?

Дело не в деструкторе, а в том, как работает куча...

Y>Если указатель класса А указывает не на начало, то я лишь потеряю несколько байт класса ( утечка памяти ), но не сбой же программы должен быть.

Менеджер памяти работает так, что может освободить только весь выделенный блок целиком.
Делать так как ты хочешь, нельзя, в том числе и потому же, почему нельзя делать так:
char* p = new char[20];
char* pp = p + 10;
delete [] pp;


Вторая причина тоньше, но тоже важная. В принципе, когда ты создаёшь полиморфные объекты, компилятор что-то делает "в тихую", чтобы обеспечить полиморфность. И при этом он вправе расчитывать на корректное удаление таких объектов тобой...

Но в целом я тебе советую остановиться пока на ответе "потому что С++", и тратить время на более систематическое изучение языка, а не на выяснение тонкостей реализации.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Потому, что С++
От: yaser Украина  
Дата: 10.10.07 14:55
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, yaser, Вы писали:


Y>>А почему так важно чтобы отработал деструктор дочернего класса? ведь мне он не нужен. В деструкторе дочернего класса что-то разрушается?

E>Дело не в деструкторе, а в том, как работает куча...

Y>>Если указатель класса А указывает не на начало, то я лишь потеряю несколько байт класса ( утечка памяти ), но не сбой же программы должен быть.

E>Менеджер памяти работает так, что может освободить только весь выделенный блок целиком.
E>Делать так как ты хочешь, нельзя, в том числе и потому же, почему нельзя делать так:
char* p = new char[20];
E>char* pp = p + 10;
E>delete [] pp;


E>Вторая причина тоньше, но тоже важная. В принципе, когда ты создаёшь полиморфные объекты, компилятор что-то делает "в тихую", чтобы обеспечить полиморфность. И при этом он вправе расчитывать на корректное удаление таких объектов тобой...


E>Но в целом я тебе советую остановиться пока на ответе "потому что С++", и тратить время на более систематическое изучение языка, а не на выяснение тонкостей реализации.


спасибо, меня ваш ответ полностью убедил.
Re: virtual деструктор
От: Roman Odaisky Украина  
Дата: 10.10.07 15:34
Оценка:
Здравствуйте, yaser, Вы писали:

[]

struct A { . . . };
struct AImpl: A { . . . };

A* makeA()
{
    return new AImpl();
}

delete makeA();

Здесь требуется виртуальный деструктор A::~A(). В этом случае поведение программы полностью определено. Иначе компилятор снимает с себя всякую ответственность.

Стандарт ISO/IEC 14882:2003, п. 5.3.5/3:

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.


Идея в том, что компилятор выделил блок памяти для производного класса, потом привел указатель к типу базового, вследствие чего указатель мог уехать на пару байт в сторону. Компилятор должен найти по указателю начало блока памяти, и перед освобождением памяти он должен вызвать правильный деструктор, для чего последний должен быть виртуальным.

Но ты, как было правильно сказано, не углубляйся (пока) в детали реализации, а помни одно: delete можно применять или к тому же, что вернул new, или к указателю типа базового класса, который обладает виртуальным деструктором, delete [] — только к результату вызова new []. Причины тому есть, и существенные, но знать их не очень обязательно.

struct Base { };
struct VBase { virtual ~VBase() { } };

struct Derived: Base { };
struct DerivedV: VBase { };
struct VDerived: Base { virtual ~VDerived() { } };

delete new Base; // OK
delete new VBase; // OK

delete static_cast<Base *>(new Derived); // undefined behaviour
delete static_cast<Base *>(new VDerived); // undefined behaviour
delete static_cast<VBase *>(new DerivedV); // OK

delete [] new ЧтоУгодно[42]; // OK

VBase* bases = new DerivedV[42];
delete bases; // undefined behaviour

Y>А почему так важно чтобы отработал деструктор дочернего класса? ведь мне он не нужен. В деструкторе дочернего класса что-то разрушается?
Y>Если указатель класса А указывает не на начало, то я лишь потеряю несколько байт класса ( утечка памяти ), но не сбой же программы должен быть.

В этом случае наступает страшное Неопределенное Поведение. Т. е., компилятор вправе интерпретировать такой (ошибочный) код как ему вздумается. В том числе отформатировать жесткий диск ;-). В числе прочего, он может допустить утечку памяти, вылететь из программы со сбоем, или — частный случай неопределенного поведения — работать так, как и ожидал программист. Последнее — хуже всего, т. к. программист не станет исправлять ошибку, и другие платформа, компилятор, фаза луны вызовут сбой, причину которого придется долго искать.
До последнего не верил в пирамиду Лебедева.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.