设计模式:单例模式

懒汉式单例模式

#include <iostream>
/*
单例模式:一个类不管创建多少个对象,永远只能得到该类型一个对象的实例
常用到,比如日志模块、数据库模块

单例模式类型:
饿汉式单例模式:还没有获取实例对象,实例对象就已经产生了(导致程序启动时就需要实例化调用构造函数,如果构造函数需要做大量消耗资源会导致程序启动耗时)
懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候,才产生
*/

//饿汉式单例模式 一定是线程安全的

class Singleton
{
public:
	static Singleton* getInstance() //#3 获取类的唯一实例对象的接口方法(必须时静态,静态时为方法便类调用,因为外部不能创建出对象,不能通过对象调用)
	{
		return &instance;
	}
private:
	
	Singleton()//#1 构造函数私有化
	{
	}
	Singleton(const Singleton&) = delete;//禁用默认拷贝构造函数
	Singleton & operator=(const Singleton&) = delete;//禁用默认赋值重载函数
	static Singleton instance;//#2 定义一个唯一的类的实例对象
};
Singleton Singleton::instance;//静态程序变量需要在类外初始化
int main()
{
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3= Singleton::getInstance();
	std::cout << " p1 memroy addr :" << p1 << std::endl;
	std::cout << " p2 memory addr: " << p2 << std::endl;
	std::cout << " p3 memory addr: " << p3 << std::endl;

	//Singleton p4(*p3);//会拷贝构造一个新的实例,所以需要禁用拷贝构造
	//std::cout << " p4 memory addr: " << &p4 << std::endl;

	//Singleton p5=*p3;//默认赋值重载函数会产生新的实例,所以需要禁用默认赋值重载
    //std::cout << " p5 memory addr: " << &p5 << std::endl;
	
}

输出信息

p1 memroy addr :00007FF75578F1B0
p2 memory addr: 00007FF75578F1B0
p3 memory addr: 00007FF75578F1B0

饿汉单例模式

方案一:锁+双重判断

#include <iostream>
#include <mutex>
/*
单例模式:一个类不管创建多少个对象,永远只能得到该类型一个对象的实例
常用到,比如日志模块、数据库模块

单例模式类型:
饿汉式单例模式:还没有获取实例对象,实例对象就已经产生了(导致程序启动时就需要实例化调用构造函数,如果构造函数需要做大量消耗资源会导致程序启动耗时)
懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候,才产生
*/

std::mutex mtx;
//懒汉式单例模式
class Singleton
{
public:
	//是不是可重入函数呢?
	//是可以重入的,比如如果线程1 new Singleton() 开辟内存且构造对象,但未给instance赋值,但是时间片给线程2,线程2因为instance==nullptr 这时重入也会new Singleton()导致重新构造对象
	//解决办法:锁+双重判断
	static Singleton* getInstance() //#3 获取类的唯一实例对象的接口方法
	{

		if (instance == nullptr)//第一重判断
		{
			std::lock_guard<std::mutex> guard(mtx);
			if (instance == nullptr)//第二重判断
			{
				/*
				开辟内存
				构造对象
				给instance赋值
				*/
				instance = new Singleton();
			}


		}
		return instance;
	}
private:

	Singleton()//#1 构造函数私有化
	{
	}
	Singleton(const Singleton&) = delete;//禁用默认拷贝构造函数
	Singleton& operator=(const Singleton&) = delete;//禁用默认赋值重载函数
	//加入volatile的好处:修饰指针,不是修饰指针的指向
	//volatile可以让线程A的修改,让线程B能立即看见。线程不会对instance进行缓存,写入和读取都是内存中的值
	static Singleton * volatile instance;//#2 定义一个唯一的类的实例对象
};
Singleton* volatile Singleton::instance = nullptr;//静态程序变量需要在类外初始化
int main()
{
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3 = Singleton::getInstance();
	std::cout << " p1 memroy addr :" << p1 << std::endl;
	std::cout << " p2 memory addr: " << p2 << std::endl;
	std::cout << " p3 memory addr: " << p3 << std::endl;

	//Singleton p4(*p3);//会拷贝构造一个新的实例,所以需要禁用拷贝构造
	//std::cout << " p4 memory addr: " << &p4 << std::endl;

	//Singleton p5=*p3;//默认赋值重载函数会产生新的实例,所以需要禁用默认赋值重载
	//std::cout << " p5 memory addr: " << &p5 << std::endl;

}

输出信息如下

 p1 memroy addr :000001C9F9A5C2A0
 p2 memory addr: 000001C9F9A5C2A0
 p3 memory addr: 000001C9F9A5C2A0

方案二:简单方式实现

#include <iostream>
#include <mutex>
/*
单例模式:一个类不管创建多少个对象,永远只能得到该类型一个对象的实例
常用到,比如日志模块、数据库模块

单例模式类型:
饿汉式单例模式:还没有获取实例对象,实例对象就已经产生了(导致程序启动时就需要实例化调用构造函数,如果构造函数需要做大量消耗资源会导致程序启动耗时)
懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候,才产生
*/

std::mutex mtx;
//懒汉式单例模式
class Singleton
{
public:

	static Singleton* getInstance() //#3 获取类的唯一实例对象的接口方法
	{
		//TODO:再linux系统上进行分析,查看汇编指令 g++ -o run 单例模式.cpp -g gdb run
		//函数静态局部变量的初始化,在汇编指令上已经自动添加线程互斥指令了
		static Singleton  instance;//#2 定义一个唯一的类的实例对象
		return &instance;
	}
private:

	Singleton()//#1 构造函数私有化
	{
	}
	Singleton(const Singleton&) = delete;//禁用默认拷贝构造函数
	Singleton& operator=(const Singleton&) = delete;//禁用默认赋值重载函数
	
	
};

int main()
{
	Singleton* p1 = Singleton::getInstance();
	Singleton* p2 = Singleton::getInstance();
	Singleton* p3 = Singleton::getInstance();
	std::cout << " p1 memroy addr :" << p1 << std::endl;
	std::cout << " p2 memory addr: " << p2 << std::endl;
	std::cout << " p3 memory addr: " << p3 << std::endl;

	//Singleton p4(*p3);//会拷贝构造一个新的实例,所以需要禁用拷贝构造
	//std::cout << " p4 memory addr: " << &p4 << std::endl;

	//Singleton p5=*p3;//默认赋值重载函数会产生新的实例,所以需要禁用默认赋值重载
	//std::cout << " p5 memory addr: " << &p5 << std::endl;

}

输出如下:

 p1 memroy addr :00007FF7D559F230
 p2 memory addr: 00007FF7D559F230
 p3 memory addr: 00007FF7D559F230
posted @ 2025-10-11 23:15  焦涛  阅读(4)  评论(0)    收藏  举报