当前位置: 首页 > article >正文

C++设计模式——Singleton单例模式

一、单例模式的定义  

单例模式,英文全称Singleton Pattern,是一种创建型设计模式,它保证一个类在程序中仅有一个实例,并对外提供一个访问的该类实例的全局接口。

单例模式通常用于需要控制对象资源的开发场景,一个类只创建一个对象的设计,既可以避免创建过多副本所造成的资源浪费现象,又可以避免引发数据一致性等问题。

在数据库连接、线程池设计、日志系统设计等开发场景,经常使用单例模式来创建对象,可以有效地降低对内存资源的占用。

在编码时,为了防止外部直接通过new关键字创建对象实例,Singleton类的构造函数必须是私有的。

在多线程开发场景,单例模式可以避免多个线程同时访问同一个资源,从而避免资源竞争的问题,如果还需要进一步保证线程安全性,可以在创建实例时添加同步锁。

单例模式在现实生活中的抽象实例:

电力公司:在一个城市或地区,通常只有一个电力公司负责供电,我们可以通过该公司来获取电力服务。

总统办公室:在一个国家,通常只有一个总统办公室负责国家事务处理,办公室负责确保国家事务的一致性和高效运作。

登录系统:用户只需要登录一次就可以打开多个应用程序的界面。

二、单例模式的结构

单例模式主要包含以下组件:

1.Singleton类:这是一个单例类,它在整个系统中只有一个实例。通常使用私有构造函数来创建、使用公共静态成员方法来访问。

2.私有构造函数:为了避免被外部实例化,Singleton类通常会定义一个私有构造函数,使得其他类无法通过调用构造函数创建Singleton的实例对象。

3.私有静态成员变量:Singleton类通常会声明一个私有静态成员变量,用来保存Singleton的唯一实例,且这个静态变量只能在Singleton类内部访问。

4.公共的对外接口:它通常被命名为getInstance(),通过该接口可以获取Singleton的唯一实例对象。该接口使用懒汉/饿汉的方式来实现,在接口内部会先判断实例是否已经存在,如果存在则直接返回,否则创建一个新的实例并返回。

组件之间的工作步骤如下:

1.饿汉模式的程序启动:在程序启动的同时就创建好Singleton实例,并提供全局唯一的外部访问接口,外部程序可以直接调用该接口来获取Singleton实例。

2.懒汉模式的程序启动:程序启动时并不会创建Singleton实例,程序在等到单例对象被第一次使用时才创建Singleton实例。

3.采用私有的静态变量存储已经创建好的Singleton实例。

4.外部客户端通过唯一的外部访问接口来访问并使用Singleton实例。

饿汉模式单例 & 懒汉模式单例:

饿汉模式(Singleton with Instantiation):

是一种线程安全的实现方法,在类初始化时就完成了单例实例的创建。

懒汉模式(Singleton with Lazy Initialization):

也被称为"双检锁"模式,只有当第一次真正需要获取单例实例时才进行单例实例的创建。

饿汉模式和懒汉模式都实现了单例,即保证在整个应用程序的生命周期中只有一个Singleton类实例。

饿汉模式的优点是线程安全,但缺点是如果该实例很复杂会增加初始化的耗时,从而导致程序的启动时间被延长。

懒汉模式的优点是延迟加载,可以节约资源和减少程序的启动耗时,缺点是需要考虑多线程环境下创建对象导致的线程安全问题,它通常在外部访问接口中使用双重检查锁定(Double-checked locking)来保证线程安全。

对应UML类图:

三、单例模式代码样例

1.单例模式的伪代码:

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    
    //私有构造函数,确保不能从外部实例化对象
    Singleton() {}
    
public:
    //获取单例实例的静态方法
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singleton = Singleton::getInstance();
    return 0;
}

2.单例模式的加锁版伪代码:

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} //私有化构造函数,防止外部创建实例

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            //在多线程环境下需要加锁保证只创建一个实例
            // std::lock_guard<std::mutex> lock(mutex);
            instance = new Singleton();
        }
        return instance;
    }

    //单例类的其他成员函数
    void doSomething() {
        // ...
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* obj1 = Singleton::getInstance();
    obj1->doSomething();

    Singleton* obj2 = Singleton::getInstance();
    obj2->doSomething();

    delete obj1; //释放资源

    return 0;
}

3.饿汉模式的伪代码:

class Singleton {
private:
    static Singleton* instance;

    //私有构造函数,防止外部直接创建对象
    Singleton() {}

public:
    //静态成员函数,用于获取单例对象
    static Singleton* getInstance() {
        return instance;
    }
};

//提前创建好单例对象
Singleton* Singleton::instance = new Singleton();

4.懒汉模式的伪代码:

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}

public:
    //使用时才创建单例对象
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Demo1:完整代码实现

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    void showMessage() {
        std::cout << "Hello, World!" << std::endl;
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    // 获取Singleton对象实例
    Singleton* singleton = Singleton::getInstance();
    
    // 调用对象的方法
    singleton->showMessage();
    
    return 0;
}

运行结果:

Hello, World!

Demo2:增加析构函数

#include <iostream>

class Singleton {
    public:
    static Singleton& getInstance()
    {
        //如果对象实例不存在就创建一个
        if (!instance) {
            instance = new Singleton();
        }
        return *instance;
    }
    //给外部调用的接口
    void Operation()
    {
        std::cout
            << "Singleton is performing some operation."
            << std::endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    private:
    //私有化的构造函数
    Singleton()
    {
        std::cout << "Singleton instance created."
            << std::endl;
    }
    //私有化的析构函数
    ~Singleton()
    {
        std::cout << "Singleton instance destroyed."
            << std::endl;
    }
    static Singleton* instance;
};

Singleton* Singleton::instance = nullptr;

int main()
{
    Singleton& singleton = Singleton::getInstance();
    singleton.Operation();
    return 0;
}

运行结果:

Singleton instance created.
Singleton is performing some operation.

四、单例模式的优缺点

单例模式的优点:

可以有效限制资源的数量,对于那些需要全局访问的资源,单例模式可以保证该资源只有一个实例。

对象的创建/销毁过程最多只有一次,可以节省系统开销。

可以统一管理全局配置,如果单例模式的对象用来存储一些配置信息,可以实现对全局配置的管理。

简化了访问资源的入口,由于全局只有一个实例,使得客户端只需要关注一个访问入口即可。

 

单例模式的缺点:

缺乏灵活性,一旦创建了单例对象,就不能更改其实例化过程。

限制了一个类只能有一个实例,难以扩展该类的功能。

由于单例对象是全局可访问的,可能引发全局变量的滥用。

存在线程安全隐患,如果不增加一些锁机制,在多线程环境下可能会创建多个实例,影响单例的特性。

五、代码实战

#include <iostream>

using namespace std;

class Singleton
{
    private:
    static bool instanceFlag;
    static Singleton* single;
    Singleton()
    {
        printf("Private constructor finished.\n");
    }
    public:
    static Singleton* getInstance();
    void method();
    ~Singleton()
    {   
        printf("Public de-constructor finished.\n");
        instanceFlag = false;
    }
};

bool Singleton::instanceFlag = false;
Singleton* Singleton::single = NULL;

Singleton* Singleton::getInstance()
{
    if (!instanceFlag)
    {
        single = new Singleton();
        instanceFlag = true;
        return single;
    }
    else
    {
        return single;
    }
}

void Singleton::method()
{
    cout << "Method of the singleton class" << endl;
}

int main()
{
    Singleton* sc1, * sc2;

    sc1 = Singleton::getInstance();
    sc1->method();
    sc2 = Singleton::getInstance();
    sc2->method();

    delete sc1, sc2;
    return 0;
}

运行结果:

Private constructor finished.
Method of the singleton class
Method of the singleton class
Public de-constructor finished.

六、参考阅读

https://www.geeksforgeeks.org/singleton-pattern-c-design-patterns/

https://scottlilly.com/c-design-patterns-the-singleton-pattern/

https://www.tutorialspoint.com/explain-cplusplus-singleton-design-pattern

https://www.codeproject.com/Articles/1921/Singleton-Pattern-its-implementation-with-Cplusplu


http://www.kler.cn/a/408447.html

相关文章:

  • python Flask指定IP和端口
  • Homebrew切换成国内源
  • 《FreeRTOS任务删除篇》
  • Excel的图表使用和导出准备
  • linux基本命令(1)
  • 采用python3.12 +django5.1 结合 RabbitMQ 和发送邮件功能,实现一个简单的告警系统 前后端分离 vue-element
  • Flask服务封装+Docker服务部署
  • Android OpenGL ES详解——Renderer接口介绍
  • 12-表的约束
  • JVM标量替换
  • mysql-分析并解决可重复读隔离级别发生的删除幻读问题
  • uniapp的列表渲染v-for 与正确写法,循环二维数组
  • 开源网络安全检测工具——伏羲 Fuxi-Scanner
  • 机器学习入门-Scikit-learn
  • 46.坑王驾到第十期:vscode 无法使用 tsc 命令
  • 04 - 尚硅谷 - MQTT 客户端编程
  • 一加ACE 3 Pro手机无法连接电脑传输文件问题
  • Window11+annie 视频下载器安装
  • Sketch在线版不存在?即时设计来填补空白
  • Flink【基于时间的双流联结 Demo】
  • 时序预测 | Matlab实现PSO-Elman粒子群优化递归神经网络时间序列预测
  • raw文件如何打开
  • shell编程之sed
  • 探索 Python 任务自动化的新境界:Invoke 库揭秘
  • 如何用Python统计Excel文件中的特定字段数量
  • 【Java系列】随机生成大小写混合的卡密