original url form : http://www.codeguru.com/cpp/cpp/cpp_mfc/oop/article.php/c9989/Using-Interfaces-in-C.htm
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+ +编译器中实现,我们一步一步来重现。
首先,在头文件中定义一些宏,包括在工程中的预编译头文件中.
/// @file CppInterfaces.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
/// @file IBar.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
/// @file Foo.h #ifndef __FOO_H__ #define __FOO_H__ #include <IBar.h> class Foo : public BasicFoo, implements IBar { // Construction & Destruction public: Foo(int x) : BasicFoo(x) { } ~Foo(); // IBar implementation public: virtual int GetBarData() const { // stuff... } virtual void SetBarData(int nData) { // stuff... } }; #endif
* 当声明一个类,使第一个基类为“结构性”的继承。 (例如: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 needed delete pBar; // Oops! _tprintf(_T("EDN\n")); /// run results /** >> ~Foo() m_pName was be free EDN */ getchar(); }如果析构函数不是虚函数, 在 delete pBar时, 执行不到析构函数, 资源就不能被释放.
/// @file BarFactory.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
/// @file CppInterfaces.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
/// @file Foo.h #ifndef __FOO_H__ #define __FOO_H__ #include "IBar.h" class Foo : implements IBar { // Internal data private: LPCTSTR m_pName; // Construction & Destruction public: Foo() { m_pName = NULL; } ~Foo() { _tprintf(_T(">> ~Foo()\n")); ReleaseName(); } // Helpers protected: void ReleaseName() { if (m_pName != NULL) { _tprintf(_T("m_pName was be free\n")); free((VOID *)m_pName); m_pName = NULL; } } // IBar implementation public: virtual LPCTSTR GetName() const { return m_pName; } virtual void SetName(const LPCTSTR name) { ReleaseName(); m_pName = _tcsdup(name); } }; #endif
/// @file IBar.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扩展对我没什么用处, 而且可能带来负面效果. 上面的接口模拟宏已经足够好用, 结束~