面试题48:不能被继承的类

C11已有关键字final

首先想到的是在C++ 中,子类的构造函数会自动调用父类的构造函数。同样,子类的析构函数也会自动调用父类的析构函数。要想一个类不能被继承,我们只要把它的构造函数和析构函数都定义为私有函数。那么当一个类试图从它那继承的时候,必然会由于试图调用构造函数、析构函数而导致编译错误。

可是这个类的构造函数和析构函数都是私有函数了,我们怎样才能得到该类的实例呢?这难不倒我们,我们可以通过定义静态来创建和释放类的实例。基于这个思路,我们可以写出如下的代码:

// ====================方法一====================
class SealedClass1
{
public:
    static SealedClass1* GetInstance() 
    {
        return new SealedClass1();
    }
 
    static void DeleteInstance( SealedClass1* pInstance)
    {
        delete pInstance;
    }
 
private:
    SealedClass1() {}
    ~SealedClass1() {}
};

// 如果试图从SealedClass1继承出新的类型,
// 将会导致编译错误。
/*
class Try1 : public SealedClass1
{
public:
    Try1() {}
    ~Try1() {}
};
*/

 

这个类是不能被继承,但在总觉得它和一般的类有些不一样,使用起来也有点不方便。比如,我们只能得到位于堆上的实例,而得不到位于栈上实例。

能不能实现一个和一般类除了不能被继承之外其他用法都一样的类呢?办法总是有的,不过需要一些技巧。请看如下代码:

 1 // ====================方法二====================
 2 template <typename T> class MakeSealed
 3 {
 4     friend T;
 5  
 6 private:
 7     MakeSealed() {}
 8     ~MakeSealed() {}
 9 };
10  
11 class SealedClass2 : virtual public MakeSealed<SealedClass2>
12 {
13 public:
14     SealedClass2() {}
15     ~SealedClass2() {}
16 };
17 
18 // 如果试图从SealedClass1继承出新的类型,
19 // 将会导致编译错误。
20 /*
21 class Try2 : public SealedClass2
22 {
23 public:
24     Try2() {}
25     ~Try2() {}
26 };
27 */

注意SealedClass2是虚继承,原因如下:

调用try的构造函数时,会先调用它包含的所有virtual base类的构造函数,然后再调用它上层的base类构造函数,然后是设置vptr,最后是初始化列表和子类构造函数体内的用户代码。try不能调用MakeFinal的私有成员,因此引发编译错误。

如果不是virtual继承,那么try首先调用的是它上层base类的构造函数,也就是FinalClass的构造函数,然后由FinalClass的构造函数来调用MakeFinal的构造函数,由于FinalClass是MakeFinal的友元,因此该调用合法,所以try得以正确构造,而没有编译错误。

posted on 2016-07-20 15:55  已停更  阅读(1180)  评论(0)    收藏  举报