日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

不要重复发明轮子:C++重用的5重境界

發布時間:2025/6/15 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 不要重复发明轮子:C++重用的5重境界 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

軟件領域有一個著名的描述軟件重用的諺語:不要重復發明輪子

這個道理是很簡單,也很明白的,誰都不想重復無用的勞動,但具體實踐中我們該如何避免重復發明輪子呢?

各位注意了,諺語中是說“重復發明”,不是說“重復使用”,也就是說我們實踐中其實也是避免不了重復使用輪子的,因此實踐中我們的對策也可以用一句簡單的語句表達:發明能夠重復使用的輪子

下面我們就以C++語言為例,看看究竟如何“發明重復使用的輪子”。

?

第一重境界:代碼重用

最簡單的當然就是代碼重用了:寫一段公共代碼,然后放到各個項目里面去編譯。

這種方式最直觀,但如果你真的在實踐中如此應用,那么將面臨如下問題(假設3個項目共用):

1)一份代碼,三份拷貝

因為采用的是代碼編譯,所以一份代碼在存儲時會占用3份的磁盤空間,在運行時會占用3份的內存空間。

當然在現在這個磁盤空間動不動上100G,內存動不動上G的年代,可能大家對這種浪費不以為然,但是回過頭去看看歷史,如果在你的內存只有32M的年代,這種浪費就不一般了,將會對系統產生很大影響。

2)一次修改,三次編譯

假如某一天這份代碼修改了,也許是一個小小的BUG,也許是一點小小的優化,但最終的結果都是一樣的:所有用到這個公共代碼的項目都需要重新編譯。

對于普通的小程序來說,編譯可能是幾秒到幾分鐘的事情,功能驗證也很簡單,編譯問題看起來還不是很嚴重;但如果是企業級、電信級的程序,這種編譯加驗證的工作量是巨大的,而且要部署到已經運行的系統中時,可能需要卸載安裝。

?

第二重境界:簡單DLL

稍有經驗的人都知道,要解決代碼重用的問題其實已經有簡單的方法了,那就是動態鏈接庫(Windows平臺是DLLLinux/UNIXso,下面以DLL為例說明)。

簡單的DLL實現如下:將對象、方法的定義放在DLL里面,使用時只需要包含DLL的頭文件即可。

這樣簡單的一個設計,就能夠解決代碼重用的一個大問題:一份DLL只占一份磁盤空間、一份內存空間

但為什么我沒有說解決了另外一個大問題——編譯的問題呢?

乍一看好像是解決了編譯的問題,例如我修改函數體內的某個執行語句,或者加一個邏輯判斷,只需要編譯DLL就可以了呀!

如果只修改函數體那當然是沒有問題,但關鍵是世界沒有那么完美和簡單,我們可以做很多的修改,例如:

1)??修改類定義:例如增加一個成員變量。


2)??修改函數定義:例如增加一個函數參數,修改某個入參類型;

3)??修改函數體:例如增加一個語句,一個調用等。

以上修改除了第三種修改只需要編譯DLL外,其它兩種修改都需要重新編譯整個項目,也就是說,DLL能夠解決空間和編譯的部分問題,不能解決所有的編譯問題。

革命尚未成功,同志還需努力!!

第三重境界:代理接口DLL

看到這個名字,可能大家有點迷糊:代理?接口?DLL?三個風馬牛不相及的東東扯到一塊是什么意思呢?

其實只要按照字面意思就能夠大概理解:

代理:就是設計模式中的代理模式;

接口:就是Java中的Interface一個概念;

DLL:就是動態鏈接庫了:)

翻譯成一句完整的話就是:DLL通過代理模式對外提供接口

?

下面我們看看這個“代理接口DLL”是如何實現的。

/*******************************DLL代碼*********************************/

//聲明部分

class __decspec(dllexport) InterfaceClass{????//聲明接口類

????class RealizeClass;???????????????????//引入實現類

????RealizeClass* m_pRealizeClass;????????//指向實現類的指針,咦,怎么會有數據

????public:

????void Function1(int param1, char param2 );

????void Function2(int param1;

????void Function3(bool param1, char param2 );

????……………………………………………….

}

?

//實現部分

InterfaceClass::InterfaceClass(){

????m_pRealizeClass = new RealizeClass();

}

?

InterfaceClass:: Function1(int param1, char param2 ){

????return m_pRealizeClass-> Function1(param1, param2 );

}

//其它函數略。

第四重境界:繼承接口DLL

看起來“代理接口DLL”已經能夠很好的完成任務了,但追求完美的你是否總覺得有的地方不夠優美呢?

關鍵就在于這部分:

InterfaceClass:: Function1(int param1, char param2 ){

????return m_pRealizeClass-> Function1(param1, param2 );

}

以上這段代碼是代理模式的一種實現方法,但也有它的不足之處:對于RealizeClass的函數,InterfaceClass都要寫一個函數,每個函數的寫法都是一樣的:

return m_pRealizeClass-> FunctionXXX(param1, param2 ……………..);

對于只有幾個方法的類來說,這可能沒有什么,但是如果RealizeClass類有幾十上百個方法,那InterfaceClass就有幾十上百個這樣類似的函數,看起來是不是很暈呢?

?

有沒有一種方法能夠不用寫這么多的無聊的函數呢?有,這就是本章要介紹的“繼承接口DLL”。我們還是按照第三重境界的方法來解釋這個方法:

繼承:就是面向對象的繼承概念

接口:就是Java中的Interface一個概念;

DLL:就是動態鏈接庫了:)

翻譯成一句話就是:DLL通過繼承的方法對外提供接口

?

如果你還記得第三重境界的實現方式,一對比就會發現,這兩個方法其實大同小異,關鍵就是具體的實現方式不一樣:一個通過代理模式,一個通過繼承方式。那么我們就來看看這個繼承方式具體如何實現。

/*******************************DLL代碼聲明部分開始**********************/

class InterfaceClass{????//聲明接口類,無成員數據,只有方法,這里不用dllexport聲明,//為什么呢,請自行查閱相關資料?

????public:

????void Function1(int param1, char param2 ) = 0?//聲明為純虛函數,子類必須改寫;

????void Function2(int param1 = 0 ;

????void Function3(bool param1, char param2 ) = 0 ;

}

?

class RealizeClass::public InterfaceClass{?//繼承接口類,函數必須改寫

???????//成員變量

???????…………………………………..

???????//繼承的函數,需要重寫。

????public:

????void Function1(int param1, char param2 );

????void Function2(int param1 );

????void Function3(bool param1, char param2 ) ;

}

?

//這兩個函數是“繼承接口DLL”實現關鍵,后面介紹為什么。

extern InterfaceClass* g_InterfaceClassPtr ; //不要和下面的extern混淆哈:)

extern “C” InterfaceClass* __decspec(dllexport)??CreateInterfaceClass();

extern “C” InterfaceClass* __decspec(dllexport)??DeleteInterfaceClass();

/******************************* DLL代碼聲明部分結束**********************/

?

/*******************************DLL代碼定義部分開始**********************/

void RealizeClass::Function1(){

???????//函數具體實現,do what you want!!!

????……………………………..

}

?

void RealizeClass::Function2(){

???????//函數具體實現,do what you want!!!

????……………………………..

}

?

void RealizeClass::Function3(){

???????//函數具體實現,do what you want!!!

????……………………………..

}

?

InterfaceClass* g_InterfaceClassPtr = NULL;

?

InterfaceClass* CreateInterfaceClass(){

???if(g_InterfaceClassPtr == NULL){

???????g_InterfaceClassPtr = new RealizeClass();?//生成的是具體的類

}

?

return g_InterfaceClassPtr;

}

?

InterfaceClass* DeleteInterfaceClass(){

?????delete g_InterfaceClassPtr;

?????g_InterfaceClassPtr = NULL;

}

?

/*******************************DLL代碼定義部分結束**********************/

?

?

/***************************使用DLL的客戶端代碼********************/

InterfaceClass* pInterfaceClass = CreateInterfaceClass();

pInterfaceClass->Function1(param1, param2);

………………………………………………………

DeleteInterfaceClass();

/***************************使用DLL的客戶端代碼********************/

?

樣例代碼到這里就結束了,我們來總結一下這種方法的關鍵實現點:

1)實現類繼承接口類,而不是“代理接口DLL”中的接口類包含實現類的指針(UML中的聚合Aggregation的一種實現方式);

2)由于第一條的存在,使得客戶端不能直接new某個對象,所以要通過CreateInterfaceClass來創建一個具體的實現類。

3)由于第二條創建了實現類,為了避免內存泄漏,所以要DeleteInterfaceClass

?


/*******************************DLL代碼*********************************/

?

/***************************使用DLL的客戶端代碼********************/

InterfaceClass???pInterfaceClass = new InterfaceClass();

pInterfaceClass->Function1(param1, param2);

/***************************使用DLL的客戶端代碼********************/

各位看完上面的樣例,基本上應該都能夠明白是如何實現的,但可能會問“為什么還是有一個指針數據類型呢”?不是說沒成員數據的嗎?

是的,這里關鍵就在于這個指針,雖然有這個成員數據,但是大家想一想,指針是一個固定大小的類型,而且客戶端程序是看不到這個指針的。因此不管對于以下哪個變化,InterfaceClass的結構都不變化,客戶端的代碼也不受任何影響,不需要重新編譯。

1)??具體實現的RealizeClass增加、修改、刪除成員數據;

2)??RealizeClass有一天改了名稱變成了RealizeClassSE

3)??RealizeClassFunction1函數改名了,甚至加了一個缺省參數了。

?

講了半天,基本上把“代理接口DLL”是一個什么東東、如何實現講完了,但是最根本的問題還沒有回答——這重境界要解決什么問題?

其實看完如何實現后,聰明的你基本上都能猜出要解決什么問題了,當然就是第二重境界遺留的兩個問題了:

1)??修改類定義:例如增加一個成員變量。

2)??修改函數定義:例如增加一個函數參數,修改某個入參類型;

代理接口DLL通過代理模式(其實本質上就是一個指針)解決了上述兩個問題,把對外呈現和內部實現分別由不同的類實現,然后通過一個簡單的指針將兩個類連接起來

第五重境界:消息通信

話說當年明教教主在連乾坤大挪移的時候,實際上并沒有所謂的第7重,這第7重只是創始人憑借著自己的聰明才智想出來的,根本無法證實是否正確,幸好張無忌沒有練才躲過一劫。

其實我們這里的所謂第5重也是我憑空想出來的:)大家接下來也可以看到,這一重境界其實和C++或者DLL完全沒有關系,但這一重境界絕對不是憑空亂想,而且也絕對不是無法證實的,這一重境界是每個IT人都知道的,也許是每個人進入IT界接觸的第一個重用方法——消息通信

aha,是不是覺得很簡單、很普通、很傻很天真?!!

但是仔細想想,這確實是最高的重用境界,我們將這種方法與DLL方法來比較一下:

1)消息通信和編譯無關,DLL和編譯相關;

2)消息通信支持分布式部署,DLL不支持分布式部署;

3)消息通信和具體語言無關,C++的程序可以和Java的程序通信;

4)消息通信可以和操作系統無關,DLL和操作系統綁定的;

看起來消息通信這種方式幾乎完美了,那我們還要DLL干嘛呢?前面講了那么多,那不是浪費口水和時間?

當然不是了,消息通信也存在缺點的:

1)要通過某種方式來收發消息,例如TCPSCTPTDM鏈路;

2)要制定協議來規定收發消息規則和行為規則;

3)要對發送消息和接受消息進行編解碼;

?

總結起來就是消息通信是重量級的,DLL是輕量級的。

?

廢話說了這么多,我們舉一個簡單的樣例,由于消息通信實現比較復雜,這里就不寫代碼了,簡單的描述一下。

例如系統有ABCD 4個模塊,都需要訪問數據庫,對數據庫進行操作,由于對數據庫的操作基本上建立連接、執行操作、釋放連接,這些操作基本上都是一樣的。

如果是DLL實現方式,那么就把建立連接、執行操作、釋放連接做成DLL,然后由每個模塊去調用DLL的對應函數。

如果是消息通信,按照如下機制實現一個消息通信:

1)新建一個模塊E,這個模塊完成建立連接、執行操作、釋放操作的功能;

2)規定ABCD通過TCP/IPE通信;

3)規定消息格式,例如采用TLV編碼,或者二進制編碼等

4)規定消息內容,例如:發1標識建立連接、100表示建立連接結果,2表示釋放連接,200表示釋放連接的結果,等等

?

例子到這里就結束了,是不是覺得很簡單,或者意猶未盡?

是的,一旦采用消息通信方式,你可以發揮的余地就很大了,還是上面那個例子,我們可以做很多的優化,例如:

1ABCD不再需要關注建立連接和釋放連接了,只需要關注數據操作就ok了;

2E模塊可以采用連接池、多線程等技術來提高性能;

3)如果底層數據庫修改了,只需要修改E就可以了,ABCD完全不需要任何修改,其實ABCD都不知道底層數據庫是Oracle還是DB2.

4E可以用任何編程語言編寫,也可以運行在任何操作系統上;

5E進程可以實現雙機主備等機制來保證可靠性或者性能;

……………………………………………………

?

總結

好不容易把這個東東講完了,也算是自己總結歸納了一下,當然,由于才疏學淺,難免出現遺漏和錯誤,還請大家糾正。

由于篇幅有限,每一篇都寫得比較簡單,基本上就是把設計思想介紹了一下,實際中應用肯定還有很多問題和細節需要大家去解決,在這里就不一一細講了(例如第4重境界需要解決多線程的同步問題、第5重境界需要設計好消息格式和消息內容等)。


總結

以上是生活随笔為你收集整理的不要重复发明轮子:C++重用的5重境界的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。