您的位置:首页 > 编程语言 > C语言/C++

在C++实现”Final”

2014-09-10 12:50 260 查看
原文链接:http://blog.csdn.net/nighsen/article/details/6363370





在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: