Виртуальная Таблица C++


Я читал много людей, пишущих "виртуальная таблица существует для класса, в котором объявлена виртуальная функция".

Мой вопрос заключается в том, существует ли vtable только для класса, имеющего виртуальную функцию, или она также существует для классов, производных от этого класса.

Например

class Base{
    public:
        virtual void print(){cout<<"Base Printn";}
};
class Derived:public Base{
    public:
        void print(){cout<<"Derived printn";}
};

//From main.cpp 
Base* b = new Derived;
b->print();

Вопрос: если бы не было vtable для класса derived, то вывод не был бы"derived print". Таким образом, IMO существует vtable для любого класса, у которого объявлена виртуальная функция и также в классах, наследующих от этого класса. Это правильно ?

4 20

4 ответа:

Поскольку рассматривается только функциональность, специфичная для виртуальной функции, в традиционном подходе к реализации vtable производный класс будет нуждаться в отдельной версии vtable тогда и только тогда, когда Этот производный класс переопределяет по крайней мере одну виртуальную функцию. В вашем примере Derived переопределяет виртуальную функцию print. Поскольку Derived имеет свою собственную версию print, соответствующая запись в Derived vtable отличается от записи в Base vtable. Обычно для этого требуется отдельная таблица vtable для Derived.

Если бы Derived вообще ничего не переопределял, формально он все равно был бы отдельным полиморфным классом, но для того, чтобы его виртуальные функции работали должным образом, мы могли бы просто повторно использовать Base vtable для Derived. Таким образом, технически не было бы никакой необходимости в отдельной vtable для Derived.

Однако в практических реализациях структура данных, которую мы обычно называем "vtable", часто содержит некоторую дополнительную информацию, относящуюся к конкретному классу. Тот дополнительная информация настолько специфична для класса, что в большинстве случаев становится невозможным совместное использование vtables между различными классами в иерархии, даже если они используют один и тот же набор виртуальных функций. Например, в некоторых реализациях указатель vtable, хранящийся в каждом полиморфном объекте, указывает на структуру данных, которая также хранит так называемую "информацию RTTI" о классе. По этой причине в большинстве (если не во всех) практических реализациях каждый полиморфный класс получает свою собственную vtable, даже если виртуальная указатели функций, хранящиеся в этих таблицах, оказываются одинаковыми.

Да, ваше понимание верно. Любой класс, имеющий базу с любыми виртуальными функциями, имеет vtable.

Да, это правда. На самом деле, данное определение базы:

class derived:public base{
public:
 void print(){cout<<"derived print\n";}
};

Полностью эквивалентно:

class derived:public base{
public:
 virtual void print(){cout<<"derived print\n";}
};

... потому что вы уже определили печать как виртуальную в базе.

Я бы хотел, чтобы компилятор принудил к этому...

Да, это правда. Класс наследует все элементы данных из своего базового класса, включая vtable. Однако записи vtable корректируются соответствующим образом (например, если класс переопределяет виртуальный метод базового класса, соответствующая запись в vtable должна указывать на его собственную реализацию).

Но имейте в виду, что понятие "vtable" является обычной практикой, используемой практически каждым компилятором, но оно не является обязательным или стандартизированным.