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

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

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

一、定义不可被继承的C++类函数

    如何让一个类不能被继承呢?简单来讲,咱们但愿达到的效果,就是若是继承这个类的话,编译直接报错。ui

    实现这个类,我但愿你提早了解过如下几个C++的简单语法:友元类虚继承这里我直接告诉你如何来定义,接下来咱们讨论为何。spa

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

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

    第三步:定义类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        // 编译出错
{};


二、定义一个只能够在栈上建立对象的类

    若是上面那种状况理解的话,这里应该不会太难。为何只能够在栈上建立对象?如何实现?其实很简单,只要咱们只对外暴露出能够在堆上建立对象的接口就能够。代码以下:

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实现了在堆上对象的获取和释放。


三、定义一个只能够在栈上建立对象的类

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

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整理

相关文章
相关标签/搜索