网上的例子,稍微有点错误。我给更改一下,附件上有源码!如有错误,请指正。

总结一下C++实现接口的技巧。

面向对象的语言诸如JAVA提供了Interface来实现接口,但C++却没有这样一个东西,尽管C++ 通过纯虚基类实现接口,譬如COM的C++实现就是通过纯虚基类实现的(当然MFC的COM实现用了嵌套类),但我们更愿意看到一个诸如 Interface的东西。下面就介绍一种解决办法。

程序6步

1、首先我们需要一些宏:

Cpp代码  
  1. //********************************************
  2. // Interface.h
  3. //主要是宏定义一些关键词,可以形成接口类
  4. //********************************************
  5. #ifndef INTERFACE_H
  6. #define INTERFACE_H
  7. #define Interface class
  8. #define DeclareInterface(name) Interface name { \
  9. public: \
  10. virtual ~name() {}
  11. #define DeclareBasedInterface(name, base) class name : \
  12. public base { \
  13. public: \
  14. virtual ~name() {}
  15. #define EndInterface };
  16. #define implements public
  17. #endif

2、有了这些宏,我们就可以这样定义我们的接口类:

Cpp代码  
  1. //***********************************************
  2. // IBar.h
  3. //通过宏定义生成我们的接口类,写一些纯虚函数
  4. //***********************************************
  5. #ifndef IBAR_H
  6. #define IBAR_H
  7. #include "Interface.h"
  8. DeclareInterface(IBar)
  9. virtual int GetBarData() const = 0;
  10. virtual void SetBarData(int nData) = 0;
  11. EndInterface
  12. #endif

3、再写一个父类BasicBar.h

Cpp代码  
  1. //***********************************************
  2. // BasicBar.h
  3. //Bar类的父亲类,用来继承测试
  4. //***********************************************
  5. #ifndef BASICBAR_H
  6. #define BASICBAR_H
  7. #include <iostream>
  8. class BasicBar
  9. {
  10. public:
  11. BasicBar(int x)
  12. {
  13. }
  14. ~BasicBar(){}
  15. virtual int getBarData1() const
  16. {
  17. std::cout <<"Get BasicBar!";
  18. return 0;
  19. }
  20. virtual void setBarData1(int nData)
  21. {
  22. }
  23. };
  24. #endif

4、现在我们可以像下面这样来实现我们的接口Bar.h了:

Cpp代码  
  1. //***********************************************
  2. //Bar.h
  3. //实现IBar接口的类
  4. //***********************************************
  5. #ifndef Bar_H
  6. #define Bar_H
  7. #include "Interface.h"
  8. #include "BasicBar.h"
  9. #include "IBar.h"
  10. #include <iostream>
  11. class Bar :public BasicBar,implements IBar
  12. {
  13. public:
  14. Bar(int x=0) : BasicBar(x)
  15. {
  16. }
  17. ~Bar(){}
  18. virtual int getBarData() const
  19. {
  20. std::cout <<"Get Bar!";
  21. return 0;
  22. }
  23. virtual void setBarData(int nData)
  24. {
  25. }
  26. };
  27. #endif

5、  怎么样,很简单吧,并不需要做很多的努力我们就可以在C++中使用接口了。在Main函数中引用我们的接口(我建立的是Qt 终端应用程序 )

Cpp代码  
  1. //***********************************************
  2. // main.h
  3. //主函数
  4. //***********************************************
  5. #include <QtCore/QCoreApplication>
  6. #include "IBar.h"
  7. #include "DataAccess.h"
  8. int main(int argc, char *argv[])
  9. {
  10. QCoreApplication a(argc, argv);
  11. //IBar *bar = new Bar();
  12. IBar *bar = DataAccess::createBar();
  13. bar->getBarData();
  14. delete bar;
  15. return a.exec();
  16. }

另外的问题:C++中不许我们实例化抽象类,如IBar *bar = new IBar(); 

  我们只能通过上述main.cpp中的IBar *bar = new Bar();实现, 这样我们还必须指定实例化那个对象Bar,好像前面的努力都白费了啊!!那怎么办呢,我们需要一个工厂:

6、建立一个数据工厂,DataAccess.h

Cpp代码  
  1. //***********************************************
  2. // DataAccess.h
  3. //数据工厂,通过它调用对象类,返回给接口函数
  4. //***********************************************
  5. #ifndef DATAACCESS_H
  6. #define DATAACCESS_H
  7. #include "IBar.h"
  8. #include "Bar.h"
  9. class DataAccess
  10. {
  11. // Construction & Destruction
  12. public:
  13. DataAccess()
  14. {
  15. }
  16. ~DataAccess()
  17. {
  18. }
  19. static IBar* createBar()
  20. {
  21. //返回对象给IBar接口
  22. return(new Bar());
  23. }
  24. };
  25. #endif

  这个时候,我们把第5步的main.cpp调用接口方法改一下:

Cpp代码  
  1. //***********************************************
  2. // main.h
  3. //主函数
  4. //***********************************************
  5. #include <QtCore/QCoreApplication>
  6. #include "IBar.h"
  7. #include "DataAccess.h"
  8. int main(int argc, char *argv[])
  9. {
  10. QCoreApplication a(argc, argv);
  11. //IBar *bar = new Bar();
  12. IBar *bar = DataAccess::createBar();
  13. bar->getBarData();
  14. delete bar;
  15. return a.exec();
  16. }

  整个过程中接口不负责任何具体操作,其他的程序要连接业务类、数据库的话,只需要构造一个业务对象、数据访问对象就OK,而不管工厂类如何变化。这就是接口的意义----抽象。

接口宏定义可以更简单点

上述步骤中我们可以把步骤1和步骤2写的更简单些,我们只需要Interface 和 implements 的宏定义,

这样我们的接口定义类IBar灵活性更强,可以继续继承其他的接口类,也更符合java、C#等语言写接口类的风格。

步骤1:Interface.h

Cpp代码  
  1. //********************************************
  2. // Interface.h
  3. //主要是宏定义一些关键词,可以形成接口类
  4. //********************************************
  5. #ifndef INTERFACE_H
  6. #define INTERFACE_H
  7. #define Interface class
  8. //#define DeclareInterface(name) Interface name { \
  9. //public: \
  10. //  virtual ~name() {}
  11. //
  12. //#define DeclareBasedInterface(name, base) class name : \
  13. //public base { \
  14. //public: \
  15. //      virtual ~name() {}
  16. //
  17. //#define EndInterface };
  18. #define implements public
  19. #endif

步骤2:IBar.h

Cpp代码  
  1. //***********************************************
  2. // IBar.h
  3. //通过宏定义生成我们的接口类,写一些纯虚函数
  4. //***********************************************
  5. #ifndef IBAR_H
  6. #define IBAR_H
  7. #include "Interface.h"
  8. Interface IBar
  9. {
  10. public:
  11. virtual ~IBar(){}
  12. virtual int getBarData() const = 0;
  13. virtual void setBarData(int nData) = 0;
  14. };
  15. //DeclareInterface(IBar)
  16. //
  17. //virtual int getBarData() const = 0;
  18. //virtual void setBarData(int nData) = 0;
  19. //EndInterface
  20. #endif

备注:

然而,由于这种C++实现接口方式并不是语言本身所直接支持的特性,所以我们需要遵循一些规则:

a)   声明一个类的时候,如果你的类除了要从接口类继承外还要从另一个类继承(结构上的继承,即is a关系),则把这个类作为第一个基类,就像我们平时做的一样,譬如CFrameWnd从CWnd继承,CBitmapButton从CButton继承,CMyDialog从CDialong继承。当你要从MFC类派生的时候,这尤其重要,把他们声明为第一个基类以避免破坏MFC的RuntimeClass机制。
         b)   其他的基类紧跟其后,有多少就跟多少,如果你需要的话。譬如:class Bar: public BasicBar, implements IBar, implements IOther, implements IWhatever, ...
         c)   接口类里面不要声明任何成员变量。接口类仅用于描述行为而不是数据。当你要作多重继承时,这样做可以避免数据成员被从同一个接口类多次继承。
         d)   接口类的所有成员函数定义为纯虚函数。这可以确保你的实现类来实现这些函数的全部,当然你也可以在抽象类实现部分函数,只要在你的派生类里实现剩下的函数。
         e)   不要从除了接口类的其他任何类派生你的接口类。DeclareBasedInterface()可以做到这个.普通类可以选择实现基接口还是派生的接口,后面一种意味着两者都要实现。
   f)  将一个指向实现接口的类的指针赋值给一个指向该接口类的指针是不需要强制类型转换的,但反过来将一个接口类的指针赋值给一个实现该接口的类的指针就需要 一个显式的强制类型转换。事实上我们可能会使用多重继承,这样这些转换我们就不能使用老式的转换。不过使用运行时类型信息(使用/GR选项)和动态类型转 换可以很好的工作当然也更安全。
   g) 此外dynamic_cast为你提供了一种查询一个对象或接口是否实现了一个指定的接口的途径。
   h) 你还要非常小心的避免不同接口函数的命名冲突。

如果你仔细观察DeclareInterface 和 DeclareBasedInterfaca宏你会发现有一个操作是必须的:每个接口类都有一个虚析构函数。你可能认为这不重要,但是如果没有这个就可能会导致一些问题,看看下面的例子:就像你看到的一样,这里有一个类工厂,它根据BarType来创建一个IBar的实现,当你使用完以后你当然希望要delete该对象,你会像下面这样做:

Cpp代码  
  1. int main()
  2. {
  3. IBar* pBar = BarFactory::CreateBar(Bar);
  4. pBar->SetName("MyFooBar");
  5. delete pBar;    // Oops!
  6. }

delete pBar 做了什么取决于该对象是否有一个虚析构函数。如果Bar没有一个虚析构函数,则只有IBar 的隐式的空析构函数被调用,Bar的析构函数不会被调用,这样就发生了内存泄露。接口类里虚析构函数的声明避免了这用状况,它确保每个实现接口的类都有一 个虚析构函数。

当你使用DeclareInterfac的时候,记得使用EndInterface和它匹配。Interface 宏和 implements宏仅仅是代替了class和public,这看起来是多余的,但我认为它们更明确的表达了代码的意图。如果我这么写:class Bar: public IBar,你可能认为这只是一个简单的继承;但如果我这么写:class Bar: implements IBar,你就会看到它实际的价值和意图---这是对一个接口的实现,而不是简单的一次继承。

C++接口实现总结 http://qimo601.iteye.com/blog/1393427相关推荐

  1. C语言下,获取文件信息 http://qimo601.iteye.com/blog/1517413

    http://qimo601.iteye.com/blog/1517413 C语言下,获取文件信息 博客分类: C/C++ C语言文件信息文件大小  C语言下,如何获取文件的生成时间,日期和文件大小等 ...

  2. dTree 动态生成树(http://luohua.iteye.com/blog/451453)

    转自<http://luohua.iteye.com/blog/451453>,感谢分享! dTree 动态生成树 dTree是个很方便在页面生成树的 js 控件,如果你下载了,我猜里在几 ...

  3. 导入工程时出现错误:Invalid project description http://berdy.iteye.com/blog/1115279...

    http://berdy.iteye.com/blog/1115279 new -> android project -> create project from exist source ...

  4. http://enki-ding-yeah-net.iteye.com/blog/1042644

    http://enki-ding-yeah-net.iteye.com/blog/1042644

  5. http://stamen.iteye.com/blog/1462899

    http://stamen.iteye.com/blog/1462899 [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/112308 ...

  6. http://hlhpyasd.iteye.com/blog/865865

    http://hlhpyasd.iteye.com/blog/865865 转载于:https://www.cnblogs.com/longshiyVip/p/5964965.html

  7. http://xmuzyq.iteye.com/blog/783218

    http://xmuzyq.iteye.com/blog/783218 在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Pro ...

  8. 如何防止SQL注入 http://zhangzhaoaaa.iteye.com/blog/1975932

    如何防止SQL注入 博客分类: 技术转载数据库 转自:http://021.net/vpsfaq/152.html -----解决方案--------------------------------- ...

  9. 微信开发之获取OAuth2.0网页授权认证和获取用户信息进行关联(转:http://playxinz.iteye.com/blog/2249634)

    最近有做了关于微信公众号和自己网站用户进行用户关联授权登录的一个功能,主要是用户关注该公众号,点击会员中心,则会弹出需要关联授权的网页授权:OAuth2.0网页授权,然后用户同意获取用户信息,进行用户 ...

最新文章

  1. python分别统计男女人数_python实现爬虫统计学校BBS男女比例(一)
  2. 死锁问题案例分析解决
  3. 多CPU,多核,多进程,多线程以及进程和线程的简单理解以及区别
  4. dubbo forbid service的解决办法
  5. java49_java培训 java49条基础知识
  6. 贝叶斯网络+推理+近似推理+变分法
  7. 关于json包爆红我有话说
  8. Cordova Android 禁用长按选中功能
  9. [Markdown]纯文本标记语言MarkdowPad2--MD语法知识
  10. 深度强化学习之稀疏奖励(Sparse Reward)
  11. java字符串中的数据排序
  12. win10启用php_zip,win10右键没有压缩文件选项怎么办
  13. 2018 网易校招 骰子游戏
  14. IKexpression解读二
  15. 蚁群算法讲解python
  16. 米家接入HomeKit系列二:通过群辉NAS的Docker搭建HomeAssistant
  17. 高中计算机竞赛官网,美国高中计算机竞赛系列:PClassic
  18. 用 Python 解数独(Sudoku)
  19. 如何制作GIF表情包,动态GIF怎么做
  20. 法学行政法论文选题有哪些?

热门文章

  1. 面试中常被问到的(22)TCP三次握手/四次挥手及问题
  2. 详细注释版的ABAQUS COH2D4单元批量添加Python脚本
  3. 人力资源社会保障部、中国人民银行关于社会保障卡加载金融功能的通知
  4. ADB调试工具的使用
  5. CT图像重建的演变——从滤波反投影到人工智能(Martin J. Willemink和Peter B. Noël)
  6. Wondershare PDFelement Pro Mac(好用的PDF编辑器)
  7. mysql中discount用法,Discounts
  8. NEC电子与东芝合并胎死腹中 或引入私募基金
  9. 调通sina33m下的GC0308(分色排版)V1.0
  10. Chapter 1 (Linear Equations in Linear Algebra): Vector equations (向量方程)