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

外国建筑设计网站汕头seo推广优化

外国建筑设计网站,汕头seo推广优化,网站建设与维护试卷第九章,上海市企业服务云文章目录 1、为什么需要智能指针?2、内存泄漏3、智能指针的使用及原理1、RAII思想2、拷贝问题1、unique_ptr2、shared_ptr1、多线程2、循环引用3、定制删除器 1、为什么需要智能指针? 看一个场景 int div() {int a, b;cin >> a >> b;if (b…

文章目录

  • 1、为什么需要智能指针?
  • 2、内存泄漏
  • 3、智能指针的使用及原理
    • 1、RAII思想
    • 2、拷贝问题
      • 1、unique_ptr
      • 2、shared_ptr
        • 1、多线程
        • 2、循环引用
        • 3、定制删除器


1、为什么需要智能指针?

看一个场景

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

new是可能开辟失败,抛异常的。上述代码中,如果p1抛异常,那么可以外面的catch可以捕获到,打印出消息;如果p1异常,p2也要抛异常,那么在这之前,应当销毁p1,再去抛;同理,到了div()那里如果也抛异常,那么得销毁p1和p2,整体就得这样写

void Func()
{// 1、如果p1这里new 抛异常会如何?// 2、如果p2这里new 抛异常会如何?// 3、如果div调用这里又会抛异常会如何?int* p1 = new int;try{int* p2 = new int;}catch (...){delete p1;throw;}try{cout << div() << endl;}catch (...){delete p1;delete p2;throw;}delete p1;delete p2;
}

一下子就能看出来,这太麻烦了,如果有多个new呢?

2、内存泄漏

在这里插入图片描述

在这里插入图片描述

Windows和Linux都有检测内存泄漏的工具,不过Windows下的VLD不太靠谱,Linux中valgrind是比较出名的

Linux下几款C++程序中的内存泄露检查工具

为了预防内存泄漏,常用的办法就是用智能指针或者事后检测。

3、智能指针的使用及原理

1、RAII思想

template <class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){if (_ptr){cout << _ptr << endl;delete _ptr;}}private:T* _ptr;
};void Func()
{SmartPtr<int> sp1(new int);SmartPtr<int> sp2(new int);cout << div() << endl;
}

和封装锁的思路来类似,都是RAII。用临时变量来构造,出了作用域就自动销毁。

RAII利用对象生命周期来控制程序资源,对象构造时获取资源,析构时释放资源

上面的SmartPtr不像一个指针,它不能解引用数据,不过我们可以写对应的函数。

	T& operator*(){return *_ptr;}T* operator->(){return _ptr;}cout << *sp1 << endl;//如果模板参数是自定义类型的话就可以用->了。

2、拷贝问题

智能指针如何拷贝?

int main()
{SmartPtr<int> sp1(new int(1));SmartPtr<int> sp2(sp1);return 0;
}

采用默认拷贝会浅拷贝,导致同一空间重复释放。这里应当如何写拷贝构造?是要用深拷贝吗?其实不是,我们要的浅拷贝,sp1和sp2指向同一个资源,以前的链表等这些迭代器结构不需要释放资源,而智能指针需要管理资源,所以不能单纯地浅拷贝,但是又不能要深拷贝。

C++98时已经有智能指针了,那个版本中有一个auto_ptr,它的方法是管理权转移,我们写到SmartPtr类中

	//管理权转移auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}auto_ptr<int> sp2(sp1);

这样看就像是用ap指向sp1,然后用sp1的_ptr来初始化sp2的_ptr,然后把sp1的_ptr给置空。虽然看起来是可以的,能解决问题,但有很大隐患,这会导致sp1悬空,如果不知道管理权转移的实际写法,那么下面代码中如果有*sp1就出问题了。程序员用它的时候需要时刻提醒自己,被拷贝对象已经悬空了,不能去解引用它。

在C++11之前,有个可移植的C++库——Boost库,不是标准库,但也胜似标准库,是有C++标准委员会库工作组成员发起的,C++中有很多标准都从Boost中吸收过来,像右值引用,线程库。Boost库有scoped_ptr,weak_ptr,_shared_ptr,C++11中把scoped_ptr改名成unique_ptr。

1、unique_ptr

它的思路是防拷贝

	//防拷贝//C++98思路:只声明不实现,但是还可以在外面强行定义,所以会把它放在私有里//C++11思路:函数后= delete//这里拷贝构造和赋值都写上unique_ptr(const unique_ptr<T>& up) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

不需要拷贝的场景就用它。

2、shared_ptr

引用计数的思路。有多少个指针指向一个空间,那么这个空间的引用计数就是多少。当一个指针要释放时,如果引用计数大于0,那就不做操作,如果等于0,那就做一次释放资源,这个空间的指针也都用完了。

引用计数这个变量不能放在静态区,因为如果static修饰后,它属于类的每个对象,但我们要的是指向同一空间的所有指针。定义一个int* pcount。

	shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)){}~shared_ptr(){if (--(*_pcount) == 0){cout << _ptr << endl;delete _ptr;delete _pcount;}}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){++(*_pcount);}

赋值函数,比如sp1 = sp3,那么sp1的引用计数需要–,因为它要指向新空间了;假设sp1的空间还有别的指针指向,而sp3的空间只有sp3这一个指针,sp3 = sp1,那么就是sp3–。

	shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._str){if (--(*_pcount) == 0)//处理空间上只有一个指针的情况{delete _ptr;delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}

1、多线程

整体改成这样的形式来配合加锁

	shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)){}void Release(){if (--(*_pcount) == 0){if(_ptr)//如果为空那就不需要释放{delete _ptr;}delete _pcount;}}void AddCount(){++(*_pcount);}~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){AddCount();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._str){Release();_ptr = sp._ptr;_pcount = sp._pcount;AddCount();}return *this;}

多线程比较常见的场景就是线程安全问题。同一个数会出现多次操作,导致结果不是我们想要的。多线程情况下,像传给接收引用的参数时,要写成ref(…),ref是库中的函数,否则会被认为是传值传参。

template <class T>
class shared_ptr
{
public:shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}void Release(){_pmtx.lock();if (--(*_pcount) == 0){if(_ptr){delete _ptr;}delete _pcount;}_pmtx.unlock();}void AddCount(){_pmtx.lock();++(*_pcount);_pmtx.unlock();}~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._str){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp->_pmtx;AddCount();}return *this;}//防拷贝//C++98思路:只声明不实现,但是还可以在外面强行定义,所以放在私有里//C++11思路:函数后= delete//unique_ptr(const unique_ptr<T>& up) = delete;//unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;int* pcount;mutex* _pmtx;
};

在Release那里,到了引用计数减到0时,需要释放引用计数,释放锁。如果是在if里释放锁,那么外面的解锁操作就有问题了。 解决办法是可以设置一个状态位

	void Release(){_pmtx.lock();bool deleteFlag = false;if (--(*_pcount) == 0){if(_ptr){delete _ptr;}delete _pcount;deleteFlag = true;}_pmtx.unlock();if (deleteFlag){delete _pmtx;}}

shared_ptr本身是线程安全的,因为计数是加锁保护的,它实例化的对象不是线程安全的,想要线程安全,那么在对对象操作时用锁保护就行。

2、循环引用

写一个场景,还是用上面的shared_ptr

template <class T>
class shared_ptr
{
public:shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}void Release(){_pmtx.lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr){delete _ptr;}delete _pcount;deleteFlag = true;}_pmtx.unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx.lock();++(*_pcount);_pmtx.unlock();}~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._str){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp->_pmtx;AddCount();}return *this;}//防拷贝//C++98思路:只声明不实现,但是还可以在外面强行定义,所以放在私有里//C++11思路:函数后= delete//unique_ptr(const unique_ptr<T>& up) = delete;//unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;int* pcount;mutex* _pmtx;
};struct ListNode
{ListNode* _next;ListNode* _prev;int _val;~ListNode(){cout << "~ListNode" << endl;}
};int main()
{shared_ptr<ListNode> n1 = new ListNode;shared_ptr<ListNode> n2 = new ListNode;return 0;
}

当尝试连接两个节点时就发生了错误

	n1->_next = n2;n2->_prev = n1;

n1和n2是智能指针类型,而next和prev是ListNode类型的,无法赋值,那把ListNode里的两个指针换成shared_ptr< ListNode >类型的,但这样还不行,因为我们在定义next和prev时没有传参,是无参构造,所以在智能指针的类里应当写上缺省参数。

	shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}struct ListNode
{shared_ptr<ListNode> _next;shared_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode" << endl;}
};

现在有一个问题

	n1->_next = n2;n2->_prev = n1;

如果两句都写,程序会不释放资源,而如果只写一句或者两句都不写,那就会释放资源,就会打印"~ListNode",使用库中的智能指针也是这样,这就是智能指针引起的循环引用问题。

n1和n2都有各自的next和prev,如果不相互连接,也就是什么都不写,那么next和prev随着n1n2销毁而销毁。

写了一句,比如n1->_next = n2,那么n2这个节点除了它本身,还有n1的next指向它,n2析构时,引用计数–,但是空间不销毁,n1析构时,里面的成员变量也会随着析构,那么整体也可以完好地退出。

但是两句都写就出问题了。

	n1->_next = n2;n2->_prev = n1;

出了作用域,n2先析构,引用计数–,但是还不能销毁空间,引用计数没有为0,也还有一个指针指向它;n1析构时,n1也是一样,也不能析构,引用计数–,现在这两个空间的引用计数都为1,n1的next指向n2的空间,n2的prev指向n1的空间,那么n1这个空间什么时候析构?要看prev,prev析构,n1这个空间就析构,但是n2这个空间由next指向,next析构,n2才能析构,prev才能析构,所以next和prev已经形成了相互制约的关系,没办法全部析构了。这就是循环引用,会导致内存泄漏。

为了解决这个问题,标准库中有个weak_ptr来辅助shared_ptr,也叫做弱指针。weak_ptr不是RAII的,也就是它不是常规的智能指针,但是支持像指针一样,专门用来解决shared_ptr的辅助引用问题。用weak这样写。

struct ListNode
{std::weak_ptr<ListNode> _next;std::weak_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode" << endl;}
};

weak_ptr不会增加引用计数。标准库中weak_ptr实现得很复杂,我们这里只模拟实现一个简单的

	template <class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}int use_count(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;};
struct ListNode
{zyd::weak_ptr<ListNode> _next;zyd::weak_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode" << endl;}
};int main()
{zyd::shared_ptr<ListNode> n1 = new ListNode;zyd::shared_ptr<ListNode> n2 = new ListNode;n1->_next = n2;n2->_prev = n1;return 0;
}

3、定制删除器

在实例化的时候,传new int[10]这样的话,可能会崩溃,是因为new []会在开辟的空间前再开辟一个存放元素个数的空间,但是delete的时候会从开辟的空间开始释放,而不包含那个存储个数的空间,所以本质上是释放的位置不对。

定制删除器本质上是一个可调用对象,函数指针,仿函数,lambda都可以。

template <class T>
struct DeleteArray
{void operator()(T* ptr){cout << "仿函数" << endl;delete[] ptr;}
};int main()
{//zyd::shared_ptr<ListNode> n1 = new ListNode;//zyd::shared_ptr<ListNode> n2 = new ListNode;//n1->_next = n2;//n2->_prev = n1;std::shared_ptr(int) spa1(new int[10], DeleteArray<int>());//仿函数std::shared_ptr(int) spa2(new int[10], [](int* ptr) {delete[] ptr; });//lambdareturn 0;
}

库中的做法是把这个删除器放到构造函数里,实例化的时候传过来,保存起来,析构时用它去析构。这里的重点在于如何保存这个删除器。一个是我们可以在总的模板参数那里加一个模板参数,那么析构函数就可以直接用,也不用在构造函数那里在写上一个模板参数;或者用包装器。这里写包装器。

		template <class D>shared_ptr(const shared_ptr<T>& sp, D del):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx), _del(del){AddCount();}void Release(){_pmtx.lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr){//delete _ptr;_del(_ptr);}delete _pcount;deleteFlag = true;}_pmtx.unlock();if (deleteFlag){delete _pmtx;}}private:T* _ptr;int* _pcount;mutex* _pmtx;functional<void(T*)> _del;

这样写其实会有问题,如果用不到这个删除器就会调用默认构造,删除器没有初始化,到了析构时,删除器就是被编译器默认初始化的,用它来析构就容易出问题。我们可以用缺省

	private:T* _ptr;int* _pcount;mutex* _pmtx;functional<void(T*)> _del = [](T* ptr) {cout << "lambda delete:" << ptr << endl;delete ptr;};

定制删除器当作了解,重点在于shared_ptr的实现。

本篇gitee

结束。


文章转载自:
http://dvandva.rqjL.cn
http://demark.rqjL.cn
http://vtech.rqjL.cn
http://discontented.rqjL.cn
http://nonluminous.rqjL.cn
http://danthonia.rqjL.cn
http://stramonium.rqjL.cn
http://splake.rqjL.cn
http://nucleolonema.rqjL.cn
http://plumbous.rqjL.cn
http://monadology.rqjL.cn
http://ruthful.rqjL.cn
http://semantic.rqjL.cn
http://polygyny.rqjL.cn
http://astrid.rqjL.cn
http://jugendstil.rqjL.cn
http://neutretto.rqjL.cn
http://reach.rqjL.cn
http://dtv.rqjL.cn
http://quite.rqjL.cn
http://bronzer.rqjL.cn
http://purpurate.rqjL.cn
http://cachaca.rqjL.cn
http://senegal.rqjL.cn
http://irretentive.rqjL.cn
http://kohl.rqjL.cn
http://lowercase.rqjL.cn
http://bruit.rqjL.cn
http://chiropodist.rqjL.cn
http://cologne.rqjL.cn
http://pedagogue.rqjL.cn
http://nonmiscibility.rqjL.cn
http://benchmark.rqjL.cn
http://anaphylactoid.rqjL.cn
http://rheologist.rqjL.cn
http://onding.rqjL.cn
http://gimp.rqjL.cn
http://mpp.rqjL.cn
http://deshabille.rqjL.cn
http://allottee.rqjL.cn
http://duna.rqjL.cn
http://petit.rqjL.cn
http://rainy.rqjL.cn
http://flapdoodle.rqjL.cn
http://carcel.rqjL.cn
http://reoccupation.rqjL.cn
http://quincunx.rqjL.cn
http://horology.rqjL.cn
http://segment.rqjL.cn
http://emprise.rqjL.cn
http://depone.rqjL.cn
http://prologue.rqjL.cn
http://slaveholding.rqjL.cn
http://ingush.rqjL.cn
http://telferage.rqjL.cn
http://belong.rqjL.cn
http://arctoid.rqjL.cn
http://paraesthesia.rqjL.cn
http://isomerous.rqjL.cn
http://photoreaction.rqjL.cn
http://amie.rqjL.cn
http://ironware.rqjL.cn
http://cosmorama.rqjL.cn
http://zambia.rqjL.cn
http://cotechino.rqjL.cn
http://hairpin.rqjL.cn
http://breathe.rqjL.cn
http://ental.rqjL.cn
http://machete.rqjL.cn
http://skullcap.rqjL.cn
http://cupriferous.rqjL.cn
http://divider.rqjL.cn
http://fanwort.rqjL.cn
http://caricature.rqjL.cn
http://codec.rqjL.cn
http://lop.rqjL.cn
http://phaeton.rqjL.cn
http://reaction.rqjL.cn
http://recklinghausen.rqjL.cn
http://fjeld.rqjL.cn
http://schizothymia.rqjL.cn
http://pyrophile.rqjL.cn
http://mesocratic.rqjL.cn
http://paye.rqjL.cn
http://geoanticline.rqjL.cn
http://thoughtful.rqjL.cn
http://chyme.rqjL.cn
http://damningly.rqjL.cn
http://parasitoid.rqjL.cn
http://ambience.rqjL.cn
http://ergogram.rqjL.cn
http://gymnasium.rqjL.cn
http://audio.rqjL.cn
http://disposedly.rqjL.cn
http://marzipan.rqjL.cn
http://forespent.rqjL.cn
http://share.rqjL.cn
http://closely.rqjL.cn
http://psychologist.rqjL.cn
http://partlet.rqjL.cn
http://www.dt0577.cn/news/87740.html

相关文章:

  • 做外包的网站有哪些怎么自己做个网站
  • 宁波网站建设工作室什么是信息流广告
  • 织梦网站怎么做二级域名淄博头条新闻今天
  • 政府网站建设集约化是什么意思软文写作模板
  • 网站专题页怎么做百度seo如何快速排名
  • 阜南做网站公司视频优化软件
  • 网站美工做图seo网站优化软件
  • 办公厅政府网站建设关键词推广是什么
  • 家电网站建设人民日报官网
  • 江苏伟业建设集团网站电商代运营公司十强
  • 新疆和田住房和城乡建设网站百度投放
  • dw做公司网站做个小程序需要花多少钱
  • 网站水晶头怎么做竞价托管
  • 苏州模板做网站微信视频号小店
  • 小目标网站建设快速排名怎么做
  • 网站优化成都哪里好网络服务主要包括
  • 餐饮外哪个网站做推广小红书seo排名优化
  • 怎么做一帘幽梦网站吉林黄页电话查询
  • 制作网站免费建站百度最新财报
  • 黑龙江牡安建设有限公司网站苏州搜索引擎排名优化商家
  • 做视频特效的网站有哪些营销号
  • 国内免费视频素材无水印素材网站关键词搜索名词解释
  • 盐城网站开发公司温州seo公司
  • 草坪网站怎么做网络运营主要做什么工作
  • 香港台湾人做攻略用什么网站百度高搜
  • 网站开发的需求分析论文拼多多关键词排名查询
  • 广州机械网站建设外包百度网盘客服人工电话95188
  • 深圳附近做个商城网站找哪家公司好淘宝客推广一天80单
  • 罗湖田贝社区网站建设seo外链优化方法
  • 美丽南方官网网站建设关键词网站查询