(http://www.cnblogs.com/dwjaissk/default.html?page=2真是一个好博客啊)
*C++接口与实现分离
在用C++写要导出类的库时,我们经常只想暴露接口,而隐藏类的实现细节。也就是说我们提供的头文件里只提供要暴露的公共成员函数的声明,类的其他所有信息都不会在这个头文件里面显示出来。这个时候就要用到接口与实现分离的技术。下面用一个最简单的例子来说明。
类ClxExp是我们要导出的类,其中有一个私有成员变量是ClxTest类的对象,各个文件内容如下:
lxTest.h文件内容:class ClxTest
{
public:
ClxTest();
virtual ~ClxTest();
void DoSomething();
};
lxTest.cpp文件内容:
#include "lxTest.h"#include <iostream>
using namespace std;
ClxTest::ClxTest()
{
}
ClxTest::~ClxTest()
{
}
void ClxTest::DoSomething()
{
cout << "Do something in class ClxTest!" << endl;
}
lxExp.h文件内容:
#include "lxTest.h"class ClxExp
{
public:
ClxExp();
virtual ~ClxExp();
void DoSomething();
private:
ClxTest m_lxTest;
void lxTest();
};
lxExp.cpp文件内容:
#include "lxExp.h"ClxExp::ClxExp()
{
}
ClxExp::~ClxExp()
{
}
// 其实该方法在这里并没有必要,我这样只是为了说明调用关系
void ClxExp::lxTest()
{
m_lxTest.DoSomething();
}
void ClxExp::DoSomething()
{
lxTest();
}
为了让用户能使用我们的类ClxExp,我们必须提供lxExp.h文件,这样类ClxExp的私有成员也暴露给用户了。而且,仅仅提供lxExp.h文件是不够的,因为lxExp.h文件include了lxTest.h文件,在这种情况下,我们还要提供lxTest.h文件。那样ClxExp类的实现细节就全暴露给用户了。另外,当我们对类ClxTest做了修改(如添加或删除一些成员变量或方法)时,我们还要给用户更新lxTest.h文件,而这个文件是跟接口无关的。如果类ClxExp里面有很多像m_lxTest那样的对象的话,我们就要给用户提供N个像lxTest.h那样的头文件,而且其中任何一个类有改动,我们都要给用户更新头文件。还有一点就是用户在这种情况下必须进行重新编译!上面是非常小的一个例子,重新编译的时间可以忽略不计。但是,如果类ClxExp被用户大量使用的话,那么在一个大项目中,重新编译的时候我们就有时间可以去喝杯咖啡什么的了。当然上面的种种情况不是我们想看到的!你也可以想像一下用户在自己程序不用改动的情况下要不停的更新头文件和编译时,他们心里会骂些什么。其实对用户来说,他们只关心类ClxExp的接口DoSomething()方法。那我们怎么才能只暴露类ClxExp的DoSomething()方法而不又产生上面所说的那些问题呢?答案就是--接口与实现的分离。我可以让类ClxExp定义接口,而把实现放在另外一个类里面。下面是具体的方法:
首先,添加一个实现类ClxImplement来实现ClxExp的所有功能。注意:类ClxImplement有着跟类ClxExp一样的公有成员函数,因为他们的接口要完全一致。
lxImplement.h文件内容:
class ClxImplement
{
public:
ClxImplement();
~ClxImplement();
void DoSomething();
private:
ClxTest m_lxTest;
void lxTest();
};
lxImplement.cpp文件内容:
#include "lxImplement.h"ClxImplement::ClxImplement()
{
}
ClxImplement::~ClxImplement()
{
}
void ClxImplement::lxTest()
{
m_lxTest.DoSomething();
}
void ClxImplement::DoSomething()
{
lxTest();
}
然后,修改类ClxExp。
修改后的lxExp.h文件内容:
class ClxImplement;
class ClxExp
{
public:
ClxExp();
virtual ~ClxExp();
void DoSomething();
private:
// 声明一个类ClxImplement的指针,不需要知道类ClxImplement的定义
ClxImplement *m_pImpl;
};
修改后的lxExp.cpp文件内容:
// 在这里包含类ClxImplement的定义头文件#include "lxImplement.h"
ClxExp::ClxExp()
{
m_pImpl = new ClxImplement;
}
ClxExp::~ClxExp()
{
if (m_pImpl)
delete m_pImpl;
}
void ClxExp::DoSomething()
{
m_pImpl->DoSomething();
}
通过上面的方法就实现了类ClxExp的接口与实现的分离。请注意两个文件中的注释。类ClxExp里面声明的只是接口而已,而真正的实现细节被隐藏到了类ClxImplement里面。为了能在类ClxExp中使用类ClxImplement而不include头文件lxImplement.h,就必须有前置声明class ClxImplement,而且只能使用指向类ClxImplement对象的指针,否则就不能通过编译。在发布库文件的时候,我们只需给用户提供一个头文件lxExp.h就行了,不会暴露类ClxExp的任何实现细节。而且我们对类ClxTest的任何改动,都不需要再给用户更新头文件(当然,库文件是要更新的,但是这种情况下用户也不用重新编译!)。这样做还有一个好处就是,可以在分析阶段由系统分析员或者高级程序员来先把类的接口定义好,甚至可以把接口代码写好(例如上面修改后的lxExp.h文件和lxExp.cpp文件),而把类的具体实现交给其他程序员开发。
接口继承和实现继承
(public) inheritance 这个表面上简单易懂的观念,一旦被近距离审视,就会被证明是由两个相互独立的部分组成的:inheritance of function interfaces(函数接口的继承)和 inheritance of function implementations(函数实现的继承)。这两种 inheritance 之间的差异正好符合本书 Introduction 中论述的 function declarations(函数声明)和 function definitions(函数定义)之间的差异。作为一个 class 的设计者,有的时候你想要 derived classes 只继承一个 member function 的 interface (declaration)。有的时候你想要 derived classes 既继承 interface(接口)也继承 implementation(实现),但你要允许它们替换他们继承到的 implementation。还有的时候你想要 derived classes 继承一个函数的 interface(接口)和 implementation(实现),而不允许它们替换任何东西。
为了更好地感觉这些选择之间的不同之处,考虑一个在图形应用程序中表示几何图形的 class hierarchy(类继承体系):
class Shape { public: virtual void draw() const = 0; virtual void error(const std::string& msg); int objectID() const; ... }; class Rectangle: public Shape { ... }; class Ellipse: public Shape { ... }; |
Shape 是一个 abstract class(抽象类),它的 pure virtual function(纯虚拟函数)表明了这一点。作为结果,客户不能创建 Shape class 的实例,只能创建从它继承的 classes 的实例。但是,Shape 对所有从它(公有)继承的类施加了非常强大的影响,因为
成员函数 interfaces are always inherited。就像 Item 32 解释的,public inheritance 意味着 is-a,所以对一个 base class 来说成立的任何东西,对于它的 derived classes 也必须成立。因此,如果一个函数适用于一个 class,它也一定适用于它的 derived classes。
Shape class 中声明了三个函数。第一个,draw,在一个明确的显示设备上画出当前对象。第二个,error,如果 member functions 需要报告一个错误,就调用它。第三个,objectID,返回当前对象的唯一整型标识符。每一个函数都用不同的方式声明:draw 是一个 pure virtual function(纯虚拟函数);error 是一个 simple (impure?) virtual function(简单虚拟函数);而 objectID 是一个 non-virtual function(非虚拟函数)。这些不同的声明暗示了什么呢?
考虑第一个 pure virtual function(纯虚拟函数)draw:
class Shape { public: virtual void draw() const = 0; ... }; |
pure virtual functions(纯虚拟函数)的两个最显著的特性是它们必须被任何继承它们的具体类重新声明,和抽象类中一般没有它们的定义。把这两个特性加在一起,你应该认识到。
声明一个 pure virtual function(纯虚拟函数)的目的是使 derived classes 继承一个函数 interface only。
这就使 Shape::draw function 具有了完整的意义,因为它要求所有的 Shape 对象必须能够画出来是合情合理的,但是 Shape class 本身不能为这个函数提供一个合乎情理的缺省的实现。例如,画一个椭圆的算法和画一个矩形的算法是非常不同的,Shape::draw 的声明告诉具体 derived classes 的设计者:“你必须提供一个 draw function,但是我对于你如何实现它不发表意见。”
顺便提一句,为一个 pure virtual function(纯虚拟函数)提供一个定义是有可能的。也就是说,你可以为 Shape::draw 提供一个实现,而 C++ 也不会抱怨什么,但是调用它的唯一方法是用 class name 限定修饰这个调用:
Shape *ps = new Shape; // error! Shape is abstract Shape *ps1 = new Rectangle; // fine ps1->draw(); // calls Rectangle::draw Shape *ps2 = new Ellipse; // fine ps2->draw(); // calls Ellipse::draw ps1->Shape::draw(); // calls Shape::draw ps2->Shape::draw(); // calls Shape::draw |
除了帮助你在鸡尾酒会上给同行程序员留下印象外,这个特性通常没什么用处,然而,就像下面你将看到的,它能用来作为一个“为 simple (impure) virtual functions 提供一个 safer-than-usual 的实现”的机制。
simple virtual functions 背后的故事和 pure virtuals 有一点不同。derived classes 照常还是继承函数的 interface,但是 simple virtual functions 提供了一个可以被 derived classes 替换的实现。如果你为此考虑一阵儿,你就会认识到
声明一个 simple virtual function 的目的是让 derived classes 继承一个函数 interface as well as a default implementation。
考虑 Shape::error 的情况:
class Shape { public: virtual void error(const std::string& msg); ... }; |
interface 要求每一个 class 必须支持一个在遭遇到错误时被调用的函数,但是每一个 class 可以*地用它觉得合适的任何方法处理错误。如果一个 class 不需要做什么特别的事情,它可以仅仅求助于 Shape class 中提供的错误处理的缺省版本。也就是说,Shape::error 的声明告诉 derived classes 的设计者:“你应该支持一个 error function,但如果你不想自己写,你可以求助 Shape class 中的缺省版本。”
结果是:允许 simple virtual functions 既指定一个函数接口又指定一个缺省实现是危险的。来看一下为什么,考虑一个 XYZ 航空公司的飞机的 hierarchy(继承体系)。XYZ 只有两种飞机,Model A 和 Model B,它们都严格地按照同样的方法飞行。于是,XYZ 设计如下 hierarchy(继承体系):
class Airport { ... }; // represents airports class Airplane { public: virtual void fly(const Airport& destination); ... }; void Airplane::fly(const Airport& destination) { default code for flying an airplane to the given destination } class ModelA: public Airplane { ... }; class ModelB: public Airplane { ... }; |
为了表述所有的飞机必须支持一个 fly 函数,并为了“不同机型可能(在理论上)需要不同的对 fly 的实现”的事实,Airplane::fly 被声明为 virtual。然而,为了避免在 ModelA 和 ModelB classes 中些重复的代码,缺省的飞行行为由 Airplane::fly 的函数体提供,供 ModelA 和 ModelB 继承。
这是一个经典的 object-oriented 设计。因为两个 classes 共享一个通用特性(它们实现 fly 的方法),所以这个通用特性就被转移到一个 base class 之中,并由两个 classes 来继承这个特性。这个设计使得通用特性变得清楚明白,避免了代码重复,提升了未来的可扩展性,简化了长期的维护——因为 object-oriented 技术,所有这些东西都受到很高的追捧。XYZ 航空公司应该引以为荣。
现在,假设 XYZ 公司的财富增长了,决定引进一种新机型,Model C。Model C 在某些方面与 Model A 和 Model B 不同。特别是,它的飞行不同。
XYZ 公司的程序员在 hierarchy(继承体系)中增加了 Model C 的 class,但是由于他们匆匆忙忙地让新的机型投入服务,他们忘记了重定义 fly function:
class ModelC: public Airplane { ... // no fly function is declared }; |
于是,在他们的代码中,就出现了类似这样的东西:
Airport PDX(...); // PDX is the airport near my home Airplane *pa = new ModelC; ... pa->fly(PDX); // calls Airplane::fly! |
这是一个灾难:企图让一个 ModelC object 像一个 ModelA 或 ModelB 一样飞行。这在旅行人群中可不是一种鼓舞人心的行为。
这里的问题并不在于 Airplane::fly 有缺省的行为,而是在于 ModelC 被允许不必明确说出它要做什么就可以继承这一行为。幸运的是,“为 derived classes(派生类)提供缺省的行为,但是除非它们提出明确的要求,否则就不交给他们”是很容易做到的。这个诀窍就是切断 virtual function(虚拟函数)的 interface(接口)和它的 default implementation(缺省实现)之间的联系。以下用的就是这个方法:
class Airplane { public: virtual void fly(const Airport& destination) = 0; ... protected: void defaultFly(const Airport& destination); }; void Airplane::defaultFly(const Airport& destination) { default code for flying an airplane to the given destination } |
注意 Airplane::fly 是被如何变成一个 pure virtual function(纯虚拟函数)的。它为飞行提供了 interface(接口)。那个缺省的实现也会出现在 Airplane class 中,但是现在它是一个独立的函数,defaultFly。像 ModelA 和 ModelB 这样需要使用缺省行为的 Classes 只是需要在他们的 fly 的函数体中做一下对 defaultFly 的 inline 调用(但是请参见 Item 30 提供的关于 inline 化和 virtual functions(虚拟函数)的交互作用的信息):
class ModelA: public Airplane { public: virtual void fly(const Airport& destination) { defaultFly(destination); } ... }; class ModelB: public Airplane { public: virtual void fly(const Airport& destination) { defaultFly(destination); } ... }; |
对于 ModelC class,不可能在无意中继承到不正确的 fly 的实现,因为 Airplane 中的 pure virtual(纯虚拟)强制要求 ModelC 提供的它自己的 fly 版本。
class ModelC: public Airplane { public: virtual void fly(const Airport& destination); ... }; void ModelC::fly(const Airport& destination) { code for flying a ModelC airplane to the given destination } |
这一方案并非十分安全(程序员还是能通过 copy-and-paste 使他们自己陷入麻烦),但是它比最初的设计更加可靠。至于 Airplane::defaultFly,它是 protected(保护的)是因为它完全是 Airplane 和它的 derived classes(派生类)的实现细节。使用飞机的客户应该只在意它能飞,而不必管飞行是如何实现的。
Airplane::defaultFly 是一个 non-virtual function(非虚拟函数)这一点也很重要。这是因为 derived class(派生类)不应该重定义这个函数,这是一个在 Item 36 中专门介绍的原则。如果 defaultFly 是 virtual(虚拟的),你就会遇到一个循环的问题:如果某些 derived class(派生类)应该重定义 defaultFly 却忘记了的时候会如何呢?
一些人反对为 interface(接口)和 default implementation(缺省实现)分别提供函数,就像上面的 fly 和 defaultFly 那样。首先,他们注意到,这样做会导致类似的相关函数名污染 class namespace(类名字空间)的问题。然而他们仍然同意 interface(接口)和 default implementation(缺省实现)应该被分开。他们是怎样解决这个表面上的矛盾呢?通过利用以下事实:pure virtual functions(纯虚拟函数)必须在 concrete derived classes(具体派生类)中被 redeclared(重声明),但是它们也可以有它们自己的实现。以下就是 Airplane hierarchy(继承体系)如何利用这一能力定义一个 pure virtual function(纯虚拟函数):
class Airplane { public: virtual void fly(const Airport& destination) = 0; ... }; void Airplane::fly(const Airport& destination) // an implementation of { // a pure virtual function default code for flying an airplane to the given destination } class ModelA: public Airplane { public: virtual void fly(const Airport& destination) { Airplane::fly(destination); } ... }; class ModelB: public Airplane { public: virtual void fly(const Airport& destination) { Airplane::fly(destination); } ... }; class ModelC: public Airplane { public: virtual void fly(const Airport& destination); ... }; void ModelC::fly(const Airport& destination) { code for flying a ModelC airplane to the given destination } |
除了用 pure virtual function(纯虚拟函数)Airplane::fly 的函数体代替了独立函数 Airplane::defaultFly 之外,这是一个和前面的几乎完全相同的设计。本质上,fly 可以被拆成两个基本组件。它的 declaration(声明)指定了它的 interface(接口)(这是 derived classes(派生类)必须使用的),而它的 definition(定义)指定它的缺省行为(这是 derived classes(派生类)可以使用的,但只是在他们明确要求这一点时)。将 fly 和 defaultFly 合并,无论如何,你失去了给予这两个函数不同的保护层次的能力:原来是 protected 的代码(通过位于 defaultFly 中实现)现在成为 public(因为它位于 fly 中)。
最后,我们看看 Shape 的 non-virtual function(非虚拟函数),objectID:
class Shape { public: int objectID() const; ... }; |
当一个 member function(成员函数)是 non-virtual(非虚拟的)时,不应该指望它在 derived classes(派生类)中的行为会有所不同。实际上,一个 non-virtual member function(非虚拟成员函数)指定了一个 invariant over specialization(超越特殊化的不变量),因为不论一个 derived class(派生类)变得多么特殊,它都把它看作是不允许变化的行为。如下所指除的,
声明一个 non-virtual function(非虚拟函数)的目的是 to have derived classes inherit a function interface as well as a mandatory implementation(使派生类既继承一个函数的接口,又继承一个强制的实现)。
你可以这样考虑 Shape::objectID 的声明,“每一个 Shape object 有一个产生 object identifier(对象标识码),而且这个 object identifier(对象标识码)总是用同样的方法计算出来的,这个方法是由 Shape::objectID 的定义决定的,而且 derived class(派生类)不应该试图改变它的做法。”因为一个 non-virtual function(非虚拟函数)被看作一个 invariant over specialization(超越特殊化的不变量),在 derived class(派生类)中他绝不应该被重定义,细节的讨论参见 Item 36。
对 pure virtual,simple virtual,和 non-virtual functions 的声明的不同允许你精确指定你需要 derived classes(派生类)继承什么东西。分别是 interface only(仅有接口),interface and a default implementation(接口和一个缺省的实现),和 interface and a mandatory implementation(接口和一个强制的实现)。因为这些不同的声明类型意味着根本不同的意义,当你声明你的 member functions(成员函数)时你必须在它们之间仔细地选择。如果你这样做了,你应该可以避免由缺乏经验的类设计者造成的两个最常见的错误。
第一个错误是声明所有的函数为 non-virtual(非虚拟)。这没有给 derived classes(派生类)的特殊化留出空间;non-virtual destructors(非虚拟析构函数)尤其有问题(参见 Item 7)。当然,完全有理由设计一个不作为 base class(基类)使用的类。在这种情况下,一套独享的 non-virtual member functions(非虚拟成员函数)是完全合理的。然而,更通常的情况下,这样的类既可能出于对 virtual(虚拟)和 non-virtual functions(非虚拟函数)之间区别的无知,也可能是对 virtual functions(虚拟函数)的性能成本毫无根据的担心的结果。事实是,几乎任何作为 base class(基类)使用的类都会有 virtual functions(虚拟函数)(还是参见 Item 7)。
如果你关心 virtual functions(虚拟函数)的成本,请允许我提起基于经验的 80-20 规则(参见 Item 30),在一个典型的程序中的情况是,80% 的运行时间花费在执行其中的 20% 的代码上。这个规则是很重要的,因为它意味着,平均下来,你的函数调用中的 80% 可以被虚拟化而不会对你的程序的整体性能产生哪怕是最轻微的可察觉的影响。在你走进对“你是否能负担得起一个 virtual function(虚拟函数)的成本”忧虑的阴影之前,应该使用一些简单的预防措施,以确保你关注的是你的程序中能产生决定性不同的那 20%。
另一个常见的错误声明所有的 member functions(成员函数)为 virtual(虚拟)。有时候这样做是正确的—— Item 31 的 Interface classes(接口类)可以作为证据。然而,它也可能是缺乏表明态度的决心的类设计者的标志。某些函数在 derived classes(派生类)中不应该被重定义,而且只要在这种情况下,你都应该通过将那些函数声明为 non-virtual(非虚拟)而明确地表达这一点。它不是为那些人服务的,他们假设如果他们只需花一些时间重定义你的所有函数,你的类就会被所有的人用来做所有的事情,如果你有一个 invariant over specialization(超越特殊化的不变量),请直说,不必害怕!
Things to Remember
·Inheritance of interface(接口继承)与 inheritance of implementation(实现继承)不同。在 public inheritance(公开继承)下,derived classes(派生类)总是继承 base class interfaces(基类接口)。
·Pure virtual functions(纯虚拟函数)指定 inheritance of interface only(仅有接口被继承)。
·Simple (impure) virtual functions(简单虚拟函数)指定 inheritance of interface(接口继承)加上 inheritance of a default implementation(缺省实现继承)。
·Non-virtual functions(非虚拟函数)指定 inheritance of interface(接口继承)加上 inheritance of a mandatory implementation(强制实现继承)。
*C++中使用接口
面向对象的语言诸如JAVA提供了Interface来实现接口,但C++却没有这样一个东西,尽管C++ 通过纯虚基类实现接口,譬如COM的C++实现就是通过纯虚基类实现的(当然MFC的COM实现用了嵌套类),但我们更愿意看到一个诸如 Interface的东西。下面就介绍一种解决办法。首先我们需要一些宏:
//
// 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
有了这些宏,我们就可以这样定义我们的接口了:
//
// IBar.h
//
DeclareInterface(IBar)
virtual int GetBarData() const = 0;
virtual void SetBarData(int nData) = 0;
EndInterface
是不是很像MFC消息映射那些宏啊,熟悉MFC的朋友一定不陌生。
现在我们可以像下面这样来实现我们的接口了:
//
// Foo.h
//
#include "BasicFoo.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
{
// add your code here
}
virtual void SetBarData(int nData)
{
// add your code here
}
};
怎么样,很简单吧,并不需要做很多的努力我们就可以在C++中使用接口了。然而,由于这并不是语言本身所直接支持的特性,所以我们需要遵循一些规则:
a) 声明一个类的时候,如果你的类除了要从接口类继承外还要从另一个类继承(结构上的继承,即is a关系),则把这个类作为第一个基类,就像我们平时做的一样,譬如CFrameWnd从CWnd继承,CBitmapButton从CButton继承,CMyDialog从CDialong继承。当你要从MFC类派生的时候,这尤其重要,把他们声明为第一个基类以避免破坏MFC的RuntimeClass机制。
b) 其他的基类紧跟其后,有多少就跟多少,如果你需要的话。譬如:class Foo : public BasicFoo, implements IBar, implements IOther, implements IWhatever, ...
c) 接口类里面不要声明任何成员变量。接口类仅用于描述行为而不是数据。当你要作多重继承时,这样做可以避免数据成员被从同一个接口类多次继承。
d) 接口类的所有成员函数定义为纯虚函数。这可以确保你的实现类来实现这些函数的全部,当然你也可以在抽象类实现部分函数,只要在你的派生类里实现剩下的函数。
e) 不要从除了接口类的其他任何类派生你的接口类。DeclareBasedInterface()可以做到这个.普通类可以选择实现基接口还是派生的接口,后面一种意味着两者都要实现。
f) 将一个指向实现接口的类的指针赋值给一个指向该接口类的指针是不需要强制类型转换的,但反过来将一个接口类的指针赋值给一个实现该接口的类的指针就需要一个显式的强制类型转换。事实上我们可能会使用多重继承,这样这些转换我们就不能使用老式的转换。不过使用运行时类型信息(使用/GR选项)和动态类型转换可以很好的工作当然也更安全。
g) 此外dynamic_cast为你提供了一种查询一个对象或接口是否实现了一个指定的接口的途径。
h) 你还要非常小心的避免不同接口函数的命名冲突。
如果你仔细观察DeclareInterface 和 DeclareBasedInterfaca宏你会发现有一个操作是必须的:每个接口类都有一个虚析构函数。你可能认为这不重要,但是如果没有这个就可能会导致一些问题,看看下面的例子:就像你看到的一样,这里有一个类工厂,它根据BarType来创建一个IBar的实现,当你使用完以后你当然希望要delete该对象,你会像下面这样做:
{
IBar* pBar = BarFactory::CreateBar(Foo);
pBar->SetName("MyFooBar");
// Use pBar as much as you want,
//
// and then just delete it when it's no longer needed
delete pBar; // Oops!
}
delete pBar 做了什么取决于该对象是否有一个虚析构函数。如果Foo没有一个虚析构函数,则只有IBar 的隐式的空析构函数被调用,Foo的析构函数不会被调用,这样就发生了内存泄露。接口类里虚析构函数的声明避免了这用状况,它确保每个实现接口的类都有一个虚析构函数。
当你使用DeclareInterfac的时候,记得使用EndInterface和它匹配。Interface 宏和 implements宏仅仅是代替了class和public,这看起来是多余的,但我认为它们更明确的表达了代码的意图。如果我这么写:class Foo : public IBar,你可能认为这只是一个简单的继承;但如果我这么写:class Foo: implements IBar,你就会看到它实际的价值和意图---这是对一个接口的实现,而不是简单的一次继承。DeclareInterface(IBar)
virtual LPCTSTR GetName() const = 0;
virtual void SetName(LPCTSTR name) = 0;
EndInterface
class Foo : implements IBar
{
// Internal data
private:
char* m_pName;
// Construction & Destruction
public:
Foo()
{
m_pName = NULL;
}
~Foo()
{
ReleaseName();
}
// Helpers
protected:
void ReleaseName()
{
if (m_pName != NULL)
free(m_pName);
}
// IBar implementation
public:
virtual const char* GetName() const
{
return m_pName
}
virtual void SetName(const char* name)
{
ReleaseName();
m_pName = _strdup(name);
}
};
class BarFactory
{
public:
enum BarType {Faa, Fee, Fii, Foo, Fuu};
static IBar CreateNewBar(BarType barType)
{
switch (barType)
{
default:
case Faa:
return new Faa;
case Fee:
return new Fee;
case Fii:
return new Fii;
case Foo:
return new Foo;
case Fuu:
return new Fuu;
}
}
};