单例模式

  • 介绍
  • 实现方式
  • 示例场景
  • 注意事项
  • 多线程中实现单例模式

介绍

单例模式是在开发中经常使用的一个设计模式,使用场景较为广泛,只要是想在工程中仅存在一份该对象,都可以使用单例模式。
单例模式分为懒汉模式饿汉模式
懒汉模式: 在第一次调用到的时候才进行初始化,所以被称为懒汉。
饿汉模式: 在工程运行起来时就已经进行初始化,所以被称为饿汉。
懒汉和饿汉的区别就在于初始化的时间点

实现方式

将构造函数做成私有的,并将不需要的构造函数以及赋值运算写成delete,再创建一个私有的本类的静态成员变量,用于构造。
最后再创建一个公有的接口,返回上述创建的静态成员变量用于外部使用。

示例场景

现在有一个数据库的操作类,对该数据库进行操作需要提供数据库的地址、用户名、密码。因为数据都不确定,所以不能在代码里写死,我们需要一个配置文件,将需要的信息读入代码中。
代码演示
类头文件

class DBConfig
{
public:
	static DBConfig* getInstance();

public:
	std::string getConn();
	std::string getUsername();
	std::string getPassword();

private:
	// 私有化 防止构造
	DBConfig();
	DBConfig(const DBConfig& config) = delete;
	DBConfig& operator=(const DBConfig& config) = delete;

private:
	static DBConfig* config;

private:
	std::string conn;
	std::string username;
	std::string password;
};

类cpp实现

// 懒汉
// DBConfig* DBConfig::config = nullptr;
// 饿汉
DBConfig* DBConfig::config = new DBConfig();

DBConfig::DBConfig()
{
	std::fstream fs("./config.txt");
	if (!fs.is_open())
	{
		std::cout << "open file failed." << std::endl;
	}
	char tempStr[1024];
	int index = 0;
	while (fs.getline(tempStr, 1024))
	{
		if (index == 0)
		{
			conn = tempStr;
		}
		else if (index == 1)
		{
			username = tempStr;
		}
		else if (index == 2)
		{
			password = tempStr;
		}
		index++;
	}
	std::cout << "Read config from file" << std::endl;
}

DBConfig* DBConfig::getInstance()
{
	// 懒汉需要判断是否为nullptr
	// 饿汉不需要
	/*if(config==nullptr)
	{
		config = new DBConfig;
	}*/
	return config;
}

std::string DBConfig::getConn()
{
	return conn;
}

std::string DBConfig::getUsername()
{
	return username;
}

std::string DBConfig::getPassword()
{
	return password;
}

使用代码

// 用于数据库连接
// 需要的配置信息:数据库连接信息、数据库用户名、数据库密码
class SqlQuery
{
public:
	SqlQuery(const std::string& conn, const std::string& username, const std::string& password)
	{
		m_conn = conn;
		m_username = username;
		m_password = password;
	}

	int query()
	{
		// 数据库查询操作
		return 0;
	}
	std::string m_conn;
	std::string m_username;
	std::string m_password;
};

int main()
{
	for(int i = 0;i<100;i++)
	{
		SqlQuery query(DBConfig::getInstance()->getConn(), DBConfig::getInstance()->getUsername(), DBConfig::getInstance()->getPassword());
		query.query();
	}
}

注意事项

以上两种写法都无法运用在多线程中。
将构造函数和赋值操作放入private或者delete的目的是为了防止出现多个实例。
单例实现方式不止有懒汉和饿汉,但是单例模式的设计思想就是让整个工程中只存在一个对象实例。

多线程中实现单例模式

C++多线程中实现线程安全的单例模式有两种方式:1.加锁;2.创建局部静态变量,以下为第二种方式的演示

class A
{
pbulic:
	A* instance()
	{
		static A a;
		return &a;
	}
private:
	// 私有化 防止构造
	A();
	A(const A& a) = delete;
	A& operator=(const A& a) = delete;
}

以此方法实现的单例模式在多线程中也能保持单一实例并且写法简单,推荐使用此方法创建单例。