How to design a class that can't be inherited(C++)

封装性,继承性和多态是面向对象的三大特征,最近思考了一下,怎么设计一个不能继承性的类呢?

C++要实现一个不能被继承的类有很多方法.使用友元、私有构造函数、虚继承等方式可以使一个类不能被继承,可是为什么必须是虚继承?背后的原理又是什么?

~的构造函数设置为私有的就okay。

因为那样的话,子类就没有办法访问基类的构造函数,从而就阻止了进行子类构造对象的任务实现,也就达到了不可继承的目的。

但是,假设那样,这个类我们在其它地方怎么使用呢?那这样子给我们的利用也造成了一定的障碍。

好了。你是不是也想到了,定义静态方法,在方法内部实现一个对象,然后返回它的指针。

Ok?那怎么释放掉呢?再照样设计一个释放内存函数,问题就会迎刃而解。

OK。按照这个逻辑分析。示例代码如下:

点击(此处)折叠或打开

  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. static A * Construct(int n)
  7. {
  8. A *pa = new A;
  9. pa->num= n;
  10. cout<<"num is:"<<pa->num<<endl;
  11. return pa;
  12. }
  13. static void Destruct(A * pIntance)
  14. {
  15. delete pIntance;
  16. pIntance = NULL;
  17. }
  18. private:
  19. A(){}
  20. ~A(){}
  21. public:
  22. int num;
  23. };
  24. void main()
  25. {
  26. A *f = A::Construct(9);
  27. cout<<f->num<<endl;
  28. A::Destruct(f);
  29. }

好了,这个类就这样子。按照我们的理论分析,我们的实践结果是完全成立的。

但是,这个题,它比较有挑战性,什么意思呢?难道你没有发现,咱们这水平也就仅仅有面试资格,还不可以破格录用的。

怎么啦?你可能会反问我。难道你真的没有看明白?确定没有看明白?如果是真的话,那我就告诉你吧!

咱们的类不可以实现在栈上创建对象。也就是说,仅仅只可以在堆上构建任何的一个对象,而在栈上就无能为力了。

私有的构造函数极大的局限性就这样一览无余了。

好吧!我们修改它,也就是所谓的为它打“补丁吧”,请看示例代码:

点击(此处)折叠或打开

#include<iostream>

using namespace std;

template <typename T>

class Base

{

friend T;

private:

Base(){}

~Base(){}

};

class Finalclass : virtual public Base<Finalclass>

{

public:

Finalclass(){}

~Finalclass(){}

};

void main()

{

Finalclass *p = new Finalclass; //堆上对象

Finalclass fs; //栈上对象

}

    OK。现在看看我们的Finalclass类。

    继承于Base,Base为虚基类,因为它是Base的友元,所以,它可以访问基类的私有构造函数,以及析构函数。编译运行时是正确的。

    也就是说,可以创建堆上的对象,并且可以构建栈上的对象。

    可否继承?假如它作为一个基类被一个类继承,在编译时是完全可以通过的。

    这一点没有什么疑问,问题就出在运行时:

    当子类在构造对象时,因为是虚继承,所以子类的构造函数会直接去调用Base类的构造函数,而Base的构造函数是私有的。运行错误error!!!

    这就是一个真正不能被继承的类。

    思路二:主要的思路就是使子类不能构造父类的部分,这样子类就没有办法实例化整个子类.这样就限制了子类的继承.
    所以我们可以将父类的构造函数声明成为私有的,但是这样父类不就不能实例化,继续思考、、、

       我们可以利用友员不能被继承的特性!

       首先假设CParent不能够被继承. 让CParent是某一个类的友员和子类,CParent可以构造,但是CParent的子类 CChild却不能继承那个友员特性,所以不能被构造.所以我们引入一个CFinalClassMixin.

      我们对这个类的功能是这么期望的:

      任何类从它继承都不能被实例化,同时这个类本身我们也不希望它被实例化.

      实现一个构造函数和析构函数都是private的类就行了.同时在这类里面将我们的CParent声明为友员. 代码如下:

      class CFinalClassMixin

      {

      friend class CParent;

      private:

       CFinalClassMixin(){}

      ~CFinalClassMixin(){}

      };

      //我们的 CParent代码应该如下:

      class CParent

      {

      public:

       CParent(){}

      ~CParent(){}

      };

      这个类(注,此时它还是能够被继承),现在我们需要它不能被继承.那么只要将代码改成

      class CParent:public CFinalClassMixin

      {

      public:

      CParent(){}

      ~CParent(){}

      };

      就行了.现在从CParent继承一个子类试试

      class CChild:public CParent{};

      编译一下代码试试,发现:竟然没有作用!!

      现在再回想一下我们这么操作的原因,也就是这个方案的原理,那就是让父类可以访问Mixin类的构造函数,但是子类不能访问.

      现在看看我们的代码,发现父类是CFinalClassMixin类的友员,可以访问它的构造函数.因为友员不能继承,所以CChild不能访问CFinalClassMixin的构造函数.所以应该不能被实例化.

      CChild的确不能访问 CFinalClassMixin的构造函数,但是它却不必调用它!我想这就是问题的原因所在.CChild是通过CParent来构造 CFinalClassMixin的,所以这个友员对他并没有什么用处!

      现在问题找到了.要解决很简单.只要让CChild必须调用 CFinalClassMixin的构造函数就行了,怎么才能达到目的呢?

      还记得虚继承吗?虚继承的一个特征就是虚基类的构造函数由最终子类负责构造!所以将CParent从CFinalClassMixin继承改成从CFinalClassMixin虚继承就可以了.代码如下:

      class CParent:virtual public CFinalClassMixin

      {

       public:

      CParent(){}

      CParent(){}

      };

      现在试试,行了.

      但是可能有些人会对多继承心有余悸!但是我们这里并没有必要这么担心!为什么?因为我们的CFinalClassMixin类是纯的!pure! 也就是说它根本没有成员变量!那么我们就根本不用担心多继承带来的最大问题.菱形继承产生的数据冗余.以及二义性.

      现在还有个不足!那就是 我们不能每次使用这个CFinalClassMixin类就在里面加上对某个类的友员声明啊!这多麻烦啊!虽然不是什么大问题,但是我觉的还是要解决,因为我充分信任C++!

      解决的方法也很简单!那就是使用模板!具体描述就省略了,给出代码大家一看就知道了

      下面是我的测试程序的完整代码(其中的CFinalClassmixin已经改成模板)

    #include "stdafx.h"

    #include <iostream>

    using namespace std;

    template<class T>   //应用模板

    class CFinalClassMixin

    {

    friend T;

    private:

    CFinalClassMixin(){}

    ~CFinalClassMixin(){}

    };

    class CParent:virtual public CFinalClassMixin<CParent>  //虚继承

    {

    public:

    CParent(){}

    ~CParent(){}

    };

    class CChild:public CParent{}; //子类继承父类

    int main(int argc, char* argv[])

    {

    CParent a; // 可以构造

    CChild b; //不能构造

    return 0;

    }

    现在只要对不想被继承的类加入一个CFinalClassMixin混合类做父类就行了.

      通过限制构造函数,我们就达到了限制继承的目的 .但是这对有些还是个例外,比如全是静态函数的类.这些类本身就不需要构造. 所以我们对它没有办法.但是在大多数情况下,一个全是静态函数的类多少暗示了程序本身的设计可能是需要斟酌的.

       其实这只是Mixin类(混合类)使用的一个小小例子.还有很多其他的用处,比如UnCopiale等等.就不多说了. 我想说明的是大家可能对多继承比较反感.但是过分否定也是得不偿失的.现在对多继承到底应不应该使用还处在争论阶段. 我觉得一个方法是否使用得当,关键还是在于使用的人.



How to design a class that can't be inherited(C++)

时间: 2024-10-13 03:30:06

How to design a class that can't be inherited(C++)的相关文章

如何获取Expression Design 4工具与Expression Blend 4工具

在VS2010+C#+WPF 开发项目过程中涉及到界面的布局与设计,网上有人讲采用Expression Design 4与Expression Blend 4工具相当方便, 于是决定试看看,下面将这个过程与大家分享. 一.安装目的 尽管程序员可以使用VS编写XAML代码的方式来构造用户界面,但是对于有设计爱好的用户来说,使用类似Photoshop一样的Expression套件能将 软件美工最大化.设计过程是先使用了Expression Design来设计图形,然后将其导入到Expression

POJ2100 Graveyard Design(尺取法)

POJ2100 Graveyard Design 题目大意:给定一个数n,求出一段连续的正整数的平方和等于n的方案数,并输出这些方案,注意输出格式: 循环判断条件可以适当剪支,提高效率,(1^2+2^2+..n^2)=n*(n+1)*(2n+1)/6; 尺取时一定要注意循环终止条件的判断. #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <

【翻】Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

译者地址:[翻]Android Design Support Library 的 代码实验--几行代码,让你的 APP 变得花俏 原文:Codelab for Android Design Support Library used in I/O Rewind Bangkok session--Make your app fancy with few lines of code 原文项目 demo: Lab-Android-DesignLibrary 双语对照地址: [翻-双语]Android D

DP什么意思 design pattern 设计模式

DP  design pattern 大话设计模式  中的DP 是设计模式的意思 设计模式的书 ,最经典最原始的就是 GOF 的<设计模式>了. 设计模式的书基本上大多是以这 20 多个模式分开讲.含<大话设计模式> 学了 OOL 写的程序基本上是 OB 的. 只有慢慢掌握了 DP 才能写出真正的 OO 程序. 思想 -> 设计原则 -> DP -> OOD

创建Material Design风格的Android应用--应用主题

昨天正式发布了android 5,同时android developer网站也更新了,增加了创建Material Design风格的Android应用指南,也更新了Support Library,在support library增加了一些Material Design风格的控件和动画等,这里给大家简单介绍一下怎样开发material design风格的Android应用. android 5使用Material Design风格 android提供了三种Material Design风格Them

Android Design 1: Back键和Up键在App导航中的表现

一,概念 1, Back键一直存在android系统中 1-1 任何页面下的返回 1-2 Floating window 1-3 Contexual Action bar/highlight select 1-4 Keyboard 2, Up键是随Android Design出来的. 2-1 android Design 定义的parent container 2-2 app的主界面是不存在Up键的 二,情景分析 1, App内部 1-1 沿逐级深入路径 Back:按照activity在栈中的顺

高煥堂 Design Thinking繁體版Blog區

高煥堂 Design ThinkingADT的繁體版Blog區 高煥堂 Design Thinking繁體版Blog區,布布扣,bubuko.com

[LeetCode] 211. Add and Search Word - Data structure design Java

题目: Design a data structure that supports the following two operations: void addWord(word) bool search(word) search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one le

poj 2100 Graveyard Design

Graveyard Design Time Limit: 10000MS   Memory Limit: 64000K Total Submissions: 7357   Accepted: 1816 Case Time Limit: 2000MS Description King George has recently decided that he would like to have a new design for the royal graveyard. The graveyard m