Имеют ли интерфейсы (полиморфный класс исключительно с чисто виртуальными функциями) виртуальную таблицу? Поскольку интерфейсы сами не реализуют полиморфную функцию и не могут быть созданы напрямую, компоновщику не нужно будет размещать виртуальную таблицу. Это так? Меня особенно беспокоит компилятор MSVC.
Виртуальная таблица интерфейса
Ответы (3)
Да, это так. И на это есть ряд веских причин.
Первая веская причина заключается в том, что даже чисто виртуальные методы имеют реализацию. Либо неявное, либо явное. Относительно легко провернуть прием, вызывающий чистую виртуальную функцию, так что вы можете в основном предоставить определение для одной из ваших, вызвать ее и посмотреть, что произойдет. По этой причине в первую очередь должна быть виртуальная таблица.
Есть еще одна причина помещать виртуальную таблицу в базовый класс, даже если все ее методы чисто виртуальные и нет других элементов данных. При использовании полиморфизма указатель на базовый класс передается по всей программе. Чтобы вызвать виртуальный метод, компилятор/среда выполнения должны определить относительное смещение виртуальной таблицы от базового указателя. Если бы в C++ не было множественного наследования, можно было бы предположить нулевое смещение от абстрактного базового класса (например), и в этом случае можно было бы не иметь там vtable (но она нам все равно нужна по причине №1). Но поскольку задействовано множественное наследование, трюк типа «vtable находится по смещению 0» не сработает, потому что может быть две или три vtable в зависимости от количества (и типа) базовых классов.
Могут быть и другие причины, о которых я не догадывался.
Надеюсь, поможет.
С чисто 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;
}
)
Виртуальная таблица не нужна, но редко оптимизируется. MSVC предоставляет расширение __declspec(novtable)
, которое явно сообщает компилятору, что виртуальную таблицу можно удалить. В противном случае компилятору пришлось бы проверять, не используется ли виртуальная таблица. Это не исключительно сложно, но все же далеко не тривиально. И поскольку в обычном коде это не дает реального преимущества в скорости, проверка не реализована ни в одном известном мне компиляторе.
declspec(novtable)
в MSVC: она позволяет интерфейсам, особенно COM, опускать виртуальную таблицу. Это имеет некоторые интересные и важные последствия, когда дело доходит до наследования (вынуждает одиночное наследование, но приводит к тому, что конечный объект имеет только одну таблицу, обеспечивающую больший полиморфизм, чем в противном случае). - person ssube   schedule 26.06.2012