现在的位置: 首页 > 综合 > 正文

不能被继承的类

2013年03月29日 ⁄ 综合 ⁄ 共 1276字 ⁄ 字号 评论关闭

来自剑指offer

题目:用C++设计一个不能被继承的类

分析:

C#中定义了关键字sealed,被sealed修饰的类不能被继承。在java中同样有关键字final表示一个类型不能被继承。在C++中没有类似于sealed和final的关键字,我们只能自己去实现。

常规解法:

        在C++中子类的构造函数会自动调用父类的构造函数,子类的析构函数也会自动调用父类的析构函数。要想一个类不能被继承,我们只要把它的构造函数和析构函数都定义为私有函数。那么当一个类试图从它那继承的时候,必然会由于调用构造函数、析构函数而导致编译错误。再定义一公有的静态函数来创建和释放类的实例。

class SealedClass1
{
public:
	static SealedClass1* GetInstance()
	{
		return new SealedClass1;
	}
	static void DeleteInstance(SealedClass1* pInstance)
	{
		delete pInstance;
	}
private:
	SealedClass1(){}
	~SealedClass1(){}
};

缺点:使用不方便,创建实例只能创建在堆上,而不能在栈上创建实例。

新奇解法:

这种方法得学习一下,我原来也不知道。

template<typename T>
class MakeSealed
{
public:
	friend T;
private:
	MakeSealed(){}
	~MakeSealed(){}
};

class SealedClass2: virtual public MakeSealed<SealedClass2>
{
public:
	SealedClass2(){}
	~SealedClass2(){}
};

这个SealedClass2使用起来和一般的类型没有区别,我们可以在堆栈创建实例。MakeSealed<SealedClass2>的构造、析构函数是私有的,但是由于类SealedClass2是它的友元类型,因此在SealedClass2中调用MakeSealed<SealedClass2>的构造函数和析构函数都不会引起编译错误。

但当我们试图从SealedClass2中派生一个类并创建它的实例的时候,却不能通过编译。比如我们从SealedClass2中派生出类型Try:

class Try:public SealedClass2
{
public:
	Try(){}
	~Try(){}
};

会报错,如下:

       这是由于SealedClass2是从类MakeSealed<SealedClass2>继承过来的,在调用Try的构造函数时,会跳过SealedClass2而直接调用MakeSealed<SealedClass2>的构造函数。非常遗憾的是,Try不是MakeSealed<SealedClass2>的友元类型,因此不能调用它的私有构造函数。

 

注:第二章方法的可移植性不好。虽然SealedClass2在VS能够编译,但由于GCC中对friend的要求不同于VS,目前在最新的GCC中还不支持模板参数类型作为友元类型。

抱歉!评论已关闭.