我們在c++編程過程中往往要用到各種不同形式的程序庫,這些庫的發(fā)布方式有動態(tài)庫和靜態(tài)庫。對于靜態(tài)類庫,設計良好的靜態(tài)類庫能實現(xiàn)功能上的隔離,無法避免類庫實現(xiàn)必須重新編譯、鏈接整個應用程序的問題。而調用各種DLL動態(tài)庫成為我們程序員的家常便飯。
以什么方式暴露庫的接口?可選的做法有:以全局(含 namespace 級別)函數(shù)為接口、以 class 的 non-virtual 成員函數(shù)為接口、以 virtual 函數(shù)為接口(interface)。本文主要講虛函數(shù)實現(xiàn)DLL接口和COM組件。
虛函數(shù)實現(xiàn)接口的做法是定義一個頭文件,將類實現(xiàn)的接口聲明,可以將這個類的成員函數(shù)暴露給客戶。而接口的實現(xiàn)部分是在派生類中,用戶獲得頭文件中的接口,無需接口的實現(xiàn)就可以調用接口。往往我們會將這個接口類的成員定義為純虛函數(shù),這樣更直觀的體現(xiàn)派生類必須完成對接口的實現(xiàn)部分。
以下是代碼實例:
接口頭文件 IPerson.h
- #include<iostream>
- #include<string>
- using namespace std;
-
- #ifdef _EXPORTING
- #define CLASS_DECLSPEC __declspec(dllexport)
- #else
- #define CLASS_DECLSPEC __declspec(dllimport)
- #endif
-
- class IPerson
- {
- public:
- IPerson(){};
-
- //接口
- virtual void SetName(const string &strName) = 0;
- virtual const string GetName() = 0;
- virtual void Work() = 0;
- };
__declspec(dllexport)用于導出符號,也就是定義該函數(shù)的dll;__declspec(dllimport)用于導入,也就是使用該函數(shù)。因為這個頭文件既要被定義該函數(shù)的dll包含,也要被使用該函數(shù)的程序包含,當被前者包含時我們希望使用__declspec(dllexport)定義函數(shù),當被后者包含時我們希望使用dllimport。于是我們使用
#ifdef _EXPORTING
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
這種技巧,在定義該函數(shù)的dll中,其編譯選項定義了_EXPORTING而使用該函數(shù)的程序則沒有定義。反正我們的頭文件是要暴露給用戶的,這樣定義可以保持DLL庫頭文件和用戶庫頭文件保持一致。
接口實現(xiàn)部分:CTeacher.cpp
- #include "IPerson.h"
-
- //定義接口
- CLASS_DECLSPEC bool GetIPersonObject(void** _RtObject);
-
- class CTeacher:public IPerson
- {
- public:
- CTeacher(){}
- //接口實現(xiàn)
- void SetName(const string &strName);
- const string GetName();
- void Work();
- private:
- string m_strName;
- };
-
- void CTeacher::SetName(const string &strName)
- {
- m_strName = strName;
- }
- const string CTeacher::GetName()
- {
- return m_strName;
- }
- void CTeacher::Work()
- {
- cout<<"I am teaching!"<<endl;
- }
-
- bool GetIPersonObject(void **_RtObject)
- {
- IPerson* pMan = NULL;
- pMan = new CTeacher();
- *_RtObject = (void*)pMan;
- return true;
- }
這樣,我們在工程->屬性->配置屬性->常規(guī)->配置類型 中選擇 動態(tài)庫(.dll)運行就可以生成對應的.dll和.lib。我們在客戶程序中就可以使用這個dll。具體做法如下:1.程序中包含接口的頭文件,即 IPerson.h 2.包含lib,在 工程->屬性->配置屬性->VC++目錄->庫目錄中加入.lib的文件目錄,在工程->屬性->配置屬性->連接器->輸入->附加依賴項中加入.lib 。
main.cpp
- #include "IPerson.h"
-
- #pragma comment(lib,"InterFace.lib")
-
- //導出接口
- bool CLASS_DECLSPEC GetIPersonObject(void** _RtObject);
-
- void main()
- {
- IPerson * _IPersonObj = NULL;
- void* pObj=NULL;
- if (GetIPersonObject(&pObj))
- {
- // 獲取對象
- _IPersonObj = (IPerson *)pObj;
- // 調用接口,執(zhí)行操作
- _IPersonObj->SetName("Tom");
- string strName = _IPersonObj->GetName();
- _IPersonObj->Work();
- }
- if (_IPersonObj !=NULL)
- {
- delete _IPersonObj ;
- _IPersonObj = NULL;
- }
- }
輸出如下:

到這里,一個簡單的虛函數(shù)實現(xiàn)DLL接口就做完了。有一點必須要說,設計庫的時候C++ 虛函數(shù)為接口是有弊端的?!耙坏┌l(fā)布,不能修改”。a)函數(shù)重名問題:我們通過函數(shù)名來調用DLL的函數(shù),在并行開發(fā)中 容易造成函數(shù)重名。b)依賴:如果采用常見的隱式連接,那DLL每發(fā)行了一個新版本都有 必要和應用程重新鏈接一次,因為DLL里面函數(shù)的地址可能已經(jīng)發(fā)生了改變。
COM組件的思想正好解決了上述問題,下一節(jié)講COM組件思想的簡單實現(xiàn)。
|