@toc
1 特殊类
在实践中,常常会有一些比较有意思的特殊场景:
- 不能被拷贝的类 - 独一无二的魔法宝物:
在一个角色扮演游戏(RPG)。在这个游戏中,玩家可以通过工匠进行装备的拷贝,但是有一件神秘的魔法宝物,被称为“永恒之心”,它拥有赋予持有者永生的力量 ,是独一无二的,因此它不能被复制或克隆。
每当玩家尝试复制“永恒之心”时,游戏会抛出一个错误:“魔法宝物独一无二,无法复制!”。这确保了游戏中只有一个“永恒之心”,增加了它的神秘感和价值。 - 只能在堆上创建对象的类 - 豪华游艇
有一个豪华游艇模拟器。在这个模拟器中,游艇是一个复杂且昂贵的对象,它需要在堆上动态分配资源,比如内存来存储游艇的详细规格和状态。
每当玩家想要创建一艘新的游艇时,他们必须通过“造船厂”接口来请求,这实际上是在堆上分配了一个新的游艇对象。 - 只能在栈上创建对象的类 - 一次性密码
你正在为银行的安全系统编写代码。为了防止密码被复制或存储在不可靠的地方,你设计了一个“一次性密码”类,它只能在栈上创建,并在使用后立即销毁。
当用户进行一次交易时,系统会生成一个一次性密码,一旦交易完成,密码就会“消失”,保证了密码的安全性。
…
在这些特殊场景中,我们需要按照需求设计是特殊类!
2 不能被拷贝的类
拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载
因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可
在C++98中我们会将拷贝构造函数和赋值重载函数进行私有化,这样外部就调用不到他们了!
class A
{
A()
{}
~A()
{}
private:
A(const A& a)
{
}
A& operator=(const A& a)
{
}
private:
int _a1;
};
- 必须设置成私有:如果只声明没有设置成private,如果在类外定义拷贝构造和赋值重载,就不能禁止拷贝了!
- 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不能防止成员函数内部拷贝了!
C++11扩展delete
的用法,delete
除了释放new
申请的资源外,如果在默认成员函数后跟上=delete
,表示让编译器删除掉该默认成员函数。
class A
{
A()
{}
~A()
{}
A(const A& a) = delete;
A& operator=(const A& a) = delete;
private:
int _a1;
};
这样就轻松的解决了问题!
3 只能在堆上创建对象的类
使用使用的类,就不能让用户,可以显式调用到构造函数,不然就在栈区创建了对象!我们需要写出一个接口,让用户可以获取到堆上的对象地址!这样就涉及到了先有鸡先有蛋
的问题了,所以我们要将这个接口设置成静态成员函数,才能正常调用!
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
操作符重载函数进行删除!这样就将在堆上创建彻底封死了!
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
关键字就可以了!
class A final
{
// ....
};
总结
C++的世界里,一些特别的类真是挺有讲究的。就像游戏里的独门魔法宝物,模拟豪华游艇的细节,还有银行系统里那些用完就丢的密码,每一个都是针对特定情况精心设计的。那些不能复制的类,就像是在说“我是特别的,不能随便复制”;只能在堆上或者栈上创建对象的类,就像是给内存管理上了把锁,保证了东西放在该放的地方;而不让继承的类,就像是定了规矩,让功能保持原汁原味。
这些设计不仅展示了C++的强大,也让我们看到程序员先辈们是如何巧妙地解决难题的。通过这几个特殊的类,我们的代码能力肯定有许多长进,C++的学习过程也变得更加丰富多彩了!