当前位置: 首页 > news >正文

做机械设计的网站北京seo助理

做机械设计的网站,北京seo助理,网站建设的主要流程有哪些,日常网站维护怎么做目录 0. 前言 1. 懒汉式单例模式 1.1 最简单的单例模式 1.2 防止内存泄漏 1.2.1 智能指针的方法 1.2.2 静态嵌套的方法 1.3 保证线程安全 1.4 C11版本的优雅解决方案 2. 饿汉式单例模式 0. 前言 起因是在程序中重复声明了一个单例模式的变量,后来程序怎么调…

目录

0. 前言

1. 懒汉式单例模式

1.1 最简单的单例模式

1.2 防止内存泄漏

1.2.1 智能指针的方法

1.2.2 静态嵌套的方法

1.3 保证线程安全

1.4 C++11版本的优雅解决方案

2. 饿汉式单例模式


0. 前言

起因是在程序中重复声明了一个单例模式的变量,后来程序怎么调都不对,最后发现变量是用单例模式,修改是全局的,所以决定好好梳理一下单例模式。

首先,为什么要用单例模式,就是因为我们希望一个类只有唯一一个实例,并且提供一个全局的访问点。从这个描述不难看出,这个实例应该是要static来修饰的。实际情况中,比如我们想申请一个内存池,程序都用这一块内存池,那么就可以单例模式来实现

1. 懒汉式单例模式

1.1 最简单的单例模式

先来看看最简单的单例模式怎么写,然后分析一下有什么问题。

#include <iostream>
using namespace std;// 最简单的单例模式
class Singleton{
public:// 获取实例的接口static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton(); // 使用new来创建一个新的实例对象} else {cout << "重复创建,返回已创建的实例。" << endl;}return instance;}
private:// 静态私有对象static Singleton* instance;// 构造函数一定要私有,外部无法直接访问构造函数Singleton() {cout << "运行构造函数" << endl;};~Singleton() {cout << "运行析构函数" << endl;};
};
// 要在类外进行初始化!!!
Singleton* Singleton::instance = nullptr;int main(){Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();return 0;
}

这里有两个点需要特别的注意:

  • 单例模式的类需要有一个静态私有的对象,是这个类的实例,且必须在类外进行初始化。
  • 获取实例的接口getInstance()函数式可以被访问和调用的,但是必须返回static类型的变量,实际上就是返回这个类的唯一实例
  • 补充下析构函数私有化的原因:保证只能在堆上new一个新的类对象。因为C++是一个静态绑定的语言。在编译过程中,所有的非虚函数调用都必须分析完成,即使是虚函数也需检查可访问性。当在栈上生成对象时,对象会自动析构,也就说析构函数必须可以访问。而堆上生成对象,由于析构时机由程序员控制,所以不一定需要析构函数。

按照上面的写法,基本上满足了单例模式的初衷,要一个只有一个实例的类。但是存在两个问题,一个是因为使用到了new进行创建,就需要人为进行delete的释放操作,否则就会造成内存泄漏。第二个是程序乍一看只会创建一块内存空间,但是如果考虑多线程,那么就有可能多个线程分别创建了多块内存空间的实例,与我们设计单例模式的初衷相违背。

1.2 防止内存泄漏

1.2.1 智能指针的方法

运行1.1的程序,结果为:

运行构造函数
重复创建,返回已创建的实例。

可以发现,并没有调用析构函数。这里,补充一下析构函数的作用:释放对象的使用资源,并销毁对象的非static数据成员。而我们定义的instance成员变量static的,所以无法直接使用析构函数进行释放。虽然事例的简单程序在运行完之后static变量会自动释放,但是在很多复杂的程序中,使用完instance却不释放是非常致命的会导致内存泄漏的问题。这里采用智能指针的方法,并借用智能指针的reset函数,定义一个销毁的成员函数,通过这个成员函数调用delete来释放我们创建的new内存,达到析构的目的。看看下面的实现。

#include <iostream>
#include <memory>
using namespace std;class Singleton{
public:// 公有接口获取唯一实例static shared_ptr<Singleton> getInstance() {if (instance == nullptr) {instance.reset(new Singleton(), destoryInstance);}else {cout << "重复创建,返回异创建的实例。" << endl;}return instance;}// 定义销毁的实例static void destoryInstance(Singleton* x) {cout << "自定义释放实例" << endl;delete x;}
private:Singleton() {cout << "运行构造函数。" << endl;};~Singleton() {cout << "运行析构函数。" << endl;};
private:// 静态私有对象static shared_ptr<Singleton> instance;
};// 初始化
shared_ptr<Singleton> Singleton::instance;int main(){shared_ptr<Singleton> s1 = Singleton::getInstance();shared_ptr<Singleton> s2 = Singleton::getInstance();return 0;
}

运行结果为:

运行构造函数。
重复创建,返回异创建的实例。
自定义释放实例
运行析构函数。

可以看到,我们通过智能指针,在使用完instance资源后调用了自定义的释放函数,即delete了new出来的空间,达到了运行析构函数的目的,防止了内存泄漏。

1.2.2 静态嵌套的方法

解决内存的泄漏的方法,总之是要把释放的过程先写好,不能靠用户每次自己释放。对于本次分享的例子,就是要把delete放进代码里。除了利用智能指针的释放函数来调用delete之外,也可以显式的调用delete函数,要单独嵌套一个类,把这个delete函数放进嵌套类的公有析构函数中。实现过程如下:

#include <iostream>
using namespace std;
class Singleton{
public:// 公有接口获取唯一实例static Singleton* getInstance() {if (instance == nullptr) {if (instance == nullptr) {instance = new Singleton();}}else {cout << "重复创建,返回已创建的实例。" << endl;}return instance;}
private:Singleton() {cout << "运行构造函数。" << endl;};~Singleton() {cout << "运行析构函数。" << endl;};// 定义一个删除器class Deleter {public:Deleter() {};~Deleter() {if (instance != nullptr) {cout << "删除器启动。" << endl;delete instance;instance = nullptr;}}};static Deleter deleter; // 删除器也是静态成员变量
private:// 静态私有对象static Singleton* instance;
};// 初始化
Singleton* Singleton::instance = nullptr;
Singleton::Deleter Singleton::deleter;int main()
{Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();return 0;
}

运行结果为:

运行构造函数。
重复创建,返回已创建的实例。
删除器启动。
运行析构函数。

1.3 保证线程安全

首先修改一下1.1中的程序,主要是增加一些打印,然后用多个线程创建Singleton的实例,看看是否每个线程都是访问的同一个内存地址。

#include <iostream>
#include <thread>
using namespace std;class Singleton{
public:// 获取实例的接口static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton(); // 使用new来创建一个新的实例对象cout << "创建地址为:" << instance << endl;} else {cout << "重复创建,返回已创建的实例。" << endl;}return instance;}
private:// 静态私有对象static Singleton* instance;// 构造函数一定要私有,外部无法直接访问构造函数Singleton() {cout << "运行构造函数" << endl;};~Singleton() {cout << "运行析构函数" << endl;};
};
// 要在类外进行初始化!!!
Singleton* Singleton::instance = nullptr;int main(){// Singleton* s1 = Singleton::getInstance();// Singleton* s2 = Singleton::getInstance();thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();return 0;
}

运行结果如下:

运行构造函数
创建地址为:0x7f0988000b60
运行构造函数
创建地址为:0x7f0980000b60

可以发现,两个线程分别new出了一段内存空间(有一定几率是同一段,会报重复创建)。显然,这违背了我们单例模式的初衷。

解决方法是进行加锁,让一个线程先执行完,另一个线程才能获得new的权限。代码如下:

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;class Singleton{
public:static Singleton* getInstance() {if (instance == nullptr) {lock_guard<mutex> l(mutex1); // 加锁保证线程安全if (instance == nullptr) {instance = new Singleton();cout << "创建地址为:" << instance << endl;}}else {cout << "重复创建,返回已创建的实例。" << endl;}return instance;}
private:static mutex mutex1;// 锁static Singleton* instance;Singleton() {cout << "运行构造函数" << endl;};~Singleton() {cout << "运行析构函数" << endl;};
};// 初始化
Singleton* Singleton::instance = nullptr;
mutex Singleton::mutex1;int main(){thread t1([](){Singleton* s1 = Singleton::getInstance();});thread t2([](){Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();return 0;
}

运行结果为:

运行构造函数
创建地址为:0x7f90d4000b60
重复创建,返回已创建的实例。

加锁后,即使是多个线程,也只会申请一块内存空间。

1.4 C++11版本的优雅解决方案

上面只是为了将这个问题表述清楚,在C++11中,static变量是可以保证线程安全的,同时直接用static变量而不用new,就可以获得线程安全的且无内存泄漏的优雅写法,如下:

#include <iostream>
#include <thread>
using namespace std;
class Singleton{
public:// 公有接口获取唯一实例static Singleton* getInstance() {static Singleton instance;cout << "地址为:" << &instance << endl;return &instance;}private:Singleton() {cout << "运行构造函数" << endl;};~Singleton() {cout << "运行析构函数" << endl;};
};int main()
{thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();return 0;
}

运行结果如下:

运行构造函数
地址为:0x55b7df269152
地址为:0x55b7df269152
运行析构函数

可以看到访问的内存地址一样,析构函数也正常的运行了。


2. 饿汉式单例模式

饿汉式和懒汉式的差别是,饿汉式提前进行了创建,而如果提前创建static变量,那么在程序开始前这个变量就创建好了,因此不存在线程不安全的问题,只需要保证不内存泄漏即可。用智能指针的方式实现代码如下:

#include <iostream>
#include <thread>
using namespace std;
class Singleton{
public:// 公有接口获取唯一实例static shared_ptr<Singleton> getInstance() {cout << "地址为:" << instance << endl;return instance;}// 定义销毁的实例static void destoryInstance(Singleton* x) {cout << "自定义释放实例" << endl;delete x;}private:Singleton() {cout << "运行构造函数" << endl;};~Singleton() {cout << "运行析构函数" << endl;};
private:// 静态私有对象static shared_ptr<Singleton> instance;
};// 初始化
shared_ptr<Singleton> Singleton::instance(new Singleton(), destoryInstance);int main(){thread t1([] {shared_ptr<Singleton> s1 = Singleton::getInstance();});thread t2([] {shared_ptr<Singleton> s2 = Singleton::getInstance();});t1.join();t2.join();return 0;
}

运行结果为:

运行构造函数。
地址为:0x55977895ceb0
地址为:0x55977895ceb0
自定义释放实例
运行析构函数。

也可以考虑优雅的写法:

#include <iostream>
#include <thread>
using namespace std;
class Singleton{
public:// 公有接口获取唯一实例static Singleton* getInstance() {static Singleton instance;cout << "地址为:" << &instance << endl;return &instance;}private:// 私有构造函数Singleton() {cout << "运行构造函数。" << endl;};// 私有析构函数~Singleton() {cout << "运行析构函数。" << endl;};
};int main(){thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();return 0;
}

输出的结果为:

运行构造函数。
地址为:0x55aa7a895152
地址为:0x55aa7a895152
运行析构函数。

http://www.yidumall.com/news/68178.html

相关文章:

  • wordpress手机客户端源码惠州seo按天付费
  • 网站建设定制设计网址收录大全
  • 三站合一 网站建设百度移动端点赞排名软件
  • 移动开发应用电脑优化大师哪个好
  • 上海电商网站设计软文推广营销平台
  • 订做网站和app公司官网怎么做
  • 公积金中心完善网站建设百度下载app安装
  • 教育网站制作公司seo是付费还是免费推广
  • 做外汇有哪些正规的网站seo推广怎么做视频教程
  • wordpress文章无法置顶抖音seo是什么意思
  • 国际新闻最新消息战争新闻广州优化营商环境条例
  • 效果好的网站建做高端网站公司
  • 做网站知道访客ip超级外链自动发布工具
  • 淘宝可以到哪些网站做推广营销型网站建设专家
  • wordpress会员vip购买扩展关键词排名优化价格
  • 江苏省建设证书变更网站seo站长工具推广平台
  • 甘肃省水利工程建设网站推广软件赚钱的平台
  • 外贸网站源代码中国重大新闻
  • 设计网页怎么插图seo网络推广培训班
  • 织梦同时运行多个网站手机自动排名次的软件
  • 手机网站切图seo技术论坛
  • wordpress解压内江seo
  • 动叫建个网站刷排名百度网盘客服24小时电话人工服务
  • 公司网站留言板如何自己制作网页
  • 利用codeing做网站上海百度推广官方电话
  • 网站导航的作用网络营销公司
  • 西安网站seo技术怎么样把广告做在百度上
  • 做一手房的网站广告加盟
  • 建设工程协会网站查询系统免费个人推广引流平台
  • 怎么通过ip查看自己做的网站今日新闻快报