如何禁止一个类被继承?

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


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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值