Re: Проблема с виртуальной функцией
От: OlegT  
Дата: 17.06.02 20:32
Оценка:
Здравствуйте Grenal, Вы писали:

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


Конечно же, нет. Но я думаю, стоит объяснить "почему" (хотелось бы верить, что это прояснит многие похожие случаи). Как сказал, по-моему, Д'Аламбер "знание нескольких базовых фактов освобождает нас от запоминания множества следствий".
Поехали ...


class Base
{
 public:
   Base():mBase(1) { f(); };
   virtual void f() {};
   virtual void g() {};

   long mBase;
};

class Derived: public Base
{
 public:
   Derived():mDerived(2) { g(); };
   virtual void f() {};
   virtual void g() {};

   long mDerived
}

int main()
{
  ...

  Derived derived_Obj;

  ...
}


Что же происходит в строчке " Derived derived_Obj; "?
Прежде всего, примем к сведению, что к этому моменту уже созданы таблицы вирт. функций для классов Base и Derived. Обозначим указатели на них vtBase_ptr и vtDerived_ptr соответственно и допустим, что они находятся в начале каждого объекта ([объект в памяти] = [указ. на табл. вирт. ф-ций][данные] (если, конечно, у класса есть вирт. ф-ции)).
То есть, в нашем случае [derived_Obj] = [vtPtr][long][long].

Virtual Table класса Base:
vtBase_ptr[0] = указатель на функцию f класса Base (1)
vtBase_ptr[1] = указатель на функцию g класса Base (2)
Virtual Table класса Derived:
vtDerived_ptr[0] = указатель на функцию f класса Derived (3)
vtDerived_ptr[1] = указатель на функцию g класса Derived (4)

Итак ...
— Выделяется память(в стеке) размером [vtPtr]+[mBase]+[mDerived] = 12 байт и указатель на нее присваивается this'у
— *(this + 4 байта) = 1 { mBase = 1 }
— *(this) = vtBase_ptr { vtPtr = vtBase_ptr }
— вызываем конструктор класса Base
— вызываем функцию (*this)[0] { (vtBase_ptr[0])() , f() класса Base, см. (1) }
— выходим из конструктора класса Base
— *(this + 8 байт) = 2 { mDerived = 2 }
— *(this) = vtDerivedPtr { vtPtr = vtDerived_ptr }
— вызываем конструктор класса Derived
— вызываем функцию (*this)[1] { (vtDerived_ptr[1])() , g() класса Derived см. (4) }
— выходим из конструктора класса Derived

То есть, по-сути при конструировании объекта самого верхнего класса вызываются все конструкторы начиная с низа иерархии
и перед тем как войти в очередной конструктор класса Derived_i указателю на таблицу вирт. функций этого объекта присваивается указатель на таблицу вирт. ф-ций класса Derived_i. А внутри этого констр. происходит вызов, скажем, i-й виртуальной функции как (*(this)[i])( parameters ) .
В деструкторах же — все с точностью доо наоборот.

Н-да, получилось длинновато, но, надеюсь, понятно. (И еще одно, тут приведена только общая схема, возможно с небольшими погрешностями ... но принцип работы она отражает).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.