Виртуальный стол / таблица диспетчеризации

Из того, что я знаю о CPP, у каждого класса есть свой vtable.

Однако эта ссылка в Википедии упоминает:

Таблица диспетчеризации объекта будет содержать адреса динамически связанных методов объекта. Вызовы метода выполняются путем извлечения адреса метода из таблицы диспетчеризации объекта. Таблица диспетчеризации одинакова для всех объектов, принадлежащих к одному и тому же классу, и, следовательно, обычно распределяется между ними.

Может кто-нибудь, пожалуйста, пролить немного света.

Спасибо!

4 ответа

Иногда это легче понять на примере:

class PureVirtual {
   public:
       virtual void methodA() = 0;
       virtual void methodB() = 0;
};

class Base : public PureVirtual {
   public:
        virtual void methodA();
        void methodC();
   private:
        int x;
 };

 class Derived : public Base {
    public:
         virtual void methodB();
    private:
         int y;
 };

Итак, для объекта типа Derived это может выглядеть так:

                         ------------
 Known offset for vtable |  0xblah  | -------> [Vtable for type "Derived"]
                         ------------
 Known offset for x      |  3       |
                         ------------
 Known offset for y      |  2       |
                         ------------

С Vtable для типа "Производные" выглядит примерно так:

                            ------------
 Known offset for "methodA" | 0xblah1   | ------> methodA from Base
                            -------------
 Known offset for "methodB" | 0xblah2   | ------> methodB from Derived
                            -------------

Обратите внимание, что поскольку methodC не был виртуальным, он вообще отсутствует в vtable. Также обратите внимание, что все экземпляры класса Derived будут иметь указатель vtable на один и тот же общий объект vtable (поскольку они имеют одинаковый тип).

Хотя реализации для C++ и Java немного отличаются, идеи не являются несовместимыми. Ключевое отличие, с концептуальной точки зрения, состоит в том, что методы Java являются "виртуальными", если они не объявлены как "окончательные". В C++ ключевое слово "virtual" должно быть задано явно, чтобы функция была в vtable. Все, что не в vtable, будет отправлено с использованием типов времени компиляции, а не типа времени выполнения объекта.

Да, виртуальные методы обрабатываются по-разному компилятором и средой выполнения.

Java: все методы в Java являются виртуальными по умолчанию. Это означает, что любой метод может быть переопределен при использовании в наследовании, если только этот метод не объявлен как final или static.

Из спецификации ВМ,

Виртуальная машина Java не требует какой-либо конкретной внутренней структуры для объектов. В книге отмечается, что в некоторых реализациях Sun виртуальной машины Java ссылка на экземпляр класса представляет собой указатель на дескриптор, который сам является парой указателей: один на таблицу, содержащую методы объекта, и указатель объекту Class, который представляет тип объекта, а другой - памяти, выделенной из кучи для данных объекта.


C++:

Всякий раз, когда функция-член класса объявляется как виртуальная, компилятор создает в памяти виртуальную таблицу, которая содержит все указатели функций, объявленные в этом классе как виртуальные. Это включает полиморфизм во время выполнения (т.е. обнаружение желаемой функции во время выполнения). Таблицы виртуальных функций также имеют дополнительный указатель в объекте на vtable. Поскольку этот дополнительный указатель и виртуальная таблица увеличивают размер объекта, разработчик класса должен быть осторожен с объявлением функций виртуальными.

Последовательность событий после вызова метода на указателе базового объекта:

  • Получить указатель vtable (этот указатель vtable указывает на начало vtable).
  • Получить указатели на функции в виртуальной таблице с помощью смещения.

Вызовите функцию косвенно через указатель vtable.

Каждый класс, имеющий виртуальные функции (т.е. в Java это просто "каждый класс"), имеет свой собственный vtable, Каждый объект скрыл ссылку на свой класс vtable. Таким образом, объекты одного класса имеют идентичные ссылки.

Когда вы делаете вызов виртуального метода, компилятор обычно делает поиск:

obj.method(args);

переводится во что-то

obj.vtable[idx_method](obj, args);

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

MyObject obj(ctor_args);
....
obj.method(args);

можно перевести на

MyObject_method(obj, args);

Который обычно будет выполняться быстрее, чем виртуальный вызов.

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

Другие вопросы по тегам