Using Interfaces in C++
来源:互联网 发布:sas分析软件 编辑:程序博客网 时间:2024/06/05 17:27
original url form : http://www.codeguru.com/cpp/cpp/cpp_mfc/oop/article.php/c9989/Using-Interfaces-in-C.htm
在C++中使用接口
Jose Lamas Rios发表于2005/6/27
摘要
目前, 无论是在Java和C#(以及许多其他语言)中, 接口都是一个强大的语言特性. 但是C++中情况却不同.
多年来, 我们的团队使用了一种方法, 在C++中实现了接口的概念. 这种方法如下所述.
从VS7开始,MS扩展的语言特性指向了同样的方法,允许编译器执行大多数接口的定义特征.
当然, 托管的C++扩展支持的定义和实现都是.Net接口. 不过这些机制之间的差异,你应该考虑。
背景
接口描述的行为和类的定义不涉及到具体实现, 代表了提供者和调用者之间的约定. 从调用者的角度定义需求, 必须提供服务, 不需要考虑如何实现.
如果你不熟悉的接口的概念,以及何时和如何使用它来提高你的设计能力. 下面的MSDN文章可能对您有帮助. 涉及到VB用户升级到VB.Net.
但是极好的解释了在面向对象的语言中使用接口的基本思路. Inheritance and Interfaces, by Andy Baron
介绍
1999年11月左右,我定义了一个方法声明C + +接口.使用类实现它们,只需使用几个宏和遵守一些基本的规则.
我着重声明,我不主张这种技术“发明”,虽然它是自主开发.
基于Roberto Lublinerman的宝贵意见, 以及互联网上的相关文章, 我开始思考.
这些天来,该解决方案可以通过使用微软扩展,在高版本的C+ +编译器中实现,我们一步一步来重现。
第一个版本
首先,在头文件中定义一些宏,包括在工程中的预编译头文件中.
/// @fileCppInterfaces.h#ifndef__CPP_INTERFACES_H__#define__CPP_INTERFACES_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
为了使用这些宏,你可以按照以下方式声明一个接口:
/// @fileIBar.h#ifndef_IBAR_H__#define_IBAR_H__#include <CppInterfaces.h>//// IBar.h//DeclareInterface(IBar)virtual int GetBarData() const = 0;virtual void SetBarData(int nData) = 0;EndInterface#endif
/// @fileFoo.h#ifndef__FOO_H__#define __FOO_H__#include <IBar.h>class Foo : public BasicFoo, implements IBar{// Construction & Destructionpublic:Foo(int x) : BasicFoo(x){}~Foo();// IBar implementationpublic:virtual int GetBarData() const{// stuff...}virtual void SetBarData(int nData){// stuff...}};#endif
容易,是不是?没有耗费太多的精力,现在就能够使用的C++的接口。然而,因为C++语言不直接支持接口,不能在编译时自动执行.
毕竟,所有的编译器可以看到的是使用普通的旧多重继承和抽象基类。下面是一些建议,需要遵循的规则:
* 当声明一个类,使第一个基类为“结构性”的继承。(例如:CFrameWnd的派生于CWnd,CBitmapButton的派生于CButton,YourDialog从CDialog派生的,等等。)如果你是从MFC类派生,这一点尤为重要;宣布他们的第一个基类,避免打破MFC的RuntimeClass机制。
* 使用额外的基类,实现你需要的接口。(例如:类Foo, 共有继承于BasicFoo,实现IBar接口,实现IOther接口,实现IWhatever接口,...)
* 不在接口内声明任何成员变量。接口是为了表达行为,而不是数据。此外,这有助于避免一些问题,例如要使用多个“结构性”的继承时, 多次继承一个相同的接口。
* 所有成员函数声明为纯虚接口. 保证实例化的类实现每一个声明的接口.
* 接口只能从其他接口继承. 可以使用DeclareBasedInterface()宏.
* 用类指针实现接口指针
* 此外, 使用dynamic_cast给出一种方法, 去决定是否实现一个给定的接口
* 必须要小心,避免在不同的接口功能之间的名称冲突,因为它是不容易发现和解决的冲突.
评估
我发布的技术文档来自我的博客, 一些读者关注以下话题.* 为什么使用这些宏? 这些宏并没有做任何特别的事, 比起下面的旧宏没有提高可读性
#define begin {#define end }* 也许接口是java/c#开发者的一根有用拐杖. 但是如果开发者需要这样的拐杖, 可能会带来其他问题.
如果你看看DeclareInterface和DeclareBasedInterface宏,你会注意到,至少有东西正被执行:每个类实现一个接口有一个虚析构函数。您可能会或可能不会认为这是非常重要的,但有情况下,缺乏一个虚析构时会造成问题。考虑下面的代码,例如:
// CppInterface.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "BarFactory.h"int _tmain(int argc, _TCHAR* argv[]){IBar* pBar = BarFactory::CreateNewBar(BarFactory::eFoo);pBar->SetName(_T("MyFooBar"));// Use pBar as much as you want,// ...// and then just delete it when it's no longer neededdelete pBar; // Oops!_tprintf(_T("EDN\n"));/// run results/**>> ~Foo()m_pName was be freeEDN*/getchar();}如果析构函数不是虚函数, 在 delete pBar时, 执行不到析构函数, 资源就不能被释放.
/// @fileBarFactory.h#ifndef __BAR_FACTORY_H__#define __BAR_FACTORY_H__#include "Foo.h"class BarFactory{public:enum BarType {eFaa, eFee, eFii, eFoo, eFuu};static IBar * CreateNewBar(BarType barType){switch (barType){// case eFaa:// return new Faa;///< 一些象 Foo 一样的实例// case eFee:// return new Fee;///< 一些象 Foo 一样的实例// case eFii:// return new Fii;case eFoo:return new Foo;// case eFuu:// return new Fuu;///< 一些象 Foo 一样的实例default:return NULL;}}};#endif
/// @fileCppInterfaces.h#ifndef__CPP_INTERFACES_H__#define__CPP_INTERFACES_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
/// @fileFoo.h#ifndef__FOO_H__#define __FOO_H__#include "IBar.h"class Foo : implements IBar{// Internal dataprivate:LPCTSTR m_pName;// Construction & Destructionpublic:Foo(){m_pName = NULL;}~Foo(){_tprintf(_T(">> ~Foo()\n"));ReleaseName();}// Helpersprotected:void ReleaseName(){if (m_pName != NULL){_tprintf(_T("m_pName was be free\n"));free((VOID *)m_pName);m_pName = NULL;}}// IBar implementationpublic:virtual LPCTSTR GetName() const{return m_pName;}virtual void SetName(const LPCTSTR name){ReleaseName();m_pName = _tcsdup(name);}};#endif
/// @fileIBar.h#ifndef_IBAR_H__#define_IBAR_H__#include "CppInterfaces.h"//// IBar.h//DeclareInterface(IBar)virtual LPCTSTR GetName() const = 0;virtual void SetName(const LPCTSTR name) = 0;EndInterface#endif
// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently, but// are changed infrequently//#pragma once#include "targetver.h"#include <windows.h>#include <stdio.h>#include <tchar.h>#include <tchar.h>// TODO: reference additional headers your program requires here
使用DeclareInterface和EndInterface做配对使用, 能看到她的实际价值和意图:实现接口的概念,而不是任何一种类继承。
说句公道话,我相信读者关心其他的事情,这些宏除了唯一的纯虚函数并没有实例数据, 不能强制实现接口。但是,至少,如果您使用DeclareInterface和EndInterface,接口包含任何实例数据或者非纯虚拟函数应该是很容易发现。按照Joel Spolsky的说法,这些宏还帮助“查看错误代码”。
翻译后记: 原文中还有关于利用MSVS7以上版本的语言扩展, 使用__interface关键字的新宏, 但是我用vs2008编译不过了. 现在工作中的应用是纯Win32C++做应用层组件, 使用MS扩展对我没什么用处, 而且可能带来负面效果. 上面的接口模拟宏已经足够好用, 结束~
- Using Interfaces in C++
- Using Cookie in C#
- How to know ip address for interfaces in c
- Using Oracle Large Object (LOB) Datatype Columns In ODI Integration Interfaces [ID 423768.1]
- programming interfaces using raw socket
- interfaces in Junos
- override in interfaces
- Using C code in symbian
- bitonic_sort using mpi in c
- Using LAPACK in C/C++
- Using LAPACK in C/C++
- [C/C++] Using `getopt' in c/c++
- 26.Difference between protocol in objective c and interfaces in java?
- Interfaces in C# (For Beginners)
- The Collection Interfaces in Java
- Thinking in Java之Interfaces
- Using abstractions and interfaces with Unity3D
- Get a list of network interfaces and their IP addresses in C
- Android Makefile探索2
- XSLT(19) document() Function to access external xml
- 关于SAP 对生产订单的月度结算
- WINCE修改分辨率有关文件
- hdu 1787 GCD Again 欧拉函数小水水 数论
- Using Interfaces in C++
- ACM学英语(四)
- objective-c的总结
- 光流法的理解
- XAMPP和MYSQL使用
- PBRT 阅读 : 第一章
- 测试管理005:面对用户反馈的缺陷,测试人员能做些什么?
- POJ 2421 Constructing Roads 最小生成树
- 小知识