HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- Технические вопросы (https://hlfx.ru/forum/forumdisplay.php?forumid=20)
-- C++ и виртуальное наследование (https://hlfx.ru/forum/showthread.php?threadid=4703)
Отправлено XaeroX 15-12-2015 в 17:57:
C++ и виртуальное наследование
Вопрос к знатокам теории или тем, кто сталкивался.
Имеем стандартную ситуацию "ромбовидное наследование" с одним хитрым нюансом: клиент библиотеки видит только интерфейсы и не использует RTTI.
Код следующий:
1. Библиотека:
C++ Source Code:
5 | virtual void foo() = 0; |
7 | class BInterface : public virtual AInterface |
10 | virtual void bar() = 0; |
12 | extern "C" __declspec(dllexport) void *get_B(); |
15 | class AImplementation : public virtual AInterface |
18 | virtual void foo() {}; |
20 | class BImplementation : public [b][size=4]virtual[/size][/b] BInterface, public AImplementation |
23 | virtual void bar() {}; |
25 | __declspec(dllexport) void *get_B() |
27 | BInterface *lib_b = new BImplementation; |
2. Клиент:
C++ Source Code:
1 | // Те же интерфейсы из библиотеки |
5 | virtual void foo() = 0; |
7 | class BInterface : public virtual AInterface |
10 | virtual void bar() = 0; |
13 | extern "C" __declspec(dllimport) void *get_B(); |
18 | BInterface *b = static_cast<BInterface*>( get_B() ); |
19 | b->bar(); // EPIC FAIL! |
При вызове на клиенте b->bar() всё рушится. Если посмотреть в отладчике - выясняется, что таблица виртуальных функций b содержит какую-то иррунду. При этом в библиотеке lib_b содержит эту таблицу правильную.
А дальше всё намного интереснее. Убираем виртуальное наследование от BInterface в библиотеке (я выделил это место), и баг пропадает: даже несмотря на статик_каст, клиентский b содержит правильную таблицу виртуальных функций.
Кто может объяснить это поведение? Насколько оно надёжно? Будет ли это работать в GCC, или это некий благоприятный баг MSVC2010, надеяться на который не стоит?
Добавлено 15-12-2015 в 23:57:
Второй вариант, который работает: слово virtual не трогаем, а меняем сигнатуру у get_B:
C++ Source Code:
2 | __declspec(dllexport) BInterface *get_B() |
4 | BInterface *lib_b = new BImplementation; |
8 | extern "C" __declspec(dllimport) BInterface *get_B(); |
Так тоже всё работает (статик_каст в этом случае, разумеется, делать не нужно).
Насколько надёжен этот вариант в сравнении с первым?
Отправлено ComradeAndrew 16-12-2015 в 04:36:
А у меня первый вариант не крашит.
toolset v140
Если я нигде не ошибся, копируя код %)
Отправлено XaeroX 16-12-2015 в 06:01:
Гм, возможно, ломается в более сложных примерах. Там, где и у AImplementation, и у BImplementation есть данные и невиртуальные функции, а также самих виртуальных функций в интерфейсах много. Как у меня и есть в реальном проекте.