单例模式
- 一,设计一个类不能被拷贝
- 二,设计一个类,只能在堆上创建对象
- 三,设计一个类,只能在栈上创建对象
- 四,设计一个类,不能被继承
- 五,设计一个类,只能创建一个对象(单例模式)
- 5.1 饿汉模式
- 5.2 懒汉模式
- 六,总结
上一节我们讲解了智能指针,到这里C++的有难度的部分已经过去了,现在我们来讲一些其他的知识。
在开发的实际场景中,有很多需求比如一个类不能被拷贝或者规定了其创建对象的地方等。这一节我们就来讲解一些常见的特殊类的设计,当然也有单例模式。废话不多说我们直接开始。
一,设计一个类不能被拷贝
在设计之前我们可以思考一下,拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载
因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
在C++98中,我们可以将拷贝构造函数与赋值运算符重载只声明不定义并且将访问权限设置为私有。
class CopyBan
{// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
设置为私有是因为如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
能禁止拷贝了
不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
在C++11中,又扩展了delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上 =delete
,表示让编译器删除掉该默认成员函数
class CopyBan
{// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;//...
};
二,设计一个类,只能在堆上创建对象
这里有两种方案
方案一是:析构函数私有化
class HeapOnly {public://...private:~HeapOnly() {}};int main() {HeapOnly heap;//static HeapOnly hheap;//静态也不能//所以只能new一个HeapOnly* ptr = new HeapOnly;return 0;
}
像上面代码中的HeapOnly heap是不能定义的,因为析构不了。包括静态的也一样。所以只能在new一个,而new的对象就创建在堆上。
那么如何析构new出来的对象呢?
这里可以单独写一个公有的析构:
class HeapOnly {public://...void Destory() {delete this;}private:~HeapOnly() {}};int main() {HeapOnly* ptr = new HeapOnly;ptr->Destory();//调用公有的去析构return 0;
}
方案二是:构造函数私有化
class HeapOnly {
public://解决办法是写一个公有的构造static HeapOnly* create() {//加上static,来解决先有鸡还是先有蛋的问题return new HeapOnly;}private:HeapOnly() {cout << "HeapOnly()" << endl;}};int main() {//HeapOnly hp1;//static HeapOnly hp2;//HeapOnly* hp3 = new HeapOnly;HeapOnly* ptr = HeapOnly::create();return 0;
}
构造函数私有后,之前的三种常见对象的方式都不能创建对象,所以还是一样,写一个公有的构造函数
注意:这里的create函数加上static才可以调用,否则会出现先有鸡还是先有蛋的问题
但是还有一个小问题,那就是如果拷贝构造的话,那么拷贝构造的这个对象还是在栈上创建的,不是堆。
解决办法就是直接在拷贝函数后面加上 delete🈲掉即可:
HeapOnly(const HeapOnly& hp) = delete;
三,设计一个类,只能在栈上创建对象
这里设计时就不能再将析构函数私有了,但是构造函数私有话还是可以的,构造函数私有是为了不让对象可以被随便创建,私有后再写一个公有的构造,使其只能在栈上创建。
class StackOnly {
public:static StackOnly create() {StackOnly obj;return obj;//返回一个临时拷贝}private:StackOnly() {cout << "StackOnly()" << endl;}
};int main() {//StackOnly st;StackOnly st = StackOnly::create();//StackOnly* ptr = new StackOnly;//不能创建对象return 0;
}
但是这里还是,如果拷贝的话还是创建在了堆上。
StackOnly* ptr = new StackOnly(st);
这里不能简单的将拷贝构造禁掉,那样的话公有的构造将不能返回
这里的解决办法是禁掉自己的operator new,因为new的时候,new是全局的,如果一个类重载了new,那么new时就会调用这个类自己重载的。禁掉自己的new后,再new时会调用这个重载的版本,但是这个又被delete了,所以new不了
void* operator new(size_t size) = delete;
四,设计一个类,不能被继承
这个我们在继承部分讲过,C++11新增加的关键字final,直接在类声明时后面加上final
就可以了
class A final
{// ....
};
或者将构造函数私有
。
C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
五,设计一个类,只能创建一个对象(单例模式)
这里就牵扯到设计模式了,设计模式一共有23种,我们只要知道几种就好。这里我们讲一下单例模式。分为两种:
5.1 饿汉模式
饿汉模式就是在main函数启动时创建好示例对象。
优点:简单
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定
5.2 懒汉模式
懒汉模式就是在调用这个对象时才创建,就解决了上面的两种问题
但是牵扯到了线程安全
问题
对于这两种模式的代码示例,大家可以去我的gitee仓库进行参考: 特殊类设计
六,总结
特殊类的设计我们只要知道常用的就可以了,单例模式还是比较的常用,所以要好好理解一下。下一节我们会带来C++的收尾内容,到这里我们C++的主要内容也快结束了,但是继续深挖C++的话还是有很多东西要学,所以希望大家可以持续学习,我也会带来更多的内容去分享。