вызов виртуальной функции в конструкторе
От: Бабошин Андрей Германия http://andreybaboshin.livejournal.com/
Дата: 30.09.05 14:40
Оценка:
Добрый день!
Код:

class CContainer {
public:
    virtual void init () = 0;
    CContainer() {
        init();
    }
    virtual ~CContainer() {
    }
};

class CTest : public CContainer {
    void init () {
        //...
    }
    CTest () {
    }
};


GCC:
abstract virtual `virtual void CContainer::init()' called from constructor


VC7.1 при линковке — unresolved external.

Почему? Неужели vtable инициализируется после вызова конструктора базового класса?


Спасибо.
Re: вызов виртуальной функции в конструкторе
От: _nn_ www.nemerleweb.com
Дата: 30.09.05 14:47
Оценка: 1 (1)
Здравствуйте, Бабошин Андрей, Вы писали:

БА>Добрый день!

БА>Код:

БА>
БА>class CContainer {
БА>public:
БА>    virtual void init () = 0;
БА>    CContainer() {
БА>        init();
БА>    }
БА>    virtual ~CContainer() {
БА>    }
БА>};

БА>class CTest : public CContainer {
БА>    void init () {
БА>        //...
БА>    }
БА>    CTest () {
БА>    }
БА>};
БА>


БА>GCC:

БА>
БА>abstract virtual `virtual void CContainer::init()' called from constructor
БА>


БА>VC7.1 при линковке — unresolved external.


БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?


Именно так.
А так как у вас тело функции не существует появляется ошибка. см. здесь
Автор: MaximE
Дата: 28.11.02


БА>Спасибо.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: вызов виртуальной функции в конструкторе
От: Глеб Алексеев  
Дата: 30.09.05 14:51
Оценка: 2 (1)
Здравствуйте, Бабошин Андрей, Вы писали:

БА>GCC:

БА>
БА>abstract virtual `virtual void CContainer::init()' called from constructor
БА>

БА>VC7.1 при линковке — unresolved external.
БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
Оставим тонкости реализации вроде vtable в стороне.

Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: вызов виртуальной функции в конструкторе
От: Бабошин Андрей Германия http://andreybaboshin.livejournal.com/
Дата: 30.09.05 14:57
Оценка:
Всем спасибо.
Понял.
Re[2]: вызов виртуальной функции в конструкторе
От: Шахтер Интернет  
Дата: 30.09.05 15:18
Оценка: 8 (1)
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Здравствуйте, Бабошин Андрей, Вы писали:


БА>>GCC:

БА>>
БА>>abstract virtual `virtual void CContainer::init()' called from constructor
БА>>

БА>>VC7.1 при линковке — unresolved external.
БА>>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
ГА>Оставим тонкости реализации вроде vtable в стороне.

ГА>Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).


Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: вызов виртуальной функции в конструкторе
От: Dobre  
Дата: 30.09.05 15:24
Оценка: -2
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, Глеб Алексеев, Вы писали:


ГА>>Здравствуйте, Бабошин Андрей, Вы писали:


БА>>>GCC:

БА>>>
БА>>>abstract virtual `virtual void CContainer::init()' called from constructor
БА>>>

БА>>>VC7.1 при линковке — unresolved external.
БА>>>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?
ГА>>Оставим тонкости реализации вроде vtable в стороне.

ГА>>Виртуальная функция по определению зависит от динамического типа объекта. В твоем случае объекта класса CContainer вр время выполнения конструктора еще не существует. Если бы у CContainer был базовый класс, в котором init была бы реализована, была бы вызвана версия базового класса (т.к. конструктор базового класса завершил выполнение, и внутри конструктора CContainer динамический тип объекта равен гипотетическому BaseContainer).


Ш>Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.



Это. Вы, молодой человек прежде чем что либо авторитетно заявлять, хоть проверьте это. К примеру, маленький тестик забубеньте. А то ведь, ребята все правильно написали, а Вы в штыки...
Re[3]: вызов виртуальной функции в конструкторе
От: Глеб Алексеев  
Дата: 30.09.05 15:30
Оценка: +1
Здравствуйте, Шахтер, Вы писали:

Ш>Ни фига подобного. При вызове виртуальной функции из конструктора вызывается та версия, которая определена в данном классе (либо в базовом классе, если она в данном классе не перегружена). Т.е. в данном примере если бы метод init был бы определён в классе CContainer, то был бы вызван именно он. Но поскольку он не определён, то возникает ошибка.


Черт, опять ерунду спорол.
Правильно было бы сказать так:
struct A {
  A () {
        f();
    }
  virtual void f();
};

struct B : A {
  void f();    
};

B b; // вызывается A::f()
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: вызов виртуальной функции в конструкторе
От: Centaur Россия  
Дата: 30.09.05 15:39
Оценка:
Здравствуйте, Бабошин Андрей, Вы писали:

БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?


Конструкторы отрабатывают по очереди, начиная с самого базового. Пока конструктор класса не отработал, объект не считается принадлежащим этому классу. Если бы конструктор базового класса мог вызвать виртуальную функцию, переопределённую в наследнике, то этот вызов бы делался на объекте, которого ещё нет. Поэтому во время конструирования объект ведёт себя ровно так, насколько он уже создан.

vtable, соответственно, инициализируется в начале каждого конструктора. Начал работать Base::Base — объект превращается из raw storage в экземпляр Base. Base::Base отработал, начался Derived::Derived — экземпляр Base становится экземпляром Derived.
Re: вызов виртуальной функции в конструкторе
От: Кодт Россия  
Дата: 30.09.05 15:39
Оценка: 8 (2) +1
Здравствуйте, Бабошин Андрей, Вы писали:

БА>Почему? Неужели vtable инициализируется после вызова конструктора базового класса?


Инициализация vtable здесь не при чём: ты до выполнения программы ещё не дошёл, споткнувшись на линкере

Виртуальный метод из конструктора вызывается статически. Поэтому линкер ищет определение объявленного метода CContainer::init() — и не находит, так как ты его не сделал. А мог, между прочим!
class CContainer
{
  virtual void init() = 0;
  .....
};

// container.cpp

void 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())
Перекуём баги на фичи!
Re[2]: вызов виртуальной функции в конструкторе
От: __LP  
Дата: 02.10.05 07:48
Оценка:
Здравствуйте, Кодт, Вы писали:


К>Ну а поскольку CContainer абстрактный, то в соответствующей позиции его vtbl стоит заглушка. Обращение к ней называется pure virtual function call и приводит к вылету.


А как собственно может произойти этот pure virtual function call, не могу представить себе такой код.
Если у нас есть указатель на абстрактный базовый класс, то указывать на объект базового же класса он не может,
а если он указывает на объект производного класса, то вызвана будет уже нормальная виртуальная функция.
C++ можно выучить за 21 день! ...если дни — полярные.
Re[3]: вызов виртуальной функции в конструкторе
От: Анатолий Широков СССР  
Дата: 02.10.05 08:47
Оценка: 4 (1)
Здравствуйте, __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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.