参考:5. 单例模式(Singleton) (yuque.com)
1、什么是单例模式
保证一个类只有一个实例,并提供一个访问该实例的全局节点;
2、什么情况下需要单例模式
某个类的对象在软件运行之初就创建,并且在软件的很多地方都需要读写这个类的信息;使用单例模式的话,类对象就只要软件启动的时候创建一次,软件停止的时候释放;
3、优点
1)保证类只有一个实列;
2)有一个指向该实例的全局节点;
4、缺点
暂没经验
5、线程安全的懒汉单例模式
Singleton.h:
cpp
#ifndef SINGLETON_H_
#define SINGLETON_H_
#include <iostream>
#include <string>
#include <mutex>
// 配置类
class SingletonConfig {
public:
static SingletonConfig* GetInstance() {
if (instance_ == nullptr) {
// 加锁保证多个线程并发调用getInstance()时只会创建一个实例
m_mutex_.lock();
if (instance_ == nullptr) {
instance_ = new SingletonConfig();
}
m_mutex_.unlock();
}
return instance_;
}
private:
SingletonConfig() {}
static SingletonConfig* instance_;
static std::mutex m_mutex_;
};
// 管理类
class SingletonManager {
public:
static SingletonManager* GetInstance() {
if (instance_ == nullptr) {
// 加锁保证多个线程并发调用getInstance()时只会创建一个实例
m_mutex_.lock();
if (instance_ == nullptr) {
instance_ = new SingletonManager();
}
m_mutex_.unlock();
}
return instance_;
}
private:
SingletonManager() {}
static SingletonManager* instance_;
static std::mutex m_mutex_;
};
#endif // SINGLETON_H_
Singleton.cpp:
cpp
#include "Singleton.h"
// 静态变量instance初始化不要放在头文件中, 如果多个文件包含singleton.h会出现重复定义问题
SingletonConfig* SingletonConfig::instance_ = nullptr;
std::mutex SingletonConfig::m_mutex_;
SingletonManager* SingletonManager::instance_ = nullptr;
std::mutex SingletonManager::m_mutex_;
main.cpp
cpp
#include <iostream>
#include "Singleton.h"
int main() {
SingletonConfig *s1 = SingletonConfig::GetInstance();
SingletonConfig *s2 = SingletonConfig::GetInstance();
std::cout << "s1地址: " << s1 << std::endl;
std::cout << "s2地址: " << s2 << std::endl;
SingletonManager *s3 = SingletonManager::GetInstance();
SingletonManager *s4 = SingletonManager::GetInstance();
std::cout << "s3地址: " << s3 << std::endl;
std::cout << "s4地址: " << s4 << std::endl;
return 0;
}
编译运行
cpp
$ g++ -g main.cpp Singleton.cpp -std=c++11 -o singleton
$ ./singleton
s1地址: 0x5594b0f12e70
s2地址: 0x5594b0f12e70
s3地址: 0x5594b0f132a0
s4地址: 0x5594b0f132a0
此单例模式代码是比较常规的,使用和理解起来也方便,但是如果有多个类需要使用单例模式的话,那么每个类都需要定义一份单例类代码,这种情况下,进一步优化的话,可以定义单例类模板;
6、升级版:线程安全的懒汉单例类模板
Config.h
cpp
#ifndef CONFIG_H
#define CONFIG_H
#include "iostream"
#include "Singleton.h"
struct stuInfo{
int index;
std::string name;
int age;
};
class Config
{
public:
Config();
~Config();
int getIndex() { return m_stuInfo.index; }
std::string getName() { return m_stuInfo.name; }
int getAge() { return m_stuInfo.age; }
void init();
void loadConfig();
void syncConfig();
private:
stuInfo m_stuInfo;
};
#define SingletonStuInfo Singleton<Config>::GetInstance()
#endif // CONFIG_H
Singleton.h
cpp
#ifndef SINGLETON_H_
#define SINGLETON_H_
#include <iostream>
#include <string>
#include <mutex>
template <class T>
class Singleton {
public:
static T* GetInstance();
private:
Singleton() {}
static T* m_instance;
static std::mutex m_mutex;
};
template<class T>
T* Singleton<T>::m_instance = nullptr;
template<class T>
std::mutex Singleton<T>::m_mutex;
template <class T>
T* Singleton<T>::GetInstance() {
if (m_instance == nullptr) {
// 加锁保证多个线程并发调用getInstance()时只会创建一个实例
m_mutex.lock();
if (m_instance == nullptr) {
m_instance = new T();
}
m_mutex.unlock();
}
return m_instance;
}
#endif // SINGLETON_H_
Config.cpp
cpp
#include "Config.h"
Config::Config()
{
}
Config::~Config()
{
}
void Config::init()
{
m_stuInfo.index = 0;
m_stuInfo.name = "zhangsan";
m_stuInfo.age = 20;
}
void Config::loadConfig()
{
}
void Config::syncConfig()
{
}
main.cpp
cpp
#include <iostream>
#include "Singleton.h"
#include "Config.h"
int main() {
SingletonStuInfo->init();
std::cout << "index: " << SingletonStuInfo->getIndex() << std::endl;
std::cout << "name: " << SingletonStuInfo->getName() << std::endl;
std::cout << "age: " << SingletonStuInfo->getAge() << std::endl;
return 0;
}
编译运行
bash
$ g++ -g main.cpp Singleton.cpp Config.cpp -std=c++11 -o singleton
$ ./singleton
index: 0
name: zhangsan
age: 20