文章

(1·设计模式分类、单例)

设计模式

为了让代码更加模块化,降低耦合度,从而更易复用,扩展,组合,提高安全性,简化使用

分类

1743247452290

创建型模式: 指导如何更好的创建对象

结构型模式:指导如何将类和对象封装为较大的结构

行为型模式:指导如何让对象和类之间高效的沟通

单例模式

作用:保证一个类只有一个实例,并提供一个全局访问点来访问这个实例

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
    private:
    // 私有的静态实例变量
    static Singleton* instance = new Singleton();
    // 私有的构造函数
    Singleton() = default;
public:
    // 公有的静态方法来获取实例
    static Singleton* getInstance() {
        return instance;
    }
};

展开

饿汉:实例在类加载时就被创建,线程安全,浪费资源(如果从未使用该实例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
    private:
    // 私有的静态实例变量
    static Singleton* instance;
    // 私有的构造函数
    Singleton() = default;
public:
    // 公有的静态方法来获取实例
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;//类外定义

展开

懒汉模式:第一次使用时才创建,非线程安全(数据竞争),节省资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx; // 互斥锁
    Singleton() = default;

public:
    static Singleton* getInstance() {
        if (instance == nullptr) { // 首次创建应加锁
            std::lock_guard<std::mutex> lock(mtx); // 加锁,阻塞其他线程,lock_guard保证作用域结束自动unlock
            if (instance == nullptr) { // 第二次检查(确保唯一)
                instance = new Singleton();
            }
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;// 类外初始化
std::mutex Singleton::mtx;

展开

双重检查锁:在懒汉模式基础上,保证线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
private:
    // 私有构造函数,防止外部直接创建对象
    Singleton() = default;

public:
    // 静态成员函数,用于获取单例实例的引用
    static Singleton& getInstance() { 
        // 静态局部变量,在首次调用该函数时初始化
        static Singleton instance; 
        // 返回单例实例的引用
        return instance;
    }
};

展开

双重检查锁优化:使用局部静态变量,c++中如果在静态局部变量正在初始化时,有其他线程同时进入该变量的声明语句,那么这些并发执行的线程会等待初始化完成,因此不会数据竞争

实现方式:

  • 私有的构造函数:防止外部代码创建类的实例,如果是public的,就可以创建多个类实例,这不符合目标
  • 私有的静态实例变量:静态保证此对象在类的唯一性质,私有保证外部不能直接访问
  • 公有的静态方法:静态保证可以不创建类实例就可以调用,公有保证外部可以直接访问

封装原则

你可能会疑惑上述的单例中为什么不直接暴露instance呢,而通过 public getInstance()去间接访问

封装原则:严格来说任何时候都不将成员变量设为 public,而通过两个接口读写

  • get() 获取成员变量
  • set() 设置成员变量

这样对外部隐藏内部实现细节,仅暴露必要的接口,提高安全性, 防止不正确的修改

本文由作者按照 CC BY 4.0 进行授权
本页总访问量