注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

C++小知识53——再论C++小知识37即动态库的导入导出定义  

2014-05-04 13:29:34|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++小知识53——再论C++小知识37

C++小知识37中“C++小知识37--__declspec(dllexport) & __declspec(dllimport)  http://fpcfjf.blog.163.com/blog/static/554697932013251618475/

有一些细节语焉不详,这里详细的说明一下。

重点在下面的这些话:

2. __declspec(dllimport)的作用主要体现在导出类的静态成员方面,

比如在动态链接库中定义这样一个导出类:

 

class __declspec(dllexport) CBtt

{

public:

 CBtt(void);

 ~CBtt(void);

public:

    CString m_str;

 static int GetValue()

 {

  return m_nValue;

 }

private:

 static int m_nValue;

};

 

照上面这样声明,外部虽然可以使用CBtt类,但不能使用CBtt类的GetValue函数,一使用就会出现无法解析的外部符号 "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下声明

 

才能使用CBtt类的GetValue函数:

#ifdef _EXPORTING

#define API_DECLSPEC    __declspec(dllexport)

#else

#define API_DECLSPEC    __declspec(dllimport)

#endif

class API_DECLSPEC CBtt

{

public:

 CBtt(void);

 ~CBtt(void);

public:

 CString m_str;

 static int GetValue()

 {

  return m_nValue;

 }

private:

 static int m_nValue;

};

上面这一大段代码是出自小知识37里面的,这里面有几个细节上的未表述清楚。

第一,   如果按照静态函数来访问静态成员的话或者非静态成员函数访问静态成员变量的话,只要你的单纯的定义了导出,那么这个库是没有问题,可以直接用。

DLL1.H

#ifdef DLL1_API

#else

#define DLL1_API extern "C" _declspec(dllimport)

#endif

 

DLL1_API int _stdcall add(int a,int b);

DLL1_API int _stdcall subtract(int a,int b);

 

 Class  _declspec(dllexport)  Point

{

public:

         void /*DLL1_API*/ output(int x,int y);

         void test();

    static int Mytest();

         static int m_int;

};

int Point::m_int = 0;

 

DLL1.CPP

#define DLL1_API extern "C" _declspec(dllexport)

#include "Dll1.h"

 

#include <Windows.h>

#include <stdio.h>

int _stdcall add(int a,int b)

{

         return a+b;

}

 

int _stdcall subtract(int a,int b)

{

         return a-b;

}

 

 

void Point::output(int x,int y)

{

         HWND hwnd=GetForegroundWindow();

         HDC hdc=GetDC(hwnd);

         char buf[20];

         memset(buf,0,20);

         sprintf(buf,"x=%d,y=%d",x,y);

         TextOut(hdc,0,0,buf,strlen(buf));

         ReleaseDC(hwnd,hdc);

}

 

void Point::test()

{

}

int Point::Mytest()

{

         m_int = 90;

         return m_int;

}

 

第二,   非静态成员函数调用静态成员变量,静态变量的初始化必须得在CPP中。否则还是报一个错误:

DLL1.H

#ifdef DLL1_API

#else

#define DLL1_API extern "C" _declspec(dllimport)

#endif

//

 

#ifdef __DLL1_

#else

#define __DLL1_  _declspec(dllimport)

#endif

DLL1_API int _stdcall add(int a,int b);

DLL1_API int _stdcall subtract(int a,int b);

 

 class __DLL1_/*_declspec(dllexport)*/ Point

{

public:

         void /*DLL1_API*/ output(int x,int y);

         void test();

    virtual int Mytest();

         static int m_int;

};

 int Point::m_int = 0;//此处在库编译没问题,应用时报错,移到CPP中就没问题了。

DLL1.CPP

#define DLL1_API extern "C" _declspec(dllexport)

#define __DLL1_  _declspec(dllexport)

#include "Dll1.h"

 

#include <Windows.h>

#include <stdio.h>

 

int _stdcall add(int a,int b)

{

         return a+b;

}

 

int _stdcall subtract(int a,int b)

{

         return a-b;

}

 

 

void Point::output(int x,int y)

{

         HWND hwnd=GetForegroundWindow();

         HDC hdc=GetDC(hwnd);

         char buf[20];

         memset(buf,0,20);

         sprintf(buf,"x=%d,y=%d",x,y);

         TextOut(hdc,0,0,buf,strlen(buf));

         ReleaseDC(hwnd,hdc);

}

 

void Point::test()

{

}

int Point::Mytest()

{

         m_int = 90;

         return m_int;

}

看红色加粗部分,为了方便开发,使用了第二个宏定义,如果将静态变量放到头文件中(无论调用函数是否为静态都产生一样的结果),库是可以顺利的编译过去,但是应用库的APP在编译时会报下面的错误:

 Point::m_int: 不允许 dllimport 静态数据成员 的定义 

于是将这个静态变量的初始化放到CPP中,就可以了。

第三,   一开始始终没有复现“在库编译成功,但在应用APP程序中静态成员变量不能访问”这个现象。记得是一开始有过一次,但后来无论如何也不能再复现出来,象37中说明的一样。找到了一种方法,即把声明和实现都放到头文件中。

后来仔细看了一下网上的相关文章,重现了这个错误如下:

.h文件:

#ifdef DLL1_API

#else

#define DLL1_API extern "C" _declspec(dllimport)

#endif

//

 

#ifdef __DLL1_

#else

#define __DLL1_ // _declspec(dllimport)  //注意此处,如果去掉这个注释,就是网上所说的可以导入静//态变量了.

#endif

 

DLL1_API int _stdcall add(int a,int b);

DLL1_API int _stdcall subtract(int a,int b);

 

class __DLL1_ Point

{

public:

         void /*DLL1_API*/ output(int x,int y);

         void test();

         virtual int Mytest(){return m_int;}

         int mysecond();//{return _cctv;}

private:

         static int m_int;

         static int _cctv;

};

//两个静态变量如果在头文件中初始化,则同样不会在APP中报使用错误,这个要注意这个跟使用//_declspec(dllimport)的结果是一样的

//int Point::m_int = 0;

//int Point::_cctv = 0;

 

CPP文件:

#define DLL1_API extern "C" _declspec(dllexport)

#define __DLL1_  _declspec(dllexport)

#include "Dll1.h"

 

#include <Windows.h>

#include <stdio.h>

int Point::m_int = 0;

int Point::_cctv = 0;

int _stdcall add(int a,int b)

{

         return a+b;

}

 

注意两个静态变量的声明

第四,   _declspec __declspec这个前面的两个下划线一个是一样的。

第五,   综合上面的分析,可以看出,静态因为是全局静态区内初始化,如果你把这变量放于不一致的环境下,就会在动态库链接时无法找到相应的变量。就象上面的一样:如果静态变量和使用静态变量的函数都在头文件,或者都在CPP文件,则在APP中使用时没有什么问题,如果交叉着,则会在APP编译时报“error LNK2001: 无法解析的外部符号 "private: static int Point::m_int" (?m_int@Point@@0HA)”,当然,如果在交叉时你使用“#define __DLL1_   _declspec(dllimport)”这种方法,就没有什么问题了,原因,正如网上的说明,在DLL中引入静态变量,需要明确的指出是从哪里来的,否则这个东西找不到。

参看以下的BLOG

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

一些起细节的问题,慢慢的都暴露了出来,非常可怕。这也是C++不招人喜欢的原因。

  评论这张
 
阅读(624)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017