Здравствуйте, Бабошин Андрей, Вы писали:
БА>GCC: БА>
БА>abstract virtual `virtual void CContainer::init()' called from constructor
БА>
БА>VC7.1 при линковке — unresolved external. БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
Оставим тонкости реализации вроде vtable в стороне.
Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Здравствуйте, Бабошин Андрей, Вы писали:
БА>>GCC: БА>>
БА>>abstract virtual `virtual void CContainer::init()' called from constructor
БА>>
БА>>VC7.1 при линковке — unresolved external. БА>>Почему? Неужели vtable инициализируется после вызова конструктора базового класса? ГА>Оставим тонкости реализации вроде vtable в стороне.
ГА>Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).
Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.
Здравствуйте, Шахтер, Вы писали:
Ш>Здравствуйте, Глеб Алексеев, Вы писали:
ГА>>Здравствуйте, Бабошин Андрей, Вы писали:
БА>>>GCC: БА>>>
БА>>>abstract virtual `virtual void CContainer::init()' called from constructor
БА>>>
БА>>>VC7.1 при линковке — unresolved external. БА>>>Почему? Неужели vtable инициализируется после вызова конструктора базового класса? ГА>>Оставим тонкости реализации вроде vtable в стороне.
ГА>>Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).
Ш>Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.
Это. Вы, молодой человек прежде чем что либо авторитетно заявлять, хоть проверьте это. К примеру, маленький тестик забубеньте. А то ведь, ребята все правильно написали, а Вы в штыки...
Здравствуйте, Шахтер, Вы писали:
Ш>Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.
Черт, опять ерунду спорол.
Правильно было бы сказать так:
struct A {
A () {
f();
}
virtual void f();
};
struct B : A {
void f();
};
B b; // вызывается A::f()
Здравствуйте, Бабошин Андрей, Вы писали:
БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
Конструкторы отрабатывают по очереди, начиная с самого базового. Пока конструктор класса не отработал, объект не считается принадлежащим этому классу. Если бы конструктор базового класса мог вызвать виртуальную функцию, переопределённую в наследнике, то этот вызов бы делался на объекте, которого ещё нет. Поэтому во время конструирования объект ведёт себя ровно так, насколько он уже создан.
vtable, соответственно, инициализируется в начале каждого конструктора. Начал работать Base::Base — объект превращается из raw storage в экземпляр Base. Base::Base отработал, начался Derived::Derived — экземпляр Base становится экземпляром Derived.
Здравствуйте, Бабошин Андрей, Вы писали:
БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
Инициализация vtable здесь не при чём: ты до выполнения программы ещё не дошёл, споткнувшись на линкере
Виртуальный метод из конструктора вызывается статически. Поэтому линкер ищет определение объявленного метода CContainer::init() — и не находит, так как ты его не сделал. А мог, между прочим!
Но очевидно, тебя бы это не спасло, т.к. ты хочешь вызвать метод наследника.
Перед выполнением конструктора vfptr устанавливается на vtbl данного класса (а не сразу на vtbl потомка). Этим гарантируется, что конструктор не вызовет косвенно методы ещё недостроенного объекта.
class Base
{
virtual void foo() { .... }
void call_foo() { foo(); } // call_foo не знает, из конструктора ли её вызвали, и всегда вызывает foo виртуальноpublic:
Base() { foo(); call_foo(); } // в обоих случаях должен быть вызван Base::foo, а не бог знает какой
};
Ну а поскольку CContainer абстрактный, то в соответствующей позиции его vtbl стоит заглушка. Обращение к ней называется pure virtual function call и приводит к вылету.
Наконец: что делать и кого виновать.
Сделай двухфазную инициализацию.
Пусть конструкторы занимаются своими делами, но у свежесконструированного объекта вызови init() (кстати, в ATL так и делают, см. FinalConstruct())
К>Ну а поскольку CContainer абстрактный, то в соответствующей позиции его vtbl стоит заглушка. Обращение к ней называется pure virtual function call и приводит к вылету.
А как собственно может произойти этот pure virtual function call, не могу представить себе такой код.
Если у нас есть указатель на абстрактный базовый класс, то указывать на объект базового же класса он не может,
а если он указывает на объект производного класса, то вызвана будет уже нормальная виртуальная функция.
C++ можно выучить за 21 день! ...если дни — полярные.
Здравствуйте, __LP, Вы писали:
__L>Здравствуйте, Кодт, Вы писали:
К>>Ну а поскольку CContainer абстрактный, то в соответствующей позиции его vtbl стоит заглушка. Обращение к ней называется pure virtual function call и приводит к вылету.
__L>А как собственно может произойти этот pure virtual function call, не могу представить себе такой код. __L>Если у нас есть указатель на абстрактный базовый класс, то указывать на объект базового же класса он не может, __L>а если он указывает на объект производного класса, то вызвана будет уже нормальная виртуальная функция.
class foo
{
public:
foo()
{
call_f();
}
virtual ~foo() {}
void void call_f()
{
f();
}
virtual void f() = 0;
};
class boo : public foo
{
public:
virtual void f() {}
};
...
boo b; // ops - pure virtual function call