Все это зависит от реализации.Но вот ответы для обычного простейшего способа с использованием «vtables».
Класс Base
имеет указатель vtable, поэтому базовое представление выглядит примерно так: псевдокод:
struct Base {
void** __vtable;
int a_number;
};
void* __Base_vtable[] = { &Base::function1, &Base::function2 };
void __Base_ctor( struct Base* this_ptr ) { this_ptr->__vtable = __Base_vtable; }
Класс Derived
включает подобъект класса Base
.Так как для vtable есть место, Derived
не нужно добавлять еще один.
struct Derived {
struct Base __base;
};
void* __Derived_vtable[] =
{ &Derived::function1, &Base::function2, &Derived::function4 };
void __Derived_ctor( struct Derived* this_ptr ) {
__Base_ctor( &this_ptr->__base );
this_ptr->__base.__vtable = __Derived_vtable;
}
"vtable для базового класса", __Base_vtable
в моем псевдокоде, необходим в случаекто-то пытается new Base();
или Base obj;
.
Все вышеперечисленное усложняется, когда задействовано множественное наследование или виртуальное наследование ....
Для строки b_ptr -> function4();
этоошибка времени компиляции, мало связанная с vtables.Когда вы приводите указатель Base*
, вы можете использовать этот указатель только способами, определенными class Base
(потому что компилятор больше не «знает», действительно ли это Derived
, Base
иликакой-то другой класс).Если Derived
имеет собственный элемент данных, вы не сможете получить к нему доступ через этот указатель.Если Derived
имеет собственную функцию-член, виртуальную или нет, вы не можете получить к ней доступ через этот указатель.