dllimport static data members is not allowed

dll.pro

TEMPLATE = lib
CONFIG += dll
CONFIG -= app_bundle

DESTDIR = ../../Release/Bin

DEFINES += MYDLL_LIB

SOURCES += \
        a.cpp

HEADERS += \
        a.h


a.h

#ifdef MYDLL_LIB
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif

class DLL_EXPORT SimpleDLLClass
{
public:
    SimpleDLLClass();
    virtual ~SimpleDLLClass();

    int getValue() { return m_nValue;}
private:
    int m_nValue;
};


a.cpp

#include "a.h"

SimpleDLLClass::SimpleDLLClass()
{
    m_nValue=12;
}

SimpleDLLClass::~SimpleDLLClass()
{
}

//int SimpleDLLClass::m_nValue = 24;


test.pro

QT += core widgets

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle

DESTDIR = ../../Release/Bin

LIBS += -L$$PWD/../../Release/Bin/
LIBS += -lDll

INCLUDEPATH += ../Common

HEADERS +=  \
    a.h


SOURCES += main.cpp


main.cpp

#include <iostream>
#include <string>
#include "a.h"

using namespace std;

intmain()
{
    SimpleDLLClass obj;
    cout << obj.getValue() << endl;
    return 0;
}











Reference: http://www.cnblogs.com/kanego/archive/2012/02/02/2336220.html

When you want to use a class, you must have its definition. There are two ways:
        1. Refer to its header file, that is, #include "xxx.h". This is the traditional way.
        2. Use the export class.

    What is an 'exported class', it is very simple, the class defined using __declspec(dllimport) is the exported class. For example:
    class __declspec(dllimport) CTest
    {
    }

    __declspec(dllimport) is a MS-specific descriptor. You can tell by the name that it is used in DLL linking. DLL is the product of WINDOWS. In terms of cross-platform, this is written as Not advisable. All generic versions of the STL do not use this descriptor, and it is puzzling to see the exported class definition using method 2.


    If you really need to use __declspec(dllimport), pay attention to VC regulations:
    data, static data members and functions can be declared, but not defined as dllimport.

   To put it bluntly, declarations and definitions are placed in .h and .cpp files respectively. That is, the __declspec(dllimport) declaration is placed in the .h header file, and the implementation is placed in the .cpp file. In this way, the so-called 'exported class' of Mode 2 can be used for ordinary functions and classes. But not for templates. There is also the problem that the compiler cannot support separate compilation of templates.

    First, let's talk about the general compilation principle of the compiler. A .cpp and all the .h it includes are compiled and called a compilation unit, that is, a .obj file, and then all the .objs are linked by the linker to generate a PE executable .exe file. For example, main.obj needs to call the test() function in the test.obj unit. At this time, there is no code for the test() function in main.obj. The linker then helps main.obj to find the address of the test() function in test.obj and link it to main.obj. Usually when you play disassembly, you often see jmp xxx/call xxx that the linker is doing the deployment.

    Let's talk about templates. Templates need to be 'reified', and the compiler will not compile the template into binary code until it encounters code that uses the template. Pay attention to the STL code and you will find that all the template code is placed in a .h file, why not separate it into a .cpp file, because placing it in a .cpp file becomes a compilation unit, and a unit is a PE structure, It is a real binary code file, but this unit does not call the compilation unit of this template, so the compiler can only strike. Is there a way to generate cells? Yes! Abnormally call self-declared templates in .cpp.
    
    After understanding this truth, it is not difficult to understand why sometimes an error is reported when it can be compiled and linked, and the linker cannot find the corresponding address of another .obj. Of course, an error is reported.



    Now let's analyze why the above template code is wrong. It is very simple:
    since the __declspec(dllimport) declaration is used, CTest() and ~CTest() are defined, which violates the VC rules "data, static data members and functions" It can be declared, but cannot be defined as dllimport."

    Solution:
    1. Remove __declspec(dllimport), unless you really want to generate a DLL export class, make it a standard template.
    2. Remove the external definition of the CTest()/~CTest() class and move the definition to the inside of the class. It has already been explained why the definitions of these two functions cannot be placed in the .cpp file.

 

The above is less than perfect: add the following description:

__declspec(dllexport)

Declaring an export function means that this function is to be exported from this DLL. I want to use it for others. It is generally used in a dll to 
save a method of manually defining which functions are exported in the DEF file. Of course, if your DLL is full of C++ classes, you cannot specify exported functions in DEF, you can only use __declspec(dllexport) to export classes

__declspec(dllimport)

Declaring an import function means that the function is imported from another DLL. I want to use. It is generally used in an exe that uses a dll 
to compile the code correctly without using __declspec(dllimport), but using __declspec(dllimport) allows the compiler to generate better code. The compiler is able to generate better code because it can determine if a function exists in a DLL, which allows the compiler to generate code that skips the level of indirection that would normally occur in functions that cross DLL boundaries Calling. However, __declspec(dllimport) must be used to import variables used in the DLL.

  
Example of use: 
  // File: SimpleDLLClass.h

#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT 
#endif

class DLL_EXPORT SimpleDLLClass
{
public:
 SimpleDLLClass();
 virtual ~SimpleDLLClass();

 virtual getValue() { return m_nValue;};
private:
 int m_nValue;
};
 

// File: SimpleDLLClass.cpp

#include "SimpleDLLClass.h"

SimpleDLLClass::SimpleDLLClass()
{
 m_nValue=0;
}

SimpleDLLClass::~SimpleDLLClass()
{
}
 

说明:
1. 在你的APP中include SimpleDLLClass.h时,如果你的APP的项目不定义SIMPLEDLL_EXPORT,则DLL_EXPORT不存在。此时APP仍可以正常运行。

// File: SimpleDLLClass.h
static int m_nValue;

// File: SimpleDLLClass.cpp
int SimpleDLLClass::m_nValue=0;
 

说明:
1. 如果你的APP的项目不定义SIMPLEDLL_EXPORT,则DLL_EXPORT不存在。此时APP无法LINK。原因是找不到m_nValue。(原因:静态变量m_nValue已被DLL导出,但SimpleDLLClass无法访问m_nValue)
Workaround:
#define DLL_EXPORT __declspec(dllimport)

Conclusion:
dllimport是为了更好的处理类中的静态成员变量(或者其他...)的,如果没有静态成员变量(或者其他...),那么这个__declspec(dllimport)无所谓.

/////////////////////////

在Windows DLL编程时,可使用__declspec(dllimport)关键字导入函数或者变量。

函数的导入
    当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。但如果你显示地导入函数,编译器会产生质量更好的代码。由于编译器确切地知道了一个函数是否在一个DLL中,它就可以产生更好的代码,不再需要间接的调用转接。
    Win32的PE格式(Portable Executable Format)把所有导入地址放在一个导入地址表中。下面用一个具体实例说明使用__declspec(dllimport)导入函数和不使用的区别:
    假设func是一个DLL中的函数,现在在要生成的.exe的main函数中调用func函数,并且不显示地导入func函数(即没有:__declspec(dllimport)),代码示例如下:
    int main()
    {
        func();
    }
编译器将产生类似这样的调用代码:
    call func
然后,链接器把该调用翻译为类似这样的代码:
    call 0x40000001       ; ox40000001是"func"的地址
并且,链接器将产生一个 Thunk,形如:
    0x40000001: jmp DWORD PTR __imp_func
这里的imp_func是func函数在.exe的导入地址表中的函数槽的地址。然后,加载器只需要在加载时更新.exe的导入地址表即可。
    而如果使用了__declspec(dllimport)显示地导入函数,那么链接器就不会产生Thunk(如果不被要求的话),而直接产生一个间接调用。因此,下面的代码:
    __declspec(dllimport) void func1(void);
   void main(void) 
    {
        func1();
    }
将调用如下调用指令:
    call DWORD PTR __imp_func1
    因此,显示地导入函数能有效减少目标代码(因为不产生Thunk)。另外,在DLL中使用DLL外的函数也可以这样做,从而提高空间和时间效率。
变量的导入
    与函数不同的是,在使用DLL中的变量时,需要显示地导入变量。使用__declspec(dllimport)关键字导入变量。若在DLL中使 用.def导出变量,则应使用DATA修饰变量,而不是使用已经被遗弃的CONSTANT。因为CONSTANT可能需要使用指针间接访问变量,不确定什 么时候会出问题。
//////////////

我相信写WIN32程序的人,做过DLL,都会很清楚__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导 出哪些 函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出 类。但是,MSDN文档里面,对于__declspec(dllimport)的说明让人感觉有点奇怪,先来看看MSDN里面是怎么说的: 
不使 用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

初看起来,这段话前面的意思是,不用它也可以正常使用DLL的导出库,但最后一句话又说,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量这个是什么意思??

那我就来试验一下,假定,你在DLL里只导出一个简单的类,注意,我假定你已经在项目属性中定义了 SIMPLEDLL_EXPORT
SimpleDLLClass.h

#ifdef SIMPLEDLL_EXPORT#define DLL_EXPORT __declspec(dllexport)#else#define DLL_EXPORT#endifclass DLL_EXPORT SimpleDLLClass{public: SimpleDLLClass(); virtual ~SimpleDLLClass(); virtual getValue() { return m_nValue;};private: int m_nValue;};SimpleDLLClass.cpp

#include "SimpleDLLClass.h"SimpleDLLClass::SimpleDLLClass() { m_nValue=0;}SimpleDLLClass::~SimpleDLLClass(){}然后你再使用这个DLL类,在你的APP中 include SimpleDLLClass.h时,你的APP的项目不用定义 SIMPLEDLL_EXPORT 所以,DLL_EXPORT 就不会存在了,这个时候,你在APP中,不会遇到问题。这正好对应MSDN上说的__declspec(dllimport)定义与否都可以正常使用。但 我们也没有遇到变量不能正常使用呀。 那好,我们改一下SimpleDLLClass,把它的m_nValue改成static,然后在cpp文件中加一行

int SimpleDLLClass::m_nValue=0;如果你不知道为什么要加这一行,那就回去看看C++的基础。 改完之后,再去LINK一下,你的APP,看结果如何, 结果是LINK告诉你找不到这个m_nValue。明明已经定义了,为什么又没有了?? 肯定是因为我把m_nValue定义为static的原因。但如果我一定要使用Singleton的Design Pattern的话,那这个类肯定是要有一个静态成员,每次LINK都没有,那不是完了? 如果你有Platform SDK,用里面的Depend程序看一下,DLL中又的确是有这个m_nValue导出的呀。
再回去看看我引用MSDN的那段话的最后一句。 那我们再改一下SimpleDLLClass.h,把那段改成下面的样子:

#ifdef SIMPLEDLL_EXPORT#define DLL_EXPORT __declspec(dllexport)#else#define DLL_EXPORT __declspec(dllimport)#endif再LINK,一切正常。原来dllimport是为了更好的处理类中的静态成员变量的,如果没有 静态成员变量,那么这个__declspec(dllimport)无所谓。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325796053&siteId=291194637