【C++】特殊类设计 — 不能被拷贝的类 , 只能在堆/栈上创建对象的类 ,不能被继承的类

2024-08-15 14:31:40 浏览数 (2)

1 特殊类

在实践中,常常会有一些比较有意思的特殊场景:

  1. 不能被拷贝的类 - 独一无二的魔法宝物: 在一个角色扮演游戏(RPG)。在这个游戏中,玩家可以通过工匠进行装备的拷贝,但是有一件神秘的魔法宝物,被称为“永恒之心”,它拥有赋予持有者永生的力量 ,是独一无二的,因此它不能被复制或克隆。 每当玩家尝试复制“永恒之心”时,游戏会抛出一个错误:“魔法宝物独一无二,无法复制!”。这确保了游戏中只有一个“永恒之心”,增加了它的神秘感和价值。
  2. 只能在堆上创建对象的类 - 豪华游艇 有一个豪华游艇模拟器。在这个模拟器中,游艇是一个复杂且昂贵的对象,它需要在堆上动态分配资源,比如内存来存储游艇的详细规格和状态。 每当玩家想要创建一艘新的游艇时,他们必须通过“造船厂”接口来请求,这实际上是在堆上分配了一个新的游艇对象。
  3. 只能在栈上创建对象的类 - 一次性密码 你正在为银行的安全系统编写代码。为了防止密码被复制或存储在不可靠的地方,你设计了一个“一次性密码”类,它只能在栈上创建,并在使用后立即销毁。 当用户进行一次交易时,系统会生成一个一次性密码,一旦交易完成,密码就会“消失”,保证了密码的安全性。 … 在这些特殊场景中,我们需要按照需求设计是特殊类!

2 不能被拷贝的类

拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载 因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可 在C 98中我们会将拷贝构造函数和赋值重载函数进行私有化,这样外部就调用不到他们了!

代码语言:javascript复制
class A
{
	A()
	{}
	~A()
	{}
private:
	A(const A& a)
	{
	}
	A& operator=(const A& a)
	{
	}
private:
	int _a1;
};
  1. 必须设置成私有:如果只声明没有设置成private,如果在类外定义拷贝构造和赋值重载,就不能禁止拷贝了!
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不能防止成员函数内部拷贝了!

C 11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。

代码语言:javascript复制
class A
{
	A()
	{}
	~A()
	{}

	A(const A& a) = delete;
	A& operator=(const A& a) = delete;

private:
	int _a1;
};

这样就轻松的解决了问题!

3 只能在堆上创建对象的类

使用使用的类,就不能让用户,可以显式调用到构造函数,不然就在栈区创建了对象!我们需要写出一个接口,让用户可以获取到堆上的对象地址!这样就涉及到了先有鸡先有蛋的问题了,所以我们要将这个接口设置成静态成员函数,才能正常调用!

代码语言:javascript复制
class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
	{
	}
	HeapOnly(const HeapOnly& h) = delete;
	HeapOnly& operator=(const HeapOnly& a) = delete;
private:
	int _a1;
};

注意:除了处理构造函数,还要处理拷贝构造和赋值拷贝函数。因为拷贝的对象也是在栈上的!必须把所有可能的方法都要封死!

这样一个只能在堆上创建对象的类就写好了!

还有一个十分新奇的写法:将析构函数私有化!这样在栈上创建对象就会报错,迫使用户只能在堆上构造对象!我们可以通过一个显式的release方法来释放空间,来完善在堆上创建对象的操作!

4 只能在栈上创建对象的类

同上将构造函数私有化,然后设计静态方法创建对象返回.注意为了防止在堆上创建对象,我们需要将new delete操作符重载函数进行删除!这样就将在堆上创建彻底封死了!

代码语言:javascript复制
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
	// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
	// StackOnly obj = StackOnly::CreateObj();
	// StackOnly* ptr3 = new StackOnly(obj);
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

5 不能被继承的类

回顾一下继承的知识:从零开始认识继承

不能被继承的类很简单!C 98是将构造函数进行私有化,在C 11之后直接使用final关键字就可以了!

代码语言:javascript复制
class A final
{
	// ....
};

总结

C 的世界里,一些特别的类真是挺有讲究的。就像游戏里的独门魔法宝物,模拟豪华游艇的细节,还有银行系统里那些用完就丢的密码,每一个都是针对特定情况精心设计的。那些不能复制的类,就像是在说“我是特别的,不能随便复制”;只能在堆上或者栈上创建对象的类,就像是给内存管理上了把锁,保证了东西放在该放的地方;而不让继承的类,就像是定了规矩,让功能保持原汁原味。

这些设计不仅展示了C 的强大,也让我们看到程序员先辈们是如何巧妙地解决难题的。通过这几个特殊的类,我们的代码能力肯定有许多长进,C 的学习过程也变得更加丰富多彩了!

0 人点赞