C++ 单例模式、饿汉和懒汉

本文详细介绍了单例模式的概念、作用及创建方法,包括通过私有化构造函数和静态成员变量确保对象唯一性。分别展示了饿汉式和懒汉式的C++实现,饿汉式在程序启动时即创建单例,而懒汉式则在首次调用时创建。此外,还讨论了线程安全问题,以及单例对象的销毁时机。
摘要由CSDN通过智能技术生成

1. 为什么需要单例模式

在这里插入图片描述

2. 什么是单例模式

在这里插入图片描述

3. 如何创建单例模式

在这里插入图片描述

3.1 禁止类外部创建对象(私有化构造函数)

在这里插入图片描述

  • 单例模式中对象的唯一性是由对象的实现者保证的。通过私有化构造函数 和 拷贝构造函数(因为缺省的构造函数都是公有的)来禁止在类的外部创建对象。
  • 其中,拷贝构造函数声明就好,不需要写具体实现,它是有缺省实现的。声明它只是为了防止拷贝构造函数被调用,并不会真的去使用它。

3.2 类自己维护其唯一对象(私有静态成员变量)

在这里插入图片描述

  • 因为如果不加 static, 编译器编译时是要确定类的大小的,类的大小又直接取决于所有类成员的大小,但这里的类成员 instance 的大小又取决于 定义它的类 Singleton, 这样就相应依赖了。语法上是不被允许的。
  • 而加了 statics_instance 就可以理解是一个被限定在类Singleton中使用的Singleton类 类型的 全局变量。而类Singleton 创建对象s_instance时,不会用到 s_instance 这个全局变量。类Singleton的大小就被确定下来了。
  • 但是… 如果 写成 Singleton* s_instance; 又不会报错, 那这又是为什么?

3.3 提供访问单例对象的方法(静态成员函数)

在这里插入图片描述

  • 获取单例对象的函数,必须是一个静态成员函数
    • 虽然普通成员函数也能访问到静态成员变量,但 普通成员函数 的调用前提是得先实例化一个对象,加上类内部的静态成员对象,这就至少有两个对象了,就不能叫单例了。
    • 并且单例构造函数是私有的,也实例化不了对象。

4. 饿汉式和懒汉式

  • 单例模式具体的实现方式分为饿汉式和懒汉式。

4.1 饿汉式

在这里插入图片描述

  • 比如计时器,需要频繁使用,但是所占用内存由少,就很适合懒汉式。
#include <iostream>
#include <cstring>
using namespace std;

class Singelton{ // 饿汉式
public:
    // 3. 通过静态成员函数获取单例对象
    // 返回对象本身是要加别名的
    static Singelton& getInstance(void){
        return s_instance;
    }
private:
    // 1. 私有化构造函数
    Singelton(int data = 0):m_data(data){
        cout << "单例对象被创建" << endl;
    }
    Singelton(const Singelton&){}
    // 2. 通过静态成员变量,表示类唯一的对象(这里只是声明, 禁止类内初始化非常量静态成员)
    static Singelton s_instance; 
    int m_data;
};

// 在外部对类唯一的对象 Singelton::s_instance 进行初始化.
// 第一个  Singelton 表示类型, 第二表示作用域
Singelton Singelton::s_instance = 1234; //"单例对象被创建" 

int main(void){
    cout << "main函数开始执行" << endl;
    // Singelton s; // error, 因为构造函数私有化了
    // Singelton* ps = new Singelton; // error
    
    // 获取单例对象对象, 要通过引用来接收, 不然就成拷贝构造了(注意)
    // s1, s2 都是 Singelton::s_instance 的别名,表示的都是同一个对象
    Singelton& s1 = Singelton::getInstance();
    Singelton& s2 = Singelton::getInstance();
    cout << &s1 << endl;
    cout << &s2 << endl;
    return 0;
}

$ ./a.out 
# 单例对象被创建在main函数创建之前,加载进程时就完成创建
单例对象被创建
main函数开始执行
0x601194
0x601194

4.2 懒汉式

在这里插入图片描述

  • 比如任务管理器,用的不频繁,占用内存又多,适合用懒汉式。
#include <iostream>
#include <cstring>
using namespace std;

class Singelton{ // 懒汉式
public:
    // 3. 通过静态成员函数动态创建单例对象 并 获取单例对象
    static Singelton& getInstance(void){
        // 第一次调用get方法,s_instance应该是NULL
        if(s_instance == NULL){ // 多线程时,可能两个线程同时判断s_instance == NULL都为真,会new两次。所需要这里需要加mutex锁
            // 创建堆区对象,通过指针 s_instance 来维护
            s_instance = new Singelton(1234);
        }
        // 计数加1
        s_count++;
        // 第二次再调用get,返回的是第一次调用get创建的对象
        // 这样就满足的单例模式下,对象唯一的要求
        return *s_instance;
    }

    // 4. 单例对象不用即销毁(销毁时机:最后一个使用者不用才销毁)
    void release(void){
        if(s_count-- == 0){
            delete s_instance;
            s_instance = NULL;
        }
    }
    void print(void){
        cout << m_data << endl;
    }
    
private:
    // 1. 私有化构造函数
    Singelton(int data = 0):m_data(data){
        cout << "单例对象被创建" << endl;
    }
    Singelton(const Singelton&){}
    // 验证上面的release
    ~Singelton(void){
        cout << "单例对象被销毁" << endl;
    }

    // 2. 通过静态成员变量,表示类唯一的对象(这里只是声明, 禁止类内初始化非常量静态成员)
    // 当进程加载时, 仅仅是存在了一个 Singelton类类型 的指针而已
    // 一个指针不管什么类型,只占4字节(32位系统)
    static Singelton *s_instance; 

    // 5. 计数: 记录单例对象使用者的使用者个数
    static int s_count;
    int m_data;
};

Singelton* Singelton::s_instance = NULL;
int Singelton::s_count = 0;

int main(void){
    cout << "main函数开始执行" << endl;

    Singelton& s1 = Singelton::getInstance();
    Singelton& s2 = Singelton::getInstance();
    Singelton& s3 = Singelton::getInstance();
    cout << &s1 << endl;
    cout << &s2 << endl;
    cout << &s3 << endl;
    // 当一个资源被多个使用者使用时,应该是最后一个使用者去销毁才适合。
    s1.print();
    s1.release(); // s_count == 2

    s2.print();
    s2.release(); // s_count == 1

    s3.release();   // s_count == 0, delete s_instance;

    return 0;
}

$ ./a.out 
main函数开始执行
单例对象被创建
0xef6030
0xef6030
0xef6030
1234
1234
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值