Имеет ли чисто абстрактный класс C++, объявленный в заголовке, который используется в разных проектах (без привязки во время компиляции), одну и ту же модель виртуальной таблицы?
У меня есть заголовок C++, объявляющий класс, состоящий только из чисто виртуальных методов. У меня есть две библиотеки DLL, использующие этот заголовок (с одной, реализующей этот интерфейс), но не связанные во время компиляции. Одна DLL загружает другую динамически, передавая указатель реализованного интерфейса другому. Разделяют ли эти DLL одну и ту же структуру виртуальных таблиц?
4 ответа
Ты в безопасности.
Порядок, в котором методы появляются в vftable
определяется структурой базового класса, и это все, что вам нужно. Но это зависит от компилятора, поэтому используйте тот же компилятор для генерации DLL. Не полагайтесь на их обратную совместимость (или, по крайней мере, проверяйте документацию).
Предположим, у вас есть следующий заголовок:
//header.h
class A
{
public:
virtual void foo() = 0;
virtual void goo() = 0;
};
И у тебя есть B.dll
со следующим классом:
class B : public A
{
public:
virtual void foo() {}
virtual void goo() {}
}
Сейчас в X.dll
, вы получите указатель на A
это B
объект, созданный в B.dll
,
Вызов
void test( A* a )
{
a->foo();
}
вызовет B::foo()
,
Один аккуратный эксперимент, который вы можете попробовать, - это скомпилировать B.dll
с header.h
и когда вы компилируете X.dll
, инвертируйте порядок методов в header.h
:
//header.h
class A
{
public:
virtual void goo() = 0;
virtual void foo() = 0;
};
В этом случае, хотя вы никогда не должны делать это, тот же вызов test()
в X.dll
вероятно, вызовет метод B::goo()
, Это потому X.dll
предполагает vftable
присутствует в шапке. Это неопределенное поведение, хотя; Я просто написал этот пример, чтобы подчеркнуть.
Конечно, заголовка класса достаточно для создания полного класса (говоря о расположении в памяти здесь, о том, как все расположено, а не о фактических данных внутри него), включая точную структуру виртуальной таблицы.
Подумайте об этом, каждый связывающий объект (ваши файлы.cpp) компилируется отдельно, только с общими заголовочными файлами, но во время компиляции компилятор должен знать точную структуру виртуальной таблицы, чтобы правильно маршрутизировать виртуальные вызовы.
Doozy вопроса читать кстати...
Если я правильно понимаю, у вас есть что-то вроде этого:
ах
class A
{
public:
virtual void foo()=0;
}
B.cpp
#include <A.h>
class B : public A
{
public:
void foo()
{}
}
C.cpp
#include <A.h>
void bar(A * a)
{}
Так B.cpp
имеет класс, реализующий A
, а также C.cpp
имеет некоторую функцию, принимающую указатель на экземпляр A
(который вы предоставляете с экземпляром B
). Это верно?
Если так, то да, они разделяют vtable. Vtable создается путем компиляции класса B
, а также C.cpp
не имеет никаких собственных vtables вообще.
Все это зависит от компилятора, но в целом, когда класс является чисто виртуальным и ни одна из функций-членов не определена в модуле перевода, компилятор сгенерирует vtable
как слабый символ. В вашем конкретном случае разные единицы перевода будут генерировать отдельные точно равные vtable
s для базового типа, и компоновщик / загрузчик будет отбрасывать все символы, кроме одного, как это было бы с любым другим слабым символом (подумайте о шаблонной не встроенной функции).
Обратите внимание, что генерация vtable
не стандартизирован, что означает, что если вы смешиваете код из двух разных компиляторов или даже версий, вы можете вызвать нарушение ODR.