在C++实现”Final”
2014-09-10 12:50
260 查看
原文链接:http://blog.csdn.net/nighsen/article/details/6363370
在C#中有sealed关键字,而Java中有final关键字,其作用就是为了提供一种机制使一个类不能被继承。当然,C++不能^_^,但是他能实现,下面来讨论一下吧。
Method1:最简单的想法就是使一个类得构造函数和析构函数成为私有函数,这样,子类的构造函数和析构函数就无法调用父类的构造函数和析构函数,也就难以构造或者析构父类对象,就可以了。当然,这样我们也无法构造对象,这个,我们可以采用静态方法来创建和释放类得实例。如下:
PS:
因为一个类的构造函数可以有很多,所以仅仅将构造函数私有是不行的,析构函数只能有一个,要私有。
这种方法只能产生堆上的实例,而且构造和析构也必须调用类的静态方法,跟平时的情况不同。
Method2:这个方法的要点有三,一是虚拟继承,也就是子类直接调用父类的构造方法,二是友元函数,因为友元关系不能被继承,三是模板方法,采用模板是为了使用方便,其实不使用也可以,但是会造成不必要的修改。直接看结果:
说一下原理:当我们创建一个类从NoDerivedClass继承时,因为NoDerivedClass是从NoDerivedClassHelper中虚拟继承而来,所以我们的类会直接调用NoDerivedClassHelper的构造方法,但是其为私有,而我们的类又不像NoDerivedClass一样是NoDerivedClassHelper的友元,所以无法调用,编译出错,无法继承。这种方法创建的NoDerivedClass类既可以在堆上,也可以在栈上创建实例。
PS:
当然,虽然我们禁止NoDerivedClass被别人继承,但是NoDerivedClass却应该可以拥有父类的,很简单,比如NoDerivedClass的父类是CFather,那么※这一行应该改为如下所示:
class NoDerivedClass: virtual public NoDerivedClassHelper < NoDerivedClass >, public CFather
虽然是多继承,但是NoDerivedClassHelper类中没有成员变量,应该不能掀起多大的风浪的,不必担心多继承带来的问题,比如菱形继承产生的数据冗余以及二义性等。
为了简便,我们可以使用宏的方式简化声明的过程:
#define FINAL_CLASS : public virtual NoDerivedClassHelper
那么定义为: class NoDerivedClass FINAL_CLASS
在这个例子中我们使用了模板,其实就是为了方便,如果不使用模板的话就是这个样子:
当然,在这种情况下每次实现NoDerivedClass类都要更改NoDerivedClassHelper,造成了不必要的修改,所以模板就很方便了。
Method3:这种方法和第二种方法类似,不过不使用friend的方法。Method2和 Method3的共同点就是使用虚继承,但是Method2采用了friend关系不继承的特性,那么我们的Method3是如何做的呢?如下:
解释一下,如果我们声明一个类从NoDerivedClass继承,那么由于是虚继承,这个类要直接调用NoDerivedClassHelper的构造函数,当然要调用无参数的构造函数,因为我们已经声明了NoDerivedClassHelper(int){},所以NoDerivedClassHelper并没有产生默认的构造函数,那么就会产生找不到合适的构造函数的错误,导致无法继承。
虚继承的奥义,就是在虚继承出现的继承层次中,总是在构造非虚基类之前构造虚基类。就是爷爷,父亲和孙子,本来的构造关系是父亲调用爷爷的构造函数,而现在是孙子直接调用爷爷的构造函数,虽然构造关系始终是基类先要被构造,但是构造函数的调用关系已经穿越…
Method4:如果我们想隐含掉虚拟继承关系,也就是我们提供的NoDerivedClassHelper并不需要用户来虚继承就好了,因为用户可能会漏掉虚继承,或者用户对虚继承心有余悸,那么看看如下的实现吧:
当然,我们最后要使用的类就是NoDerivedClass,也就是我们以后要创建”final”类的时候只要从NoDerivedClassHelper中直接继承就可以了并不需要使用虚拟继承,虚拟继承已经被我们封装到了内部,对用户透明。可见,如果没有template<typename CDerive> 模板的使用,到NoDerivedClassHelper这一层已经是”final”了,但是这里的技巧就是使用模板使友元关系又降低了一层,使NoDerivedClass也是CNoDerivedClassHelperBase的友元,也可以调用CNoDerivedClassHelperBase的私有构造和析构函数,但是由于CNoDerivedClassHelperBaseà
NoDerivedClassHelperà NoDerivedClass的继承关系,以及虚继承的使用,直接构造CNoDerivedClassHelperBase还是必须的,导致NoDerivedClass的无法继承。
说了这么多怎么做了,现在来说说为啥这么做吧,也就是为什么要实现一个”final”类。
如果我们创建的一个类不含有virtual的析构函数,那么如果我们继承了它,就有可能导致资源泄漏。
还有,如果我们创建的类不希望别人继承,但是什么声明啊,警告啊,帮助文档是没有用的,尤其你在提供一个封装库的类,用户不会时时想着你的告诫,那么咋办,就根本不能让用户可以用,而不是靠用户头脑中的清规戒律来使用,事实不能用,他肯定就不会用了。
好了,就到这吧。
Final,To be, or not to be...
引用:http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4143
在C#中有sealed关键字,而Java中有final关键字,其作用就是为了提供一种机制使一个类不能被继承。当然,C++不能^_^,但是他能实现,下面来讨论一下吧。
Method1:最简单的想法就是使一个类得构造函数和析构函数成为私有函数,这样,子类的构造函数和析构函数就无法调用父类的构造函数和析构函数,也就难以构造或者析构父类对象,就可以了。当然,这样我们也无法构造对象,这个,我们可以采用静态方法来创建和释放类得实例。如下:
class NoDerivedClass { public : static NoDerivedClass* CreateInstance() { return new NoDerivedClass; } static void DeleteInstance(NoDerivedClass* pInstance) { delete pInstance; pInstance = 0; } private : NoDerivedClass() {} ~NoDerivedClass() {} };
PS:
因为一个类的构造函数可以有很多,所以仅仅将构造函数私有是不行的,析构函数只能有一个,要私有。
这种方法只能产生堆上的实例,而且构造和析构也必须调用类的静态方法,跟平时的情况不同。
Method2:这个方法的要点有三,一是虚拟继承,也就是子类直接调用父类的构造方法,二是友元函数,因为友元关系不能被继承,三是模板方法,采用模板是为了使用方便,其实不使用也可以,但是会造成不必要的修改。直接看结果:
template <typename T> class NoDerivedClassHelper { friend T; private : NoDerivedClassHelper() {}; ~NoDerivedClassHelper () {}; }; class NoDerivedClass: virtual public NoDerivedClassHelper<NoDerivedClass> //※ { public : NoDerivedClass () {} ~ NoDerivedClass () {} };
说一下原理:当我们创建一个类从NoDerivedClass继承时,因为NoDerivedClass是从NoDerivedClassHelper中虚拟继承而来,所以我们的类会直接调用NoDerivedClassHelper的构造方法,但是其为私有,而我们的类又不像NoDerivedClass一样是NoDerivedClassHelper的友元,所以无法调用,编译出错,无法继承。这种方法创建的NoDerivedClass类既可以在堆上,也可以在栈上创建实例。
PS:
当然,虽然我们禁止NoDerivedClass被别人继承,但是NoDerivedClass却应该可以拥有父类的,很简单,比如NoDerivedClass的父类是CFather,那么※这一行应该改为如下所示:
class NoDerivedClass: virtual public NoDerivedClassHelper < NoDerivedClass >, public CFather
虽然是多继承,但是NoDerivedClassHelper类中没有成员变量,应该不能掀起多大的风浪的,不必担心多继承带来的问题,比如菱形继承产生的数据冗余以及二义性等。
为了简便,我们可以使用宏的方式简化声明的过程:
#define FINAL_CLASS : public virtual NoDerivedClassHelper
那么定义为: class NoDerivedClass FINAL_CLASS
在这个例子中我们使用了模板,其实就是为了方便,如果不使用模板的话就是这个样子:
class NoDerivedClassHelper { friend class NoDerivedClass; private: NoDerivedClassHelper(){}; ~NoDerivedClassHelper(){}; }; class NoDerivedClass :public virtual NoDerivedClassHelper { public: NoDerivedClass(){}; ~NoDerivedClass(){}; };
当然,在这种情况下每次实现NoDerivedClass类都要更改NoDerivedClassHelper,造成了不必要的修改,所以模板就很方便了。
Method3:这种方法和第二种方法类似,不过不使用friend的方法。Method2和 Method3的共同点就是使用虚继承,但是Method2采用了friend关系不继承的特性,那么我们的Method3是如何做的呢?如下:
class NoDerivedClassHelper { protected: NoDerivedClassHelper(int){} ~NoDerivedClassHelper(){} }; class NoDerivedClass : public virtual NoDerivedClassHelper { public: NoDerivedClass() : NoDerivedClassHelper(0) {} ~NoDerivedClass(){} };
解释一下,如果我们声明一个类从NoDerivedClass继承,那么由于是虚继承,这个类要直接调用NoDerivedClassHelper的构造函数,当然要调用无参数的构造函数,因为我们已经声明了NoDerivedClassHelper(int){},所以NoDerivedClassHelper并没有产生默认的构造函数,那么就会产生找不到合适的构造函数的错误,导致无法继承。
虚继承的奥义,就是在虚继承出现的继承层次中,总是在构造非虚基类之前构造虚基类。就是爷爷,父亲和孙子,本来的构造关系是父亲调用爷爷的构造函数,而现在是孙子直接调用爷爷的构造函数,虽然构造关系始终是基类先要被构造,但是构造函数的调用关系已经穿越…
Method4:如果我们想隐含掉虚拟继承关系,也就是我们提供的NoDerivedClassHelper并不需要用户来虚继承就好了,因为用户可能会漏掉虚继承,或者用户对虚继承心有余悸,那么看看如下的实现吧:
template<typename CDerive, typename CHelper> class CNoDerivedClassHelperBase { friend CDerive; friend CHelper; private: CNoDerivedClassHelperBase(){} ~CNoDerivedClassHelperBase(){} }; template<typename CDerive> class NoDerivedClassHelper : virtual public CNoDerivedClassHelperBase<CDerive, NoDerivedClassHelper> { public: NoDerivedClassHelper(){} ~NoDerivedClassHelper(){} }; class NoDerivedClass : public NoDerivedClassHelper<NoDerivedClass> { public: NoDerivedClass(){} ~NoDerivedClass(){} };
当然,我们最后要使用的类就是NoDerivedClass,也就是我们以后要创建”final”类的时候只要从NoDerivedClassHelper中直接继承就可以了并不需要使用虚拟继承,虚拟继承已经被我们封装到了内部,对用户透明。可见,如果没有template<typename CDerive> 模板的使用,到NoDerivedClassHelper这一层已经是”final”了,但是这里的技巧就是使用模板使友元关系又降低了一层,使NoDerivedClass也是CNoDerivedClassHelperBase的友元,也可以调用CNoDerivedClassHelperBase的私有构造和析构函数,但是由于CNoDerivedClassHelperBaseà
NoDerivedClassHelperà NoDerivedClass的继承关系,以及虚继承的使用,直接构造CNoDerivedClassHelperBase还是必须的,导致NoDerivedClass的无法继承。
说了这么多怎么做了,现在来说说为啥这么做吧,也就是为什么要实现一个”final”类。
如果我们创建的一个类不含有virtual的析构函数,那么如果我们继承了它,就有可能导致资源泄漏。
class B {
. . .
};
class D : public B {
. . .
};
B* pB = new D;
delete pB; // resource leak
还有,如果我们创建的类不希望别人继承,但是什么声明啊,警告啊,帮助文档是没有用的,尤其你在提供一个封装库的类,用户不会时时想着你的告诫,那么咋办,就根本不能让用户可以用,而不是靠用户头脑中的清规戒律来使用,事实不能用,他肯定就不会用了。
好了,就到这吧。
Final,To be, or not to be...
引用:http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4143
相关文章推荐
- 在C++实现”Final”
- C++如何实现类似JAVA中加了FINAL关键字的类
- 在C++实现”Final”
- c++ final class 实现 非final 关键字
- 在C++实现”Final”
- 用C++设计一个不能被继承的类实现java final的作用
- 如何在C++中实现java final标识的类的功能
- C++设计模式:Singleton的模板实现之一
- 在C++中实现“属性 (Property)”
- C++机理:虚拟机制的实现[兼谈对比于传统机制]
- 用C++ std::priority_queue 实现哈夫曼算法
- 用C++实现C#中的委托/事件(标准C++之升级版)
- 再探C++的单件实现
- 用 C++ 实现 C# 中的 委托/事件 (2-delegate event functor)
- 在C++中实现属性
- 用PHP实现通过Web执行C/C++程序
- 使用c++实现Format函数
- More Effective C++ Item 附2:一个auto_ptr的实现实例
- 分析模式-计量的C++实现——完美版本
- Singleton模式的C++实现研究(转贴)