教你如何用C++创建一个特殊的类

就语言而言,个人还是比较喜欢C++,尽管 C++有些语法方面确实比较深奥,但这些确实挡不住它在实际应用中不可被替代的位置。

开始谈今天的重点,如何定义一个特殊的C++类。

1、定义不可被继承的C++类

如何让一个类不能被继承呢?简单来说,我们希望达到的效果,就是如果继承这个类的话,编译直接报错。

实现这个类,我希望你提前了解过以下几个C++的简单语法:友元类、虚继承。这里我直接告诉你如何来定义,接下来我们讨论为什么。

第一步:定义一个空类A,显式给出构造和析构,构造和析构必须定义为private。

第二步:定义不可被继承的类B,给出正常的构造和析构,public类型,让 B虚继承A类,继承方式不限,同时将B设置为A的友元类。

第三步:定义类C去尝试虚拟继承类B,编译错误!

代码如下:

class A
{
	friend class B;
private:
	A(){}
	~A(){}
};

class B:virtual public A
{
public:
	B()
	{}
};

class C :public B
{
public:
	C()
	{}
};

我们来讨论,为什么编译会出错?

首先,将类A的构造函数和析构函数定义为私有,如果没有显示定义,默认的构造和析构函数是public的。那定义为protected类型可以吗?答案是“NO”。了解过继承的话,应该知道,基类中private类型的成员变量或方法,在所有派生类中是不可见,但protected类型的成员变量或方法是可见的。我们的目的是如果以后构造类A的派生类时,需要调用类A构造,必须在类A内部调用该方法,即使是派生类,我也希望它遵守,即对派生类不可见。

其次,类B继承了类A,继承方式无所谓,因为基类私有部分不论以何种方式继承,都是不可见的。为什么要虚继承呢?这个稍后说。同时,类B这里定义成类A的友元类,那么以后类B就可以直接访问类A的私有成员了,也就是说,这里的友元,打破了我们一开始定义的对派生类不可见的限制。那么以后在使用类B实例化对象时,完全可以成功,构造基类部分是通过友元实现的,和继承方式无关。这里的类B就是我们定义的不可被继承的类。

最后,我们尝试让类C继承类B,那么当类C实例化对象时(或显式定义了构造函数),就会首先去构造属于类B的部分。这里注意,如果类B不是虚继承类A的话,那么这里构造属于类B的部分时,是通过类B构造类A部分,这必然是成功的。但我们不希望,因此,这里类B虚继承了类A,当构造属于类B部分时,由于B虚继承了A,那么会由类C直接去访问A,尝试构造类A的部分,很明显,由于访问不到类A的构造函数,因此C实例化对象失败。之后,所有尝试继承类B的类都会在编译时失败(前提要求C显示给出了构造,或类C实例化了对象)。

到这里,不可被继承了类B创建成功,这里很巧妙的应用了友元类的概念,从而实现了仅有B可以实例化属于A的部分。

如果觉得这种方式太过灵活,不容易理解,那么还有一种更加简单的方式。C++11引入了final关键字,被该关键字修饰的类,都是不可被继承的,这个和java中的用法基本是一致的。

class A final
{
public:
	A(){}
};

class B :public A        // 编译出错
{};

2、定义一个只可以在栈上创建对象的类

如果上面那种情况理解的话,这里应该不会太难。为什么只可以在栈上创建对象?如何实现?其实很简单,只要我们只对外暴露出可以在堆上创建对象的接口就可以。代码如下:

class A
{
public:
	static A* Get_A(int x)
	{
		return new A(x);
	}
	static void Delete_A(A* a)
	{
		delete a;
	}
private:
	A(int a = 10)
		:_a(a)
	{}
private:
	int _a;
};

int main()
{
	A* pa = A::Get_A(9);
	A* pb = A::Get_A(7);
	return 0;
}

和之前一样,将构造函数和析构函数都设置为私有(因为这里不涉及继承,private和protected在这里没有区别),由于构造函数都是私有的,无法创建对象,因此,提供了静态方法,该方法中通过new和delete实现了在堆上对象的获取和释放。

3、定义一个只可以在栈上创建对象的类

如果理解了上一种,就应该知道这里该如何去做。只要我们只提供在栈上获取对象的方式即可,由于栈空间是由操作系统维护了,没有特殊需要,析构函数就没有必要显式给出,代码如下:

class A
{
public:
	static A Get_A(int x)
	{
		return A(x);
	}
private:
	A(int a = 10)
		:_a(a)
	{}
private:
	int _a;
};

int main()
{
	A pa = A::Get_A(9);
	A pb = A::Get_A(7);
	return 0;
}

只能在栈上或者只能在堆上创建的对象,实现起来原理是一样的,类的构造和析构函数都设置为私有,当我的接口函数提供的方法是从堆中创建的对象时,类就只能在堆上创建对象,当我的接口函数提供的是从栈上直接得到的对象的话,类就只可以在栈上创建对象。需要注意一点的是,创建栈上对象的时候,不可以返回临时对象的引用,这个就不再多解释。

------muhuizz整理

时间: 2024-10-17 22:47:57

教你如何用C++创建一个特殊的类的相关文章

创建一个单例类

关于单例的概念此处不做表述,直接上代码演示如何创建一个单例类. 1 #import <Foundation/Foundation.h> 2 3 @interface MJDemo : NSObject 4 5 + (instancetype)sharedDemo; 6 7 @end 8 9 10 #import "MJDemo.h" 11 12 @implementation MJDemo 13 14 //在iOS中所有对象分配内存空间,最终都会调用allocWithZon

如何用Maven创建一个普通Java项目

一下内容包括:用Maven创建一个普通Java项目,并把该项目转成IDEA项目,导入到IDEA,最后把这个项目打包成一个jar文件. 有时候运行mvn命令失败,重复运行几次就OK了,无解. 1.用Maven模板创建一个项目 打开控制台,进入到想要创建项目的目录,然后运行如下命令,参数自由填写: 1 mvn archetype:generate -DgroupId={project-packaging} 2 -DartifactId={project-name} 3 -DarchetypeArti

如何用Unity创建一个的简单的HoloLens 3D程序

注:本文提到的代码示例下载地址>How to create a Hello World 3D holographic app with Unity 之前我们有讲过一次如何在HoloLens中创建一个2D程序的,没看过或者忘记的同学可以看这里回忆一下^_^ 如果说上次的2D版就是个带了个HoloLens面具的UWP程序,那我们这次要做的呢可是正宗的3D程序哦. 先来看看我们要做些什么准备. 1. Visual Studio 2015 Update 3 2. Windows 10 (10.0.105

【iOS开展-50】使用它来创建一个新的类的实现代码包,因此,不自觉地练习简单MVC实验,附带动画

接下来说说代码封装最后一个个案. 最后一种情况看:[iOS开展-48]九宫格案例:自己主动布局.字典转模型运用.id和instancetype差别.xib反复视图运用及与nib关系 (1)代码封装的原则是:要保证视图控制器尽量少的接触到其它对象的属性,也就是说,尽量把数据或者属性封装到一个类里面,然后利用类或者对象的方法来调用或者设置数据.而是赤裸裸地把属性都写在视图控制器中. 核心作用在于:降低视图控制器的代码量,把数据和属性的处理封装起来,这样也便于其它视图控制器的使用. 要做到的结果就是例

创建一个动作-Action类:

让我们创建一个Java文件HelloWorldAction.java的Java资源> SRC下一个的包名com.yiibai.struts2与下面的内容. package com.yiibai.struts2; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport{ private String name; public String execute()

如何用eclipse创建一个c项目

  

如何用nodejs创建一个webservice

Posted on March 25th, 2012 under Express.js, Node.jsTags: Express.js, git, GitHub, node.js Looking for a good tutorial on Express.js to help you get quickly productive in it? You have come to the right place. This tutorial is for Express 3, the curre

如何创建一个不可变类

将类声明为final,所以它不能被继承 将所有的成员声明为私有的,这样就不允许直接访问这些成员 对变量不要提供setter方法 将所有可变的成员声明为final,这样只能对它们赋值一次 通过构造器初始化所有成员,进行深拷贝(deep copy) 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝 原文地址:https://www.cnblogs.com/ldddd/p/11213326.html

Android | 教你如何用代码一键实现银行卡绑定

前言 ??小编前面几期文章分别给大家介绍了用代码实现微笑抓拍.证件照DIY.拍照翻译的功能开发(链接见文章末尾),本次小编给大家带来的是用代码一键实现银行卡识别与绑定功能. 银行卡识别的应用场景 ??介绍开发步骤前,我们先来谈谈银行卡识别的具体应用场景,银行APP.移动支付.缴费类APP.电商类APP或者其它带支付功能的APP在使用过程中往往会遇到如下常见的几个应用场景: 绑卡支付 ??常用于支付类APP,或者带支付功能的APP,用来绑定信用卡.银联借记卡,提供在线支付功能. 转账汇款 ??常用