【C++ | 设计模式】单例模式的详解与实现

1.简述

单例模式(Singleton Pattern)是设计模式中最简单的形式之一,其目的是使得类的一个对象成为系统中的唯一实例

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。

2.UML 结构图

3.要点

单例模式的要点有三个:

  • 单例类有且仅有一个实例

  • 单例类必须自行创建自己的唯一实例

  • 单例类必须给所有其他对象提供这一实例

从具体实现角度来说,可分为以下三点:

  • 提供一个 private 构造函数(防止外部调用而构造类的实例)

  • 提供一个该类的 static private 对象

  • 提供一个 static public 函数,用于创建或获取其本身的静态私有对象(例如:GetInstance())

除此之外,还有一些关键点(需要多加注意,很容易忽视):

  • 线程安全(双检锁 - DCL,即:double-checked locking)

  • 资源释放

4.单例模式的实现

4.1 单例 - 懒汉式(非线程安全)

在这种实现中,实例在第一次调用 GetInstance 时创建。由于没有线程安全机制,这种方式不适用于多线程环境。

#include <iostream>

class Singleton {
public:
    static Singleton* GetInstance();

private:
    Singleton() {}  // 构造函数(私有)
    static Singleton* p;  // 指向单例对象的指针
};

// 初始化静态成员变量
Singleton* Singleton::p = nullptr;

// 获取单例实例
Singleton* Singleton::GetInstance() {
    if (p == nullptr) {
        p = new Singleton();
    }
    return p;
}

int main() {
    Singleton* instance1 = Singleton::GetInstance();
    Singleton* instance2 = Singleton::GetInstance();
    std::cout << (instance1 == instance2) << std::endl;  // 输出1,两个实例是相同的
    return 0;
}

懒汉式的特点:

  • Lazy 初始化

  • 非多线程安全

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁(在“线程安全”部分分享如何加锁)才能保证单例,但加锁会影响效率。  

4.2 单例 - 饿汉式(线程安全)

在这种实现中,单例对象在类加载时就被创建,确保了线程安全,但可能导致启动时的资源浪费。

#include <iostream>

class Singleton {
public:
    static Singleton* GetInstance();

private:
    Singleton() {}  // 构造函数(私有)
    static Singleton* p;  // 指向单例对象的指针
};

// 初始化静态成员变量
Singleton* Singleton::p = new Singleton();  // 在类加载时就创建实例

// 获取单例实例
Singleton* Singleton::GetInstance() {
    return p;
}

int main() {
    Singleton* instance1 = Singleton::GetInstance();
    Singleton* instance2 = Singleton::GetInstance();
    std::cout << (instance1 == instance2) << std::endl;  // 输出1,两个实例是相同的
    return 0;
}

饿汉式的特点:

  • 非 Lazy 初始化

  • 多线程安全

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

4.3 单例 - 懒汉式(双重检查锁,线程安全)

这种实现使用双重检查锁定(DCL)机制来确保线程安全,并且仅在需要时才创建单例对象。

#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton* GetInstance();

private:
    Singleton() {}  // 构造函数(私有)
    static Singleton* p;  // 指向单例对象的指针
    static std::mutex m_mutex;  // 互斥锁,用于线程安全
};

// 初始化静态成员变量
Singleton* Singleton::p = nullptr;
std::mutex Singleton::m_mutex;

// 获取单例实例
Singleton* Singleton::GetInstance() {
    if (p == nullptr) {  // 第一次检查
        std::lock_guard<std::mutex> lock(m_mutex);  // 锁定互斥量
        if (p == nullptr) {  // 第二次检查
            p = new Singleton();
        }
    }
    return p;
}

int main() {
    Singleton* instance1 = Singleton::GetInstance();
    Singleton* instance2 = Singleton::GetInstance();
    std::cout << (instance1 == instance2) << std::endl;  // 输出1,两个实例是相同的
    return 0;
}

需要进行两次检验的原因

第一次判断是为了验证是否创建对象,第二次判断是为了避免重复创建单例,因为可能会存在多个线程通过了第一次判断在等待锁,进 而创建新的实例对象。

  • 当A与B同时调用GetInstance时,判断第一个if都为空,这时A拿到锁,进行第二层if判断,条件成立new了一个对象;

  • B在外层等待,A创建完成,释放锁,B拿到锁,进行第二层if判断,条件不成立,结束释放锁;

  • C调用GetInstance时第一层判断不成立,直接拿到singleton对象返回,避免进入锁,减少性能开销。

4.4单例 - 局部静态变量实现

#include <iostream>

class Singleton {
public:
    // 获取单例实例
    static Singleton& GetInstance() {
        static Singleton instance;  // 局部静态变量
        return instance;
    }

    // 禁止拷贝构造函数和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 示例方法
    void ShowMessage() const {
        std::cout << "Singleton instance address: " << this << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
};

int main() {
    Singleton& instance1 = Singleton::GetInstance();
    Singleton& instance2 = Singleton::GetInstance();
    
    instance1.ShowMessage();  // 输出单例对象的地址
    instance2.ShowMessage();  // 输出单例对象的地址

    // 验证两个实例是相同的
    std::cout << "Are both instances the same? " << ( &instance1 == &instance2 ) << std::endl;  // 输出1,两个实例是相同的

    return 0;
}

在这种实现中,单例对象是静态局部变量,只有在首次调用 GetInstance 时才会被创建。由于局部静态变量的初始化是线程安全的,这种方式不需要额外的锁机制来确保线程安全。

5.单例模式的资源释放

5.1使用局部静态变量(自动释放)

#include <iostream>

class Singleton {
public:
    static Singleton& GetInstance() {
        static Singleton instance;  // 局部静态变量
        return instance;
    }

    // 禁止拷贝构造函数和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 示例方法
    void ShowMessage() const {
        std::cout << "Singleton instance address: " << this << std::endl;
    }

private:
    Singleton() {
        std::cout << "Singleton created" << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton destroyed" << std::endl;
    }
};

int main() {
    Singleton& instance1 = Singleton::GetInstance();
    instance1.ShowMessage();
    return 0;
}

5.2显式释放资源(使用懒汉式)

要手动释放资源,添加一个 static 函数,编写需要释放资源的代码,然后在需要释放的时候,手动调用该函数。

#include <iostream>

class Singleton {
public:
    static Singleton* GetInstance() {
        if (p == nullptr) {
            p = new Singleton();
        }
        return p;
    }

    static void DestroyInstance() {
        if(p != nullpter){
            delete p;
            p = nullptr;
        }
    }

    // 禁止拷贝构造函数和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 示例方法
    void ShowMessage() const {
        std::cout << "Singleton instance address: " << this << std::endl;
    }

private:
    Singleton() {
        std::cout << "Singleton created" << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton destroyed" << std::endl;
    }

    static Singleton* p;  // 指向单例对象的指针
};

// 初始化静态成员变量
Singleton* Singleton::p = nullptr;

int main() {
    Singleton* instance1 = Singleton::GetInstance();
    instance1->ShowMessage();

    // 显式释放单例对象
    Singleton::DestroyInstance();
    return 0;
}

5.3 使用嵌套类进行资源释放

#include <iostream>

using namespace std;

class Singleton
{
public:
    // 获取单例实例
    static Singleton* GetInstance();

private:
    Singleton() {
        cout << "Singleton created" << endl;
    }  // 构造函数(私有)

    // 嵌套类,用于管理资源释放
    class GC
    {
    public:
        ~GC()
        {
            // 在析构函数中销毁单例对象
            if (m_pSingleton != nullptr) {
                cout << "Here destroy the m_pSingleton..." << endl;
                delete m_pSingleton;
                m_pSingleton = nullptr;
            }
        }
        static GC gc;  // 静态成员,用于在程序结束时自动销毁单例对象
    };

    static Singleton* m_pSingleton;  // 指向单例对象的指针
};

// 初始化静态成员
Singleton* Singleton::m_pSingleton = nullptr;
Singleton::GC Singleton::GC::gc;  // 实例化 GC 类的静态成员

// 获取单例实例
Singleton* Singleton::GetInstance()
{
    if (m_pSingleton == nullptr)
    {
        m_pSingleton = new Singleton();
    }
    return m_pSingleton;
}

int main()
{
    // 获取并使用单例实例
    Singleton* instance1 = Singleton::GetInstance();
    Singleton* instance2 = Singleton::GetInstance();

    cout << "Are both instances the same? " << (instance1 == instance2) << endl;  // 输出1,两个实例是相同的

    // 当程序结束时,`GC` 的析构函数会被调用,自动释放单例对象的资源
    return 0;
}

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值