如何禁止一个类被继承?

来源:互联网 发布:sql server新建语句 编辑:程序博客网 时间:2024/05/23 23:10

当我们想将一个类设置为禁止继承时该用什么方式?有两种解决方案。提示:虚继承与友元!!


Way1:强行将该类的构造函数与析构函数均设为 private,并设定该类的一个友元函数来构建该类对象,并返回指针。此时继承自该类的类将无法调用该类的构造函数,因此将在编译时报错

    • 缺点:将类的使用变得与普通类不兼容,不能在友元函数外定义类对象,用户定义该类必须知道友元函数    • 优点:简单易理解

Eg:

class A    {        friend A* get_Aptr(int);        A(int i = 0):val(i){}        int val;    };    A * get_Aptr(int i)    {        A * ptr = new A(i);            //在用完该对象时还需要手动释放内存空间        return ptr;    }

分析:在友元函数外将不能获得实例对象本身,只能获得其指针,但实际上,因为 NRVO(Named return value optimical) 的作用,在某些情况下,将可以返回类对象。如:

Eg:

    class A    {        friend A get_A(int);        //与之前一样    };    A get_A(int i)    {        A a(i);        return a;    }    int main()    {        A a = get_A(3);    }

分析:

• 若 A 中未定义复制构造函数,则这种使用 get_A(int)的方式正确,此时编译器进行了 NRV 优化,A a = get_A(3)的这句话中并没有调用复制构造函数,因此,该语句正确(g++ 与 VS2013均正确)

• 若A中定义了复制构造函数,且复制构造函数位于private段,则此时用 get_A(int)的方式在编译时报错,但并不代表并未使用 NRV 优化,只是跟编译器的处理方式有关。个人认为:编译器发现复制构造函数在 private 段就会将复制构造操作认为是错误的操作

• 若A中定义了复制构造函数,且赋值构造函数位于 public 段,则此时使用 get_A(int) 的方式正确,但是否进行了NRV 与编译器有关。对 g++ 此时进行了 NRV 优化,对 VS2013 则未进行 NRV 优化

• 在《深入探索C++对象模型》中,李普曼老师说若未定义复制构造函数将不会开启 NRV 优化;但在现在的编译器中,NRV 优化的开启一般都与复制构造函数没有关系,主要由编译器option来决定。


way2 : 定义一个类A,将其构造函数与析构函数均放在 private 段,并将类 B 设为类A 的友元,让 B 虚继承自 A,B将变为不可继承的类。使用类C 继承类B 来检查是否不可继承

分析:在该过程中有两个重要操作,<1>将 类B 设为类A 的友元,<2>将类B 虚继承自类A。以下逐个分析。

• 将B设为A的友元后,B可以访问A的私有构造函数与析构函数,则B的使用正常,从理论上说,新定义一个类C 将不能继承类B,因为类C 不能访问 A类子对象的构造函数

§ 问题:事实上,若只是如此,将能够通过编译,因为 A类子对象的构造函数将有 B类子对象调用,而 B类子对象是 A类子对象的友元,因此能通过编译§ 解决方法:强行让最底层的派生类访问基类的构造函数,即让 C类对象直接访问 A类子对象的构造函数,而不能让 B类子对象访问 A类子对象的构造函数

• 要使类C 的对象直接访问 A子对象的构造函数,办法是使 A 类成为派生链中的虚基类

§ 虚基类的子对象由继承链中最低层的类对象维护,因此类A 的子对象将由链中最底层的 类C 的对象调用,而类C 没有类A 子对象的构造函数的访问权,因此,类C 的继承操作将报错

说明:

• 缺点:理解起来比较复杂
• 优点:使用不可继承的类与使用一般类相同

0 0
原创粉丝点击