C++ 设计模式->单例模式
单例模式
特点: 该类有且只有一个实例,并只能通过接口函数获取到该类的实例
所以,该类的设计需要满足:
1.构造函数私有化
2.禁止赋值和拷贝
3.提供获取该类的实例接口
代码演示
1.简单的单例实现
#include
using namespace std;
class MyClass
{
private:
MyClass(){ }; // 私有化构造函数
MyClass(MyClass&) = delete; // 禁止拷贝构造函数
MyClass& operator=(const MyClass&) = delete; // 禁止赋值运算符
public:
~MyClass() { };
public:
static MyClass* instance() { // 单例接口函数
if (nullptr == _instance) {
_instance = new MyClass();
}
return _instance;
}
private:
static MyClass *_instance;
};
MyClass *MyClass::_instance = nullptr;
int main(int arg, char **argv)
{
MyClass* a = MyClass::instance(); // 获取实例
MyClass* b = MyClass::instance(); // 获取实例
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
调试结果:可看到对象 a 和对象 b 是一样的;
那多线程的情况下上边的代码还能适用吗
我们测试一下
修改一下代码
#include
#include
using namespace std;
class MyClass
{
private:
MyClass() // 私有化构造函数
{
// 假设初始化时需要的时间
this_thread::sleep_for(chrono::milliseconds(100));
};
MyClass(MyClass&) = delete; // 禁止拷贝构造函数
MyClass& operator=(const MyClass&) = delete; // 禁止赋值运算符
public:
~MyClass() { };
public:
static MyClass* instance() { // 单例接口函数
if (nullptr == _instance) {
_instance = new MyClass();
}
return _instance;
}
private:
static MyClass *_instance;
};
MyClass *MyClass::_instance = nullptr;
MyClass *a = nullptr;
MyClass *b = nullptr;
// 线程函数1
void threadFun1()
{
a = MyClass::instance(); // 获取实例
}
// 线程函数2
void threadFun2()
{
b = MyClass::instance(); // 获取实例
}
int main(int arg, char **argv)
{
// 开启2个线程,差不多同时去访问接口
thread t1(threadFun1);
thread t2(threadFun2);
t1.detach();
t2.detach();
// 测试使用延迟一会儿。
this_thread::sleep_for(chrono::milliseconds(1000));
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
调试结果
可以看到 a 对象 和 b 对象是不一样的,与我们想要的单例不符合
那这时候如何去改进呢。
没错,给它加上锁
接下来,我们给接口函数加上锁
#include
#include
#include
using namespace std;
static mutex _mutex;
class MyClass
{
private:
MyClass() // 私有化构造函数
{
// 假设初始化时需要的时间
this_thread::sleep_for(chrono::milliseconds(100));
};
MyClass(MyClass&) = delete; // 禁止拷贝构造函数
MyClass& operator=(const MyClass&) = delete; // 禁止赋值运算符
public:
~MyClass() { };
public:
static MyClass* instance() { // 单例接口函数
if (nullptr == _instance) {
_mutex.lock(); // 加锁
if (nullptr == _instance) {
_instance = new MyClass();
}
_mutex.unlock(); // 解锁
}
return _instance;
}
private:
static MyClass *_instance;
};
MyClass *MyClass::_instance = nullptr;
MyClass *a = nullptr;
MyClass *b = nullptr;
// 线程函数1
void threadFun1()
{
a = MyClass::instance(); // 获取实例
}
// 线程函数2
void threadFun2()
{
b = MyClass::instance(); // 获取实例
}
int main(int arg, char **argv)
{
// 开启2个线程,差不多同时去访问接口
thread t1(threadFun1);
thread t2(threadFun2);
t1.detach();
t2.detach();
// 测试使用延迟一会儿。
this_thread::sleep_for(chrono::milliseconds(1000));
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
调试结果
可以看到 a 和 b 是一样的了,符合单例。
这样就好了吗?
但还有一个问题,我再修改一下代码
在构造函数和析构函数添加一句输出语句
#include
#include
#include
using namespace std;
static mutex _mutex;
class MyClass
{
private:
MyClass() // 私有化构造函数
{
// 假设初始化时需要的时间
this_thread::sleep_for(chrono::milliseconds(100));
cout << "我是构造函数" << endl;
};
MyClass(MyClass&) = delete; // 禁止拷贝构造函数
MyClass& operator=(const MyClass&) = delete; // 禁止赋值运算符
public:
~MyClass() { cout << "我是析构函数" << endl; };
public:
static MyClass* instance() { // 单例接口函数
if (nullptr == _instance) {
_mutex.lock(); // 加锁
if (nullptr == _instance) {
_instance = new MyClass();
}
_mutex.unlock(); // 解锁
}
return _instance;
}
private:
static MyClass *_instance;
};
MyClass *MyClass::_instance = nullptr;
MyClass *a = nullptr;
MyClass *b = nullptr;
// 线程函数1
void threadFun1()
{
a = MyClass::instance(); // 获取实例
}
// 线程函数2
void threadFun2()
{
b = MyClass::instance(); // 获取实例
}
int main(int arg, char **argv)
{
// 开启2个线程,差不多同时去访问接口
thread t1(threadFun1);
thread t2(threadFun2);
t1.detach();
t2.detach();
// 测试使用延迟一会儿。
this_thread::sleep_for(chrono::milliseconds(1000));
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
调试结果
我们发现了什么?
该类的实例没有被析构
也就是需要手动去析构
但有时候我们会忘了去给对象析构,这样就造成内存泄漏
那这时候怎么办呢? 使用智能指针!
嗯,智能指针是可以完成这个重任,但本次不采用智能指针的形式
我们修改一下代码
#include
#include
#include
using namespace std;
static mutex _mutex;
class MyClass
{
private:
MyClass() // 私有化构造函数
{
// 假设初始化时需要的时间
this_thread::sleep_for(chrono::milliseconds(100));
cout << "我是构造函数" << endl;
};
MyClass(MyClass&) = delete; // 禁止拷贝构造函数
MyClass& operator=(const MyClass&) = delete; // 禁止赋值运算符
public:
~MyClass() { cout << "我是析构函数" << endl; };
public:
static MyClass& instance() { // 单例接口函数
static MyClass _instance;
return _instance;
}
};
MyClass *a = nullptr;
MyClass *b = nullptr;
// 线程函数1
void threadFun1()
{
a = &MyClass::instance(); // 获取实例
}
// 线程函数2
void threadFun2()
{
b = &MyClass::instance(); // 获取实例
}
int main(int arg, char **argv)
{
// 开启2个线程,差不多同时去访问接口
thread t1(threadFun1);
thread t2(threadFun2);
t1.detach();
t2.detach();
// 测试使用延迟一会儿。
this_thread::sleep_for(chrono::milliseconds(1000));
cout << "a=" << a << endl;
cout << "b=" << b << endl;
return 0;
}
调试结果
把它修改为 static 方式后,不用线程锁也不用手动去释放了
不好的地方可能就是需要用引用的方式获取实例
目前来说,本人觉得该方式应该是比较好的一种方式了
至于什么懒汉式,饿汉之类的区分。。。
这里就不区分了。。。。
理解就好了。
...
_End
完事儿。