Post

使用单例模式编写工具类范式

使用单例模式编写工具类范式

单例模式是最常用的设计模式之一,当类对象只需要一个实例、或者重复创建的无意义且消耗资源的时候需要,通常用于全局唯一工具类的编写,比如:日志、配置、uuid生成器等。单例类需要提供一个全局访问的入口 / global access point。下面仅讨论cpp下单例类的实现。

cpp中单例类的编写范式有两种:Meyer’s Singleton 、 Lazy Singleton 和 Eager Singleton。

全局静态变量初始化依赖问题

单例模式需要解决的问题就是如何保证对象只被创建一次!假设现在有两份需要单独编译的源文件 / 编译单元,每个文件都包含着全局静态变量。如果一个翻译单元中的非局部静态变量对象的初始化要使用另一个翻译单元中的非局部静态变量对象,那么这两个对象的初始化顺序是不确定的。这就是静态初始化顺序问题(Static Initialization Order Fiasco)。

解决方案就是把每个非局部静态对象移动到自己的函数中,并在其中声明其static。这些函数返回对它们包含的对象的引用。然后调用函数而不是应用对象。C++保证在调用函数期间第一次遇到对象的定义的时候初始化局部静态对象。

Meyer’s Singleton

Meyer’s Singleton 出自 Effective C++ 中的Item 4;Make sure that objects are initialized before they’re used。Meyer’s Singleton 是一种在对象第一次使用时初始化的单例模式,其实现原理是利用了C++的静态局部变量的特性,静态局部变量在程序运行时初始化,且只初始化一次。

特点:

  • 仅当程序第一次执行到GetInstance函数时,执行instance对象的初始化;
  • 在CPP 11之后,被 static 修饰的变量是可以保证线程安全的。

代码示范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T>
class Singleton 
{
public:
    static T& GetInstance()
    {
        static T instance;
        return instance;
    }

    Singelton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    void operator=(const Singleton&&) = delete;
protected:
    Singleton() = default;
    virtual ~Singleton() = default;
}

Lazy Singleton

Lazy Singleton的实现比较经典,其实现原理是在GetInstance函数中判断instance是否为空,为空则创建对象,否则直接返回对象。

代码示范:

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
template<typename T, bool is_thread_safe = true>
class LazySingleton
{
private:
    static unique_ptr<T> t_;
    static mutex mtx_;

public:
    static T& GetInstance()
    {
        if (is_thread_safe == false)
        {
            if (t_ == nullptr)
                t_ = unique_ptr<T>(new T);
            return *t_;
        }

        if (t_ == nullptr)
        {
            unique_lock<mutex> unique_locker(mtx_);
            if (t_ == nullptr)
                t_ = unique_ptr<T>(new T);
            return *t_;
        }

    }

    LazySingleton(T&&) = delete;
    LazySingleton(const T&) = delete;
    void operator= (const T&) = delete;

protected:
    LazySingleton() = default;
    virtual ~LazySingleton() = default;
};

template<typename T, bool is_thread_safe>
unique_ptr<T> LazySingleton<T, is_thread_safe>::t_;

template<typename T, bool is_thread_safe>
mutex LazySingleton<T, is_thread_safe>::mtx_;

Eager Singleton

Eager Singleton 是一种在程序启动时就初始化的单例模式,其实现原理是在程序启动时就初始化对象。

代码示范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template<typename T>
class EagerSingleton
{
private:
    static T* t_;

public:
    static T& GetInstance()
    {
        return *t_;
    }

    EagerSingleton(T&&) = delete;
    EagerSingleton(const T&) = delete;
    void operator= (const T&) = delete;

protected:
    EagerSingleton() = default;
    virtual ~EagerSingleton() = default;
};

template<typename T>
T* EagerSingleton<T>::t_ = new (std::nothrow) T;

但是存在问题:

  • 不被使用的单例对象也会被初始化;
  • 存在static initialization order fiasco问题。
This post is licensed under CC BY 4.0 by the author.