C++接口实现总结 http://qimo601.iteye.com/blog/1393427
网上的例子,稍微有点错误。我给更改一下,附件上有源码!如有错误,请指正。
总结一下C++实现接口的技巧。
面向对象的语言诸如JAVA提供了Interface来实现接口,但C++却没有这样一个东西,尽管C++ 通过纯虚基类实现接口,譬如COM的C++实现就是通过纯虚基类实现的(当然MFC的COM实现用了嵌套类),但我们更愿意看到一个诸如 Interface的东西。下面就介绍一种解决办法。
程序6步
1、首先我们需要一些宏:
![](http://qimo601.iteye.com/images/icon_star.png)
- //********************************************
- // 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、有了这些宏,我们就可以这样定义我们的接口类:
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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了:
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- //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 终端应用程序 )
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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调用接口方法改一下:
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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
![](http://qimo601.iteye.com/images/icon_star.png)
- //********************************************
- // 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
![](http://qimo601.iteye.com/images/icon_star.png)
- //***********************************************
- // 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该对象,你会像下面这样做:
![](http://qimo601.iteye.com/images/icon_star.png)
- 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,你就会看到它实际的价值和意图---这是对一个接口的实现,而不是简单的一次继承。
C++接口实现总结 http://qimo601.iteye.com/blog/1393427相关推荐
- C语言下,获取文件信息 http://qimo601.iteye.com/blog/1517413
http://qimo601.iteye.com/blog/1517413 C语言下,获取文件信息 博客分类: C/C++ C语言文件信息文件大小 C语言下,如何获取文件的生成时间,日期和文件大小等 ...
- dTree 动态生成树(http://luohua.iteye.com/blog/451453)
转自<http://luohua.iteye.com/blog/451453>,感谢分享! dTree 动态生成树 dTree是个很方便在页面生成树的 js 控件,如果你下载了,我猜里在几 ...
- 导入工程时出现错误:Invalid project description
http://berdy.iteye.com/blog/1115279...
http://berdy.iteye.com/blog/1115279 new -> android project -> create project from exist source ...
- http://enki-ding-yeah-net.iteye.com/blog/1042644
http://enki-ding-yeah-net.iteye.com/blog/1042644
- http://stamen.iteye.com/blog/1462899
http://stamen.iteye.com/blog/1462899 [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/112308 ...
- http://hlhpyasd.iteye.com/blog/865865
http://hlhpyasd.iteye.com/blog/865865 转载于:https://www.cnblogs.com/longshiyVip/p/5964965.html
- http://xmuzyq.iteye.com/blog/783218
http://xmuzyq.iteye.com/blog/783218 在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Pro ...
- 如何防止SQL注入 http://zhangzhaoaaa.iteye.com/blog/1975932
如何防止SQL注入 博客分类: 技术转载数据库 转自:http://021.net/vpsfaq/152.html -----解决方案--------------------------------- ...
- 微信开发之获取OAuth2.0网页授权认证和获取用户信息进行关联(转:http://playxinz.iteye.com/blog/2249634)
最近有做了关于微信公众号和自己网站用户进行用户关联授权登录的一个功能,主要是用户关注该公众号,点击会员中心,则会弹出需要关联授权的网页授权:OAuth2.0网页授权,然后用户同意获取用户信息,进行用户 ...
最新文章
- Xcode Git 使用
- LIVE555再学习 -- FFmpeg + live555实现RTSP直播
- c#如何通过ftp上传文件_定时上传文件到ftp,如何使用工具定时上传文件到ftp
- 计算机网络实验(华为eNSP模拟器)——第五章 单臂路由
- jfinal mysql 事务_jfinal事物为啥这么用不生效呢,只要执行update数据就进库了,数据库用的是oracle...
- 南京理工大学计算机专业考研,2020南京理工大学计算机考研初试科目、参考书目、复试详情汇总...
- apache 搭建PHP多站点
- GJB150军用设备环境(霉菌、淋雨、湿热、盐雾、冲击)试验
- 关于SQL注入及防御
- OpenCV读写视频(编解码器)
- SSM框架整合总结—案例
- Springboot 基于微信小程序的高校学生疫情在校封闭管理系统的设计与实现 毕业设计-附源码240904
- 49次全国计算机以及靠,第49次全国计算机等级考试(国考)证书领取的通知
- linux 网络配置 nm_controlled,[转帖]NM_CONTROLLED的含义以及网卡配置参数
- 惠普m154a状态页_惠普M154a说明书
- (2015)最新破解企业QQ拦截个人QQ功能
- 2021年危险化学品生产单位安全生产管理人员新版试题及危险化学品生产单位安全生产管理人员考试总结
- 荣耀V20设置国科大邮箱步骤
- Thiol-PEG-SH,巯基聚乙二醇巯基,Thiol-PEG-Thiol
- 7-16 藏头诗 (15 分)
热门文章
- 多商户商城系统功能拆解06讲-平台端商家入驻协议
- Linux打印出netstat -anp 里的Send_Q发送堵的TCP连接
- linux steam root,Steam
- 怎么为图片添加流烟烟雾?可以使用25种流烟烟雾效果PS笔刷
- 太阳电池仿真模块 matlab 程序,基于MATLAB的月球车锂离子电池充放电过程仿真
- 手机国际版应用商店APKPure/代替谷歌商店的应用商店
- java 列表面板_java-用JPanels列表制作表
- 树莓派+串口墨水电子屏幕+温度湿度传感器打造专属时钟
- C#中获取跟拼音相关的东西
- 兰顿蚂蚁 java模拟