单例模式:
单例是设计模式的一种,保证一个类只有一个实例,并提供一个可以访问它的全局访问点。
通常可以让一个全局变量使得一个对象被访问,但是不能阻止实例化多个对象,解决办法是:让类自身负责保存它的唯一实例,这个类保证没有其他的实例可以被创建,并且它提供一个可以访问该实例的方法。
Singleton类中,定义一个getInstance()静态成员函数,允许用户访问它的唯一的实例,getInstance是一个静态方法,主要负责创建自己的唯一实例。
//不安全的单例模式 存在线程安全和内存泄漏
class Singleton1 {
private:
Singleton1() { cout<< "Singleton1 Construct!"<<endl;}
Singleton1(const Singleton1 &) = delete;
Singleton1 & operator =(const Singleton1&) = delete;
static Singleton1* resptr_1;
public:
~Singleton1() {cout<< "Singleton1 Destruct"<<endl;}
static Singleton1* getInstance();
void func();
};
Singleton1* Singleton1::getInstance() {
if (resptr_1 == nullptr) {
resptr_1 = new Singleton1;
}
return resptr_1;
}
void Singleton1::func () {
cout << "do something here!"<< endl;
}
客户端代码:
Singleton1* Singleton1::resptr_1 = nullptr;
int main() {
Singleton1 * instance1 = Singleton1::getInstance();
Singleton1 * instance2 = Singleton1::getInstance();
instance1->func();
instance2->func();
if (instance1== instance2)
cout << "instance1 == instance2"<<endl;
cout << endl;
system("pause");
return 0;
}
执行结果
上述代码有内存泄漏的问题,以及再多线程情况下存在线程安全的情况,对于以上情况,可以使用双检锁来解决线程安全的问题,使用智能指针托管创建的实例。如下类Singletion2实现
//shared_ptr 解决内存泄漏,双检锁解决线程安全
class Singleton2 {
private:
Singleton2() { cout << "Singleton2 Construct!"<< endl;}
static shared_ptr<Singleton2> resptr_2;
static mutex mutex_;
//void func();
public:
~Singleton2() {cout<< "Singleton2 Destruct"<<endl;}
Singleton2(const Singleton2&) = delete;
Singleton2& operator =(const Singleton2&) = delete;
static shared_ptr<Singleton2> getInstance();
void func();
};
shared_ptr<Singleton2> Singleton2::getInstance() {
if (resptr_2 == nullptr) {
lock_guard<mutex> lk(mutex_);
if (resptr_2 == nullptr) {
resptr_2 = shared_ptr<Singleton2>(new Singleton2);
}
}
return resptr_2;
}
void Singleton2::func() {
cout << "do something here!"<< endl;
}
客户端代码:
shared_ptr<Singleton2> Singleton2::resptr_2 = nullptr;
mutex Singleton2::mutex_;
int main() {
shared_ptr<Singleton2> instance3 = Singleton2::getInstance();
shared_ptr<Singleton2> instance4 = Singleton2::getInstance();
instance3->func();
instance4->func();
if (instance3 == instance4)
cout << "instance3 == instance4"<<endl;
cout <<endl;
system("pause");
return 0;
}
image.png
C++11标准中的Magic Static特性:如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。利用该特性就可以写出一根非常好的线程安全的单例模式代码。如下Singleton3:
/局部静态变量的方式。如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束
class Singleton3 {
private:
Singleton3() {cout << "Singleton3 Construct!"<<endl;}
public:
~Singleton3() {cout<< "Singleton3 Destruct"<<endl;}
Singleton3(const Singleton3&) = delete;
Singleton3& operator =(const Singleton3&) = delete;
static Singleton3& getInstance();
void func();
};
Singleton3& Singleton3::getInstance() {
static Singleton3 instance;
return instance;
}
void Singleton3::func() {
cout << "do something here!"<<endl;
}
客户端代码:
int main() {
Singleton3 & instance5 = Singleton3::getInstance();
Singleton3 & instance6 = Singleton3::getInstance();
instance5.func();
instance6.func();
if (&instance5 == &instance6)
cout << "instance5 == instance6"<<endl;
system("pause");
return 0;
}
image.png
人们又根据实例的创建时间将单例模式分为了懒汉式和饿汉式
饿汉式:类构造的时候就创建实例对象,空间换时间的策略
懒汉式:需要的时候,创建实例,时间换空间的策略
很明显上述的三款代码都是懒汉式的单例模式,饿汉式的特点是线程安全的,因为在类产生的时候就已经将实例创建了每次获取的收直接返回的是预先创建好额实例,也就不存在创建多个的情况了,饿汉式的单例模式如下所示:
class Singleton {
private:
Singleton() { cout<< "Singleton Construct!"<<endl;}
Singleton(const Singleton &) = delete;
Singleton & operator =(const Singleton&) = delete;
static Singleton instance;//创建实例。
public:
~Singleton() {cout<< "Singleton Destruct"<<endl;}
static Singleton* getInstance();
void func();
};
Singleton* Singleton::getInstance() {
return &instance;
}
void Singleton::func () {
cout << "do something here!"<< endl;
}
网友评论