- 浏览: 3416545 次
- 性别:
- 来自: 苏州
文章分类
最新评论
-
sonichy:
Qt5改动很多,要改改了。
基于QItemDelegate的例子1 SpinBoxDelegate -
我的主页6:
楼主,2.2子查询的分页方式:SELECT * FROM ar ...
Mysql 分页语句Limit用法 -
liguoqi:
非常感谢楼主的用心指导,工具以及图片例子都提供了 赞!
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
问下这个图片怎么解压损坏呀
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
楼主讲解的非常详细,还附带工具和图片例子,非常感谢
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载
网上的例子,稍微有点错误。我给更改一下,附件上有源码!如有错误,请指正。
总结一下C++实现接口的技巧。
面向对象的语言诸如JAVA提供了Interface来实现接口,但C++却没有这样一个东西,尽管C++ 通过纯虚基类实现接口,譬如COM的C++实现就是通过纯虚基类实现的(当然MFC的COM实现用了嵌套类),但我们更愿意看到一个诸如 Interface的东西。下面就介绍一种解决办法。
程序6步
1、首先我们需要一些宏:
//******************************************** // Interface.h //主要是宏定义一些关键词,可以形成接口类 //******************************************** #ifndef INTERFACE_H #define INTERFACE_H #define Interface class #define DeclareInterface(name) Interface name { \ public: \ virtual ~name() {} #define DeclareBasedInterface(name, base) class name : \ public base { \ public: \ virtual ~name() {} #define EndInterface }; #define implements public #endif
2、有了这些宏,我们就可以这样定义我们的接口类:
//*********************************************** // IBar.h //通过宏定义生成我们的接口类,写一些纯虚函数 //*********************************************** #ifndef IBAR_H #define IBAR_H #include "Interface.h" DeclareInterface(IBar) virtual int GetBarData() const = 0; virtual void SetBarData(int nData) = 0; EndInterface #endif
3、再写一个父类BasicBar.h
//*********************************************** // BasicBar.h //Bar类的父亲类,用来继承测试 //*********************************************** #ifndef BASICBAR_H #define BASICBAR_H #include <iostream> class BasicBar { public: BasicBar(int x) { } ~BasicBar(){} virtual int getBarData1() const { std::cout <<"Get BasicBar!"; return 0; } virtual void setBarData1(int nData) { } }; #endif
4、现在我们可以像下面这样来实现我们的接口Bar.h了:
//*********************************************** //Bar.h //实现IBar接口的类 //*********************************************** #ifndef Bar_H #define Bar_H #include "Interface.h" #include "BasicBar.h" #include "IBar.h" #include <iostream> class Bar :public BasicBar,implements IBar { public: Bar(int x=0) : BasicBar(x) { } ~Bar(){} virtual int getBarData() const { std::cout <<"Get Bar!"; return 0; } virtual void setBarData(int nData) { } }; #endif
5、 怎么样,很简单吧,并不需要做很多的努力我们就可以在C++中使用接口了。在Main函数中引用我们的接口(我建立的是Qt 终端应用程序 )
//*********************************************** // main.h //主函数 //*********************************************** #include <QtCore/QCoreApplication> #include "IBar.h" #include "DataAccess.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //IBar *bar = new Bar(); IBar *bar = DataAccess::createBar(); bar->getBarData(); delete bar; return a.exec(); }
另外的问题:C++中不许我们实例化抽象类,如IBar *bar = new IBar();
我们只能通过上述main.cpp中的IBar *bar = new Bar();实现, 这样我们还必须指定实例化那个对象Bar,好像前面的努力都白费了啊!!那怎么办呢,我们需要一个工厂:
6、建立一个数据工厂,DataAccess.h
//*********************************************** // DataAccess.h //数据工厂,通过它调用对象类,返回给接口函数 //*********************************************** #ifndef DATAACCESS_H #define DATAACCESS_H #include "IBar.h" #include "Bar.h" class DataAccess { // Construction & Destruction public: DataAccess() { } ~DataAccess() { } static IBar* createBar() { //返回对象给IBar接口 return(new Bar()); } }; #endif
这个时候,我们把第5步的main.cpp调用接口方法改一下:
//*********************************************** // main.h //主函数 //*********************************************** #include <QtCore/QCoreApplication> #include "IBar.h" #include "DataAccess.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //IBar *bar = new Bar(); IBar *bar = DataAccess::createBar(); bar->getBarData(); delete bar; return a.exec(); }
整个过程中接口不负责任何具体操作,其他的程序要连接业务类、数据库的话,只需要构造一个业务对象、数据访问对象就OK,而不管工厂类如何变化。这就是接口的意义----抽象。
接口宏定义可以更简单点
上述步骤中我们可以把步骤1和步骤2写的更简单些,我们只需要Interface 和 implements 的宏定义,
这样我们的接口定义类IBar灵活性更强,可以继续继承其他的接口类,也更符合java、C#等语言写接口类的风格。
步骤1:Interface.h
//******************************************** // Interface.h //主要是宏定义一些关键词,可以形成接口类 //******************************************** #ifndef INTERFACE_H #define INTERFACE_H #define Interface class //#define DeclareInterface(name) Interface name { \ //public: \ // virtual ~name() {} // //#define DeclareBasedInterface(name, base) class name : \ //public base { \ //public: \ // virtual ~name() {} // //#define EndInterface }; #define implements public #endif
步骤2:IBar.h
//*********************************************** // IBar.h //通过宏定义生成我们的接口类,写一些纯虚函数 //*********************************************** #ifndef IBAR_H #define IBAR_H #include "Interface.h" Interface IBar { public: virtual ~IBar(){} virtual int getBarData() const = 0; virtual void setBarData(int nData) = 0; }; //DeclareInterface(IBar) // //virtual int getBarData() const = 0; //virtual void setBarData(int nData) = 0; //EndInterface #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该对象,你会像下面这样做:
int main() { IBar* pBar = BarFactory::CreateBar(Bar); pBar->SetName("MyFooBar"); delete pBar; // Oops! }
delete pBar 做了什么取决于该对象是否有一个虚析构函数。如果Bar没有一个虚析构函数,则只有IBar 的隐式的空析构函数被调用,Bar的析构函数不会被调用,这样就发生了内存泄露。接口类里虚析构函数的声明避免了这用状况,它确保每个实现接口的类都有一 个虚析构函数。
当你使用DeclareInterfac的时候,记得使用EndInterface和它匹配。Interface 宏和 implements宏仅仅是代替了class和public,这看起来是多余的,但我认为它们更明确的表达了代码的意图。如果我这么写:class Bar: public IBar,你可能认为这只是一个简单的继承;但如果我这么写:class Bar: implements IBar,你就会看到它实际的价值和意图---这是对一个接口的实现,而不是简单的一次继承。
- TestInterface源码.rar (712.5 KB)
- 下载次数: 91
评论
你看看CSDN上的java与C++接口的区别。
http://bbs.csdn.net/topics/477099
#define Interface class Interface IBase {} class Derive : public IBase {} void main(...) { IBase *ba = new Derive; }
是不是这样定义有什么弊端呢?
兄弟,文章开头已经说明了。这是想向java编程风格靠拢。java里有“接口”这种定义,C++没有。
class Bar :public BasicBar,implements IBar
#define Interface class Interface IBase {} class Derive : public IBase {} void main(...) { IBase *ba = new Derive; }
是不是这样定义有什么弊端呢?
兄弟,文章开头已经说明了。这是想向java编程风格靠拢。java里有“接口”这种定义,C++没有。
class Bar :public BasicBar,implements IBar
#define Interface class Interface IBase {} class Derive : public IBase {} void main(...) { IBase *ba = new Derive; }
是不是这样定义有什么弊端呢?
兄弟,文章开头已经说明了。这是想向java编程风格靠拢。java里有“接口”这种定义,C++没有。
class Bar :public BasicBar,implements IBar
#define Interface class Interface IBase {} class Derive : public IBase {} void main(...) { IBase *ba = new Derive; }
是不是这样定义有什么弊端呢?
兄弟,文章开头已经说明了。这是想向java编程风格靠拢。java里有“接口”这种定义,C++没有。
class Bar :public BasicBar,implements IBar
#define Interface class Interface IBase {} class Derive : public IBase {} void main(...) { IBase *ba = new Derive; }
是不是这样定义有什么弊端呢?
发表评论
-
更换博客地址告示
2012-12-03 18:56 2525更换博客地址告示 ... -
收款主页设计
2012-11-17 11:01 0文档信息 版权声明:自由转载-非商用-非衍生- ... -
10个精选国外免费空间
2012-11-12 16:06 6257最近想申请一个免费空间,发现国内基本没有。。。。唉。 ... -
长光所考博信息
2012-09-24 14:25 2628总结一下,以后有个奔头。。搞科研的收入太微薄了。唉。。。 ... -
The file contains a character that cannot be represented in the current code pag
2012-08-17 10:17 6242vs2008 打开网上下载的Qt项目,结果编译时无法通过,出 ... -
64位操作系统,编译程序出现X86和X64的冲突
2012-08-16 20:10 0问题1:1>QtGuid4.lib(QtGuid4.d ... -
通用日志系统开发【转】
2012-08-06 15:20 41791.前言 中大型软件项 ... -
U盘制作WinPE系统【转】
2012-07-27 11:36 2444不带光驱的笔记本的网友和没有光驱的台式机的网友如何安 ... -
博客文章标题横线框
2012-06-28 11:47 0分析 ... -
触摸屏介绍
2012-05-30 16:03 1928有一个项目,需要购买一台触摸屏设备。 了解一下触摸屏的基础知 ... -
有谁知道高速大数据量无线传输技术
2012-05-28 11:53 2428有谁知道高速大数据量无线传输技术? 想了解一下无线传输 ... -
自由在博客里插入广告,有钱你不赚吗???
2012-05-25 18:37 01、一直在ITeye里写技 ... -
自由在博客里插入广告,有钱你不赚吗???
2012-05-25 15:22 6547其实这些广告也没啥意 ... -
支持javascript的博客汇总
2012-05-25 09:27 2419写这篇文章的目的主要是为想在公共博客上布置广告的朋友提供一些参 ... -
支持js代码的博客有哪些?
2012-05-25 09:20 32151、支持js代码的博客谷歌博客, 雅虎奇摩,博客大巴,jimd ... -
VS2008无法下断点调试的原因
2012-05-02 11:18 19272VS2008单步调试时出错,原来的断点处显示一个空心圆加 ... -
VS2008无法下断点调试的若干解决办法
2012-05-02 09:19 3986我使用了第一种方法,暂时恢复了断点调试。 调试程 ... -
【转】串口通讯—通信协议
2012-02-29 00:11 2597转:http://cs.nju.edu.cn/y ... -
Ankhsvn 改名出错
2012-02-16 18:32 3125总结只改变文件名字大小写的最终解决方法。 这种只需要改 ... -
visio2010 用键盘上的上下键为什么移动不了选中的部分,而是整个画布都在移动?
2011-12-23 14:56 10492visio2010画的流程图,选中框图后,用键盘上的上下键为什 ...
相关推荐
有关状态机设计方面的书籍,我这里隆重推荐一本:《Practical Statecharts in C/C++ Quantum Programming for Embedded Systems》,中文名叫做《嵌入式系统的微模块化程序设计-实用状态图C/C++实现》,北航出版的,...
8.5.1 多态——同一个接口,不同的行为 8.5.2 什么是虚函数 8.5.3 虚函数的作用 8.6 总结 8.7 练习 复习题 附录A 学习使用Visual C++2005Express Edition 附录B C++关键字表 附录C C++运算符 附录D ASCII码 附录E ...
通过对地球物理软件研发过程中比较常见内存错误的调研与总结,设计并实现了一个基于C/C++的动态内存检测工具, 采用内嵌与关键函数截获方式,对编译器开放接口进行扩展与改进,该工具通过对软件运行过程中堆内存使用...
Java c++通过des加密的结果不一样【已解决】 最近做了一个接口,需要和C++进行通讯,通讯的参数采用des加密,但调试的时候却发现同样的明文和密钥加密出来的结果却是不一样的。 收藏网络总结代码
8.2 C++异常处理的实现 35 8.2.1异常基本语法 35 8.2.2栈解旋(unwinding) 39 8.2.3异常接口声明 40 8.2.4异常类型和异常变量的生命周期 40 8.2.5异常的层次结构(继承在异常中的应用) 46 8.3标准程序库异常 47 8.4...
8.5.1 多态——同一个接口,不同的行为 8.5.2 什么是虚函数 8.5.3 虚函数的作用 8.6 总结 8.7 练习 复习题 附录A 学习使用Visual C++2005Express Edition 附录B C++关键字表 附录C C++运算符 附录D ASCII码 附录E 位...
面试题2:谈谈C++编译器是如何实现多态 6 面试题3:谈谈你对重写,重载理解 6 #include <cstdlib> 6 #include <iostream> 6 using namespace std; 6 class Parent01 6 { 6 public: 6 Parent01() 6 { 6 cout()..do"; ...
如何构建高效、健壮、稳定且可扩展...对于不同api风格与模式,总结出了api设计的种种最佳策略,着重针对大规模长期开发项目,辅以翔实的代码范例,从而有助于设计决策的成功实施,以及软件项目的健壮性及稳定性的实现。
本项目就是基于跳表实现的轻量级键值型存储引擎,使用C++实现。插入数据、删除数据、查询数据、数据展示、数据落盘、文件加载数据,以及数据库大小显示。 在随机写读情况下,该项目每秒可处理啊请求数(QPS): 24.39...
RPC简介与接口定义.mkv72. 代码实现与运行实例.mkv73. Go语言客户端.mkv74. RPC 负载均衡.mkv75. 多机求平均数和中位数的算法.mkv76. 代码实现及运行实例.mkv77. 实现RCP框架:服务端.mkv78. 实现RPC框架:客户端....
3.2.3 同时继承基类的接口和实现 3.3 继承语义(Semantie) 3.4 继承的障碍 3.4.1 非虚成员函数 3.4.2 过度保护 3.4.3 模块化不足 3.4.4 friend关键字的使用 3.4.5 成员变量过多 3.4.6 非虚(Nonvirtual)...
笔者最近工作中使用到了对温箱的程控,特对此进行梳理总结,供大家参考。 本文涉及的温箱包括 德国 VOTSCH, 上海庆生,莱思测试以及 GWS等等厂家。 温箱控制由于数据量不大,所以都是通过串口控制,波特率...
初期的MFC只实现了文档-视结构以及相关的Windows编程接口,那时候MFC代码还是比较简捷的,但是随着Windows窗口元素的复杂化,以及OLE和多线程等特性的加入,MFC已经非常膨大、非常累赘了,这时再要根据MFC源代码来...
初期的MFC只实现了文档-视结构以及相关的Windows编程接口,那时候MFC代码还是比较简捷的,但是随着Windows窗口元素的复杂化,以及OLE和多线程等特性的加入,MFC已经非常膨大、非常累赘了,这时再要根据MFC源代码来...
3.2.1 C++类的定义与实现 3.2.2 构造函数与析构函数 3.2.3 静态成员 3.2.4 this指针 3.2.5 友元函数 3.3 类的继承机制 3.3.1 基类与派生类概念 3.3.2 单一派生 3.3.3 多重继承 3.3.4 虚基类 3.4 多态性与虚函数 3.5 ...
3.2.1 C++类的定义与实现 3.2.2 构造函数与析构函数 3.2.3 静态成员 3.2.4 this指针 3.2.5 友元函数 3.3 类的继承机制 3.3.1 基类与派生类概念 3.3.2 单一派生 3.3.3 多重继承 3.3.4 虚基类 3.4 多态性与虚函数 3.5 ...
初期的MFC只实现了文档-视结构以及相关的Windows编程接口,那时候MFC代码还是比较简捷的,但是随着Windows窗口元素的复杂化,以及OLE和多线程等特性的加入,MFC已经非常膨大、非常累赘了,这时再要根据MFC源代码来...