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是有限制的,即不能有靜態成員變量。如果有,最好的辦法就是不要使用嵌套了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章