From:http://blog.csdn.net/lazy_tiger/article/details/2224899

一个类不能被继承,也就是说它的子类不能构造父类,这样子类就没有办法实例化整个子类从而实现子类无法继承父类。我们可以将一个类的构造函数声明为私有,使得这个类的构造函数对子类不可见,那么这个类也就不能继承了。但是,这引出一个问题,客户程序岂不是也无法实例化这个类了?OK,让我们参考一下Singleton模式,用一个static函数来帮助创建这个类的实例,问题就解决了!

class CParent
{
private:
    CParent(int v){m_v = v;}
    ~CParent(){}

    int m_v;
    static CParent * m_instance;
public:
void fun(){cout << "The value is: " << m_v << endl;}
    static CParent * getInstance(int v);
};
CParent * CParent::m_instance = NULL;

CParent * CParent::getInstance(int v)
{
    m_instance = new CParent(v);
    return m_instance;
}

这是一个有效的方法,但是static函数创建出来的实例必然是static的。而且,这个类不能像普通的类那样构建对象,比如:

CParent c;  // impossible

换个思路考虑一下,友元不也是不能被继承的么?我们可以把类的构造函数定义为private的同时,定义友元函数来帮助构造类的实例。

class CParent
{
private:
    CParent(int v){m_v = v;}
    ~CParent();

    int m_v;
public:
    void fun(){cout << "The value is: " << m_v << endl;}
    friend CParent * getInstance(int v);
};

CParent * getInstance(int v)
{
    CParent * pinstance = new CParent(v);
    return pinstance;
}

这个类也是不能被继承的,但是出现的问题和前面一样:我们还是不能像对普通类那样对待这个类。

现在设想一下,有一个CParent类,我们不希望他能够被继承。在友元不能被继承的思路指引下,我们要考虑让CParent的友元属性不能被继承。假设有一个辅助类CNoHeritance,CParent是CNoHeritance类的友元。还要假设一个CChild类,它试图去继承CParent类(如果它有这个能耐的话)。

先把CNoHeritance类的构造函数定义成private,然后将CParent声明为CNoHeritance的友元类。同时CParent继承了CNoHeritance类。到目前为止,CNoHeritance除了CParent类以外,谁也无法对它进行访问和实例化。CChild因为无法继承CParent的友元特性,所以CChild无法对CNoHeritance直接进行实例化(但是可以通过CParent)。

到目前为止,CParent还是可以被继承的。这是一个trick.让我们整理一下思路,下面的图说明了CNoHeritance, CParent和CChild三个类之间的关系。

如果我们让CParent类虚继承CNoHeritance类,根据虚继承的特性,虚基类的构造函数由最终的子类负责构造。因此CChild如果要想继承CParent,它必须能够构造CNoHeritance,这是不可能的!因此,我们的CParent也就终于成为了一个无法继承的类。

class CNoHeritance
{
private:
    CNoHeritance(){}
    ~CNoHeritance(){}
    friend class CParent;
};

class CParent : virtual public CNoHeritance
{
public:
    CParent(int v){m_v = v;}
    ~CParent(){};
private:
    int m_v;

public:
    void fun(){cout << "The value is: " << m_v << endl;}
};

class CChild : public CParent
{
public:
    CChild():CParent(10){}
    ~CChild(){}
};

需要注意的是,我们这里引入的CNoHeritance类对整个程序而言,只引入了Private的构造函数和析构函数,所以不会因为可能的菱形继承带来二义性.

[转]实现一个无法被继承的C++类的更多相关文章

  1. 用C++设计一个不能被继承的类(用私有构造函数+友元函数)

    题目:用C++设计一个不能被继承的类. 分析:这是Adobe公司2007年校园招聘的最新笔试题.这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目. 在Java中定义了关键字f ...

  2. 探讨C++实现一个不可被继承的类

    C#和Java都提供了一种机制让一个类不能被继承,如C#中的sealed关键字和Java的final关键字,然而C++程序员就没这么好命了.不过C++也可以模拟出这种效果,原理基于:子类的构造函数会自 ...

  3. 用C++设计一个不能被继承的类(转)

    在Java 中定义了关键字final,被final修饰的类不能被继承. 首先想到的是在C++中,子类的构造函数会自动调用父类的构造函数.同样,子类的析构函数也会自动调用父类的析构函数.要想一个类不能被 ...

  4. 3.实现一个名为Person的类和它的子类Employee,Employee有两个子类Faculty 和Staff。

    23.实现一个名为Person的类和它的子类Employee,Employee有两个子类Faculty 和Staff. 具体要求如下: (1)Person类中的属性有:姓名name(String类型) ...

  5. classmethod一个用处是创建可选类构造器

    Definition and Introduction通常来说, descriptor 是一种绑定着特殊行为属性的对象, 在访问它时行为被descriptor协议定义的方法所重载.这些方法是__get ...

  6. 一天一个Java基础——对象和类

    1.在Java中你所做的全部工作就是定义类,产生那些类的对象,以及发送消息给这些对象 2.可以在类中设置两种类型的元素:字段(也被称作数据成员)和方法(也被称作成员函数) 3.字段可以是任何类型的对象 ...

  7. 一个漂亮的php验证码类

    一个漂亮的php验证码类(分享)   作者: 字体:[增加 减小] 类型:转载 下面小编就为大家分享一个漂亮的php验证码类.需要的朋友可以过来参考下   直接上代码: 复制代码 代码如下: //验证 ...

  8. 设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”。。。

    设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”,则抛出一个异常信息“This is a XYZ”,如果从命令行输入 ABC,则没有抛出异常.(只有 ...

  9. CSS一个元素同时使用多个类选择器(class selector)

    CSS类选择器参考手册 一个元素同时使用多个类选择器 CSS中类选择器用点号表示.实际项目中一个div元素为了能被多个样式表匹配到(样式复用),通常div的class中由好几段组成,如<div ...

随机推荐

  1. Net中的常见的关键字

    Net中的关键字有很多,我们最常见的就有new.base.this.using.class.struct.abstract.interface.is.as等等.有很多的,在这里就介绍大家常见的,并且有 ...

  2. Memcache缓存系统构建一

    在如今这个高效率的社会中,怎样将这个高效率应用到自己的程序中,是一个值得追寻和值得探讨的问题.因为这个memcache能够很好的提高检索速度,提升用户体验,而且重要的是减少数据库的访问.这就大大的提高 ...

  3. .Net 序列化(去除默认命名空间,添加编码)

    1.序列化注意事项 (1).Net 序列化是基于对象的.所以只有实例字段呗序列化.静态字段不在序列化之中. (2)枚举永远是可序列化的. 2.XML序列化时去除默认命名空间xmlns:xsd和xmln ...

  4. (转)从P1到P7——我在淘宝这7年

    (一) 2011-12-08 [原文链接] 今天有同事恭喜我,我才知道自己在淘宝已经七周年了.很多人第一句话就是七年痒不痒,老实说,也曾经痒过,但往往都是一痒而过,又投入到水深火热的工作中去.回家之后 ...

  5. ASP.NET Core官方计划路线及需要废除的一些Framework技术

    概述 下面是 ASP.NET Core的时间表和路线图. 注意日期和特性都可能更改. 作为.NET Core这么大的一个项目,很难准确预测每一个计划的是否有变动. 即便如此,我们还是计划公开和透明的实 ...

  6. Basic Tutorials of Redis(1) - Install And Configure Redis

    Nowaday, Redis became more and more popular , many projects use it in the cache module and the store ...

  7. LINQ to SQL语句(19)之ADO.NET与LINQ to SQL

    它基于由 ADO.NET 提供程序模型提供的服务.因此,我们可以将 LINQ to SQL 代码与现有的 ADO.Net 应用程序混合在一起,将当前 ADO.NET 解决方案迁移到 LINQ to S ...

  8. Xamarin Android 之起始篇

    序言: 在博客园注册了已经有2年多了,快三年了.从开始学习这一行开始就在博客园注册了这个账号.至今也还没有写过一篇随笔,大多时候都是在园子里头潜水,看大牛写的文章,学习. 写博客不为啥,就是自己对自己 ...

  9. Date.parse

    JavaScript: Date.parse(),一个参数,参数类型是 JavaScript 中的 Date 类型. 返回值 : 得到一个 Unix 时间戳,比如说,1470993235000,这种东 ...

  10. Python3.5安装及opencv安装

    Python安装注意事项(版本3.5,系统windows)1.安装好Python后将D:\Program Files\Python.D:\Program Files\Python\Scripts加入P ...