Использует ли чистый абстрактный класс С++, объявленный в заголовке, используемом в разных проектах (без привязки времени компиляции), одну и ту же модель виртуальной таблицы?

У меня есть заголовок С++, объявляющий класс, состоящий только из чистых виртуальных методов. У меня есть две библиотеки DLL, использующие этот заголовок (с одной реализацией этого интерфейса), но не связанные во время компиляции. Одна DLL динамически загружает другую, передавая ей указатель реализованного интерфейса. Используют ли эти DLL одну и ту же структуру виртуальной таблицы?


person uzul    schedule 07.02.2012    source источник


Ответы (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 в заголовке. Однако это неопределенное поведение; Я просто написал этот пример, чтобы подчеркнуть.

person Luchian Grigore    schedule 07.02.2012

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

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

Кстати, глупый вопрос, чтобы прочитать...

person Blindy    schedule 07.02.2012
comment
Хх, если бы он выбрал другие настройки для компилятора (например, выравнивание памяти) или использовал другие компиляторы, то он испытал бы забавные вещи :P - person Vyktor; 08.02.2012
comment
@Vyktor: Опять же, если вы компилируете с другими настройками, даже если класс не является чисто виртуальным и сгенерирован один vtable, вы также нарушаете ODR и получаете забавные вещи... но это не связан с конкретным поведением, о котором идет речь в вопросе. - person David Rodríguez - dribeas; 08.02.2012
comment
Спасибо, мне нужно было это подтверждение, теперь я уверен :) - person uzul; 08.02.2012

Если я правильно понял, у вас есть что-то вроде этого:

A.h

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). Это правильно?

Если да, то да, они совместно используют виртуальную таблицу. Виртуальная таблица создается путем компиляции класса B, а C.cpp вообще не имеет собственных виртуальных таблиц.

person Derek Park    schedule 07.02.2012

Все это зависит от компилятора, но в целом, когда класс чисто виртуальный и ни одна из функций-членов не определена в единице трансляции, компилятор сгенерирует vtable как слабый символ. В вашем конкретном случае разные единицы перевода будут генерировать отдельные точно равные vtable для базового типа, а компоновщик/загрузчик отбросит все символы, кроме одного, как и с любым другим слабым символом (подумайте о шаблонной не встроенной функции) .

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

person David Rodríguez - dribeas    schedule 07.02.2012