c++单例模式singleton实现

2017-04-30

1.singleton 单例模式:

设计一个类,我们只能生成该类的一个实例。eg. 地球就是一个singleton,据我们所知,宇宙中目前只有一个地球

2.实现思路:

设一个static private变量:instance。每次要生成该类实例时都检查一下这个instance是否为nullptr:是的话就新建一个实例赋给该instance,否的话就直接返回这个实例。

为什么instance需要是static的呢?

因为instance应该是一个类变量,它需要被整个class共享,static关键字在上一篇笔记中讲过:它可以限制变量的作用域,static变量在类中就会变成类变量,整个类的实例都能访问。如果是实例变量的话,那么每个实例都将拥有分别拥有各自的instance,就无从判断该类是不是已经有一个实例了。
eg. 宇宙在创造地球的时候,为了让人类珍惜赖以生存的家园,它决定整个宇宙中只能有一个地球,但是它要创造的星球很多,常常忘记已经创造了哪个,还没创造哪个。所以为了保证只有一个地球,它把地球类设计成了单例模式,并且给了这个类一张白纸。有一天它决定要创造地球了,于是就先让地球出示了那张纸,它发现:那张纸是空白的,这说明地球还没有被创造过,所以它就选好了一个位置(内存),并把这个位置记在了白纸上,然后它在这个位置上创造了地球。一个月后,忙碌的宇宙又想起了创造地球这件事,但是它不确定自己是否已经创造过地球了,于是它找到地球类,让它出示那张纸,发现那张纸上已经有一个地址了!地球类对宇宙说:“我已经有一个实例了,它就在这个纸上写的位置上,不信你去瞧瞧!”宇宙按照地址找到了地球,发现它就在那里,于是宇宙就决定进行下一步:改善地球环境。这当然都是后话了。

为什么instance需要是private的呢?

因为如果instance是public的,那么任意一个该类的实例都可以修改它的值,比如:创建完一个实例之后,编程人员不小心把instance又置为了nullptr,那么下次就还能够创建新的实例,这是不符合单例模式宗旨的。

3.实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
using namespace std;
class Singleton{
private:
static Singleton* instance;
int no=0;
Singleton(){no++; cout<<"Construct!"<<endl;}
~Singleton(){cout<<"Delete!"<<endl;}
public:
static Singleton* getInstance(){
if(instance == nullptr){
instance = new Singleton();
}
return instance;
}
void Print(){cout<<no<<endl;}
};
// 初始化instance
Singleton* Singleton::instance = nullptr;
int main(){
Singleton* single1 = Singleton::getInstance();
Singleton* single2 = Singleton::getInstance();
cout<<"single1 addr:"<<single1<<endl;
cout<<"single2 addr:"<<single2<<endl;
if(single1 == single2){
cout<<"same"<<endl;
}
single1->Print();
single2->Print();
return 0;
}

运行结果图:
运行结果图
我说说我有疑问的的几点:

  1. instance变量为什么是Singleton*类型,不能是Singleton或者Singleton&类型?
    答:如果是Singleton类型:那就是递归结构了,自身包含自身,没法做到只有一个实例;
    如果是Singleton&引用类型,对于引用类型的成员, 只能通过初始化表达式进行初始化。Singleton(Singleton &s):instance(s) { },这还是递归结构嘛,先有鸡还是先有蛋的问题。
  2. instance变量如何初始化呢?
    答:在类的外面,初始化为nullptr。不能在里面初始化,否则zongshinullptr,总能创建新的实例。
  3. 构造函数和析构函数为什么是private的?
    答:构造函数是private:构造函数就是生成实例的,如果public则其他实例可以被任意生成;
    析构函数在实例被销毁时自动调用,析构函数只能有一个, 不能被重载。析构函数可以被显式的调用, 以释放对象中动态申请的内存。如果析构函数是public,则实例将能够被随意销毁,instance变量如果没有了,下次就不能够再生成了。
  4. 为什么~Singleton()析构函数没有被调用,而构造函数被调用了?
    答:因为new Singleton()所以构造函数被调用了,以instance地址为起点,开辟了一块空间,给instance变量进行了初始化;
    因为该实例存在于静态存储区,static的变量要程序关闭才会释放。
  5. 如何验证这个类真的只有一个实例?
    答:在本例中,==用来判断两个指针是否相同,即判断两个实例是否在同一个地址。