在C++实现”Final”

在C++实现”Final” .
 

在C#中有sealed关键字,而Java中有final关键字,其作用就是为了提供一种机制使一个类不能被继承。当然,C++不能^_^,但是他能实现,下面来讨论一下吧。

 

Method1简单的想法就是使一个类得构造函数和析构函数成为私有函数,这样,子类的构造函数和析构函数就无法调用父类的构造函数和析构函数,也就难以构造或者析构父类对象,就可以了。当然,这样我们也无法构造对象,这个,我们可以采用静态方法来创建和释放类得实例。如下:

  1. class NoDerivedClass  
  2. {  
  3. public :  
  4.       static NoDerivedClass* CreateInstance()  
  5.       {  
  6.             return new NoDerivedClass;  
  7.       }  
  8.   
  9.    
  10.   
  11.       static void DeleteInstance(NoDerivedClass* pInstance)  
  12.   
  13.       {  
  14.             delete pInstance;  
  15.             pInstance = 0;  
  16.       }  
  17.   
  18.    
  19.   
  20. private :  
  21.   
  22.       NoDerivedClass() {}  
  23.       ~NoDerivedClass() {}  
  24.   
  25. };  
class NoDerivedClass{public : static NoDerivedClass* CreateInstance() { return new NoDerivedClass; } static void DeleteInstance(NoDerivedClass* pInstance) { delete pInstance; pInstance = 0; } private : NoDerivedClass() {} ~NoDerivedClass() {}};  

PS

因为一个类的构造函数可以有很多,所以仅仅将构造函数私有是不行的,析构函数只能有一个,要私有。

这种方法只能产生堆上的实例,而且构造和析构也必须调用类的静态方法,跟平时的情况不同。

 

Method2这个方法的要点有三,一是虚拟继承,也就是子类直接调用父类的构造方法,二是友元函数,因为友元关系不能被继承,三是模板方法,采用模板是为了使用方便,其实不使用也可以,但是会造成不必要的修改。直接看结果:

  1. template <typename T>   
  2. class NoDerivedClassHelper  
  3. {  
  4.       friend T;  
  5.   
  6. private :  
  7.   
  8.       NoDerivedClassHelper() {};  
  9.       ~NoDerivedClassHelper () {};  
  10.   
  11. };  
  12.   
  13.    
  14.   
  15. class NoDerivedClass: virtual public NoDerivedClassHelper<NoDerivedClass>          //※   
  16. {  
  17.   
  18. public :  
  19.   
  20.       NoDerivedClass () {}  
  21.       ~ NoDerivedClass () {}  
  22.   
  23. };  
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

在这个例子中我们使用了模板,其实就是为了方便,如果不使用模板的话就是这个样子:

  1. class NoDerivedClassHelper  
  2. {  
  3.     friend class NoDerivedClass;  
  4.   
  5. private:  
  6.   
  7.       NoDerivedClassHelper(){};  
  8.       ~NoDerivedClassHelper(){};  
  9.   
  10. };  
  11.   
  12.    
  13.   
  14. class NoDerivedClass :public virtual NoDerivedClassHelper  
  15.   
  16. {  
  17.   
  18. public:  
  19.   
  20.        NoDerivedClass(){};  
  21.   
  22. ~NoDerivedClass(){};  
  23.   
  24. };  
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是如何做的呢?如下:

  1. class NoDerivedClassHelper  
  2. {  
  3.   
  4. protected:  
  5.               NoDerivedClassHelper(int){}  
  6.               ~NoDerivedClassHelper(){}  
  7. };  
  8.   
  9. class NoDerivedClass : public virtual NoDerivedClassHelper  
  10.   
  11. {  
  12. public:  
  13.   
  14.               NoDerivedClass() : NoDerivedClassHelper(0) {}  
  15.   
  16.               ~NoDerivedClass(){}  
  17.   
  18. };  
class NoDerivedClassHelper{protected: NoDerivedClassHelper(int){} ~NoDerivedClassHelper(){}};class NoDerivedClass : public virtual NoDerivedClassHelper{public: NoDerivedClass() : NoDerivedClassHelper(0) {} ~NoDerivedClass(){}};

解释一下,如果我们声明一个类从NoDerivedClass继承,那么由于是虚继承,这个类要直接调用NoDerivedClassHelper的构造函数,当然要调用无参数的构造函数,因为我们已经声明了NoDerivedClassHelper(int){},所以NoDerivedClassHelper并没有产生默认的构造函数,那么就会产生找不到合适的构造函数的错误,导致无法继承。

虚继承的奥义,就是在虚继承出现的继承层次中,总是在构造非虚基类之前构造虚基类。就是爷爷,父亲和孙子,本来的构造关系是父亲调用爷爷的构造函数,而现在是孙子直接调用爷爷的构造函数,虽然构造关系始终是基类先要被构造,但是构造函数的调用关系已经穿越

 

Method4如果我们想隐含掉虚拟继承关系,也就是我们提供的NoDerivedClassHelper并不需要用户来虚继承就好了,因为用户可能会漏掉虚继承,或者用户对虚继承心有余悸,那么看看如下的实现吧:

  1. template<typename CDerive, typename CHelper>   
  2. class CNoDerivedClassHelperBase   
  3. {   
  4.               friend CDerive;   
  5.               friend CHelper;   
  6.   
  7. private:   
  8.               CNoDerivedClassHelperBase(){}   
  9.               ~CNoDerivedClassHelperBase(){}   
  10.   
  11. };   
  12.   
  13. template<typename CDerive>   
  14. class NoDerivedClassHelper : virtual public CNoDerivedClassHelperBase<CDerive, NoDerivedClassHelper>   
  15. {   
  16.   
  17. public:   
  18.               NoDerivedClassHelper(){}   
  19.               ~NoDerivedClassHelper(){}   
  20. };   
  21.   
  22.    
  23.   
  24. class NoDerivedClass : public NoDerivedClassHelper<NoDerivedClass>   
  25. {   
  26.   
  27. public:   
  28.               NoDerivedClass(){}   
  29.               ~NoDerivedClass(){}   
  30. };  
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

还有,如果我们创建的类不希望别人继承,但是什么声明啊,警告啊,帮助文档是没有用的,尤其你在提供一个封装库的类,用户不会时时想着你的告诫,那么咋办,就根本不能让用户可以用,而不是靠用户头脑中的清规戒律来使用,事实不能用,他肯定就不会用了。

好了,就到这吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值