VC++导入导出类

一、导出类

VC++中导出类很简单,下面列出了两个等价的方法:

方法1:

class __declspec(dllexport) CTest

{

public:

int        m_nValue;

CObj    m_obj;

};

方法2:

class __declspec(dllexport) CTest;        //类声明,说明是一个导出类

class CTest

{

public:

int        m_nValue;

CObj    m_obj;

};

注意:方法2的类声明必须放在 class CTest 的前面,最好放在预编译头文件 StdAfx.h 里。

使用方法1比较麻烦,要导出一个类还得修改类定义;方法2就比较方便了,可以将要导出的类声明集中放在 StdAfx.h 里,方便维护。

需要注意以下几个问题:

1、成员对象所属类也需要被导出

m_obj是CTest的成员对象,它所属的类CObj也需要被导出。否则编译的时候会产生警告,客户程序可能无法正常构造CTest类(Debug版正常,Release版分配内存但不调用构造函数)。如果导出CObj比较困难,如它是一个模板类,则应该将CObj m_obj更改为CObj*m_obj;

2、内联成员函数

内联函数相当于宏,编译的时候用来替换源代码,用以提高效率。一般它是不会被编译成目标代码的,但是一旦使用了__declspec(dllexport),编译程序将会为其生成一份目标代码,客户程序调用内联成员函数时,可能直接调用目标代码,此时函数将不再是内联的了。当然,导入类时也可以控制客户程序,使其使用内联的成员函数。

3、友元函数

需要注意的是,上述两个方法均不能导出类的友元函数。要导出友元函数,必须专门声明。方法1、2的改进方案为:

方法1:

class __declspec(dllexport) CTest

{

public:

int m_nValue;

public:

//导出友元函数要专门声明

friend __declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

};

方法2:

class __declspec(dllexport) CTest;

//导出友元函数要专门声明

__declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

class CTest

{

public:

int m_nValue;

public:

friend CTest operator+(const CTest&a,const CTest&b);

};

4、嵌套类

要导出嵌套类,方法1的改进方案为:

class __declspec(dllexport) CTest

{

public:

class __declspec(dllexport) CNest    //前面也要使用__declspec(dllexport)

{

}

};

方法2无法导出嵌套类,因为不能按下面的语法进行嵌套类声明:

class __declspec(dllexport) CTest::CNest;    //这样声明是错误的

此时,解决方案可能只有使用DEF文件了。

二、使用DEF文件导出类

按下图设置,使得编译时生成map文件。

编译如下代码

class __declspec(dllexport) CTest

{

public:

void        SetValue(int v)        {m_Value = v;}

int            GetValue();

private:

int            m_Value;

public:

static int    s_nValue;

};

int CTest::s_nValue = 1;

int CTest::GetValue()                {return m_Value;}

查看 map 文件,提取包含 CTest 的函数或变量:

Address 

Publics by Value 

Rva+Base 

Lib:Object 

0001:00000030

?SetValue@CTest@@QAEXH@Z 

10001030 f i 

tDLL.obj 

0001:00000070 

??4CTest@@QAEAAV0@ABV0@@Z 

10001070 f i 

tDLL.obj 

0001:000000b0 

?GetValue@CTest@@QAEHXZ 

100010b0 f 

tDLL.obj 

0003:00000a30 

?s_nValue@CTest@@2HA 

1002ba30 

tDLL.obj 

注意上表的第三列,f表示函数,i表示内联。如果将 class __declspec(dllexport) CTest 中的 __declspec(dllexport) 去掉,重新编译,则上表第三列包含 f i 的在map文件中不会再出现。

内联函数没有目标代码,所以无法通过 DEF 导出。只能通过DEF文件导出非内联的成员函数,如:下面的DEF文件内容导出了CTest::GetValue函数。

EXPORTS

?GetValue@CTest@@QAEHXZ

?s_nValue@CTest@@2HA对应着CTest::s_nValue,它应该被导出,但是使用DEF导出变量需要注意:客户程序导入该变量时,只能导入该变量的地址。

三、导入类

导出类到动态链接库的同时,就生成了库文件(*.lib)。VC++很智能,客户程序包含这个库文件即可完成类的导入。要特别注意这种导入的内联成员函数。下面的代码中,客户程序调用内联成员函数GetValue时,将不会使用导出类的GetValue目标代码,而是使用类声明中的代码。此时,GetValue还是内联函数。

class CTest

{

public:

int m_nValue;

public:

int GetValue()

{

return m_nValue;

}

};

如果不想客户程序使用类声明中的代码,有两种方法:

1、删除内联函数GetValue的实现代码,仅仅保留函数声明;

2、使用__declspec(dllimport),如下所示:

class __declspec(dllimport) CTest

{

public:

int m_nValue;

public:

int GetValue()

{

return m_nValue;

}

};

或在 StdAfx.h 中,增加class __declspec(dllimport) CTest;这句类声明。使用__declspec(dllimport)声明类之后,友元函数就不再需要__declspec(dllimport)声明了。

四、总结

1、导出类有两种方法,一种是使用 __declspec(dllexport);另一种方法是使用DEF文件,该方法不能导出内联函数,导出类的静态成员变量也较为不便;

2、友元函数不属于类,要导出的话必须使用 __declspec(dllexport) 声明,或在 DEF 文件中导出;

3、导入类可以使用 __declspec(dllimport),也可以不使用。前者将取消内联成员函数的内联性质,后者不会;

应用实例:

下面是类CTest的定义部分,在 Test.h 里

class CTest

{

public:

class CNest

{

public:

void SetValue(int v);

int GetValue();

friend CNest operator+(const CNest&a,const CNest&b);

private:

int m_nValue;

};

public:

void SetValue(int v);

int GetValue();

friend CTest operator+(const CTest&a,const CTest&b);

private:

int m_nValue;

public:

static int s_nValue;

};

下面是类CTest的实现部分,在Test.cpp里

//CTest-------------------------------------------------------------------------

int CTest::s_nValue = 1;

void CTest::SetValue(int v)

{

m_nValue = v;

}

int CTest::GetValue()

{

return m_nValue;

}

CTest operator+(const CTest&a,const CTest&b)

{

CTest c;

c.m_nValue = a.m_nValue + b.m_nValue;

return c;

}

//CTest::CNest------------------------------------------------------------------

void CTest::CNest::SetValue(int v)

{

m_nValue = v;

}

int CTest::CNest::GetValue()

{

return m_nValue;

}

CTest::CNest operator+(const CTest::CNest&a,const CTest::CNest&b)

{

CTest::CNest c;

c.m_nValue = a.m_nValue + b.m_nValue;

return c;

}

导出 CTest 类及其友元函数,只需在 StdAfx.h 里增加如下代码:

class __declspec(dllexport) CTest;

__declspec(dllexport) CTest operator+(const CTest&a,const CTest&b);

导出CTest::CNest的成员函数稍微麻烦些,需要在DEF文件中导出:

EXPORTS

?SetValue@CNest@CTest@@QAEXH@Z

?GetValue@CNest@CTest@@QAEHXZ

??H@YA?AVCNest@CTest@@ABV01@0@Z

客户程序导入CTest类,只需在 StdAfx.h 里增加如下代码:

class __declspec(dllimport) CTest;

//有了上面的声明,CTest的友元函数就不再需要特别声明了

//下面这句话反而会引起警告

__declspec(dllimport) CTest operator+(const CTest&a,const CTest&b);

客户程序对 CTest::CNest 的导入无需特别声明,VC++编译器能够自动完成。但是,它对CNest是有限制的,即不能有静态成员变量。如果有,最好的办法就是不要使用嵌套了。

原文地址:https://www.cnblogs.com/hanford/p/6177883.html