首页 > 编程 > C++ > 正文

C++实现“隐藏实现,开放接口”的方案

2020-05-23 14:17:18
字体:
来源:转载
供稿:网友

本文从一个实例讲解了C++实现“隐藏实现,开放接口”的方案,文章条理清新,内容充实,需要的朋友可以参考下

为什么要有接口?

接口就是一个程序与其它程序交流的窗口。就比如有一个电视机,我并不需要知道它是怎样工作的,我只要知道按电源键就可以开启电视,按节目加(+)减(-)可以切换电视频道就可以了。

Java程序员都知道Java中有interface可以实现对外的接口,但C++并没有接口这样的语法,那它要好怎样实现对外提供接口呢?我们可以通过纯虚函数定义一个抽象类,专门用来声明一个类的功能。

我们完成了一个程序模块的开发,要把这个程序模块给别人用,你肯定不会把源代码给他(那别人就完全撑屋你的技术了),你会把这个程序模块编译成一个库(静态库lib或动态库dll)再给别人用。那别人拿到你的库后怎样用呢?这就需要看你的程序所提供的接口。C++的封装性是特别好的(个人觉得比Java好多了,Java打成的jar包很容易就可以被反编译,C++要反编译就困难多了),我只要给你编译出的库和接口的头文件就可以了。

从一个实例讲讲实现方案

需要

我们先来看一个场景。假设有一个电子文档(Document)、一个文档下有多个页(Page),每个页下有多个文本单元(TextUnit,表示文档内元素的基本单位),一个文档中的所有文本单元对象都有唯一的ID。其类图关系如下:

C++实现“隐藏实现,开放接口”的方案

图1 :类的关系图

设计

根据需求,我们可以定义三个类Document、Page、TextUnit分别表示文档、页、文本单元,每个类我们还需要一个对外的接口,于是需要三个对外的接口类IDocument、IPage、ITextUnit。

根据这些类我们先创建.cpp文件和.h文件,组织一下工程(EBook)目录结构如下:

C++实现“隐藏实现,开放接口”的方案

图2: 工程目录结构

这里Document、Page、TextUnit就是具体的实现类,IDocument、IPage、ITextUnit就是对外提供的接口,这样就实现了实现与接口分离。

代码实现

 

 
  1. IDocument.h: 
  2.  
  3. #pragma once 
  4.  
  5. class IPage; 
  6.  
  7. class IDocument 
  8. public
  9. virtual ~IDocument(void){} 
  10.  
  11. public
  12.  
  13. //--------------------------------------------------------------- 
  14. //function:  
  15. // GenerateId 生成本文档内唯一的文本对象ID 
  16. //Access: 
  17. // virtual public  
  18. //Parameter: 
  19. //Returns: 
  20. // int - 返回ID 
  21. //Remarks: 
  22. // ... 
  23. //author: luoweifu 
  24. //--------------------------------------------------------------- 
  25. virtual int GenerateId() = 0; 
  26.  
  27. //--------------------------------------------------------------- 
  28. //function:  
  29. // AddPage 添加一页 
  30. //Access: 
  31. // virtual public  
  32. //Parameter: 
  33. //Returns: 
  34. // IPage* - 返回页对象 
  35. //Remarks: 
  36. // ... 
  37. //author: luoweifu 
  38. //--------------------------------------------------------------- 
  39. virtual IPage* AddPage() = 0; 
  40. }; 
  41.  
  42. IPage.h: 
  43.  
  44. #pragma once 
  45.  
  46. class ITextUnit; 
  47.  
  48. class IPage 
  49. public
  50. virtual ~IPage(void){} 
  51.  
  52. public
  53.  
  54. //--------------------------------------------------------------- 
  55. //function:  
  56. // AddTextUnit 添加一个文本单元 
  57. //Access: 
  58. // virtual public  
  59. //Parameter: 
  60. //Returns: 
  61. // ITextUnit* - 文本单元对象 
  62. //Remarks: 
  63. // ... 
  64. //author: luoweifu 
  65. //--------------------------------------------------------------- 
  66. virtual ITextUnit* AddTextUnit() = 0; 
  67. }; 
  68.  
  69. ITextUnit.h 
  70.  
  71. #pragma once 
  72.  
  73. class ITextUnit 
  74. public
  75. ~ITextUnit(void){} 
  76.  
  77. public
  78. //--------------------------------------------------------------- 
  79. //function:  
  80. // GetId 获得ID 
  81. //Access: 
  82. // virtual public  
  83. //Parameter: 
  84. //Returns: 
  85. // int - 返回ID 
  86. //Remarks: 
  87. // ... 
  88. //author: luoweifu 
  89. //--------------------------------------------------------------- 
  90. virtual int GetId() = 0; 
  91.  
  92. //--------------------------------------------------------------- 
  93. //function:  
  94. // SetId 设置ID 
  95. //Access: 
  96. // virtual public  
  97. //Parameter: 
  98. // [in] int id - 要设置的ID 
  99. //Returns: 
  100. // void -  
  101. //Remarks: 
  102. // ... 
  103. //author: luoweifu 
  104. //--------------------------------------------------------------- 
  105. virtual void SetId(int id) = 0; 
  106.  
  107. }; 

提供C接口

从上面的代码我们可以看到IPage可以由IDocument创建,ITextUnit可以由IPage创建。那问题来了,IDocument由谁来创建呢?这时我们可以提供两个全局的函数CreateDoc和DestroyDoc用来创建和销毁IDocument的对象指针,这两个函数是全局函数(C类型的函数),我们需要为其提供C的导出接口(这很重要)。其接口定义如下:

 

 
  1. #pragma once 
  2.  
  3. #include "IDocument.h" 
  4. #include "IPage.h" 
  5. #include "ITextUnit.h" 
  6.  
  7.  
  8. //=============================================================== 
  9. //要导出静态库时,导出库的工程和使用库的工程都要加预编译宏EXPORT_STATIC 
  10. //要导出动态库时,导出库的工程要加预编译宏EXPORT_STATIC,使用库的工程不用 
  11. //=============================================================== 
  12. #ifdef EXPORT //导出库 
  13. #define _API_ __declspec(dllexport) 
  14. #else //导入库 
  15. #define _API_ __declspec(dllimport) 
  16. #endif //EXPORT 
  17.  
  18.  
  19. #ifdef EXPORT_STATIC //导出静态库 
  20. #define EBAPI int 
  21. #else //导出动态库 
  22. #define EBAPI extern "C" _API_ int 
  23. #endif //EXPORT_STATIC 
  24.  
  25.  
  26. //--------------------------------------------------------------- 
  27. //function:  
  28. // CreateDoc 创建Document对象 
  29. //Access: 
  30. // public  
  31. //Parameter: 
  32. // [in] IDocument * & pDocument - 
  33. //Returns: 
  34. // EBAPI - 
  35. //Remarks: 
  36. // ... 
  37. //author: luowf[/luoweifu] 
  38. //--------------------------------------------------------------- 
  39. EBAPI CreateDoc(IDocument*& pDocument); 
  40.  
  41.  
  42. //--------------------------------------------------------------- 
  43. //function:  
  44. // DestroyDoc 销毁一个Document对象 
  45. //Access: 
  46. // public  
  47. //Parameter: 
  48. // [in] IDocument * pDocument - 
  49. //Returns: 
  50. // EBAPI - 
  51. //Remarks: 
  52. // ... 
  53. //author: luowf[/luoweifu] 
  54. //--------------------------------------------------------------- 
  55. EBAPI DestroyDoc(IDocument* pDocument); 

使用库

我们可以将EBook编译成一个静态库,然后再创建一个新的工程使用它。EBook工程设置:

C++实现“隐藏实现,开放接口”的方案

C++实现“隐藏实现,开放接口”的方案

创建一个新的工程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

测试代码:

 

 
  1. #include "stdafx.h" 
  2.  
  3. #include <iostream> 
  4.  
  5. int _tmain(int argc, _TCHAR* argv[]) 
  6. IDocument* pDoc = NULL; 
  7. if(CreateDoc(pDoc) != 0) 
  8. return -1; 
  9.  
  10. IPage* pPage = pDoc->AddPage(); 
  11. ITextUnit* pTextUnit = pPage->AddTextUnit(); 
  12. std::cout << pTextUnit->GetId() << std::endl; 
  13.  
  14. DestroyDoc(pDoc); 
  15.  
  16. return 0; 

以上就是本文的全部内容,希望对大家的学习有所帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表