Особенности использования виртуальной абстрактной функции в виртуальном деструкторе C++

Народ, хто-нить может объяснить почему линковщик вижуалки не видит виртуальную абстактную функциию в виртуальном деструкторе?

Этот код не собирается, падает линковщик с сообщением "error LNK2001: unresolved external symbol "public: virtual void __thiscall A::func(void)const " (?func@A@@UBEXXZ)"class A {public: virtual void func() const abstract; virtual ~A(){ func(); }

};

class B : public A {public: virtual void func() const override {}

};

void main() { A* a = new B(); a->func(); delete a;}Этот код собирается, но падает в рантайме с сообщением "pure virtual function call":class A {public: virtual void func() const abstract; void funcA() const { func(); }; virtual ~A(){ funcA(); }

};

class B : public A {public: virtual void func() const override {}

};

void main() { A* a = new B(); a->func(); delete a;}

Неужели в деструкторе таблица виртуальных методов уже уничтожена?

Последняя правка: ср, 06/04/2011 - 15:23
Submitted by Victor on

Комментарии

Quote:
Victor писал(а):
Этот код собирается:
Но валится в рантайме.
Submitted by Relyer on

Несколько не по теме, но я обычно на собеседовании задавал вопрос типа "какие проблемы вас ждут при использовании виртуальных функций в конструкторах/деструкторах". Т.е. не стоит такое вообще использовать Smile

Submitted by BLK Dragon on
Quote:
Relyer писал(а):
Quote:
Victor писал(а):
Этот код собирается:
Но валится в рантайме.
Переформулировал.
Submitted by Victor on
Ха ха ха Smile Витек, вруби уровень 4 варнингов и сам поймешь ответ
Ну во-первых, не стоит тянуть из джавы в плюсы такие вещи как abstract и override (C4481)
нормальная абстракция: virtual void func() const = 0;
почему не ругается? Smile а сам посмотри, чей деструктор будет вызываться при ~A() не виртуальном
он откомпилит норма, только как Саня сказал, упадет в рантайме

когда ты сделал виртуальный, то класс В унаследовал деструктор А, а реализации то нет Smile

Submitted by MaxImuS on
Максим, если бы все так было просто...

abstract и override это вижуаловские ключевые слова, они есть в ворнингах, но суть они не меняют в данном случае, они эквиваленты =0.

В классе B, фукнция func реализована.

Submitted by Victor on
Ты же сам написал код ошибки линковщика virtual void __thiscall A::func(void)const " (?func@A@@UBEXXZ)
где здесь происходит вызов перегруженного метода?

виртуальные деструкторы на производят override друг над другом, они вызываются все (если они виртуальные: от родителя к дочке)

правка: override - это не стандар с++ и студии (откуда ты это взял), это притянутая какая-то форма либо из каменного века или из других языков

Submitted by MaxImuS on
Quote:
MaxImuS писал(а):
где здесь происходит вызов перегруженного метода?
в строчке 5 должна вызваться функция из строчки 11, или я чегото недогоняю.

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

Хорошо, перепишим пример по другому.

class A {public: virtual void func() const =0; void funcA() const { func(); }; virtual ~A(){ funcA(); }

};

class B : public A {public: virtual void func() const {} virtual ~B(){ }

};

void main() { A* a = new B(); a->funcA(); ((B*) a)->~B();}

Другими словами меня интересует почему вызов функции func в 21 строчке проходит, а в 8 строчке, когда вызывается через деструктор (22 строчка), то происходит рантайм ошибка.

Submitted by Victor on
Пройдя по шагам у нас такая картина

a->funcA(); вызывается унаследованная функция класса В c с реализацией
с деструкторами я немного напутал, вызывается от дочки к родителю
а теперь смотри: реализация класса В в деструкторе А уже отсутствет -> связь уже разрушена с дочкой
осталась только А::func

вывод?

Submitted by MaxImuS on
> Другими словами меня интересует почему вызов функции funcA в 21 строчке проходит, > а в 8 строчке, когда вызывается через деструктор (22 строчка), то происходит рантайм ошибка. Да потому что никто не гарантирует, что vtbl будет в валидном состоянии в деструкторе объекта унаследованного класса. Поэтому при вызове вирт.функи из деструктора получишь эффекты в диапазоне от "странная-фигня-происходит" до "краш".
Submitted by BLK Dragon on
Quote:
MaxImuS писал(а):
вывод?
Логично... Хотя и не ожиданно-с...
Вот жеж хрень (

Всем спасибо.

Submitted by Victor on
Да глупо полагатся на то что виртуальная таблица создается до а удаляется после удаления обьекта Smile особенно в vc++

Тоже самое и про конструкторы и this в нем Smile

Submitted by Relyer on

> Тоже самое и про конструкторы и this в нем

Как раз this в конструкторе гарантированно валиден ибо память под объект выделена _до_ вызова конструктора. Аналогично, this валиден в деструкторе ибо память удаляется _после_ вызова деструктора/ов.

Виртуальная таблица создаётся ещё до входа в main, вот указатель на неё в объекте создаётся в неопределённый момент времени. Как бы логично чтобы до конструкторов, но может быть и после Smile

Submitted by BLK Dragon on

От жаж втиснулся - уже хотел бегать кричать что идиоты Wink

>>Виртуальная таблица создаётся ещё до входа в main, вот указатель на неё в объекте создаётся в неопределённый момент времени.

Не совсем - vptr перед входом в конструктор валиден - просто он указывает на vtbl для *текущего* класса а не итогового.

Submitted by discouraged_one (не проверено) on
> Не совсем - vptr перед входом в конструктор валиден -

> просто он указывает на vtbl для *текущего* класса а не итогового.

Это -- "валиден"? Smile

Не т.е. да, можно довольно осознанно писать код, который даже будет работать. Но как то стрёмно.

Submitted by BLK Dragon on
нуууу... валиден да ;-)))

я прицепился к неопределенному моменту во времени - там всё определено и валидно - просто не то что ожидают простые смертные Wink

Submitted by discouraged_one (не проверено) on
Quote:
discouraged_one писал(а):
я прицепился к неопределенному моменту во времени - там всё определено и валидно - просто не то что ожидают простые смертные ;-)
Я так понимаю это поведение зависит сугубо от компилятора?
Submitted by Victor on
> я прицепился к неопределенному моменту во времени -

> там всё определено и валидно - просто не то что ожидают простые смертные

В стандарте вроде как не определено, посему per-compiler/per-platform hack.

Короче, не нужно вызывать виртуальных функций в конструкторе, и в деструкторе тоже не нужно.

Submitted by BLK Dragon on

Не-не-не

в стандарте всё написано (даже если между строк ;-))

Но

>> Короче, не нужно вызывать виртуальных функций в конструкторе, и в деструкторе тоже не нужно.

чертовски правильно Wink

Submitted by discouraged_one (не проверено) on
> Не-не-не

> в стандарте всё написано (даже если между строк )

OK, я буду занудствовать до конца :)

Номера пунктов стандарта (касательно темы обсуждения) в студию!

Submitted by BLK Dragon on

12.7 Construction and destruction [class.cdtor]

-1- For an object of non-POD class type (clause class), before the constructor begins execution and after the destructor finishes execution, referring to any nonstatic member or base class of the object results in undefined behavior.

-3- Member functions, including virtual functions (class.virtual), can be called during construction or destruction (class.base.init). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor's own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor's class, or overriding it in one of the other base classes of the most derived object (intro.object). If the virtual function call uses an explicit class member access (expr.ref) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor's own class or one of its bases, the result of the call is undefined.

(devil)

Submitted by discouraged_one (не проверено) on

Я жутко извиняюсь, не подумайте, что я тролль, но я хотел бы спросить (не применительно к данному случаю):

Что такое дает ООП, чего нельзя получить без оного?

Я им пользуюсь только в случае крайней необходимости. Глупо, по-моему, создавать класс Тойота на основе класса Автомобиль, если Тойота будет использоваться только один раз, а в другом проекте мне нужна будет Хонда.

Ну собственно, вижуалка ведёт себя по стандарту, даже зануляет виртуальные методы в vtbl в деструкторе, я думаю что vc++ это делает в дебаге, в релизе это лишние накладные расходы поэтому код топикстартера собраный в релизе скорее всего прокатит.

Submitted by Relyer on
-3- Member functions, including virtual functions (class.virtual), can be called during construction or destruction (class.base.init). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor's own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor's class, or overriding it in one of the other base classes of the most derived object (intro.object). If the virtual function call uses an explicit class member access (expr.ref) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor's own class or one of its bases, the result of the call is undefined. О. Это оно :) Behaviour, несомненно, defined. Однако чем объяснять кому как не отстрелить ногу, проще запретить такое вообще, что в-общем и практикуется повсеместно :)
Submitted by BLK Dragon on
> Что такое дает ООП, чего нельзя получить без оного? Ты уверен, что пользуешься ООП? Toyota от Honda в примере, скорее всего будет отличаться только рядом пропертей (т.е. данными) без изменения функционала (т.е. кода).
Submitted by BLK Dragon on
Quote:
Come-from-Beyond писал(а):
Что такое дает ООП, чего нельзя получить без оного?
ООП это просто подход к разработке, по хорошему можно писать и на ассемблере, используя ООП Biggrin во загнул.

ПЫСЫ: если хочешь нормальные ответы на вопросы, заканчивай офтопить, создаешь тему - обсуждаем.

Submitted by Relyer on

GameDev.by