Когда вы создаете экземпляр подкласса из DLL, правильная ли виртуальная таблица?

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

Я ищу статью/ответ, который прояснит, что происходит, когда вы

  • загрузить DLL
  • вызвать функцию DLL, которая возвращает plugin* (у нее есть виртуальные функции)
  • используйте этот плагин в коде вашего основного исполняемого файла
  • удалить, выгрузить

Я думаю о vtable и других проблемах C++. Например, если вы выгружаете DLL с работающими плагинами... "Код" исчезнет?


person Kalamar Obliwy    schedule 15.01.2014    source источник
comment
Да, виртуальная таблица верна, даже если вы наследуете класс, определенный в .dll, и перегружаете виртуальные функции. Почему вы подозреваете, что это не так?   -  person Codie CodeMonkey    schedule 15.01.2014
comment
Вы загружаете эту DLL явно с помощью LoadLibrary или неявно при запуске программы?   -  person pentadecagon    schedule 15.01.2014
comment
Для этого я использовал несколько утилит Boost. Я загружаю DLL во время выполнения, я передаю строку в Boost, я получаю функцию обратно. Я вызываю функцию, чтобы получить плагин. Но вы также можете предположить, что я использую нативные API. Что тогда? Различия между временем выполнения и запуском могут быть интересными   -  person Kalamar Obliwy    schedule 15.01.2014
comment
@CodeCodeMonkey: предположительно, виртуальная таблица будет известна только в том случае, если DLL и исполняемый файл были созданы с помощью одних и тех же инструментов? Универсального стандарта не существует, не так ли?   -  person Harry Johnston    schedule 16.01.2014
comment
@HarryJohnston Хороший вопрос, хотя я полагаю, что это та же проблема, если вы создаете разные объектные файлы в одном проекте с двумя разными компиляторами, которые не согласны со структурой v-таблицы. Я не думаю, что это проблема .dll, просто несоответствие стандартов.   -  person Codie CodeMonkey    schedule 16.01.2014
comment
@CodeCodeMonkey: это, скорее всего, будет проблемой для DLL, особенно когда DLL является плагином, потому что это обычно означает, что DLL создается отдельно от исполняемого файла, обычно другим человеком. Вместо того, чтобы прилагать усилия, чтобы получить несовпадающие виртуальные таблицы, как в случае со статическим связыванием, вы должны приложить усилия, чтобы избежать (потенциально) несоответствующих виртуальных таблиц.   -  person Harry Johnston    schedule 16.01.2014
comment
@ГарриДжонстон Согласен.   -  person Codie CodeMonkey    schedule 16.01.2014
comment
@HarryJohnston - просто из любопытства - как с этим справляются профессиональные проекты? Как вы выпускаете программное обеспечение, которое расширяется по замыслу (например, некоторые музыкальные программы)? Должны ли разработчики такого программного обеспечения указывать точную версию и флаги компилятора, чтобы избежать вышеуказанных проблем? Или есть лучший подход к (объектно-ориентированным) плагинам?   -  person Kalamar Obliwy    schedule 16.01.2014
comment
Понятия не имею, извините, хотя у меня сложилось впечатление, что большинство архитектур плагинов не являются объектно-ориентированными.   -  person Harry Johnston    schedule 16.01.2014


Ответы (1)


Поскольку вы говорите о плагинах, вы должны делать что-то вроде LoadLibrary. Предполагая, что Windows:

  • Загрузите библиотеку. Обычно вы бы назвали LoadLibrary, а затем GetProcAddress. DLL загружается в адресное пространство процесса, и у вас есть указатель на открытую функцию.
  • Вызовите функцию DLL, которая возвращает plugin* (у нее есть виртуальные функции). Вы должны привести возвращаемое значение из GetProcAddress к типу указателя функции и вызвать его. Все должно работать как обычно.
  • Используйте этот плагин в основном коде исполняемого файла. Ничего особенного.
  • Удалите плагин. Я предполагаю, что вы используете для этого функцию в DLL, и все должно быть в порядке. Прямой вызов delete может быть опасен, так как DLL может иметь отдельный диспетчер памяти (в зависимости от того, какую среду выполнения вы используете).
  • Выгрузите DLL. Вы звоните FreeLibrary, а код пропал. Указатели, которые вы ранее получили от GetProcAddress, становятся недействительными.

Я не знаю конкретных проблем с vtable. Если вы выгрузите DLL во время выполнения кода, я предполагаю, что в этот момент программа рухнет, так как адресное пространство кода станет недействительным.

person Yongwei Wu    schedule 15.01.2014
comment
Спасибо за ответ. Две вещи нуждаются в разъяснении. Во-первых, вы написали, что указатели становятся недействительными. Они становятся null или просто становятся непригодными для дальнейшего использования? Будет ли приложение аварийно завершать работу немедленно или после вызова виртуальной функции для указателя? Или вообще какая-то функция? Во-вторых, вы говорите, что для каждого new в DLL также должно быть delete? Что делать, если функция DLL возвращает unique_ptr? Проблема с отдельным диспетчером памяти связана только с Windows? - person Kalamar Obliwy; 15.01.2014
comment
Указатели никогда не становятся автоматически нулевыми в мире C/C++. Они просто становятся дикими указателями и опасны. Сбои вероятны. Да, создание/удаление в одной и той же DLL — это функция Windows, поскольку в Windows нет стандартной среды выполнения C. Вероятно, вы не захотите использовать unique_ptr, если не убедитесь, что хост и плагины используют одну и ту же среду выполнения C (одинаковую версию MSVC и параметр /MD). - person Yongwei Wu; 15.01.2014
comment
Итак, в Linux вы можете удалить внешний указатель и все будет в порядке? - person Kalamar Obliwy; 15.01.2014
comment
@KalamarObliwy: Насколько я знаю, да. - person Yongwei Wu; 15.01.2014