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的派生于CWndCBitmapButton的派生于CButtonYourDialog从CDialog派生等等。)如果你是从MFC类派生这一点尤为重要;宣布他们一个基类避免打破MFC的RuntimeClass机制

* 使用额外基类实现你需要的接口(例如:类Foo, 共有继承于BasicFoo实现IBar接口实现IOther接口实现IWhatever接口,...

* 不在接口内声明任何成员变量接口是为了表达行为,而不是数据此外,这有助于避免一些问题例如要使用多个“结构性”的继承时, 多次继承一个相同的接口

* 所有成员函数声明接口. 保证实例化的类实现每一个声明的接口.

* 接口只能从其他接口继承. 可以使用DeclareBasedInterface()宏.

* 用类指针实现接口指针

* 此外, 使用dynamic_cast给出一种方法, 去决定是否实现一个给定的接口

* 必须要小心,避免不同的接口功能之间名称冲突,因为它是不容易发现和解决的冲突.

评估

我发布的技术文档来自我的博客, 一些读者关注以下话题.

* 为什么使用这些宏? 这些宏并没有做任何特别的事, 比起下面的旧宏没有提高可读性

#define begin {#define end }
* 也许接口是java/c#开发者的一根有用拐杖. 但是如果开发者需要这样的拐杖, 可能会带来其他问题.


如果你看看DeclareInterfaceDeclareBasedInterface,你注意到,至少有东西正被执行每个类实现一个接口有一个虚析构函数您可能会或可能不会认为这是非常重要,但情况下,缺乏一个虚析构会造成问题考虑下面的代码例如

// 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做配对使用, 能看到她的实际价值和意图实现接口的概念不是任何一种类继承

说句公道话,我相信读者关心其他的事情这些除了唯一的纯虚函数没有实例数据, 不能强制实现接口但是至少如果使用DeclareInterfaceEndInterface接口包含任何实例数据或者虚拟函数应该是很容易发现按照Joel Spolsky的说法这些宏帮助查看错误代码


翻译后记:  原文中还有关于利用MSVS7以上版本的语言扩展, 使用__interface关键字的新宏, 但是我用vs2008编译不过了. 现在工作中的应用是纯Win32C++做应用层组件, 使用MS扩展对我没什么用处, 而且可能带来负面效果. 上面的接口模拟宏已经足够好用, 结束~



原创粉丝点击