Виртуальная таблица интерфейса

Имеют ли интерфейсы (полиморфный класс исключительно с чисто виртуальными функциями) виртуальную таблицу? Поскольку интерфейсы сами не реализуют полиморфную функцию и не могут быть созданы напрямую, компоновщику не нужно будет размещать виртуальную таблицу. Это так? Меня особенно беспокоит компилятор MSVC.


person Sebastian Hoffmann    schedule 25.06.2012    source источник
comment
Очень важное замечание — это функция declspec(novtable) в MSVC: она позволяет интерфейсам, особенно COM, опускать виртуальную таблицу. Это имеет некоторые интересные и важные последствия, когда дело доходит до наследования (вынуждает одиночное наследование, но приводит к тому, что конечный объект имеет только одну таблицу, обеспечивающую больший полиморфизм, чем в противном случае).   -  person ssube    schedule 26.06.2012


Ответы (3)


Да, это так. И на это есть ряд веских причин.

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

Есть еще одна причина помещать виртуальную таблицу в базовый класс, даже если все ее методы чисто виртуальные и нет других элементов данных. При использовании полиморфизма указатель на базовый класс передается по всей программе. Чтобы вызвать виртуальный метод, компилятор/среда выполнения должны определить относительное смещение виртуальной таблицы от базового указателя. Если бы в C++ не было множественного наследования, можно было бы предположить нулевое смещение от абстрактного базового класса (например), и в этом случае можно было бы не иметь там vtable (но она нам все равно нужна по причине №1). Но поскольку задействовано множественное наследование, трюк типа «vtable находится по смещению 0» не сработает, потому что может быть две или три vtable в зависимости от количества (и типа) базовых классов.

Могут быть и другие причины, о которых я не догадывался.

Надеюсь, поможет.

person Community    schedule 25.06.2012
comment
Хорошая аргументация! Не думал об этом. - person Luchian Grigore; 26.06.2012
comment
Трюк, на который вы ссылаетесь, не требует ли определения чистой виртуальной функции (в соответствии с правилами odr) или иным образом вызывает неопределенное поведение, потому что я не знал о таком трюке? - person CB Bailey; 26.06.2012
comment
Меня не убеждает ваша первая причина. Я почти уверен, что единственными способами вызвать чистую виртуальную функцию являются (а) вызов ее не виртуально, которая не использует виртуальную таблицу, или (б) вызвать неопределенное поведение, вызвав его из конструктора/деструктора частично сконструированный объект, который вообще не требуется для его вызова. Или ты знаешь еще один трюк, которого не знаю я? - person Mike Seymour; 26.06.2012
comment
@MikeSeymour: я думаю, это зависит от того, насколько хорошо определено undefined. Я не стандартный наркоман. На практике это может зависеть от реализации, но очень хорошо определено. Еще один трюк - выяснить смещение метода в vtable и вызвать его напрямую. Но vtable должна быть там, либо с адресом 0x0 для функции/метода, либо с адресом, адресом пользовательского определения или реализации по умолчанию. - person ; 26.06.2012

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

Если вас особенно беспокоит компилятор MSVC, вы можете украсить свои интерфейсы __declspec(novtable).

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

struct Base {
    Base();
    virtual void f() {}
    virtual void g() = 0;
};

void h(Base& b) {
    b.f(); // Call f on a Base that is not (yet) a Derived
           // vtable for Base required
}

Base::Base() {
    h(*this);
}

struct Derived : Base {
    void g() {}
};

int main() {
    Derived d;
}

)

person CB Bailey    schedule 25.06.2012
comment
Я хочу посмотреть, как их можно реализовать, не используя виртуальные таблицы или любой другой подход, где по крайней мере sizeof(void *) должен быть добавлен к классу, служащему отправной точкой... - person ; 26.06.2012
comment
@VladLazarenko: Я думаю, что почти общепризнано, что решение на основе vptr/vtable является оптимальным, но непрактичные реализации, такие как сохранение карты адреса в информацию динамического типа в той или иной форме, по крайней мере, теоретически возможны. - person CB Bailey; 26.06.2012
comment
Вы поняли суть. Я думаю, что я имею в виду, что независимо от того, как вы это называете, вам понадобится хотя бы какая-то отправная точка для доступа к ней, будь то указатель на vtable, указатель на хэш с адресами или сам хэш на какой-то глобальный хеш с картами для работы указатели. - person ; 26.06.2012

Виртуальная таблица не нужна, но редко оптимизируется. MSVC предоставляет расширение __declspec(novtable), которое явно сообщает компилятору, что виртуальную таблицу можно удалить. В противном случае компилятору пришлось бы проверять, не используется ли виртуальная таблица. Это не исключительно сложно, но все же далеко не тривиально. И поскольку в обычном коде это не дает реального преимущества в скорости, проверка не реализована ни в одном известном мне компиляторе.

person MSalters    schedule 26.06.2012