Хотелось бы с Вашей помощью разобраться в деталях виртуального наследования.
Вопрос такой: что на физическом уровне означает выражение
class B : public virtual A {};
Вот с виртуальными функциями все вроде ясно: к объекту добавляется еще 4 байта, которые содержат указатель на виртуальную таблицу класса, которая, в свою очередь, содержит адреса виртуальных функций.
А от сюда уже ясно как реализуется полиморфизм.
Здравствуйте Amor, Вы писали:
A>Хотелось бы с Вашей помощью разобраться в деталях виртуального наследования. A>Вопрос такой: что на физическом уровне означает выражение
A>class B : public virtual A {};
A>Вот с виртуальными функциями все вроде ясно: к объекту добавляется еще 4 байта, которые содержат указатель на виртуальную таблицу класса, которая, в свою очередь, содержит адреса виртуальных функций. A>А от сюда уже ясно как реализуется полиморфизм.
A>А здесь как?
A>Спасибо.
Одна из возможных реализаций очень похожа на таблицы виртуальных функци. В классе B есть указатель на статическую структуру, в которой прописываются смещения для всех виртуальных базовых подобъектов. При обращении к виртуальной базе ее смещение относительно целого класса получается из это таблице по индексу базового класса.
Здравствуйте dupamid, Вы писали:
D>Здравствуйте Amor, Вы писали:
A>>Хотелось бы с Вашей помощью разобраться в деталях виртуального наследования. A>>Вопрос такой: что на физическом уровне означает выражение
A>>class B : public virtual A {};
A>>Вот с виртуальными функциями все вроде ясно: к объекту добавляется еще 4 байта, которые содержат указатель на виртуальную таблицу класса, которая, в свою очередь, содержит адреса виртуальных функций. A>>А от сюда уже ясно как реализуется полиморфизм.
A>>А здесь как?
A>>Спасибо.
D>Одна из возможных реализаций очень похожа на таблицы виртуальных функци. В классе B есть указатель на статическую структуру, в которой прописываются смещения для всех виртуальных базовых подобъектов. При обращении к виртуальной базе ее смещение относительно целого класса получается из это таблице по индексу базового класса.
Не понял!
1) "Виртуальные базовые подобъекты" — в моем случае это класс А?
2) Какой смысл этого смещения?
Вообщем хотелось бы пояснее, пожалуйста. В итоге я хочу понять как из всей этой лабуды какой нибудь далекий потомок понимает, что экземпляр класса А надо создать в единственном экземпляре (в случае множественного наследования)?
Здравствуйте Amor, Вы писали:
A>Не понял! A>1) "Виртуальные базовые подобъекты" — в моем случае это класс А?
Да.
A>2) Какой смысл этого смещения? A>Вообщем хотелось бы пояснее, пожалуйста. В итоге я хочу понять как из всей этой лабуды какой нибудь далекий потомок понимает, что экземпляр класса А надо создать в единственном экземпляре (в случае множественного наследования)?
Вот на псевдокоде реализация того, о чем я говорил.
// class A
// {
// public:
// int i;
// };struct _A
{
int i;
};
struct _B_vbtable
{
unsigned offset[1];
};
// class B : public virtual A
// {
// public:
// int j
// }; struct _B
{
_B_vbtable* _vbtable;
_A _a;
int j;
};
_B_vbtable vbtable_for_B = { offsetof(_B, _a) };
void _B_ctor(_B* p)
{
p->_vbtable = &vbtable_for_B;
}
int main()
{
// B b;
_B b;
_B_ctor(&b);
// b.i = 0;
((_A*)((char*)(&b) + b._vbtable->offset[0]))->i = 0;
}
Здравствуйте Amor, Вы писали:
A>Вообщем хотелось бы пояснее, пожалуйста. В итоге я хочу понять как из всей этой лабуды какой нибудь далекий потомок понимает, что экземпляр класса А надо создать в единственном экземпляре (в случае множественного наследования)?
Подобъект А создается в самом производном классе, а в конструкторы базовых классов передается скрытый параметр, который говорит надо ли создавать подобъект A или нет, так как его создали в производном классе.
Следует понимать, что все, что мы обсуждаем это только один из возможных способов реализации виртуального наследования. Конкретная реализация может действовать как-нибудь по другому.
Вообщем понятно: создается таблица в которой хранятся смещения всех базовых объектов.
Сам же объект класса-наследника содержит указатель на эту таблицу.
Что же происходит теперь?
class C : public virtual A
{};
class D : public B, public C
{};
Здравствуйте Amor, Вы писали:
A>Вообщем понятно: создается таблица в которой хранятся смещения всех базовых объектов. A>Сам же объект класса-наследника содержит указатель на эту таблицу. A>Что же происходит теперь? A>
A>class C : public virtual A
A>{};
A>class D : public B, public C
A>{};
A>
Примерно следующее:
// class A
// {
// public:
// int a;
// };struct _A
{
int a;
};
// class B : virtual public A
// {
// public:
// int b;
// };struct _B_Self
{
unsigned* _vbtable;
int b;
};
struct _B
{
_B_Self _self;
_A _A_base;
};
unsigned _B_vbtable[1] = { offsetof(_B, _A_base) };
void _B_ctor(_B_Self* p, bool b)
{
if(b) p->_vbtable = _B_vbtable;
}
// class C : virtual public A
// {
// public:
// int c;
// };struct _C_Self
{
unsigned* _vbtable;
int c;
};
struct _C
{
_C_Self _self;
_A _A_base;
};
unsigned _C_vbtable[1] = { offsetof(_C, _A_base) };
void _C_ctor(_C_Self* p, bool b)
{
if(b) p->_vbtable = _C_vbtable;
}
// class D : public B, public C
// {
// public:
// int d;
// };struct _D_Self
{
_B_Self _B_base;
_C_Self _C_base;
int d;
};
struct _D
{
_D_Self _self;
_A _A_base;
};
// внимание: разное смещение!unsigned _D_vbtable1[1] = { offsetof(_D, _A_base) - offsetof(_D_Self, _B_base) };
unsigned _D_vbtable2[1] = { offsetof(_D, _A_base) - offsetof(_D_Self, _C_base) };
void _D_ctor(_D_Self* p, bool b)
{
if(b)
{
p->_B_base._vbtable = _D_vbtable1;
p->_C_base._vbtable = _D_vbtable2;
}
_B_ctor(&p->_B_base, false);
_C_ctor(&p->_C_base, false);
}
int main()
{
// D d;
_D d;
_D_ctor(&d._self, true);
// C* pc;
_C_Self* pc = &d._self._C_base;
// pc->a = 0;
((_A*)((char*)pc + pc->_vbtable[0]))->a = 0;
// C* pb;
_B_Self* pb = &d._self._B_base;
// pb->a = 1;
((_A*)((char*)pb + pb->_vbtable[0]))->a = 1;
}
Другая возможная реализация виртуального наследования заключается в том, что в классе вместо указателя на статическую таблицу смещений виртуальных базовых классов, хранятся сами указатели на виртуальные базы. При таком подходе для одного виртуального базового класса размер класса то же, но при обращении к членам виртуального базового класса на одну косвенность меньше плюс нет статических таблиц. Минус — размер класса увеличивается на один указатель на каждый виртуальный базовый класс.