注意:下面的代码在VC2003上编译过,但在VC2005Beta版上会有问题。我想可能是试用版上有bug吧。
引出问题:
前天,一个同事给我看了一段代码,问我这段代码的涵义。乍一看我没有看出明堂。后来在与几个同事一起深入研究后发现了它的奥妙。这其中涉及到一些C++中的高级技术,很有意思。我把我们的分析作了一个总结,借这块宝地,拿出来供大家共同学习。原始代码如下(不知道她从哪里弄来的这么好的代码,在这里谢了!):
//file name: NoInherit.h template <class T> class NoInherit_;
template <class T,template<class T> class A > class NoInherit_< A<T> > { private: friend A<T>; friend T; NoInherit_(){} };
template <class T> class NoInherit:virtual public NoInherit_< NoInherit< T> > { public: typedef T type; private: friend T; NoInherit():NoInherit_< NoInherit< T> >() {} }; |
如果你是大虾一下子就看明白了这段代码,那么这篇文章对你就没有多大价值了,兄弟就不敢再耽搁你的时间了。如果你不能一下子看出来,那就让我来告诉你吧。它是一个不能被其它类继承的类模板。不信你试试……
按照下面的方法使用这个模板:
class CA:NoInherit<CA> { //…… }; |
这个类CA不能再被其它类继承了.像这样
Class CB:CA {
} //error……编译会出错。提示不能访问NoInherit_的私有构造函数。 |
那么这个类模板是怎么样达到“不让其它类继承”的目的的呢?也许你已经看出来了。它是通过私有构造函数和虚拟继承来实现的。在我分析这个模板的过程中我思考过下面这几个问题:
1、 有哪些方法能实现“不让其它类继承”呢?
2、 为什么要用到friend A<T>;
3、 虚拟继承是怎么一回事?为什么在这里要使用虚拟继承呢?
4、 NoInherit_中friend A<T>好理解,friend T怎么理解?
下面我就这几个问题展开讨论。
有哪些方法实现“不让其它类继承”
我们知道,子类继承父类,肯定会做的事情是:调用父类的构造函数(注意:子类不一定会调用父类的析构函数。为什么?自己想去吧! 卖点拐子)。我们只要父类的构造函数是private的,子类想继承,没门儿!
“不让其它类继承”就这么简单。是的,就这么简单!
看官一定会问,既然这么简单,上面的代码为什么那么复杂呢?别急别急,待我下面慢慢道来……
我们先分析下面几种情况:
A) 单件(single)模式
//single.h class CSingle { public: static CSingle *CreateInstance() ; static void DeleteInstance() ; …… private: static CSingle *m_pSingle; Csingle(){}; Csingle(Csingle&){}; };
//single.cpp #include “single.h” CSingle * CSingle ::m_pSingle = NULL; CSingle * CSingle ::CreateInstance() { If(!m_pSingle) m_pSingle = new Csingle; return m_pSingle; } CSingle :: DeleteInstance () { delete m_pSingle; :m_pSingle = NULL; } |
它能够实现“不让其它类继承”,但很显然这种方式不好,它只能有一个实例。
那么下面这样呢?
class CExample { public: CExample* CreateInstance() { return new CExample; } private: CExample(); CExample(CExample&); }; |
网上很多人提到这种方法。我个人也认为这个方法可行。但是这不是最理想的方法,因为这种方法限制了CExample对象的创建:new CExample 和CExample example。
B)http://springsun.bokee.com/218785.html上提供了下面这种方法。
// Usable.h
class Usable;
class Usable_lock { friend class Usable; private: Usable_lock() {} Usable_lock(const Usable_lock&) {} };
class Usable : public virtual Usable_lock { // ... public: Usable(); Usable(char*);//这是为什么没有明白。 // ... };
Usable a; class DD : public Usable { }; DD dd; // error: DD::DD() cannot access // Usable_lock::Usable_lock(): private member //(参见《The Design and Evolution of C++》, 11.4.3 节) |
这跟上面一开始给出的代码原理是一样的。只不过上面将其模板化了。我个人推荐这里给出的方法。
下面的讨论,如果你看最上面的代码费劲,看这里的代码好了。我也将以这里的代码为例进行说明。
为什么需要friend class
当创建Usable对象时需要调用Usable_lock的构造函数,而Usable_lock的构造函数是私有的,所以friend class Usable;是必要的。
虚拟继承
说老实话,我以前做项目没有使用过这个东西。这次正好有机会,仔细研究了一下。
从《C++程序设计语言(特别版)》和《c++_primer》中可以知道虚拟继承是一项专为解决多继承中的一个问题的。看下面的代码:
//非虚拟继承 class Base { public: Base() { cout<<"Base construct"<<endl; } };
class CA: public Base { public: CA() { cout<<"CA construct"<<endl; } };
class CB: public Base { public: CB() { cout<<"CB construct"<<endl; } };
class CC:public CA,public CB { public: CC() { cout<<"CC construct"<<endl; } };
int main() { CC tt; getchar(); return 0; } | //虚拟继承 class Base { public: Base() { cout<<"Base construct"<<endl; } };
class CA: virtual public Base { public: CA() { cout<<"CA construct"<<endl; } };
class CB: virtual public Base { public: CB() { cout<<"CB construct"<<endl; } };
class CC:public CA,public CB { public: CC() { cout<<"CC construct"<<endl; } };
int main() { CC tt; getchar(); return 0; } |
运行结果如下: Base construct CA construct Base construct CB construct CC construct | 运行结果如下: Base construct CA construct CB construct CC construct
|
后面一段程序使用了虚拟继承,运行时少调了一次Base construct。为什么呢?
四个类的继承关系如下:
在没有虚拟继承的情况下,多重继承下的对象可能的布局。D对象有两个Z子对象。
在存在虚拟继承的情况下,多重继承下的对象的可能布局。D对象有一个A子对象。B和C子对象只保留了一个对A子对象的引用:
(这两幅图摘自《C++程序设计陷阱》)
也就是说:在虚拟继承的情况下由最底层的派生类的构造函数初始化虚基类。
在非虚拟继承中,子类只能初始化它的直接父类,而在虚拟继承中最底层的子类可以初始化虚基类。比如:
class Base { public: Base(char * pName = "Base") { strName = pName; cout<<"Base construct"<<endl; cout<<strName.data()<<endl; } private: string strName; };
class CA:virtual public Base { public: CA():Base("CA") { cout<<"CA construct"<<endl; } };
class CB:virtual public Base { public: CB():Base("CB") { cout<<"CB construct"<<endl; } };
class CC:public CA,public CB { public: CC():Base("CC") { cout<<"CC construct"<<endl; } }; |
我们可以这样理解:在非虚拟继承中,创建孙子类对象时,孙类对象先构造它的父类,父类又先构造父类的父类,这样一层一层向上追溯;在虚拟继承中,孙子类对象是直接调用了虚基类的构造函数,再处理非虚拟父类的(详细解释可以参考《C++程序设计陷阱》)。
到这里我们就能解释为什么上面提到的http://springsun.bokee.com/218785.html中的代码要使用虚拟继承了。如果不使用虚拟继承,那么class DD构造时就先调用class Usable_lock的构造函数。因为class Usable_lock是class Usable的友元类,所以它能够调用class Usable的构造函数了。这样就能实现class DD继承于class Usable_lock了。如果使用的是虚拟继承,那么class DD构造时就先调用class Usable的构造函数,显然这是不行的。因为class Usable的构造函数是私有的,而class DD又不是它的友元类。这样编译器就会提示不能访问class Usable的私有构造函数了。
到这里我们也就能理解最开始给出的那个模板类了。
NoInherit_中friend T怎么理解
我们象下面这样来使用这个模板:
class CTest :public NoInherit< CTest >
{
//……
}
这显然是行得通的。因为有了friend T,CTest就成了NoInherit和NoInherit_的友元,从而能够初始化这两个类。
如果我们这样:
class CMyClass:public CTest //error
{
}
就会出错。因为创建CMyClass对象时不能够造NoInherit和NoInherit_。从而CTest就是一个不能被人继承的类了。我们自己的工作就在CmyClass中完成。
参考文档:
1、http://springsun.bokee.com/218785.html
2、http://blog.csdn.net/hsustc/archive/ 2007/08/14 /1743330.aspx
3、《c++_primer》(中文第4版) P622~P627
4、《C++ 程序设计陷阱》(中文版) P142
[http://www.diybl.com/course/3_program/c++/cppsl/20071019/78123.html]