1. 程式人生 > >談談vc如何寫dll(封裝性,隱藏標頭檔案,私有成員) .

談談vc如何寫dll(封裝性,隱藏標頭檔案,私有成員) .

最近專案進行到一定階段,老闆要求把已完成部分分離開並把各模組封裝成dll。用vs開發dll當然很簡單,是用vs的嚮導可以很快寫一個dll.但是我遇到了一些問題:剛開始只需要把各個模組的標頭檔案和cpp加入到一個新的dll工程,然後又把匯出類的成員函式和成員變數用到的結構體型別、類型別定義的標頭檔案加進來,編譯後dll就寫出來了。問題是當我要使用這個dll時我就得把所有的相關標頭檔案包含到引用dll的工程中。這樣顯然違背的我寫dll的初衷。我認為我寫dll的目的主要有兩個:一是封裝性。我不希望將我演算法細節暴露給使用dll的人。因此我想把匯出類引用的一些特殊型別(指的是除了簡單型別之外的結構體或者類型別)的私有成員隱藏起來,這樣就需要把那些標頭檔案還有引用標頭檔案的define語句也隱藏。二是易用性。把我這些dll給別人用時要讓他們用起來很方便。如果不加修改的把現在的東西給他們用時需要他們先來熟悉這些特殊型別的定義,比如這些型別資料的初始化等等。而且我的dll引用到的標頭檔案有好幾個,而這些標頭檔案又引用了其他標頭檔案。也就是說如果直接提供一個dll我同時還用提供10多個頭檔案。

在網上我查了很多地方,總結別人的經驗,總算把自己的dll封裝好了。

    廢話一大堆,現在說說我是怎麼做的吧。

    現在我有一個匯出類BClass,他的成員函式的引數型別和私有成員變數型別都被很多其他標頭檔案所定義,網上提供的一個解決辦法是把標頭檔案的define引用語句放到對應的cpp中,這當然是必須的,但是僅僅這樣做我們在匯出類中使用這些特殊型別的變數只能使用指標。這一點是和dll的特性有關的,原因在於dll只在執行時才被程式載入,在編譯時我們無法知道cpp引用標頭檔案裡定義的型別。這樣如果不使用指標型別變數,由於我們不知道那些型別的變數的大小,在編譯時就會報錯。所以我們必須把那些非指標型特殊變數改成指標型別。可是如果我們不想這樣改的話呢?另外還有缺點就是這樣仍然可以讓別人知道我的匯出類的完整結構,仍然可以略微知道dll開發者的一些細節,封裝性仍然不夠好。

這樣在我的匯出類裡面還要把一些成員函式和成員變數也給隱藏。容易解決的辦法仍然有,把這些函式和變數都從匯出類裡面拿出來,放到cpp裡面作為普通的全域性變數和普通函式。這樣也可以解決問題但是不是一個好辦法,因為這樣破壞了類結構(呵呵,曾經有同事直接告訴我不用匯出類了,全部改成到處函式得了)。

    問題的解決辦法:現有的匯出類結構什麼都不用改,但是這個類不能作為匯出類,在此類的基礎上定義他的抽象基類,如IBClass。然後我們把IBClass作為匯出類,並且只把我們希望匯出的成員函式或者公有變數放在抽象基類裡面,把這些函式都作為虛擬函式。那麼新的dll我們怎麼使用呢,現在只涉及到類的初始化問題以及析構問題。在這裡我定義了一個工廠類用來初始化匯出類。

現在把過程寫出來:

最初的BClass標頭檔案:

#include"CClass.h"
#include"DClass.h"

class __declspec(dllexport) BClass{

func1(); // 這個我希望對dll使用者可見

func2(CClass param1);//這個我不希望對使用者可見,包括CClass型別以及定義它的標頭檔案CClass.h

DClass param2;//這個我不希望對使用者可見,包括DClass型別以及定義它的標頭檔案DClass.h
}


因此我的做法是在此基礎上新增標頭檔案IBClass.h:

class __declspec(dllexport) IBClass{
virtual func1(); // 這個我希望對dll使用者可見
}


然後我添加了BFactory.h標頭檔案和BFactory.cpp檔案
BFactory.h:

#include "IBClass.h"

class BClass;

class __declspec(dllexport) TrackerFactory
{
public:
 static IBClass *GetB();
 static void Destroy();
private:
 static BClass *pB;
}
;

BFactory.cpp:

#include "BClass.h"
#include "BFactory.h"

BClass *BFactory::pB = 0;

IBClass *BFactory::GetB()
{
 if(!pB)
 {
  pB = new BClass(); 
 }

 return pB;
}


void BFactory::Destroy()
{
 if(pB)
  delete pB;
}