Вопросы по наследованию на С++/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.