单例懒汉模式

话不多说,直接上代码

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
36
37
38
39
40
41
42
43
44
#include <mutex>

class Singleton{
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;


//双重检查锁定模式
//这样做的目的是,如果实例已经被创建,可以避免每次加锁解锁的操作,提高性能
//但看文字可能不好理解,这里给出不是双重检查锁定模式就好理解了

/*
static Singleton& getInstance(){
std::lock_guard<std::mutex> lock(mutex_);
if(instance_ == nullptr){
instance_ = new Singleton();
}
return *instance_;
}
这种写法性能非常低下,因为每次调用instance()都会加锁释放锁,而这个步骤只有在第一次new Singleton()才是有必要的。
只要p被创建出来了,不管多少线程同时访问,使用if (p == nullptr) 进行判断都是足够的(只是读操作,不需要加锁),没有线程安全问题,
加了锁之后反而存在性能问题。
*/

static Singleton& getInstance(){
if(instance_ == nullptr){
std::lock_guard<std::mutex> lock(mutex_);
if(instance_ == nullptr){
instance_ = new Singleton();
}
}
return *instance_;
}
private:
Singleton() {}
~Singleton() {}

static std::mutex mutex_;
static Singleton *instance_;
};

std::mutex Singleton::mutex_;
Singleton* Singleton::instance_ = nullptr;

问题

构造析构为什么要私有

解答:

在单例模式中,构造函数和析构函数被设置为私有的原因是为了确保满足单例模式的核心要求:系统中某个类只能存在一个实例。

通过将构造函数和析构函数设为私有,我们可以确保以下几点:

  1. 外部无法实例化:由于构造函数是私有的,这意味着不能在类的外部直接创建该类的实例。这确保了实例的创建只能通过单例类提供的某些特定方法(如getInstance)来完成,从而控制实例的数量。

  2. 禁止复制:单例模式要确保只有一个实例存在,所以我们不希望该类的对象被复制。将构造函数设为私有可以防止复制构造,但为了进一步确保不被复制,我们通常还需要禁止拷贝构造函数和拷贝赋值操作符(通过= delete)。

  3. 外部无法销毁:将析构函数设为私有可以确保外部代码无法直接删除单例对象。通常,单例对象在程序结束时自动销毁,或者单例类提供了一个专门的方法来手动销毁它。

  4. 继承控制:由于构造函数和析构函数是私有的,这也意味着这个类不能被继承(因为派生类的构造函数需要调用基类的构造函数)。

综上所述,将构造函数和析构函数设为私有是为了确保满足单例模式的设计原则,即系统中该类只有一个实例,并提供对该实例的全局访问点。