Вопросы по наследованию на С++/VTable
Обновление: заменил пример деструктора примером вызова метода прямого вызова.
Привет,
Если у меня есть следующий код:
class a
{
public:
virtual void func0(); // a has a VTable now
void func1();
};
class b : public a
{
public:
void func0() { a::func0(); }
void func2();
};
- Есть ли VTable в B? B не имеет виртуальных функций, но вызывает a:: func0() из b:: func0()
- Поддерживает ли func1 в VTable? Это не виртуально.
- Поддерживает ли func2 в VTable?
- Будет ли ответ на вышеизложенное отличаться, если не было вызова a:: func0() в b:: func0()?
Спасибо
Ответы
Ответ 1
Если вы объявляете виртуальные функции, вы также должны объявить свой деструктор виртуальным; -).
-
B имеет виртуальную таблицу, поскольку она имеет виртуальную функцию, а именно func0()
. Если вы объявите функцию (включая деструктор) виртуальной в базовом классе, все ее производные классы будут иметь функцию с такой же виртуальной подписью. И это заставит их иметь vtable. Более того, B будет иметь vtable, даже если вы не объявили func0
в нем явно.
-
Не виртуальные функции не ссылаются на vtables.
-
См. 2.
-
Нет. Кластеры vtables построены на основе объявлений классов. Тела функций класса (не говоря уже о других функциях) не учитываются. Следовательно, B имеет vtable, потому что его функция func0()
является виртуальной.
Также есть сложная деталь, хотя это не суть вашего вопроса. Вы объявили свою функцию B::func0()
как встроенную. В компиляторе gcc
, если виртуальная функция объявлена встроенной, она сохраняет свой слот в виртуальной таблице, а слот указывает на специальную функцию, испускаемую для этой встроенной (которая учитывает свой адрес, что делает исходящий поток). Это означает, что встроенная функция не влияет на количество слотов в vtable и ее необходимость в классе.
Ответ 2
-
Да, потому что его базовый класс имеет один; также его деструктор является виртуальным (хотя вы его не объявляли виртуальным), потому что деструктор базового класса является виртуальным.
-
Нет
-
Нет.
-
Нет. На самом деле я не думаю, что текущий код является законным: компилятор вызовет деструктор A после того, как он вызовет деструктор B, даже если вы явно не назове ~ A из ~ B; поэтому я не думаю, что вы должны вызывать ~ A от ~ B, даже если компилятор позволяет вам.
Ответ 3
Ссылаясь на обновленный пример:
- Да, b имеет vtable. Обратите внимание, что b:: func0() является виртуальным (переопределяет a:: func0()), хотя вы явно не указали его как виртуальный. Похоже, weird С++ "лазейка".
- Нет. Не виртуальные функции не находятся в vtable.
- См. 2.
- Нет. Вы переопределили: func0(); неважно, вызываете ли вы:: func0() или нет.
Несколько дополнительных заметок (зависит от компилятора, но это довольно распространенные обобщения):
- Каждый экземпляр b будет иметь указатель на vtable, потому что вы являетесь производным от класса с виртуальными функциями.
- Это будет так, даже если вы не определили b:: func0().
- В этой ситуации компилятор может иметь экземпляры b для статической таблицы vtable или создать статическую таблицу vtable для b и наполнить ее указателями на указатели на члены a.
- Но это все еще требуется, так что вы можете правильно получить доступ к экземпляру b с помощью указателя на.
Ответ 4
- Если функция базового класса является виртуальной, то если вы переопределите эту функцию в производном классе, она неявно виртуальна, даже если вы не укажете явно. Если класс имеет виртуальную функцию, то он имеет таблицу v.
- Только виртуальные функции, присутствующие в vtable, function1 не будут находиться в vtable
- function2 не будет находиться в vtable той же причине выше
- Создание vtable не зависит от того, вызываете ли вы эту функцию из базового класса или из другого места. Вызов функции не определяет создание vtable.