也许在你看了上述文字之后,会有一些想法:为什么要为宏费心呢?而它们并没有用到什么强制性的规则,也没有提高老式#define begin {、#define end }语法的可读性啊。
但如果你仔细观察DeclareInterface和DeclareBasedInterface宏,你会注意到还是有一些强制性规则存在的:实现了一个接口的类都有一个虚拟析构函数;也许你认为它并不重要,但是如果缺少虚拟析构函数,会导致某些问题,请看下面的代码:
如上所示,这里有一个类工厂(factory),可以基于BarType参数,请求它来创建IBar的某个实现;在用完之后,往往会想到删除这个对象,目前为止,一切都正常,但如果用在某些程序的main函数中呢:DeclareInterface(IBar) virtual LPCTSTR GetName() const = 0; virtual void SetName(LPCTSTR name) = 0; EndInterface class Foo : implements IBar { //内部数据 private: char* m_pName; //构造与析构函数 public: Foo() { m_pName = NULL; } ~Foo() { ReleaseName(); } protected: void ReleaseName() { if (m_pName != NULL) free(m_pName); } // IBar的实现 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; } } };
int main() { IBar* pBar = BarFactory::CreateBar(Foo); pBar->SetName("MyFooBar"); //尽可能地多使用pBar // ... //在不需要时删除它 delete pBar; //啊呀! }
在执行到delete pBar这一行时会发生什么,完全依赖于对象的类是否有一个虚拟析构函数。如果Foo没有一个虚拟析构函数,编译器只会生成一个对IBar隐式空析构函数的调用,而Foo的构析函数并没有被调用,因此会造成内存漏泄。在接口声明宏中的虚拟析构函数就是为了要防止这种情况发生的,它们保证了每个实现了一个接口的类,都会有一个虚拟析构函数。
既然我们使用了DeclareInterface,那么用EndInterface来进行配对还是说得过去的,至少好过用一个花括号来匹配吧。至于Interface与implements,它们各自会解析为class与public,使用它们看上去似乎有点多余,但它们能更好地表达代码所代表的意图,比如说从Foo : public IBar中,就只能看出继承关系,如果写成Foo implements IBar,就可看出实际的用意了。