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


Общие правила и ограничения

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

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

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

  • Если один модуль в программе содержит оба dllimport и dllexport объявления для одной функции или объекта, dllexport атрибут имеет приоритет над атрибутом dllimport . Однако компилятор создает предупреждение. Например:

    __declspec( dllimport ) int i;
    __declspec( dllexport ) int i;   // Warning; inconsistent;
                                     // dllexport takes precedence.
    
  • В C++можно инициализировать глобально объявленный или статический локальный указатель данных или адрес объекта данных, объявленного атрибутом dllimport , который создает ошибку в C. Кроме того, можно инициализировать указатель статической локальной функции с адресом функции, объявленной атрибутом dllimport . В C такое присваивание задает указатель на адрес преобразователя импорта библиотеки DLL (заглушки кода, передающей контроль функции), а не адрес функции. В C++ оно задает указатель на адрес функции. Например:

    __declspec( dllimport ) void func1( void );
    __declspec( dllimport ) int i;
    
    int *pi = &i;                             // Error in C
    static void ( *pf )( void ) = &func1;     // Address of thunk in C,
                                              // function in C++
    
    void func2()
    {
       static int *pi = &i;                  // Error in C
       static void ( *pf )( void ) = &func1; // Address of thunk in C,
                                             // function in C++
    }
    

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

    __declspec( dllexport ) void func1( void );
    __declspec( dllexport ) int i;
    
    int *pi = &i;                              // Okay
    static void ( *pf )( void ) = &func1;      // Okay
    
    void func2()
    {
        static int *pi = &i;                   // Okay
        static void ( *pf )( void ) = &func1;  // Okay
    }
    
  • При применении dllexport к обычному классу с базовым классом, который не помечен как dllexport, компилятор создаст C4275.

    Компилятор создает то же самое предупреждение, если базовый класс является специализацией шаблона классов. Чтобы обойти эту проблему, пометьте базовый класс с dllexportпомощью . Проблема с специализацией шаблона класса заключается в том, где разместить __declspec(dllexport)шаблон класса; вы не можете пометить шаблон класса. Вместо этого явно создайте экземпляр шаблона класса и пометьте этот явный экземпляр dllexport. Например:

    template class __declspec(dllexport) B<int>;
    class __declspec(dllexport) D : public B<int> {
    // ...
    

    Этот обходной путь завершается сбоем, если аргумент шаблона — это производный класс. Например:

    class __declspec(dllexport) D : public B<D> {
    // ...
    

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

    class __declspec(dllexport) D : public B<D> {
    // ...
    

Завершение блока, относящегося только к системам Майкрософт

См. также

dllexport, dllimport