а вот не надо из конструкторов/деструкторов вызывать виртуальные функции.
в контрукторе базового наследник еще не создан.
в деструкторе базового -- уже разрушен.
такие дела.
Re: Почему не вызывается перегруженная виртуальная функция?
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Symon, Вы писали:
S>>Че это за бред такой???
J>А слабо было в поиске написать "виртуальная функция в деструкторе"?
Хорошо хоть не читайте Страуструпа
Re[2]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Symon, Вы писали:
J>>А слабо было в поиске написать "виртуальная функция в деструкторе"?
S>Хорошо хоть не читайте Страуструпа :)
Если бы ты задал этот вопрос ему, я думаю, такой ответ от него был бы вполне логичным.
Поиск на этом сайте сделан специально для того, чтобы людям не приходилось по сто раз отвечать на одни и те же вопросы.
Даже кнопочка есть: "Найти ответ".
Имей уважение к тем, к кому обращаешься за помощью, цени их время.
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Symon, Вы писали:
S>>Че это за бред такой??? B>Виртуальные вызовы не работают в конструкторах и деструкторе. Это обсуждалось не один раз.
На самом деле работают, более того, работают именно как
виртуальные, просто вызывается последний override на момент
создания объекта класса исполняемого конструктора/деструктора.
Лазар
Re[3]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>На самом деле работают, более того, работают именно как ЛБ>виртуальные, просто вызывается последний override на момент ЛБ>создания объекта класса исполняемого конструктора/деструктора.
В моем понимании фраза "работают именно как виртуальные" подразумевает, что в общем случае механизмы вызова виртуальной и "обычной" функции в конструкторе/деструкторе могут отличаться. Если именно это имелось ввиду, то нельзя ли привести пример, иллюстрирубщий это утверждение? Если нет, то фраза "работают именно как виртуальные" лишена смысла, ИМХО.
Я что-то упустил?
Любите книгу — источник знаний (с) М.Горький
Re[3]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>На самом деле работают, более того, работают именно как ЛБ>виртуальные, просто вызывается последний override на момент ЛБ>создания объекта класса исполняемого конструктора/деструктора.
Для интереса посмотрел, что делает MS VC++ 7.1:
class Base
{
public:
virtual void Foo() { }
void Bar() { }
virtual ~Base()
{
Foo(); // 3
Bar(); // 4
}
};
class Derived: public Base
{
public:
virtual void Foo() { }
};
int main()
{
Base* pb = new Derived();
pb->Foo(); // 1
pb->Bar(); // 2delete pb;
return 0;
}
Строчки (1) и (2) породили следующий машинный код:
Видно, что в первом случае вызов производится "виртуальный" вызов (т.е. адрес функции берется из таблицы виртуальных методов объекта), а во втором случае — "обычный", т.е. по известному заранее (на момент компиляции) адресу. В-общем ничего удивительного, все как и ожидается.
Теперь смотрим, что получилось из строчек (3), (4) (вызовов виртуальной и невиртуальной функций в деструкторе):
Как видно из этого листинга, ни о каком виртуальном вызове (в случае с Foo), тут речи и не идет. Адрес берется не из vtable, а уже известен на момент компиляции. Поэтому фраза "работают именно как виртуальные", как справедливо заметил Bell, совершенно несправедлива.
Кстати если Foo сделать в классе Base чисто виртуальной, то получим ошибки линковки:
Win32Console error LNK2019: unresolved external symbol "public: virtual void __thiscall Base::Foo(void)" (?Foo@Base@@UAEXXZ) referenced in function "public: virtual __thiscall Base::~Base(void)" (??1Base@@UAE@XZ)
Если при этом убрать вызов Foo из деструктора Base, но оставить этот же вызов в main(), то ошибки нет.
В-общем все это ИМХО говорит о том, что ни о какой виртуальности вызовов в конструкторе/деструкторе и речи не идет.
ЗЫ: насколько это согласуется со стандартом, не знаю.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[4]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Лазар Бешкенадзе, Вы писали:
B>>>Виртуальные вызовы не работают в конструкторах и деструкторе. Это обсуждалось не один раз.
ЛБ>>На самом деле работают, более того, работают именно как ЛБ>>виртуальные, просто вызывается последний override на момент ЛБ>>создания объекта класса исполняемого конструктора/деструктора.
B>В моем понимании фраза "работают именно как виртуальные" подразумевает, что в общем случае механизмы вызова виртуальной и "обычной" функции в конструкторе/деструкторе могут отличаться. Если именно это имелось ввиду, то нельзя ли привести пример, иллюстрирубщий это утверждение? Если нет, то фраза "работают именно как виртуальные" лишена смысла, ИМХО. B>Я что-то упустил?
Фразу, что я комментировал можно понимать как отключение виртуального механизма вызова и переход на статический при вызове виртуального метода из конструктора/деструктора. Такая мысль звучала явно, например, в параллельном обсуждении:
Да, собственно, "в общем случае механизмы вызова виртуальной и "обычной" функции" отличаются и не только в конструкторе/деструкторе. Виртуальный вызов — он косвенный, что позволяет реализовать полиморфизм.
А вот и пример для вызова в конструкторе:
#include <stdio.h>
struct A {
void sf() { ::printf("A::sf\n"); }
virtual void vf() { ::printf("A::vf\n"); }
};
struct B : A {
void sf() { ::printf("B::sf\n"); }
virtual void vf() { ::printf("B::vf\n"); }
};
struct C : B {
C() {
((A *) this)->sf();
((A *) this)->vf();
}
};
void main() { C c; }
Лазар
Re[4]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, ekamaloff, Вы писали:
E>Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>>На самом деле работают, более того, работают именно как ЛБ>>виртуальные, просто вызывается последний override на момент ЛБ>>создания объекта класса исполняемого конструктора/деструктора.
E>Для интереса посмотрел, что делает MS VC++ 7.1:
[snip] интересное исследование asm
E>Как видно из этого листинга, ни о каком виртуальном вызове (в случае с Foo), тут речи и не идет. Адрес берется не из vtable, а уже известен на момент компиляции. Поэтому фраза "работают именно как виртуальные", как справедливо заметил Bell, совершенно несправедлива.
А мы когда-нибудь слышали об оптимизации?
E>Кстати если Foo сделать в классе Base чисто виртуальной, то получим ошибки линковки:
E>
Win32Console error LNK2019: unresolved external symbol "public: virtual void __thiscall Base::Foo(void)" (?Foo@Base@@UAEXXZ) referenced in function "public: virtual __thiscall Base::~Base(void)" (??1Base@@UAE@XZ)
E>Если при этом убрать вызов Foo из деструктора Base, но оставить этот же вызов в main(), то ошибки нет.
Я уже писал, что если чисто виртуальную определить и вызвать напрямую, то может показаться, что работает.
Отсюда не следует, что поведение перестало быть undefined.
E>В-общем все это ИМХО говорит о том, что ни о какой виртуальности вызовов в конструкторе/деструкторе и речи не идет.
Вглядись в пример, что я привел в ответ на реплику Bell.
E>ЗЫ: насколько это согласуется со стандартом, не знаю.
HTH
Лазар
Re[5]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>А мы когда-нибудь слышали об оптимизации?
Не думаю, что в данном случае имело место оптимизация
ЛБ>Вглядись в пример, что я привел в ответ на реплику Bell.
Ответ на реплику Bell совершенно не согласуется с вашей изначальной фразой:
более того, работают именно как
виртуальные, просто вызывается последний override на момент
создания объекта класса исполняемого конструктора/деструктора
Хотелось бы понять, что вы хотели этим сказать.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[3]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Symon, Вы писали:
[...] S>Хорошо хоть не читайте Страуструпа
У Страуструпа это, кстати, трудно найти (я не нашел ) — но наверняка оно там есть
Проще разбираться по более поздней, "компилятивной" литературе. Например "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices" (Herb Sutter, Andrei Alexandrescu).
Там Item 49 так и называется: Avoid calling virtual functions in constructors and destructors.
Прямо говорится:
...any direct or indirect call to an unimplemented pure virtual function from a constructor or destructor results in undefined behavior.
Успехов
Re[6]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, ekamaloff, Вы писали:
E>Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>>А мы когда-нибудь слышали об оптимизации?
E>Не думаю, что в данном случае имело место оптимизация
ЛБ>>Вглядись в пример, что я привел в ответ на реплику Bell.
E>Ответ на реплику Bell совершенно не согласуется с вашей изначальной фразой:
E>
E>более того, работают именно как
E>виртуальные, просто вызывается последний override на момент
E>создания объекта класса исполняемого конструктора/деструктора
E>Хотелось бы понять, что вы хотели этим сказать.
По-моему здесь все ясно. Виртуальные вызовы виртуальных функций
делаются виртуально везде, в том числе и в ctor/dtor.
Это и демонстрирует пример.
Демонстрация оптимизированного компилятором кода в твоем сообщении
ничего не доказывает. Могу только привести еще один пример:
#include <stdio.h>
struct A {
virtual void v() { ::printf("A::v\n"); }
};
struct B : A {
B() { f(); }
void f() { v(); }
};
struct C : B {
virtual void v() { ::printf("C::v\n"); }
};
void main() { C().f(); }
Куда яснее?
Лазар
Re[5]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>На самом деле работают, более того, работают именно как виртуальные
ЛБ>Фразу, что я комментировал можно понимать как отключение виртуального механизма вызова и переход на статический при вызове виртуального метода из конструктора/деструктора.
Я, быть может, чего-то не понимаю, но для меня эти реплики не эквивалентны.
Что же касается моего утверждения о механизмах вызова, то тут я погорячился.
Стандарт требует, чтобы вызов (как приямой, так и опосредованный) виртуальной функции в конструкторе/деструкторе соответствовал прямому вызову "обычной" функции. Это просто сделать, когда имеет место прямой вызов (косвенно это подтверждает приведенный пример
), но в случае опосредованного вызова (как в твоем примере) все не так просто, и компилятору приходится идти на некоторые ухищрения, чтобы выполнить требования стандарта. Один из вариантов реализации этих самых ухищрений — использовать готовый механизм виртуального вызова через таблицу виртуальных функций. Единственное, что нужно сделать для обеспечения требуемого поведения — последовательно подменять указатель на таблицу виртуальных функций по мере создания/разрушения объекта. Однако в общем случае может быть использован другой механизм, и поэтому говорить о том, что вызов именно "виртуальный" — некорректно.
Любите книгу — источник знаний (с) М.Горький
Re[7]: Почему не вызывается перегруженная виртуальная функци
Приведенный пример не в тему. Речь шла о непосредственном вызове виртуальной функции из конструктора/деструктора, а не о косвенном (через другую функцию). И повторюсь, что оптимизация тут ни при чем, она была отключена в приведенном мной примере.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Re[4]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, functional, Вы писали:
F>Прямо говорится:
F>...any direct or indirect call to an unimplemented pure virtual function from a constructor or destructor results in undefined behavior.
Прямой вызов виртуальной функции из ктора/дтора — статический. Если функция не определена (неважно, =0 она или нет), то получим не UB, а ошибку линковки.
Перекуём баги на фичи!
Re[8]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, ekamaloff, Вы писали:
E>Здравствуйте, Лазар Бешкенадзе, Вы писали:
E>[...]
E>Приведенный пример не в тему.
E>Речь шла о непосредственном вызове виртуальной функции из конструктора/деструктора, а не о косвенном (через другую функцию).
Ни в исходном сообщении, ни в сообщении на которое отвечал я, ни в стандарте
ничего не говорится о непосредственности.
E> И повторюсь, что оптимизация тут ни при чем, она была отключена в приведенном мной примере.
Отсюда не следует, что не делаются никакие оптимизации.
Проснись и пойми, что из того, что вызов сделан не через vtbl, не следует, что он невиртуальный.
Вот тебе пример непосредственного вызова:
#include <stdio.h>
struct A {
void sf() { ::printf("A::s\n"); }
virtual void vf() { ::printf("A::v\n"); }
};
struct B : A {
void sf() { ::printf("B::s\n"); }
virtual void vf() { ::printf("B::v\n"); }
};
struct C : B {
using A::sf;
using A::vf;
C() {
sf();
vf();
}
};
void main() { C(); }
Re[6]: Почему не вызывается перегруженная виртуальная функци
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Лазар Бешкенадзе, Вы писали:
ЛБ>>На самом деле работают, более того, работают именно как виртуальные
ЛБ>>Фразу, что я комментировал можно понимать как отключение виртуального механизма вызова и переход на статический при вызове виртуального метода из конструктора/деструктора.
B>Я, быть может, чего-то не понимаю, но для меня эти реплики не эквивалентны.
B>Что же касается моего утверждения о механизмах вызова, то тут я погорячился. B>Стандарт требует, чтобы вызов (как приямой, так и опосредованный) виртуальной функции в конструкторе/деструкторе соответствовал прямому вызову "обычной" функции.
Вот чего требует стандарт:
12.7 (3)
... 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
a 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, ...
Покажи, где тут говорится о каком-либо соответствии.
К тому же приведенный здесь пример
), но в случае опосредованного вызова (как в твоем примере) все не так просто, и компилятору приходится идти на некоторые ухищрения, чтобы выполнить требования стандарта. Один из вариантов реализации этих самых ухищрений — использовать готовый механизм виртуального вызова через таблицу виртуальных функций. Единственное, что нужно сделать для обеспечения требуемого поведения — последовательно подменять указатель на таблицу виртуальных функций по мере создания/разрушения объекта. Однако в общем случае может быть использован другой механизм, и поэтому говорить о том, что вызов именно "виртуальный" — некорректно.
Тут так все перевернуто с ног на голову, что мне остается только улыбнуться: