Chapter3—单例模式

发布于:2025-09-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

单例模式

1 概念

单例模式是指软件设计中当前系统中只有一个实例对象,同时提供集中、统一的访问接口,以使系统行为保持协调一致。

2 意图(Intent)

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

3 动机(Motivate)

在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?这应该是类设计者的责任,而不是类使用者的责任。

4 类图结构

在这里插入图片描述

5 角色定义

  • Singleton(单例类):包含一个自己的类实例的属性,并把构造方法用private关键字隐藏起来,只对外提供GetInstance()方法以获得这个单例对象。

6 程序示例

饿汉式:在调用之初就创建

/**
 * @brief 饿汉式单例类实现
 */ 
class Singleton
{
private:
    Singleton() = default;

public:
    ~Singleton() = default;

    Singleton(const Singleton& src) = delete;
    Singleton(Singleton&& src) = delete;
    Singleton& operator=(const Singleton& src) = delete;
    Singleton& operator=(Singleton&& src) = delete;

public:
    void Show();

public:
    static Singleton* GetInstance();
    static void DestroyInstance();

private:
    static Singleton* m_spInstance;
};

void Singleton::Show()
{
    qDebug() << "我是一个单例类";
}

Singleton *Singleton::GetInstance()
{
    return m_spInstance;
}

void Singleton::DestroyInstance()
{
    if (nullptr != m_spInstance)
    {
        delete m_spInstance;
        m_spInstance = nullptr;
    }
}

Singleton* Singleton::m_spInstance = new Singleton();			// 静态创建,系统初始化的时候自动就创建了

/**
 * @brief 饿汉式单例测试函数
 */ 
void TestSingleton()
{
    Singleton::GetInstance()->Show();
    Singleton::DestroyInstance();
}

懒汉式:在调用之后再创建

/**
 * @brief 懒汉式单例类实现
 */
class Singleton
{
private:
    Singleton() = default;

public:
    ~Singleton() = default;

    Singleton(const Singleton& src) = delete;
    Singleton(Singleton&& src) = delete;
    Singleton& operator=(const Singleton& src) = delete;
    Singleton& operator=(Singleton&& src) = delete;

public:
    void Show();

public:
    static Singleton* GetInstance();
    static void DestroyInstance();

private:
    static Singleton* m_spInstance;
};

void Singleton::Show()
{
    qDebug() << "我是一个单例类";
}

Singleton *Singleton::GetInstance()
{
    if (nullptr == m_spInstance)
        m_spInstance = new Singleton();         // 在第一次调用的时候才创建

    return m_spInstance;
}

void Singleton::DestroyInstance()
{
    if (nullptr != m_spInstance)
    {
        delete m_spInstance;
        m_spInstance = nullptr;
    }
}

Singleton* Singleton::m_spInstance = nullptr;   // 初始化为 nullptr

/**
 * @brief 懒汉式单例测试函数
 */ 
void TestSingleton()
{
    Singleton::GetInstance()->Show();
    Singleton::DestroyInstance();
}

局部静态变量方式

此方式代码简洁且高效,这种好处就是不用管理内存,局部静态变量会自动析构,对于饿汉式和懒汉式都需要去手动释放下内存。

/**
 * @brief 局部静态变量方式的单例类实现
 */
class Singleton
{
private:
    Singleton() = default;

public:
    ~Singleton() = default;

    Singleton(const Singleton& src) = delete;
    Singleton(Singleton&& src) = delete;
    Singleton& operator=(const Singleton& src) = delete;
    Singleton& operator=(Singleton&& src) = delete;

public:
    void Show();

public:
    static Singleton* GetInstance();
};

void Singleton::Show()
{
    qDebug() << "我是一个单例类";
}

Singleton *Singleton::GetInstance()
{
    static Singleton instance;
    return &instance;
}

/**
 * @brief 单例测试函数,不需要主动调用释放内存了
 */ 
void TestSingleton()
{
    Singleton::GetInstance()->Show();
}

懒汉式单例线程安全

#include <mutex>

using std::string;
using std::vector;
using std::mutex;

/**
 * @brief 懒汉式单例类实现
 */
class Singleton
{
private:
    Singleton() = default;

public:
    ~Singleton() = default;

    Singleton(const Singleton& src) = delete;
    Singleton(Singleton&& src) = delete;
    Singleton& operator=(const Singleton& src) = delete;
    Singleton& operator=(Singleton&& src) = delete;

public:
    void Show();

public:
    static Singleton* GetInstance();
    static void DestroyInstance();

private:
    static Singleton* m_spInstance;
    static std::mutex m_sMutex;             // 增加锁
};

void Singleton::Show()
{
    qDebug() << "我是一个单例类";
}

Singleton *Singleton::GetInstance()
{
    // 双重检查锁定是一种高效的线程安全实现方式,通过减少锁的使用频率来提升性能。
    // 如果直接加在最外面,每调用一次就得进行一次加锁,这样效率十分低下
    if (nullptr == m_spInstance)
    {
        std::lock_guard<std::mutex> lockGuard(m_sMutex);

        if (nullptr == m_spInstance)
            m_spInstance = new Singleton();
    }
    return m_spInstance;
}

void Singleton::DestroyInstance()
{
    // 析构函数不会经常调用,所以直接在最外层加锁即可
    std::lock_guard<std::mutex> lockGuard(m_sMutex);

    if (nullptr != m_spInstance)
    {
        delete m_spInstance;
        m_spInstance = nullptr;
    }
}

Singleton* Singleton::m_spInstance = nullptr;   // 初始化为 nullptr

std::mutex Singleton::m_sMutex = std::mutex();

/**
 * @brief 单例测试函数
 */
void TestSingleton()
{
    Singleton::GetInstance()->Show();
    Singleton::DestroyInstance();
}

模板类单例

单例模板类:

#include <mutex>

using std::string;
using std::vector;
using std::mutex;

template <typename T>
class Singleton
{
public:
    template <typename... Args>
    static void InitInstance(Args... args);
    static T* GetInstance();
    static void DestroyInstance();

private:
    static T* sm_pInstance;
    static mutex sm_Mutex;

private:
    Singleton() = default;
    ~Singleton() = default;

    Singleton(const Singleton& src) = delete;
    Singleton(Singleton&& src) = delete;
    Singleton& operator=(const Singleton& src) = delete;
    Singleton& operator=(Singleton&& src) = delete;
};

template <typename T>
T* Singleton<T>::sm_pInstance = nullptr;

template <typename T>
mutex Singleton<T>::sm_Mutex = mutex();

template<typename T>
template<typename... Args>
void Singleton<T>::InitInstance(Args... args)
{
    if (nullptr == sm_pInstance)
    {
        std::lock_guard<mutex> lockGuard(sm_Mutex);

        if (nullptr == sm_pInstance)
            sm_pInstance = new T(std::forward<Args>(args)...);      // 参数完美转发
    }
}

template<typename T>
T *Singleton<T>::GetInstance()
{
    return sm_pInstance;
}

template<typename T>
void Singleton<T>::DestroyInstance()
{
    std::lock_guard<mutex> lockGuard(sm_Mutex);

    if (nullptr != sm_pInstance)
    {
        delete sm_pInstance;
        sm_pInstance = nullptr;
    }
}

一般类:

class Person
{
public:
    Person(const string& strName, int nAge);
    ~Person();

public:
    void Show();

private:
    string m_strName;
    int m_nAge;
};

Person::Person(const string &strName, int nAge):
    m_strName(strName), m_nAge(nAge)
{
    qDebug() << "Create Perosn";
}

Person::~Person()
{
    qDebug() << "Release Perosn";
}

void Person::Show()
{
    qDebug() << "[Name =" << m_strName.c_str() << ", Age =" << m_nAge << "]";
}

采用模板单例类框架生成一个一般类的单例类:

void TestSingleton()
{
    Singleton<Person>::InitInstance("ZhangSan", 23);
    Singleton<Person>::GetInstance()->Show();
    Singleton<Person>::DestroyInstance();
}

7 思考小结

当系统中某个类型仅需要一个对象时候可以考虑使用单例模式。在C++开发中,我们也通常建议使用局部静态变量的方式,也就是第三种方式。单例模式一直是一个有争议的设计模式,因为其本身不存在抽象的概念,没有虚函数(接口),系统难以进行扩展,所以有些人甚至建议将单例模式剔除出设计模式的范畴。

8 附录

如何实现饿汉式单例的内存自动释放?思路有点类似于C++的RAII,我们拿一个身份证类的单例来实现,程序代码如下

class IdentityCard
{
private:
    struct AutoRelease              // 内部类,负责单例对象自动析构,在析构函数中释放单例对象
    {
        AutoRelease()
        {

        }

        ~AutoRelease()
        {
            if (nullptr != sm_pInstance)
            {
                delete sm_pInstance;
                sm_pInstance = nullptr;
            }
        }
    };

private:
    IdentityCard();

public:
    ~IdentityCard();

public:
    IdentityCard(const IdentityCard& src) = delete;
    IdentityCard(IdentityCard&& src) = delete;
    IdentityCard& operator=(const IdentityCard& src) = delete;
    IdentityCard& operator=(IdentityCard&& src) = delete;

public:
    static IdentityCard* GetInstance();

private:
    static IdentityCard* sm_pInstance;
    static AutoRelease sm_AutoRelease;              // 声明为静态成员变量,在程序结束时会自动调用析构函数,从而释放掉单例的对象

public:
    void SetNumber(const string& strNumber);
    string GetNumber();


    string m_strNumber;
};

IdentityCard* IdentityCard::sm_pInstance = new IdentityCard();

IdentityCard::AutoRelease IdentityCard::sm_AutoRelease = IdentityCard::AutoRelease();

IdentityCard::IdentityCard()
{
    qDebug() << "Create IdentityCard!";
}

IdentityCard::~IdentityCard()
{
    qDebug() << "Release IdentityCard!";
}

IdentityCard *IdentityCard::GetInstance()
{
    return sm_pInstance;
}

void IdentityCard::SetNumber(const string &strNumber)
{
    m_strNumber = strNumber;
}

string IdentityCard::GetNumber()
{
    return m_strNumber;
}


自动释放内存的单例测试函数

void TestIdentityCard()
{
    IdentityCard::GetInstance()->SetNumber("ACB");
    qDebug() << IdentityCard::GetInstance()->GetNumber().c_str();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    TestIdentityCard();
    return 0;
}

输出结果

Create IdentityCard!
ACB
Release IdentityCard!

内存的释放过程如下图
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到