1、C++设计模式——单例模式
2、
一个类中只产生一个对象,并提供一个外部访问点,被程序全员共享,简化了在复杂环境下的配置管理,这种模式被成为单例模式。
不管用不用,在程序开始就加载,会导致程序启动慢,且如果有多个单例类对象实例启动顺序不确定
线程安全,一共只生成一个静态对象
饿汉模式虽好,但其存在隐藏的问题,在于非静态对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。
/*
* HungerSingleton.h
*/
#pragma once
#ifndef _HUNGERSINGLETON_H_
#define _HUNGERSINGLETON_H_
#include
#include
class HungerSingleton {
private:
static HungerSingleton instance; //静态对象
HungerSingleton();
~HungerSingleton();
//防拷贝
HungerSingleton(const HungerSingleton&) = delete;
HungerSingleton& operator=(const HungerSingleton&) = delete;
public:
static HungerSingleton* GetInstance();
void func(int n)
{
std::cout << n << std::endl;
Sleep(n);
}
};
#endif // !_HUNGERSINGLETON_H_
/*
* HungerSingleton.cpp
*/
#include"HungerSingleton.h"
HungerSingleton HungerSingleton::instance; //类外初始化
HungerSingleton* HungerSingleton::GetInstance() {
return &instance;
}
HungerSingleton::HungerSingleton() {
std::cout << "HungerSingleton构造函数" << std::endl;
}
HungerSingleton::~HungerSingleton() {
std::cout << "HungerSingleton析构函数" << std::endl;
}
#include"HungerSingleton.h"
#include
#include
using namespace std;
int main() { //HungerSingleton构造函数
auto hung = HungerSingleton::GetInstance();
hung->func(5); //5
hung->func(3); //3
return 0; //HungerSingleton析构函数
}
运行后:
HungerSingleton构造函数
…
HungerSingleton析构函数
等到用的的时候程序再创建实例对象
第一次使用实例对象时,创建对象,进程启动无负载。多个单例实例启动顺序自由控制
非线程安全,当多进程同时创建时,会发生争抢,需要加锁和双重检验
无内存管理,会发生内存泄漏
为保证多线程下的线程安全,使用双重检验,对单例对象加锁保证竞争不会重复创建
/*
* LazySingleton.h
*/
#pragma once
#ifndef _LAZYSINGLETON_H_
#define _LAZYSINGLETON_H_
#include
#include
class LazySingleton {
private:
static LazySingleton* instance; //指向对象的指针
LazySingleton();
~LazySingleton();
static std::mutex m_mtx; //互斥锁
public:
static LazySingleton* GetInstance();
};
#endif // !_LAZYSINGLETON_H_
/*
* LazySingleton.cpp
*/
#include"LazySingleton.h"
LazySingleton* LazySingleton::instance = nullptr; //初始化为空指针
std::mutex LazySingleton::m_mtx;
LazySingleton::LazySingleton() {
std::cout << "LazySingleton构造函数" << std::endl;
}
LazySingleton::~LazySingleton() {
std::cout << "LazySingleton析构函数" << std::endl;
}
LazySingleton* LazySingleton::GetInstance() {
if (instance == nullptr) {
//加锁
std::lock_guard<std::mutex>lock(m_mtx); //类模板,只提供构造函数和析构函数,在析构时自动解锁,可以使用{}来限制使用范围
if (instance == nullptr) { //双重检验
instance = new LazySingleton();
}
}
return instance;
}
#include"LazySingleton.h"
#include
#include
using namespace std;
void func(int n)
{
cout << LazySingleton::GetInstance() << endl;
}
int main() {
thread t1(func, 10);
thread t2(func, 10);
t1.join(); //进程1运行:创建LazySingleton 单例对象地址不变
t2.join(); //进程2运行:不用创建
cout << LazySingleton::GetInstance() << endl; //不用创建
cout << LazySingleton::GetInstance() << endl; //不用创建
//懒汉模式不会自动析构
return 0;
}
运行后:
LazySingleton构造函数
…
结束时不会自动调用析构函数
在11新标准中,规定静态变量的创建符合线程安全
1.变量在代码第一次执行到变量声明的地方时初始化。
2.初始化过程中发生异常的话视为未完成初始化,未完成初始化的话,需要下次有代码执行到相同位置时再次初始化。
3.在当前线程执行到需要初始化变量的地方时,如果有其他线程正在初始化该变量,则阻塞当前线程,直到初始化完成为止。
4.如果初始化过程中发生了对初始化的递归调用,则视为未定义行为。
详细讲解参见博客,大佬讲的很详细了
//c++11中局部静态变量的生命周期从第一次创建到程序结束
#pragma once
#ifndef _SINGLETON_H_
#define _SINGLETON_H_
#include
class Singleton {
public:
//局部静态特性的方式实现单例。
//静态局部变量只在当前函数内有效,其他函数无法访问。
//静态局部变量只在第一次被调用的时候初始化,也存储在静态存储区,生命周期从第一次被初始化起至程序结束止。
static Singleton& GetInstance() {
static Singleton instan;
return instan;
}
void Print() {
std::cout << "单例地址:" << this << std::endl;
}
private:
Singleton() {
std::cout << "构造" << std::endl;
}
};
#endif // !_SINGLETON_H_
#include"Singleton.h"
#include
#include
using namespace std;
void func(int n)
{
//cout << LazySingleton::GetInstance() << endl;
Singleton::GetInstance().Print();
}
int main() {
Singleton::GetInstance().Print(); //在单线程下先创建静态变量,防止多线程竞争
/*
*其实不必要提前使用GetInstance()创建静态变量,
*c++11中对静态变量的创建已经是线程安全的了,
*在多线程竞争的情况下,若有线程已经开始初始化静态变量,其余线程就会阻塞以保证线程安全。
*/
thread t1(func, 10);
thread t2(func, 10);
t1.join();
t2.join();
return 0;
}
运行后:
构造
单例地址:0046643C
单例地址:0046643C
结束时也不会自动调用析构函数
使用内嵌静态垃圾回收类实现单例的析构
在懒汉类中加入GC垃圾回收类,该类只有一个公有析构函数,在此析构函数中析构懒汉类的静态变量成员,需要在类外声明其静态对象
当主函数结束时,调用GC垃圾回收类静态对象的析构函数,释放资源
/*
* LazySingleton.h
*/
#pragma once
#ifndef _LAZYSINGLETON_H_
#define _LAZYSINGLETON_H_
#include
#include
class LazySingleton {
private:
static LazySingleton* instance;
LazySingleton();
~LazySingleton();
static std::mutex m_mtx; //互斥锁
class GCarba { //GC垃圾回收类,该类为私有成员
public: //其析构函数设为公有
~GCarba() {
std::cout << "GCarba析构" << std::endl;
if (instance != nullptr) { //若静态对象不为空,显示调用其析构函数
delete instance;
}
}
};
static GCarba garba;
public:
static LazySingleton* GetInstance();
};
#endif // !_LAZYSINGLETON_H_
/*
* LazySingleton.cpp
* 多加一行
*/
LazySingleton::GCarba LazySingleton::garba; //声明GC垃圾回收类静态对象
运行后:
LazySingleton构造函数
…
GCarba析构
LazySingleton析构函数
GetInstance()函数返回智能指针时,创建了临时对象,导致引用计数又加了1
销毁采用局部静态类进行销毁,析构函数要声明为公有,否则智能指针析构时无法访问
/*
* LazySingleton.h
*/
#pragma once
#ifndef _LAZYSINGLETON_H_
#define _LAZYSINGLETON_H_
#include
#include
class LazySingleton {
private:
//static LazySingleton* instance;
static std::shared_ptr<LazySingleton> instance;
/**/
LazySingleton();
//~LazySingleton();
LazySingleton(LazySingleton const&) = delete;
LazySingleton& operator=(LazySingleton const&) = delete;
static std::mutex m_mtx; //互斥锁
/**/
class GCarba {
public:
~GCarba() {
std::cout << "GCarba析构" << std::endl;
if (instance != nullptr) { //若静态对象不为空,显示调用其析构函数
//std::cout << instance.use_count() << std::endl;
instance.reset();
//instance.reset();
//std::cout << instance.use_count() << std::endl; //静态类析构调用智能指针析构,引用计数变0
std::cout << "智能指针析构" << std::endl;
}
}
};
static GCarba garba;
public:
static std::shared_ptr<LazySingleton> GetInstance();
~LazySingleton();
};
#endif // !_LAZYSINGLETON_H_
/*
* LazySingleton.cpp
*/
#include"LazySingleton.h"
std::shared_ptr<LazySingleton> LazySingleton::instance;
std::mutex LazySingleton::m_mtx;
LazySingleton::GCarba LazySingleton::garba;
LazySingleton::LazySingleton() {
std::cout << "LazySingleton构造函数" << std::endl;
}
/**/
LazySingleton::~LazySingleton() {
std::cout << "LazySingleton析构函数" << std::endl;
}
std::shared_ptr<LazySingleton> LazySingleton::GetInstance() {
if (instance == nullptr) {
//加锁
std::lock_guard<std::mutex>lock(m_mtx);
if (instance == nullptr) {
std::cout << instance.use_count() << std::endl; //还没智能指针置空,引用计数为0
instance.reset(new LazySingleton());
//instance = std::make_shared(new LazySingleton());
std::cout << instance.use_count() << std::endl; //智能指针指向新对象,引用计数为1
}
}
return instance; //返回一个智能指针,引用计数为2
}
/*
* main
*/
cout << LazySingleton::GetInstance().use_count() << endl; // 2
cout << LazySingleton::GetInstance() << endl;
cout << LazySingleton::GetInstance().use_count() << endl; // 2
cout << LazySingleton::GetInstance().use_count() << endl; // 2
return 0; //先析构静态GC类,在其中析构instance智能指针。在类内instance引用计数为1,
/*
* 使用LazySingleton::GetInstance().use_count()中GetInstance()返回智能指针,创建了临时对象,导致引用计数变为2。
*/
运行后:
LazySingleton构造函数
…
GCarba析构
LazySingleton析构函数
智能指针析构
熟悉了几种单例模式的实现方法,了解了如何保证线程安全以及如何自动调用析构函数的方法,对于智能指针有了初步的了解。