单例模式概念

什么是单例模式

单例模式,英文叫做Singleton Rattern。是一种创建型设计模式,它保证一个类在程序中仅有一个实例,并对外提供一个访问的该类实例的全局接口。单例模式通常用于需要控制对象资源的开发场景,一个类只创建一个对象的设计,既可以避免创建过多副本所造成的资源浪费现象,又可以避免引发数据一致性等问题。
单例模式将所有构造函数设计成私有的,防止外部创建对象;给出公有静态成员函数为获取对象实例的唯一渠道
单例模式经常被用在数据缓存、日志log调用、参数配置、线程池等开发场景。经常使用单例模式来创建对象,可以有效地降低内存对资源的占用。另外在多线程的开发场景中,单例模式可以避免多个线程同时访问同一个资源,从而避免资源竞争的问题,如果还需要进一步保证线程安全性,可以在创建实例时添加同步锁。


单例模式在现实生活中的抽象示例
电力公司:在一个城市或地区,通常只有一个电力公司负责供电,我们可以通过该公司获取电力服务。
登录系统:用户只需要登录一次就可以打开多个应用程序的界面。

单例模式的基础结构

  • 构造函数和析构函数为私有类型,目的是静止外部构造和析构。
  • 拷贝构造函数和赋值构造函数是私有类型,目的是禁止外部拷贝和赋值,确保实例的唯一性。
  • Singleton类通常会声明一个私有静态成员变量,用来保存Singleton的唯一实例,且这个静态变量只能在Singleton类内部访问。
  • 类中有一个获取实例的静态方法,可以全局访问。它通常被命名为getinstance(),通过该接口可以获取Singleton的唯一实例对象。该接口使用懒汉/饿汉的方式来实现,在接口内部会先判断实例是否已经存在,如果存在则直接返回,否则创建一个新的实例并返回。

为什么static Singleton instance要设为静态的?
在单例模式中,将实例变量设置为静态的是确保在整个程序生命周期内只有一个实例存在的关键。
当类的成员变量被声明为静态时,它们不再属于类的任何特定实例,而是属于类本身。这意味着无论创建了多少个类的实例,静态成员变量都只有一个副本
对于单例模式,在C++中通过静态成员变量来实现,可以确保在程序运行期间只有一个实例存在,并且可以通过类名直接访问这个静态实例,而不需要先创建类的实例。


基础结构之间的工作步骤如下

  1. 饿汉模式的程序启动:在程序启动的同时创建号Singleton实例,并提供全局唯一的外部访问接口,外部程序可以直接调用该接口来获取Singleton实例。
  2. 懒汉模式的程序启动:程序启动时并不会创建Singleton实例,程序在等到单例对象被第一次使用时才创建Singleton实例。
  3. 采用私有的静态变量存储已经创建好的Singleton实例。
  4. 外部客户端通过唯一的外部访问接口来访问并使用Singleton实例。

饿汉模式

饿汉模式是一种主动模式,在类初始化时就完成了单例模式的创建。这是因为主动式单例模式将类的实例声明成了类的静态变量static Singleton instance,类的静态成员变量需要在类外初始化。故程序启动的时候就创建对象,不管用不用。
饿汉模式的优点是线程安全,但缺点是如果该实例很复杂会增加初始化的耗时,从而导致程序的启动时间被延迟。
饿汉模式伪代码:

#include   
using namespace std;  
  
class Singleton  
{  
private:  
    static Singleton* instance;  
        // 私有构造函数和析构函数,防止外部直接创建对象  
    Singleton() {}  
    ~Singleton(){}  
    public:  
    // 静态成员函数,用于获取单例对象  
    static Singleton* getInstance()  
    {  
        return instance;  
    }  
        // 将其拷贝构造、移动构造和赋值构造delete,防止外部拷贝和赋值  
    Singleton(const Singleton &signal) = delete;  
    Singleton(const Singleton &&signal) = delete;  
    Singleton &operator=(const Singleton &signal) = delete;  
};  
  
//提前创建好单例对象  
Singleton* Singleton::instance = new Singleton();

懒汉模式

懒汉模式是一种被动模式,只有当第一次真正需要获取单例实例时才进行单例实例的创建,也被称为“双检锁”模式。
懒汉模式的优点是延迟加载,可以节约资源和减少程序的启动耗时,缺点是需要考虑多线程环境下创建对象导致的线程安全问题,它通常在外部访问接口中使用双重检查锁定来保证线程安全。

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

双重检查锁
每次调用getInstance()函数都会导致一次加锁的开销,理论上我们只需要在第一次创建的时候加锁,当s_instance已经被分配内存后,直接返回s_instance指针就行,这样就演化出了双重检查锁的方式。

static Singleton* getInstance()  
{  
    if (nullptr == s_instance)  
    {  
        std::unique_lock<std::mutex> lock(m_mutex);  
        if (nullptr == s_instance)  
            s_instance = new Singleton();  
    }  
  
    return s_instance;  
}

单例模式代码样例

  1. 单例模式加锁版伪代码
class Singleton  
{  
private:  
    static Singleton* instance;  
    Singleton(){}  
    public:  
    // 使用时才创建单例对象  
    static Singleton* getInstance()  
    {  
        if(instance == nullptr)  
        {  
            // 在多线程环境下需要加锁保证只创建一个实例  
            lock_guard<mutex> lock(mutex);  
            instance = new Singleton();  
        }  
        return instance;  
    }  
        // 单例类的其它成员函数  
    void dosomething(){}  
};  
  
Singleton* Singleton::instance = nullptr;  
int main()  
{  
    Singleton* obj1 = Singleton::getInstance();  
    obj1->dosomething();  
    delete obj1;  // 释放资源  
    return 0;  
}
  1. 完整代码实现
#include 
using namespace std;
class Singleton
{
public:
	static Singleton& getInstance()
	{
		// 如果对象不存在就创建一个
		if (!instance)
			instance = new Singleton();
		return *instance;
	}

	void Operation()
	{
		cout << "Singleton is performing some operation." << endl;
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	static Singleton* instance;
	Singleton()
	{
		cout << "Singleton instance created." << endl;
	}

	~Singleton()
	{
		cout << "Singleton instance destroyed." << endl;
	}
};

Singleton* Singleton::instance = nullptr;

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

代码结果:

Singleton instance created.
Singleton is performing some operation.

单例模式优缺点

单例模式的优点:
可以有效限制资源的数量,对于那些需要全局访问的资源,单例模式可以保证该资源只有一个实例。
对象的创建/销毁过程最多只有一次,可以节省系统开销。
可以统一管理全局配置,如果单例模式的对象用来存储一些配置信息,可以实现对全局配置的管理。
简化了访问资源的入口,由于全局只有一个实例,使得客户端只需要关注一个访问入口即可。


单例模式的缺点:
缺乏灵活性,一旦创建了单例对象,就不能更改其实例化过程。
限制了一个类只能有一个实例,难以扩展该类的功能。
由于单例对象是全局可访问的,可能引发全局变量的滥用。
存在线程安全隐患,如果不增加一些锁机制,在多线程环境下可能会创建多个实例,影响单例的特性。

你可能感兴趣的:(C++,单例模式,开发语言)