跟我學c 中級篇 pimpl

2021-10-08 12:53:56 字數 2248 閱讀 8807

private implementation,私有化實現。在c++中,由於語言本身的限制,沒有純粹的介面定義。這就導致了在介面的使用上很多c++的人員都是隨心而動。有用抽象類的純虛函式的,有直接用c型別的介面的。有乾脆提供介面類的…不一而足吧。根據實際情況,實事求是的選擇才是乙個好的標準。

在c++中,大量的標頭檔案的安全包含,本身就是乙個重要的問題,普通的重複定義這都是小問題。一些莫名的,甚至標頭檔案的順序都引起的「血案」也是經常發生的。正所謂,**量大了,啥情況都遇得到。而使用pimpl機制,可以優化減少標頭檔案**現類似問題的風險。同時,在c++系統中還有乙個重要問題,比如乙個標頭檔案被廣泛包含,如果修改其中一些內容,可能導致大規模的**重編譯。但是如果採用pimpl機制就會大幅降低這種情況,看下面的例子:

class  content

;#include "a.h"

class usecontent1

;

在上面的例子中,usecontent1可能會被上層更廣泛的呼叫,這樣,一旦content增加了私有和保護這些本來不會影響對外介面的的**時,仍然會引起上述的大規模的編譯單元的重構。這個代價是很大的。但是,假如做一下下面的**修改:

//#include "a.h"

class content;

class usecontent

;

你就會發現,在這個檔案裡,已經看不到content的標頭檔案了,這樣,如果不對公有介面進行修改,就不會出現上述的情況。指標化,同時將實現移動到cpp檔案中,就實現了降低耦合,提高編譯速度,隱藏實現細節的好處。在小的工程裡可能這樣做的意義確實並不是多大,但是如果工程量大到編譯需要小時以上時,他的意義就體現出來。

這也是軟體程式設計原則裡,提到的介面只能增加,不能修改的原因。因為如果只增加,老版本就可以正常使用,而新版本使用新介面,實現很好的版本相容。

從本質上講,pimpl有些類似於設計模式中的橋接模式,也就是常說的,介面隔離變化。

標頭檔案的遞迴包含,編譯器類似「驚群」一樣的重編譯原始碼,都可以利用pimpl進行有效安全的降低風險,當然,pimpl也不是萬能的,還是要根據情況來處理。

主要的應用場景有下面三種:

1、**解耦

這個很容易理解,通過指標來實現不同的類之間的互相隱藏,非常容易實現**層次上的隔離,從而實現降低耦合。

2、降低編譯依賴和標頭檔案的巢狀層級,降低標頭檔案應用風險

正如上面所述,減少了在標頭檔案中,對其它標頭檔案的包含,如此下去,就會有效的減少標頭檔案的巢狀層級。同樣,標頭檔案的減少,就意味著編譯的互相依賴性降低。大家在程式設計時,可能經常會遇到***.h無法發現的編譯錯誤,完美的狀態下,**檔案中除了自己的標頭檔案不包含其它任何其它標頭檔案,出了錯也極其容易定位。

3、隔離介面變化,穩定介面實現版本相容

外界只看到指標,而看不到指標具體實現,而具體的實現由介面來提供,這樣,介面穩定,具體的指標實現可以靈活掌握。

使用pimpl,對於新手來說,乙個最常見的問題就是「不完整型別定義」,很多新手在遇到這種問題,特別是**量大的情況下,可能瞬間就懵了。在上面例子中,就可能隱藏著類似的問題。看下面的**:

#include "b.h"

void usecontent::usecontentfunc()

這就會出現不完整型別定義?為什麼出現呢。因為編譯器看不到c,即content這個類的定義。也就是說,編譯器只知道有c,但不知道c裡面到底有沒有test,不清楚。其實編譯器都考慮不到test就報錯了,因為它還看不到c這個物件的建構函式。編譯器就是這麼傻,為啥看不到?因為沒包含標頭檔案,現在在上面的**中增加標頭檔案包含,就ok了。

#include "a.h"

#include "b.h"

void usecontent::usecontentfunc()

需要說明的是,pimpl的乙個容易被人忽略的問題,那就是不同的編譯器和不同的編譯級別情況下,可能產生的問題有不同。這個就需要程式設計的實踐經驗了,但是其基本的原理都是上面所說。

另外需要注意的是,如果不使用智慧型指標,要注意指標的釋放問題。

通過上面分析可能看出來,pimpl的優勢還是比較明顯的,但是也不代表這種方式就是完美的,引入這個機制,必然增加設計的複雜性,相對來說降低了**的易讀性和維護性。這也是本文開頭總是提到要實事求是,根據實際情況來引入各種設計的原因。既不能因噎食,更不能刻板教條。這其實是程式設計思想的一部分了。

跟我學c 中級篇 動態庫

在linux下建立動態庫的編譯選項有乙個 fpic,它的意思是與位置無關 position independet code 那麼它有什麼意義呢?在沒有出現這個選項前,是不是就不能建立動態庫呢。答案肯定是否定的。其實這就是上面提到的發展過程中的,如果沒有這個選項,只能完全載入副本,而如果使用了這個選項...

跟我學C 中級篇 STL的學習

c 的標準庫主要包含兩大類,首先是包含c的標準庫的,當然,為了適應c 對一些c庫進行了少許的修改和增加。最重要的當然是物件導向的c 庫 而c 庫又可以分成兩大類,即物件導向的c 庫和標準模板庫,也就是題目中的stl。另外在此基礎上,還要提醒同學們的是,除了上面的庫,在各個平台的開發廠商中,還會針對實...

跟我學C 中級篇 STL的容器Array

stl中的array陣列型別是在c tr1中才提出的,在之前只有vector這個類似於陣列的型別。但在實際應用中發現,vector和實際應用陣列還是有非常大的區別,包括迭代器訪問的控制,記憶體大小的控制等。用過vector的很容易發現它和實際使用中的陣列的諸多不同之處。換句話說,實際開發過程中,還是...