一、简介
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
二、应用场景
希望某个类只产生一个实例化对象,如:windows的回收站,任务管理器等不能多开,产生多个窗口。
三、两种方式实现单例模式
类的实例化方式:1.构造函数 2.拷贝构造函数
我们将其设置为私有方法,便可以防止其在类外创建实例化对象。
将构造函数设置为私有方法,并不会影响静态成员在类外创建,因为他们同属于一个类域,可以访问类的所有成员。
1.饥汉模式:不管需不需要用,提前创建好对象
方法一:
class Int
{
private:
int value;
static Int obj; //静态对象 存储在数据区
private:
Int(int x = 0) :value(x) { cout << "Create Int : " << this << endl; }
Int(const Int& it) = delete; //删除拷贝构造
Int& operator=(const Int& s) = delete;//删除赋值public:
~Int() { cout << "Destroy Int: " << this << endl; }
int& Value() { return value; }
const int& Value() const { return value; }
void Print() const { cout << value << endl; }
static Int& getInt() //静态方法访问静态成员
{
return Int::obj;
}
};
Int Int::obj(10);int main()
{
Int& a=Int::getInt();
return 0;
}
方法二:
class Int
{
private:
int value;
private:
Int(int x = 0) :value(x) { cout << "Create Int : " << this << endl; }
Int(const Int& it) = delete; //删除拷贝构造
Int& operator=(const Int& s) = delete;//删除赋值public:
~Int() { cout << "Destroy Int: " << this << endl; }
int& Value() { return value; }
const int& Value() const { return value; }
void Print() const { cout << value << endl; }
static Int& getInt(int x=10) //静态方法访问静态成员
{static Int obj(x); //静态对象 存储在数据区
return obj;
}
};int main()
{
Int& a=Int::getInt();
return 0;
}
2.懒汉模式:用的时候在通过new申请
class Object
{
private:
int value;
static Object* pobj;
private:
Object(int x = 0) :value(x) {
cout << "Create Object" << this << endl;
}
Object(const Object&) = delete;
Object& operator=(const Object&) = delete; // C++ 11;
public:
~Object() { cout << "Destroy Object" << this << endl; }
int& Value() { return value; }
const int& Value()const { return value; }
void show() const { cout << "value: " << value << endl; }
static Object* GetObject(int x)
{
if (nullptr == pobj)
{
pobj = new Object(x); // .heap
}
return pobj;
}
};
Object* Object::pobj = nullptr;
int main()
{
Object* pobja = Object::GetObject(100);
return 0;
}
三、单例模式和单线程
单线程:单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。(ps : 一心一意的痴汉)
1.饥汉模式:
方法一,方法二都是安全的,不会多次重复构建。
2.懒汉模式:
上述方法也是安全的。
四、单例模式和多线程
1.饥汉模式:
上述方法一是安全的,因为静态成员在线程执行前已经创建。
2.懒汉模式:
上述方法种添加测试代码:
void funa()
{
Int* pa = Int::getInt(50);
pa->Print();
}
void funb()
{
Int* pb = Int::getInt(25);
pb->Print();
}
int main()
{
thread tha(funa);
thread thb(funb);
tha.join();
thb.join();
return 0;
}
测试结果:
我们发现多线程模式下创建了多个对象,这不符合我们只实例化一个实体的要求。
我们可以进行以下修改:
#include <iostream>
#include <thread>
#include <mutex> //互斥量class Int
{
private:
int value;
static Int* pobj ;
static mutex mutex_;
private:
Int(int x = 0) :value(x) { cout << "Create Int : " << this << endl; }
Int(const Int& it) = delete;
Int& operator=(const Int& s) = delete;public:
~Int() { cout << "Destroy Int: " << this << endl; }
int& Value() { return value; }
const int& Value() const { return value; }
void Print() const { cout << value << endl; }
static Int* getInt(int x=10)
{
std::unique_lock<std::mutex> lock(mutex_);//加锁,确保一个进程实例化对象结束后,其他进程才能进入
if (nullptr == pobj)
{
pobj = new Int(x);
}
return pobj;
}
};
Int* Int::pobj = nullptr;
std::mutex Int::mutex_;
void funa()
{
Int* pa = Int::getInt(50);
pa->Print();
}
void funb()
{
Int* pb = Int::getInt(25);
pb->Print();
}
int main()
{
thread tha(funa);
thread thb(funb);
tha.join();
thb.join();
return 0;
}
但是加锁之后,仍然存在效率问题。