本文從一個實例講解了C++實現“隱藏實現,開放接口”的方案,文章條理清新,內容充實,需要的朋友可以參考下
為什么要有接口?
接口就是一個程序與其它程序交流的窗口。就比如有一個電視機,我并不需要知道它是怎樣工作的,我只要知道按電源鍵就可以開啟電視,按節目加(+)減(-)可以切換電視頻道就可以了。
Java程序員都知道Java中有interface可以實現對外的接口,但C++并沒有接口這樣的語法,那它要好怎樣實現對外提供接口呢?我們可以通過純虛函數定義一個抽象類,專門用來聲明一個類的功能。
我們完成了一個程序模塊的開發,要把這個程序模塊給別人用,你肯定不會把源代碼給他(那別人就完全撐屋你的技術了),你會把這個程序模塊編譯成一個庫(靜態庫lib或動態庫dll)再給別人用。那別人拿到你的庫后怎樣用呢?這就需要看你的程序所提供的接口。C++的封裝性是特別好的(個人覺得比Java好多了,Java打成的jar包很容易就可以被反編譯,C++要反編譯就困難多了),我只要給你編譯出的庫和接口的頭文件就可以了。
從一個實例講講實現方案
需要
我們先來看一個場景。假設有一個電子文檔(Document)、一個文檔下有多個頁(Page),每個頁下有多個文本單元(TextUnit,表示文檔內元素的基本單位),一個文檔中的所有文本單元對象都有唯一的ID。其類圖關系如下:
圖1 :類的關系圖
設計
根據需求,我們可以定義三個類Document、Page、TextUnit分別表示文檔、頁、文本單元,每個類我們還需要一個對外的接口,于是需要三個對外的接口類IDocument、IPage、ITextUnit。
根據這些類我們先創建.cpp文件和.h文件,組織一下工程(EBook)目錄結構如下:
圖2: 工程目錄結構
這里Document、Page、TextUnit就是具體的實現類,IDocument、IPage、ITextUnit就是對外提供的接口,這樣就實現了實現與接口分離。
代碼實現
- IDocument.h:
- #pragma once
- class IPage;
- class IDocument
- {
- public:
- virtual ~IDocument(void){}
- public:
- //---------------------------------------------------------------
- //function:
- // GenerateId 生成本文檔內唯一的文本對象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 設置ID
- //Access:
- // virtual public
- //Parameter:
- // [in] int id - 要設置的ID
- //Returns:
- // void -
- //Remarks:
- // ...
- //author: luoweifu
- //---------------------------------------------------------------
- virtual void SetId(int id) = 0;
- };
提供C接口
從上面的代碼我們可以看到IPage可以由IDocument創建,ITextUnit可以由IPage創建。那問題來了,IDocument由誰來創建呢?這時我們可以提供兩個全局的函數CreateDoc和DestroyDoc用來創建和銷毀IDocument的對象指針,這兩個函數是全局函數(C類型的函數),我們需要為其提供C的導出接口(這很重要)。其接口定義如下:
- #pragma once
- #include "IDocument.h"
- #include "IPage.h"
- #include "ITextUnit.h"
- //===============================================================
- //要導出靜態庫時,導出庫的工程和使用庫的工程都要加預編譯宏EXPORT_STATIC
- //要導出動態庫時,導出庫的工程要加預編譯宏EXPORT_STATIC,使用庫的工程不用
- //===============================================================
- #ifdef EXPORT //導出庫
- #define _API_ __declspec(dllexport)
- #else //導入庫
- #define _API_ __declspec(dllimport)
- #endif //EXPORT
- #ifdef EXPORT_STATIC //導出靜態庫
- #define EBAPI int
- #else //導出動態庫
- #define EBAPI extern "C" _API_ int
- #endif //EXPORT_STATIC
- //---------------------------------------------------------------
- //function:
- // CreateDoc 創建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編譯成一個靜態庫,然后再創建一個新的工程使用它。EBook工程設置:
創建一個新的工程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;
- }
以上就是本文的全部內容,希望對大家的學習有所幫助。
新聞熱點
疑難解答