您好,登錄后才能下訂單哦!
本篇內容介紹了“C++接口類工程化方法有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
接口分為調用接口與回調接口,調用接口主要實現模塊解耦的作用,只要保持接口兼容性,模塊內部的升級對用戶可以做到無感知。良好的接口分層有助于各業務團隊高效率開發。
回調接口主要用于系統有異步事件需要通知用戶。系統預定義接口形式,并由用戶注冊,具體調用時機由系統決定。
調用接口
假設有一個網絡發送模塊類Network,類定義如下:
class Network{public:bool send();}
虛函數
最常用的就是虛函數,可以使用虛函數定義Network接口類:
class Network{ public: virtual bool send()=0 static Network* New(); static void Delete(Network* network_);}
將send定義為虛函數,由繼承類去實現(比如由PLC模塊或者以太模塊繼承),以靜態方法創建子類對象,以基類Network的指針返回給業務使用。資源遵循誰創建誰銷毀的原則,基類還提供Delete方法銷毀對象。因為對象銷毀封裝在接口內部,因此Network接口類可以不需要虛析構函數。
代碼使用虛函數易讀性較高,但是虛函數開銷較大(需要使用虛函數表指針間接調用),無法在編譯期間內聯優化,而事實上調用接口在編譯期就能確定使用哪個函數,不需要用到虛函數的動態特性。此外由于虛函數使用虛函數表指針間接調用的原因,增加虛函數會導致函數地址表索引變化,新增接口不能在二進制層面兼容老接口。而且由于用戶可能繼承了Network接口類,在末尾增加虛函數也有風險,因此虛函數接口一旦發布上線就基本無法修改。
指向實現的指針
可以使用指向實現的指針來定義Network接口類:
class NetworkImpl;class Network{ public: bool send(); static Network* New(); Network() ~Network(); private: NetworkImpl* impl;}
Network的具體實現由NetworkImpl完成,通過使用指向實現的指針的方式來定義接口,接口類對象的創建和銷毀可以由用戶負責,更好的管理對象生命周期。
此外該方法通用性強,新增接口不會影響二進制兼容性,有利于項目快速迭代。
但是該方法還是增加了一層調用,對性能還是略微有影響,不符合C++的零開銷原則。
隱藏的子類
隱藏的子類思想很簡單,接口要實現的目標就是解耦,主要就是隱藏實現,也就是隱藏接口類的成員變量。如果能將接口類的成員變量都轉移到另一個隱藏類中,那么接口類就不需要任何成員變量,那么就達到了隱藏實現的目的。具體實現方法如下:
class Network{ public: bool send(); static Network* New(); static void Delete(Network* network_); protected: Network(); ~ Network();}
Network接口類只有成員函數,沒有成員變量。提供靜態方法New創建對象,Delete方法銷毀對象。New方法的實現中會創建隱藏的子類NetworkImol的對象,并以父類Network指針的形式返回。NetworkImol類中定義了Network類的成員變量,并將Network類聲明為friend:
class NetworkImol:public Network{ friend class Network ; private: // 定義Network類的成員變量}
Network類的實現中創建NetworkImol子類對象,并以父類指針形式返回,通過將this強制轉換為子類NetworkImol類型的指針來訪問成員變量:
bool Network::send(){ NetworkImpl* impl = (NetworkImpl*)this; //通過impl訪問成員變量,實現Network的功能}static Network* New(){ return new NetworkImpl();}static Delete(Network* network){ delete (NetworkImpl*)network;}
該方法符合C++零開銷原則,且同樣符合二進制兼容性。
Rust語言中有一種Trait功能,可以在類外面實現一個Trait(不需要修改類代碼),那么C++同樣可以參考實現Trait功能假設需要在Network類中實現發送序列化數據,重新設計Network接口,Serializable類定義如下:
class Serializable{public: virtual void serialize()const =0;};
Network類定義如下:
class Network{public: bool send(const char* host, uint16_t port,constSerializable& buf);}
Serializable接口類似于Rust中的Trait,現在任何實現了Serializable接口類的對象都可以通過調用Network類接口完成數據發送功能。那么問題來了,加入項目迭代需要增加通過Network類發送int型數據,如何在不修改類定義的同時實現Serializable接口呢?很簡單:
class IntSerializable :public Serializable{public:IntSerializable(const int i):intthis(i){}virtual void serialize() const override{ buffer += std::to_string(*intthis);}private: const int* const intthis;};
之后就可以通過Network發送int型數據了:
Network* network = Network::New();int i=1;network->send(ip,port, IntSerializable(i));
非侵入式接口將類和接口區分開來,類中的數據只包含成員變量,不包含虛函數表指針,因此類不會因為實現了n個接口而引入n個虛函數表指針。而接口中只包含虛函數表指針,不包含數據成員,類和接口之間通過實現類進行類型轉換,類只有在充當接口使用的時候才會引入虛函數表指針,不充當接口的時候不會引入,符合C++零開銷原則。
Rust編譯器通過impl關鍵字記錄了每個類實現了哪些Trait,因此在賦值時編譯器可以自動完成將對象轉換為對應的Trait類型。而g++等C++編譯器并沒有記錄這些轉換信息,因此需要手動轉換類型。本質上還是通過代碼幫編譯器記錄每個接口類實現了哪些Trait,使用模板類的繼承,在編譯期實現類似“靜態多態”的功能。
“C++接口類工程化方法有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。