Поделиться через


Использование dllimport и dllexport в классах C++

Блок, относящийся только к системам Microsoft

Можно объявить классы C++ с атрибутом dllimport или dllexport. Эти формы подразумевают, что импортирован или экспортирован весь класс. Классы, которые можно экспортировать таким образом, называются экспортируемыми классами.

В следующем примере определяется экспортируемый класс. Экспортируются все его функции-члены и статические данные.

#define DllExport   __declspec( dllexport )

class DllExport C {
   int i;
   virtual int func( void ) { return 1; }
};

Обратите внимание, что явное использование атрибутов dllimport и dllexport в членах экспортируемого класса запрещено.

Классы dllexport

При объявлении класса dllexport экспортируются все его функции-члены и статические данные-члены. Необходимо предоставить определения всех таких членов в одной программе. В противном случае возникает ошибка компоновщика. Единственным исключением из этого правила являются чистые виртуальные функции, для которых не требуется предоставлять явные определения. Однако, поскольку деструктор абстрактного класса всегда вызывается деструктором базового класса, чистые виртуальные деструкторы должны всегда предоставлять определение. Обратите внимание, что те же правила применяются и к неэкспортируемым классам.

При экспорте данных типа класса или функций, которые возвращают классы, не забудьте экспортировать класс.

Классы dllimport

При объявлении класса dllimport импортируются все его функции-члены и статические данные-члены. В отличие от поведения dllimport и dllexport в неклассовых типах, статические данные-члены не могут указать определение в той же программе, в которой определен класс dllimport.

Наследование и экспортируемые классы

Все базовые классы экспортируемого класса должны быть экспортируемыми. В противном случае создается предупреждение компилятора. Кроме того, все доступные члены, которые также являются классами, должны быть доступными для экспорта. Это правило разрешает наследование класса dllexport от класса dllimport, и класса dllimport от класса dllexport (хотя последнее не рекомендуется). Как правило, все, что доступно клиенту библиотеки DLL (в соответствии с правилами доступа C++), должно быть частью экспортируемого интерфейса. Сюда входят закрытые данные-члены, на которые ссылаются подставляемые функции.

Выборочный импорт и экспорт членов

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

Аналогичным образом можно объявить функции-члены с атрибутами dllimport или dllexport. В этом случае необходимо указать определение dllexport в любом месте той же программы.

Обратите внимание на несколько важных аспектов выборочного импорта и экспорта членов.

  • Выборочный импорт и экспорт членов рекомендуется использовать для предоставления версии экспортированного интерфейса класса, который является более ограничивающим, то есть для которого можно разработать библиотеку DLL, предоставляющую меньше открытых и закрытых компонентов, чем разрешил бы язык. Кроме того, это может пригодиться для точной настройки экспортируемого интерфейса: если известно, что клиент по определению не может получить доступ к некоторым закрытым данным, не требуется экспортировать целый класс.

  • При экспорте одной виртуальной функции в классе необходимо экспортировать все функции или хотя бы предоставить версии, которые клиент может использовать напрямую.

  • Если имеется класс, в котором используется выборочный импорт и экспорт членов с виртуальными функциями, функции должны быть расположены в экспортируемом интерфейсе или определены встроенным образом (видимым клиенту).

  • Если член определяется как dllexport, но не включается в определение класса, возникает ошибка компилятора. Необходимо определить член в заголовке класса.

  • Хотя определение членов класса как dllimport или dllexport допускается, невозможно переопределить интерфейс, указанный в определении класса.

  • При указании функции-члена в месте, отличном от тела определения класса, в котором она была объявлена, создается предупреждение, если функция определена как dllexport или dllimport (если это определение отличается от указанного в объявлении класса).

См. также

Ссылки

dllexport, dllimport