饿汉模式:
1。返回静态对象(唯一对象)地址
class Singleton
{
public:
static Singleton* GetInstance()
{
return &instance;
}
~Singleton() {}
private:
Singleton() {}
Singleton(const Singleton& src) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton instance;
};
Singleton Singleton::instance; //饿汉式单例模式 一定是线程安全的
int main()
{
Singleton* ps = Singleton::GetInstance();
Singleton* pb = Singleton::GetInstance();
// ps pb 指向同一个对对象,即类只生成一个实例
return 0;
}
1。返回静态对象(唯一对象)引用
class Singleton
{
public: // 使用引用返回更安全,我们无需对其检查是否为空引用
static Singleton& GetInstance()
{
return instance;
}
~Singleton() {}
private:
Singleton() {}
Singleton(const Singleton& src) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton instance; // 注意:这里只能是静态类型,否则会产生无限递归创建Singleton对象
// 因为,静态类型不属于类对象,因此在创建对象时不会再次创建..
};
Singleton Singleton::instance; //饿汉式单例模式 一定是线程安全的
int main()
{
Singleton& ps = Singleton::GetInstance();
Singleton& pb = Singleton::GetInstance();
// ps pb 指向同一个对对象,即类只生成一个实例
return 0;
}
3.在静态函数中生成静态对象,注意这里是无参的生成静态对象。我们认为次方案是线程安全的。但是只生成一个实例没有意义,我们需要实际运用时必定需要传递参数,而一旦如此就会成为线程不安全的。
class Singleton
{
public:
static Singleton& GetInstance()
{
static Singleton instance; // 线程安全 ?
return instance;
}
~Singleton() {}
private:
Singleton() {}
Singleton(const Singleton& src) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main()
{
Singleton& ps = Singleton::GetInstance();
Singleton& pb = Singleton::GetInstance();
// ps pb 指向同一个对对象,即类只生成一个实例
return 0;
}
线程不安全,函数内的静态变量,是全局变量。因此在第一次初始化时可能线程不安全。
#include <iostream>
class Singleton
{
public:
static Singleton& GetInstance(int x)
{
static Singleton instance(x); // 线程安全 ?
return instance;
}
~Singleton() {}
int val; // 便于演示,这里设置为public
private:
Singleton(int x) :val(x){}
Singleton(const Singleton& src) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main()
{
Singleton& ps = Singleton::GetInstance(10);
Singleton& pb = Singleton::GetInstance(20);
std::cout << ps.val << pb.val << std::endl; // 10
// 同一个线程中,只会是第一个生成的实例,但存在多个线程时值可能会被修改
return 0;
}
优缺点
优点:程序加载时就进行实例化,之后的操作效率会很高。
缺点: 由于程序加载时就进行实例化,如果后续不对此类进行任何操作,就会导致内存的浪费。
饿汉模式
注,此方法一定线程不安全。前者线程安全是因为,程序开始执行时,单例对象已经创建完毕,而此时线程还未开始,不会有创建过个单例对象的可能。 而饿汉模式中,我们用到时才生成对象,如果两个或多个线程同时进行了这一步,存在多个线程同时访问临界区的情况。
class Singleton
{
private:
static Singleton* instance;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
~Singleton() {}
static Singleton* GetInstance()
{
if (instance == nullptr)
{ // 需要时才创建
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
上述方案不安全,因此我们需要加锁保证线程安全。
#include <mutex>
std::mutex mtx;
class Singleton
{
private:
static Singleton* instance;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
~Singleton() {}
static Singleton* GetInstance()
{
if (instance == nullptr)
{ // 需要时才创建
std::lock_guard<std::mutex> lock(mtx); // 加锁保证线程安全
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
优缺点
优点: 在第一次调用的时候才进行实例化。
缺点: 当多个线程同时进入到 if(instance == nullptr) {…} 时,会创建多个对象
单例模式模板,继承单例模式
#include <iostream>
template < class T>
class Singleton
{
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
protected:
Singleton() {}
virtual ~Singleton() {}
public:
static T& instance()
{
static T theInstance;
return theInstance;
}
};
class MyClass : public Singleton < MyClass>
{
int x;
protected:
friend class Singleton<MyClass>;
MyClass() { x = 0; }
public:
void setValue(int n) { x = n; }
int getValue() const { return x; }
};
int main()
{
MyClass& m = Singleton<MyClass>::instance();
//MyClass& m = MyClass::instance();
std::cout << m.getValue() << std::endl;
m.setValue(1);
std::cout << m.getValue() << std::endl;
return 0;
}
问题:如何实现MyClass的拷贝构造,赋值语句等。
使用线程函数 thread_once 设计线程安全的单例模式
…
https://vlambda.com/wz_x3M25HKl72.html