C++實現(xiàn)“隱藏實現(xiàn),開放接口”的方案
為什么要有接口?
接口就是一個程序與其它程序交流的窗口。就比如有一個電視機,我并不需要知道它是怎樣工作的,我只要知道按電源鍵就可以開啟電視,按節(jié)目加(+)減(-)可以切換電視頻道就可以了。
Java程序員都知道Java中有interface可以實現(xiàn)對外的接口,但C++并沒有接口這樣的語法,那它要好怎樣實現(xiàn)對外提供接口呢?我們可以通過純虛函數(shù)定義一個抽象類,專門用來聲明一個類的功能。
我們完成了一個程序模塊的開發(fā),要把這個程序模塊給別人用,你肯定不會把源代碼給他(那別人就完全撐屋你的技術(shù)了),你會把這個程序模塊編譯成一個庫(靜態(tài)庫lib或動態(tài)庫dll)再給別人用。那別人拿到你的庫后怎樣用呢?這就需要看你的程序所提供的接口。C++的封裝性是特別好的(個人覺得比Java好多了,Java打成的jar包很容易就可以被反編譯,C++要反編譯就困難多了),我只要給你編譯出的庫和接口的頭文件就可以了。
從一個實例講講實現(xiàn)方案
需要
我們先來看一個場景。假設(shè)有一個電子文檔(Document)、一個文檔下有多個頁(Page),每個頁下有多個文本單元(TextUnit,表示文檔內(nèi)元素的基本單位),一個文檔中的所有文本單元對象都有唯一的ID。其類圖關(guān)系如下:
圖1 :類的關(guān)系圖
設(shè)計
根據(jù)需求,我們可以定義三個類Document、Page、TextUnit分別表示文檔、頁、文本單元,每個類我們還需要一個對外的接口,于是需要三個對外的接口類IDocument、IPage、ITextUnit。
根據(jù)這些類我們先創(chuàng)建.cpp文件和.h文件,組織一下工程(EBook)目錄結(jié)構(gòu)如下:
圖2: 工程目錄結(jié)構(gòu)
這里Document、Page、TextUnit就是具體的實現(xiàn)類,IDocument、IPage、ITextUnit就是對外提供的接口,這樣就實現(xiàn)了實現(xiàn)與接口分離。
代碼實現(xiàn)
IDocument.h: #pragma once class IPage; class IDocument { public: virtual ~IDocument(void){} public: //--------------------------------------------------------------- //function: // GenerateId 生成本文檔內(nèi)唯一的文本對象ID //Access: // virtual public //Parameter: //Returns: // int - 返回ID //Remarks: // ... //author: luoweifu //--------------------------------------------------------------- virtual int GenerateId() = 0; //--------------------------------------------------------------- //function: // AddPage 添加一頁 //Access: // virtual public //Parameter: //Returns: // IPage* - 返回頁對象 //Remarks: // ... //author: luoweifu //--------------------------------------------------------------- virtual IPage* AddPage() = 0; }; IPage.h: #pragma once class ITextUnit; class IPage { public: virtual ~IPage(void){} public: //--------------------------------------------------------------- //function: // AddTextUnit 添加一個文本單元 //Access: // virtual public //Parameter: //Returns: // ITextUnit* - 文本單元對象 //Remarks: // ... //author: luoweifu //--------------------------------------------------------------- virtual ITextUnit* AddTextUnit() = 0; }; ITextUnit.h #pragma once class ITextUnit { public: ~ITextUnit(void){} public: //--------------------------------------------------------------- //function: // GetId 獲得ID //Access: // virtual public //Parameter: //Returns: // int - 返回ID //Remarks: // ... //author: luoweifu //--------------------------------------------------------------- virtual int GetId() = 0; //--------------------------------------------------------------- //function: // SetId 設(shè)置ID //Access: // virtual public //Parameter: // [in] int id - 要設(shè)置的ID //Returns: // void - //Remarks: // ... //author: luoweifu //--------------------------------------------------------------- virtual void SetId(int id) = 0; };
提供C接口
從上面的代碼我們可以看到IPage可以由IDocument創(chuàng)建,ITextUnit可以由IPage創(chuàng)建。那問題來了,IDocument由誰來創(chuàng)建呢?這時我們可以提供兩個全局的函數(shù)CreateDoc和DestroyDoc用來創(chuàng)建和銷毀IDocument的對象指針,這兩個函數(shù)是全局函數(shù)(C類型的函數(shù)),我們需要為其提供C的導(dǎo)出接口(這很重要)。其接口定義如下:
#pragma once #include "IDocument.h" #include "IPage.h" #include "ITextUnit.h" //=============================================================== //要導(dǎo)出靜態(tài)庫時,導(dǎo)出庫的工程和使用庫的工程都要加預(yù)編譯宏EXPORT_STATIC //要導(dǎo)出動態(tài)庫時,導(dǎo)出庫的工程要加預(yù)編譯宏EXPORT_STATIC,使用庫的工程不用 //=============================================================== #ifdef EXPORT //導(dǎo)出庫 #define _API_ __declspec(dllexport) #else //導(dǎo)入庫 #define _API_ __declspec(dllimport) #endif //EXPORT #ifdef EXPORT_STATIC //導(dǎo)出靜態(tài)庫 #define EBAPI int #else //導(dǎo)出動態(tài)庫 #define EBAPI extern "C" _API_ int #endif //EXPORT_STATIC //--------------------------------------------------------------- //function: // CreateDoc 創(chuàng)建Document對象 //Access: // public //Parameter: // [in] IDocument * & pDocument - //Returns: // EBAPI - //Remarks: // ... //author: luowf[/luoweifu] //--------------------------------------------------------------- EBAPI CreateDoc(IDocument*& pDocument); //--------------------------------------------------------------- //function: // DestroyDoc 銷毀一個Document對象 //Access: // public //Parameter: // [in] IDocument * pDocument - //Returns: // EBAPI - //Remarks: // ... //author: luowf[/luoweifu] //--------------------------------------------------------------- EBAPI DestroyDoc(IDocument* pDocument);
使用庫
我們可以將EBook編譯成一個靜態(tài)庫,然后再創(chuàng)建一個新的工程使用它。EBook工程設(shè)置:
創(chuàng)建一個新的工程UseEBook使用EBook庫。UseEBook工程配制:
Generation Properties\C++\Preprocess\Preprocess Definitions:EXPORT_STATIC
Generation Properties\Linker\General\Addtional Library Directories:lib庫所在路徑
Generation Properties\Linker\Input\Addtional Dependencies:EBook.lib
測試代碼:
#include "stdafx.h" #include <iostream> int _tmain(int argc, _TCHAR* argv[]) { IDocument* pDoc = NULL; if(CreateDoc(pDoc) != 0) { return -1; } IPage* pPage = pDoc->AddPage(); ITextUnit* pTextUnit = pPage->AddTextUnit(); std::cout << pTextUnit->GetId() << std::endl; DestroyDoc(pDoc); return 0; }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
相關(guān)文章
C++中set/multiset與map/multimap的使用詳解
這篇文章主要為大家詳細介紹了C++中set/multiset與map/multimap的使用,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下2023-02-02C語言數(shù)據(jù)結(jié)構(gòu)之判斷循環(huán)鏈表空與滿
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之判斷循環(huán)鏈表空與滿的相關(guān)資料,希望通過本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下2017-10-10