0. 概述
创建全局唯一对象,保证一个类只有一个实例
解决思路:
- 默认构造函数私有
- 使用一个静态方法作为构造函数,返回的是引用
1. 饿汉式
/**
* 单例模式饿汉式实现
*/
class Singleton {
private:
Singleton() {} //构造函数私有
static Singleton instance; //实例static,并且是private
public:
static Singleton& getInstance() {
return instance; // 返回实例引用
}
};
优点: 将耗时的初始化提前,不会出现首次调用性能问题;
缺点:不支持延迟加载(也就是真正用到的时候再创建实例);static Singleton instance;和static Singleton& getInstance()二者的初始化顺序不确定,如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。不能保证线程唯一,若需要线程唯一,需要加锁。
2. 懒汉式
/**
* 单例模式懒汉式实现
*/
class Singleton {
private:
Singleton() {} //构造函数私有
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
};
优点:延时加载,c++11后是线程安全的,C++11规定了local static在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。
测试代码:
#include <bits/stdc++.h>
using namespace std;
/**
* 单例模式懒汉式实现
*/
class Singleton {
private:
Singleton(string value) : value_(value){} //构造函数私有
string value_; //为了测试线程安全
public:
/**
* Singletons should not be cloneable.
*/
Singleton(Singleton &other) = delete;
/**
* Singletons should not be assignable.
*/
void operator=(const Singleton &) = delete;
static Singleton* GetInstance(const string& value) {
static Singleton* instance = new Singleton(value);
return instance;
}
//为了测试线程安全
string value() {
return value_;
}
};
/**
* 一个线程的主函数
*/
void ThreadFoo() {
this_thread::sleep_for(chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("Foo");
cout << singleton->value() << "\n";
}
/**
* 另外一个线程的主函数
*/
void ThreadBar() {
this_thread::sleep_for(chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("BAR");
cout << singleton->value() << "\n";
}
int main() {
cout << "If you see the same value, then singleton was reused(yay!\n" <<
"If you see different values, then 2 singletons were created (booo!!)\n\n" <<
"RESULT:\n";
thread t1(ThreadFoo);
thread t2(ThreadBar);
t1.join();
t2.join();
return 0;
}
输出结果:
If you see the same value, then singleton was reused(yay!
If you see different values, then 2 singletons were created (booo!!)
RESULT:
BAR
BAR
或者
If you see the same value, then singleton was reused(yay!
If you see different values, then 2 singletons were created (booo!!)
RESULT:
Foo
Foo
同4的结果,该次运行中多个线程之间实例是唯一的,只是不能确定哪一个线程最先初始化,最先初始化的就按照那种方式实例化对象
3. 基础单例的另外实现(线程不唯一,类似于实现1)
#include <bits/stdc++.h>
using namespace std;
class Singleton {
private:
// 构造函数设置为私有
Singleton(const string value) : value_(value) {}
static Singleton* singleton_;
string value_;
public:
// 删除拷贝构造函数
Singleton(Singleton&) = delete;
// 删除赋值操作运算符
Singleton& operator=(const Singleton&) = delete;
// 获取实例的静态方法
static Singleton* GetInstance(const string& value) {
if (nullptr == singleton_) {
singleton_ = new Singleton(value);
}
return singleton_;
}
// 为了测试
string value() const {
return value_;
}
};
Singleton* Singleton::singleton_ = nullptr;
/**
* 一个线程的主函数
*/
void ThreadFoo() {
this_thread::sleep_for(chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("Foo");
cout << singleton->value() << "\n";
}
/**
* 另外一个线程的主函数
*/
void ThreadBar() {
this_thread::sleep_for(chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("BAR");
cout << singleton->value() << "\n";
}
int main() {
cout << "If you see the same value, then singleton was reused(yay!\n" <<
"If you see different values, then 2 singletons were created (booo!!)\n\n" <<
"RESULT:\n";
thread t1(ThreadFoo);
thread t2(ThreadBar);
t1.join();
t2.join();
return 0;
}
更加完整,补充了拷贝构造函数,指针实现,并且用多线程验证
结果:
If you see the same value, then singleton was reused(yay!
If you see different values, then 2 singletons were created (booo!!)
RESULT:
Foo
BAR
线程之间不唯一
4. 线程唯一的另外实现
这里线程安全指的是多线程的实例能否唯一
思路:使用互斥锁
#include <bits/stdc++.h>
/**
* The Singleton class defines the `GetInstance` method that serves as an
* alternative to constructor and lets clients access the same instance of this
* class over and over.
*/
class Singleton
{
/**
* The Singleton's constructor/destructor should always be private to
* prevent direct construction/desctruction calls with the `new`/`delete`
* operator.
*/
private:
static Singleton * pinstance_;
static std::mutex mutex_;
protected:
Singleton(const std::string value): value_(value)
{
}
~Singleton() {}
std::string value_;
public:
/**
* Singletons should not be cloneable.
*/
Singleton(Singleton &other) = delete;
/**
* Singletons should not be assignable.
*/
void operator=(const Singleton &) = delete;
/**
* This is the static method that controls the access to the singleton
* instance. On the first run, it creates a singleton object and places it
* into the static field. On subsequent runs, it returns the client existing
* object stored in the static field.
*/
static Singleton *GetInstance(const std::string& value);
/**
* Finally, any singleton should define some business logic, which can be
* executed on its instance.
*/
void SomeBusinessLogic()
{
// ...
}
std::string value() const{
return value_;
}
};
/**
* Static methods should be defined outside the class.
*/
Singleton* Singleton::pinstance_{nullptr};
std::mutex Singleton::mutex_;
/**
* The first time we call GetInstance we will lock the storage location
* and then we make sure again that the variable is null and then we
* set the value. RU:
*/
Singleton *Singleton::GetInstance(const std::string& value)
{
std::lock_guard<std::mutex> lock(mutex_);
if (pinstance_ == nullptr)
{
pinstance_ = new Singleton(value);
}
return pinstance_;
}
void ThreadFoo(){
// Following code emulates slow initialization.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("FOO");
std::cout << singleton->value() << "\n";
}
void ThreadBar(){
// Following code emulates slow initialization.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("BAR");
std::cout << singleton->value() << "\n";
}
int main()
{
std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
"If you see different values, then 2 singletons were created (booo!!)\n\n" <<
"RESULT:\n";
std::thread t1(ThreadFoo);
std::thread t2(ThreadBar);
t1.join();
t2.join();
return 0;
}
总结:各个线程之间实例唯一了,但是不能确定是具体是哪一个,结果是先执行的线程的那一个实例,但是实际中无法确定到底是哪个,而且性能较差