C++单例模式

本文介绍了C++中的单例模式,包括其设计思想、饿汉式和懒汉式的实现方式,以及它们之间的异同。重点讲解了饿汉式和懒汉式的代码实现,强调了线程安全和内存管理在懒汉式中的注意事项。
摘要由CSDN通过智能技术生成

目录

单例模式

单例模式的设计思想

饿汉式

懒汉式

懒汉饿汉异同:


单例模式

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

        应用场景:在软件系统中,经常有一些特殊的类,必须要保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性,以及良好的效率。

单例模式的设计思想

        问题:如何绕过常规的构造函数,提供一种机制来保证一个类只有一个实例?

        步骤1:既然外部不可以定义对象,也就时说不可以从外部调到类的构造函数,所以就必须把构造函数私有化。

                但私有化带来的问题是,在外部一个对象也定义不出来了。

                所以必须在类内定义一个公开的接口,返回本类对象的指针。

        步骤2:在public权限下定义一个函数,返回出本类对象的指针。

                但是,如果这个返回函数是个普通函数的花,那么他还是依赖于类对象的调用才可以,这又与只产生一个单例向矛盾了。

                所以必须把这个函数升级为静态函数。

        步骤3.升级为静态函数后,函数就没有 this 指针,无法调取类中属性(即无法返回本类对象的指针)。

                所以也要把类中的这个属性升级为静态属性,(即本类对象的指针升级为静态属性)。

        步骤4.把类中本类对象指针属性、构造函数,二者升级为静态。

                至此,一个单例模式设计完成。

       单例模式又分为:饿汉式、懒汉式。

        饿汉式

                不管需不需要,程序运行前就已经创建了。

                缺点:有时候我不需要他,但他已经创建好了,白白占用一块内存空间。

                知识扩展

1. 静态变量的初始化是在编译时进行,变量的赋值是在函数或程序运行时进行。
2. 静态变量只初始化一次,但可以通过赋值的方式多次修改静态变量的值。
3. 全局变量和静态变量 在进入 main 前被初始化

                饿汉式代码实现:

                定义饿汉类:

#include <iostream>
using namespace std;

class Singlenton
{
private:
    static Singlenton * instance;
    Singlenton()
    {
        cout << "Singlenton 的构造" << endl;
    }

public:
    static Singlenton* getInstance()
    {
        //在公有属性中,访问私有属性
        return instance;
    }
};

             在全局,对静态属性(本类对象指针)进行初始化:

//在全局中初始化单例
Singlenton * Singlenton::instance = new Singlenton;

                主函数:

int main()
{
    Singlenton * s1 = Singlenton::getInstance();
    cout << s1 << endl;
    Singlenton * s2 = Singlenton::getInstance();
    cout << s2 << endl;
    Singlenton * s3 = Singlenton::getInstance();
    cout << s3 << endl;
}

                运行结果:

                

 

懒汉式

        懒汉饿汉异同:

        同:

1.返回的本类对象指针都是静态属性。

2.构造函数都是私有属性。

3.公有权限下,定义静态函数返回本类对象指针。

4.都需在全局中,初始化本类对象指针。

        异:

1.饿汉式直接创建对象,懒汉式则在类中 对本类对象指针判断是否为nullptr。

2.懒汉式在全局初始化本类对象指针时,赋nullptr。

3.懒汉式不能使用析构函数释放内存。

        懒汉式代码实现:

        知识拓展:

lock_guard        C++的智能锁

 lock_guard简化了 lock/unlock 的写法, lock_guard在构造时自动锁定互斥量, 而在退出作用域时会析构自动解锁, 保证了上锁解锁的正确操作, 大大降低了死锁的风险,正是典型的 RAII 机制。

    头文件: #include <mutex>

    在全局自定义锁名:mutex my_mutex;

    在线程中上锁:lock_guard<mutex> my_lock(my_mutex);        //my_lock自定义锁类对象

        懒汉式思想:在类中的返回函数中,对本类对象指针进行判断,若为nullptr,则在堆区开辟空间,若不为nullptr,则直接返回本类对象。

        注意:1.出于线程安全考虑,每个类在进行判断前,需要先上锁。

                2.在单例模式中,不能使用析构函,调用delete instance; 时,会调用本类的析构,接着会再次调用本类的析构,陷入死循环。

                所以应自定义一个释放堆区空间的函数,自定义的释放函数,不会被系统自动层层调用。

        定义懒汉类:

#include <iostream>
#include <mutex>
using namespace std;

mutex my_mutex;
class Singleton
{
private:
    static Singleton* instance;
    Singleton()
    {
        cout << "singleton的构造" << endl;
    }

public:
    static Singleton * getInstance()
    {
        //C++11新特性之智能锁 lock_guard
        lock_guard<mutex> my_lock(my_mutex);
        if(instance == nullptr)
        {
            instance = new Singleton();
        }

        return instance;
    }

    //在单例模式中,不能用析构函数释放,会陷入层层析构,无法自拔
//    ~Singlenton()
//    {
//        delete instance;
//    }


    //可使用静态成员函数用于销毁
    static void destroy()
    {
        delete instance;
    }
};

        在全局,初初始化静态属性(本类对象指针):

//单例类中静态属性指针类外进行初始化
Singleton * Singleton::instance = nullptr;

        主函数:

int main()
{
    Singleton * s1 = Singleton::getInstance();
    Singleton * s2 = Singleton::getInstance();
    Singleton * s3 = Singleton::getInstance();

    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
}

        运行结果:

        

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值