单例:对于一些类来说,只有一个实例很重要。保证类只有一个实例,并提供一个访问它的全局访问点
声明一个getInstance()静态方法来返回其所属类的一个相同实例
对构造函数做私有化处理,
拷贝构造函数,赋值运算符重载,做delete处理
多线程下要特殊处理
#include<iostream>
#include<thread>
#include<mutex>
#include<string>
using namespace std;
//多线程下的单例模式
class Singleton{
private:
string m_value;
Singleton(const string value):m_value(value){};
~Singleton(){}
public:
Singleton(Singleton& othrer) = delete;
Singleton(const Singleton& other) = delete;
void operator=(const Singleton&) = delete;
string value()const{ return m_value; }
static Singleton* getInstance(const string& value);//静态函数要在外部定义
private:
static Singleton* m_instance;
static mutex m_mutex;//锁
};
Singleton* Singleton::m_instance = nullptr;
mutex Singleton::m_mutex;
Singleton* Singleton::getInstance(const string& value)
{
//方法一:
//lock_guard<mutex> lock(m_mutex);//加锁直接锁住,整个函数内部,但是效率较低
//if (m_instance == nullptr)//两个线程同时进入判断就会出现两个实例
//{
// m_instance = new Singleton(value);
//}
//return m_instance;
//方法二:
//C++11初始化一个变量时线程安全的
static Singleton* instance = new Singleton(value);
return instance;
}
void Cat()
{
Singleton* singleton = Singleton::getInstance("Cat");
cout<<singleton->value()<<'\n';
}
void Dog()
{
Singleton* singleton = Singleton::getInstance("Dog");
cout << singleton->value() << '\n';
}
int main()
{
thread t1(Cat);
thread t2(Dog);//有安全隐患,会创建两个实例
t1.join();
t2.join();
}
//int main()
//{
// Cat();
// Dog();//单线程情况都是Cat
// cout << "hello world!";
//}
静态局部变量的线程安全性是由C++11标准中的一个名为"静态初始化跨越线程边界问题"的规定所保证的。
具体来说,当一个线程第一次执行到包含某个静态局部变量定义的语句块时,编译器会确保该静态变量被正确地初始化,并且只有一个线程能够执行这个初始化过程。在这个过程中,编译器会使用特殊的机制来防止两个线程同时初始化同一个静态局部变量,从而保证了线程安全性。
例如,对于下面的代码片段:
class SingletonObject
{
public:
static SingletonObject& getInstance()
{
static SingletonObject instance;
return instance;
}
};
当一个线程第一次调用getInstance()时,它将创建唯一的SingletonObject实例并返回它。在这个过程中,编译器会确保仅有当前线程可以进行静态变量instance的初始化。当其它线程再次执行到这个函数时,它们仅仅会返回已经初始化好的实例。由此,静态局部变量具有天然的线程安全性。
在C++中,单例模式的实现方法大致可以分为两种:静态变量和动态分配。下面是分别使用这两种方法实现单例模式的示例代码:
1.使用静态变量实现单例模式:
class SingletonObject
{
public:
static SingletonObject& getInstance()
{
static SingletonObject instance;
return instance;
}
private:
SingletonObject() {}
~SingletonObject() {}
SingletonObject(const SingletonObject&) = delete;
SingletonObject& operator=(const SingletonObject&) = delete;
};
// 使用方式:SingletonObject::getInstance();
在这个示例中,我们使用了一个静态局部变量instance来实现单例模式,该变量只会在第一次调用getInstance()时被创建,并在程序结束时销毁。由于静态局部变量具有线程安全性,因此该实现方法也天然地具有了线程安全性。
2.使用动态分配实现单例模式:
class SingletonObject
{
public:
static SingletonObject* getInstance()
{
if (m_instance == nullptr)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_instance == nullptr)
{
m_instance = new SingletonObject();
}
}
return m_instance;
}
private:
SingletonObject() {}
~SingletonObject() {}
SingletonObject(const SingletonObject&) = delete;
SingletonObject& operator=(const SingletonObject&) = delete;
static SingletonObject* m_instance;
static std::mutex m_mutex;
};
SingletonObject* SingletonObject::m_instance = nullptr;
std::mutex SingletonObject::m_mutex;
// 使用方式:SingletonObject::getInstance();
在这个示例中,我们使用了一个静态指针m_instance来保存唯一的实例对象,并在需要时进行动态分配。为了避免多线程环境下的数据竞争,我们使用了std::mutex来保证线程安全,即只有一个线程能够创建实例对象。