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

个人手机网站建设珠海城乡建设厅网站

个人手机网站建设,珠海城乡建设厅网站,网站运营频道内容建设,网站建设需求表模板搭配异常可以让异常的代码更简洁 文章目录 智能指针 内存泄漏的危害 1.auto_ptr(非常不建议使用) 2.unique_ptr 3.shared_ptr 4.weak_ptr总结 智能指针 C中为什么会需要智能指针呢#xff1f;下面我们看一下样例#xff1a; int div() {int a, b;cin 中为什么会需要智能指针呢下面我们看一下样例 int div() {int a, b;cin a b;if (b 0)throw invalid_argument(除0错误);return a / b; } void Func() {// 1、如果p1这里new 抛异常会如何// 2、如果p2这里new 抛异常会如何// 3、如果div调用这里又会抛异常会如何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; } 在上面的代码中一旦出现异常那就会造成内存泄漏什么是内存泄漏呢 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对 该段内存的控制因而造成了内存的浪费。 内存泄漏的危害长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现 内存泄漏会导致响应越来越慢最终卡死。 内存泄漏分类 堆内存泄漏 (Heap leak) 堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一 块内存用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分 内存没有被释放那么以后这部分空间将无法再被使用就会产生 Heap Leak 。 系统资源泄漏 指程序使用系统分配的资源比方套接字、文件描述符、管道等没有使用对应的函数释放 掉导致系统资源的浪费严重可导致系统效能减少系统执行不稳定。 如果func函数里的p1new的时候抛异常该怎么办呢对于第一个new如果抛异常会直接跳到main函数中的catch被捕获那么p2new失败了会怎么办呢div函数抛异常我们可以捕获一下 那么如果是p2失败了我们还要再catch一下 void Func() {// 1、如果p1这里new 抛异常会如何// 2、如果p2这里new 抛异常会如何// 3、如果div调用这里又会抛异常会如何int* p1 new int;int* p2 nullptr;try{p2 new int;try{cout div() endl;}catch (...){delete p1;delete p2;throw;}}catch (...){delete p1;delete p2;} } 那么这样的代码看起来会不会有些冗余呢为了处理这样的问题智能指针就能起到很好的作用 template class T class SmartPtr { public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;cout _ptr endl;} private:T* _ptr; }; int div() {int a, b;cin a b;if (b 0)throw invalid_argument(除0错误);return a / b; } void Func() {int* p1 new int;SmartPtrint sp1(p1);int* p2 new int;SmartPtrint sp2(p2);cout div() endl;delete p1;delete p2; } int main() {try{Func();}catch (exception e){cout e.what() endl;}return 0; } 可以看到有了智能指针即使抛异常了我们没有释放的空间也会被自动释放因为抛异常后自定义类型出了作用域我们智能指针的析构函数会将这个空间释放。当然我们的智能指针也可以直接创建资源比如下面这样 但是我们这样写不就不可以对指针解引用访问指针的资源了吗其实我们只需要再给智能指针多加加个功能让它变得像指针一样就解决了这个问题 template class T class SmartPtr { public://保存资源SmartPtr(T* ptr):_ptr(ptr){}//释放资源~SmartPtr(){delete _ptr;cout _ptr endl;}//像指针一样T operator*(){return *_ptr;}T* operator-(){return _ptr;}T operator[](size_t pos){return _ptr[pos];} private:T* _ptr; }; 这个时候我们来使用一下这个指针 可以看到现在我们的这个智能指针也可以正常使用了再次说明一下智能指针的构造函数是为了保存资源析构函数是为了释放资源其他功能是为了和指针一样。我们上面将资源管理的责任托管给对象的做法就叫做RAII资源获得即初始化这就是避免内存泄漏的一种方法。 避免内存泄漏 1. 工程前期良好的设计规范养成良好的编码规范申请的内存空间记着匹配的去释放。 ps 这个理想状态。但是如果碰上异常时就算注意释放了还是可能会出问题。需要下一条智 能指针来管理才有保证。 2. 采用 RAII 思想或者智能指针来管理资源。 3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。 4. 出问题了使用内存泄漏工具检测。 ps 不过很多工具都不够靠谱或者收费昂贵。 总结一下 : 内存泄漏非常常见解决方案分为两种 1 、事前预防型。如智能指针等。 2 、事后查错型。如泄 漏检测工具。 那么C库里面有没有智能指针呢答案是有的并且有好几种。 auto_ptr: 下面我们看一下C中被骂了很多年的一款智能指针auto_ptr. 上图中出错的原因是重复析构了两次sp1,这是因为我们用的编译器自动生成的拷贝构造是个浅拷贝。auto_ptr的最大问题在于就像我们的SmartPtr一样支持拷贝但是又会让另一个指针悬空什么意思呢我们先来调试看一下然后把代码写出来 上图是拷贝之前sp1的资源 上图是拷贝之后sp2的资源我们可以看到auto_ptr的拷贝就是将资源管理权转移原本sp1指向的内容被sp2指向了但是问题就在于auto_ptr竟然让原先的sp1指针悬空了也就是说什么也没指向这就导致不知道的人对原先sp1这个指针解引用等操作这样就对空指针进行解引用了这就是auto_ptr被吐槽的根源所在。下面我们看看auto_ptr是如何实现的 namespace sxy {template class Tclass auto_ptr{public://保存资源auto_ptr(T* ptr):_ptr(ptr){}//拷贝构造auto_ptr(auto_ptrT ap):_ptr(ap._ptr){ap._ptr nullptr;}//释放资源~auto_ptr(){delete _ptr;cout _ptr endl;}//像指针一样T operator*(){return *_ptr;}T* operator-(){return _ptr;}T operator[](size_t pos){return _ptr[pos];}private:T* _ptr;}; } 上面代码中大家重点看auto_ptr的构造函数就可以了 一旦我们对之前的空指针sp1进行解引用操作程序立马就挂掉了。注意auto_ptr这款指针指针很多公司都明确规定不能使用它如果有面试官让你写一款智能指针一定不要写auto_ptr!!! 下面我们讲解三种算是经常被使用的智能指针unique_ptr,   shared_ptr,    weak_ptr.我们可以先看看unique_ptr是如何实现的 unique_ptr: 其实unique_ptr的实现很简单就是直接禁掉了拷贝构造函数和赋值重载。 template class Tclass unique_ptr{public://保存资源unique_ptr(T* ptr):_ptr(ptr){}//拷贝构造unique_ptr(const unique_ptrT up) delete;unique_ptrT operator(const unique_ptrT) delete;//释放资源~unique_ptr(){delete _ptr;cout _ptr endl;}//像指针一样//.........private:T* _ptr;}; 后面像指针一样的功能都是相同的我们就直接删掉了可以看到unique_ptr的实现还是非常简单粗暴的。 当然也不能都不能拷贝吧所以又出现了一个可以进行拷贝的指针指针shared_ptr这个智能指针可算是在这之中最优秀的了下面我们来讲讲 shared_ptr int main() {shared_ptrint sp1(new int(0));shared_ptrint sp2(sp1);(*sp1);(*sp2);cout *sp1 endl;cout *sp2 endl;return 0; } 我们可以看到shared_ptr不仅可以拷贝还没有之前那么多的问题那么shared_ptr是如何实现的呢实际上shared_ptr的是借助引用计数实现的我们可以调试看一下 经过拷贝后引用计数由1变成了2如下图 那么如何实现引用计数比较好呢是在类中加一个私有成员变量count吗首先直接加私有变量肯定是不可以的因为我们的引用计数是要所有的类对象都能看到并且只有一份如果是私有变量count那么多个对象每个对象都有一个count就不叫引用计数了。那么能否用static静态成员变量呢静态成员变量不就是所有对象都有的吗注意静态的是不可以的静态变量是属于整个类的前三个指针指向的都是同一块资源计数为3然后第四个指针指向不同的资源这个不同的指针的计数器应该是1才对但是如果将静态计数器改为1那么前三个指针的计数右不对了所以不能使用静态变量。这里我们可以使用静态的map来做计数器让每个不同的资源与计数器做一个KV映射拷贝哪个资源就映射到map让V值即可这里提供一个最好的方式多开一个指针这个指针里保存的就是一个计数器相同拷贝的资源里的计数器指针直接指向这个计数器即可。 了解了这个后我们就来实现一下不理解的也没关系看着下面的代码就理解了 template class Tclass shared_ptr{public://保存资源shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}//拷贝构造shared_ptr(const shared_ptrT sp):_ptr(sp._ptr),_pcount(sp._pcount){(*_pcount);}//释放资源~shared_ptr(){if (--(*_pcount) 0){delete _ptr;delete _pcount;}}//像指针一样//.......private:T* _ptr;int* _pcount;}; 首先类中私有成员多了一个_pcount的引用计数注意这是在堆上开的空间。我们在构造函数初始化的时候每当有新的对象被创建我们就给这个引用计数初始化为1释放资源的时候我们不能直接释放因为有可能其他拷贝的对象和我们指向同一块资源所以这个时候我们只需要将引用计数--即可注意我们用的前置--只要进入判断语句就会先解引用拿到计数器的值然后--之后才会判断即使判断条件不满足还是会--计数器只有当计数器为0说明没有对象在指向这个资源了那么这个时候就可以将资源释放了释放的时候记得将引用计数也释放了防止内存泄漏。我们的拷贝构造就非常简单了直接让ptr和pcount指向被拷贝的那个对象的资源然后让计数器就行了。 当然即使支持了拷贝构造那么赋值重载也是能支持的因为已经不惧怕拷贝了嘛。对于赋值重载我们一定要铭记防止相同资源进行赋值防止直接释放资源导致其他对象不能使用其资源下面我们给出代码 //赋值重载shared_ptrT operator(shared_ptrT sp){if (_ptr ! sp._ptr){if (--(*_pcount) 0){delete _ptr;delete _pcount;}_ptr sp._ptr;_pcount sp._pcount;(*_pcount);}return *this;} 首先我们判断了相同资源赋值的情况因为我们本身是被sp赋值的所以我们本身的计数一定会少一个一旦计数少了那么就要判断是否需要释放资源所以我们还是减自身的计数器如果减到0了我们就将自身的资源释放掉如果没有到0就不释放然后获取sp的资源和引用计数因为sp赋值给我们我们本身少了一个sp多了一个所以获取sp的计数器资源后我们还要加加一下计数器。下面我们验证一下是否正确 int main() {sxy::shared_ptrint sp1(new int(0));sxy::shared_ptrint sp2(sp1);(*sp1);(*sp2);cout *sp1 endl;cout *sp2 endl;sxy::shared_ptrint sp3 sp1;sxy::shared_ptrint sp4(new int(10));sxy::shared_ptrint sp5 sp4;return 0; } 可以看到程序是没有问题的不管是赋值还是拷贝都可以完成任务。下面我们提出一个新问题如果我们目前的场景是多线程并发的那么引用计数还能正确的计数吗我们来看看 #include threadint main() {sxy::shared_ptrint sp(new int(1));int n 10000;thread t1([, n](){for (int i 0; i n; i){sxy::shared_ptrint cp1(sp);}});thread t2([, n](){for (int i 0; i n; i){sxy::shared_ptrint cp2(sp);}});t1.join();t2.join();cout sp.use_count() endl;return 0; } 首先我们创造了一个场景这个场景是两个线程多次对sp这个智能指针进行拷贝最后我们输出这个智能指针的引用计数注意shared_ptr中通常会有use_count这个接口返回当前资源的引用计数实现如下 int use_count() const{return *_pcount;} 注意如果use_count返回1是正确的因为我们是在返回前打印的所以这个时候还有sp这个指针指向这个资源所以是1.下面我们运行起来 如果我们多运行几次就会发现程序有时候是正常的有时候是不正常的那么这种情况一定是有问题的那么该如何解决这个问题呢其实很简单加锁就可以了。 因为我们的shared指针可以支持拷贝和赋值所以我们定义锁的时候还是像引用计数一样不能让每个对象都有一个锁并且这个锁还要支持赋值等操作要知道库里的锁是不支持赋值的直接禁掉了所以我们只需要定义一个锁的指针赋值的时候把锁资源让另一个对象指向即可。注意我们的锁一定是要保护每个资源对应的引用计数器的所以相当于每个对象有三个资源数据资源计数器资源锁资源。 当然加锁之前我们还可以优化一下 template class Tclass shared_ptr{public://保存资源shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}//拷贝构造shared_ptr(const shared_ptrT sp):_ptr(sp._ptr),_pcount(sp._pcount){(*_pcount);}void Release(){if (--(*_pcount) 0){delete _ptr;delete _pcount;}}//赋值重载shared_ptrT operator(shared_ptrT sp){if (_ptr ! sp._ptr){Release();_ptr sp._ptr;_pcount sp._pcount;(*_pcount);}return *this;}//释放资源~shared_ptr(){Release();}int use_count() const{return *_pcount;}//像指针一样//.........private:T* _ptr;int* _pcount;}; 我们直接用一个Release()函数代替释放资源的函数这样代码看起来会更简单下面我们开始加锁 template class Tclass shared_ptr{public://保存资源shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)),_pmtx(new mutex){}//拷贝构造shared_ptr(const shared_ptrT sp):_ptr(sp._ptr),_pcount(sp._pcount),_pmtx(sp._pmtx){_pmtx-lock();(*_pcount);_pmtx-unlock();}void Release(){_pmtx-lock();if (--(*_pcount) 0){delete _ptr;delete _pcount;}_pmtx-unlock();}//赋值重载shared_ptrT operator(shared_ptrT sp){if (_ptr ! sp._ptr){Release();_ptr sp._ptr;_pcount sp._pcount;_pmtx sp._pmtx;_pmtx-lock();(*_pcount);_pmtx-unlock();}return *this;}//释放资源~shared_ptr(){Release();}int use_count() const{return *_pcount;}//像指针一样// ...............private:T* _ptr;int* _pcount;mutex* _pmtx;}; 我们构造初始化的时候先给锁的指针开一个锁拷贝构造如果谁和我们指向同一块资源那么就让他的锁的指针指向我们开好的锁然后遇到计数器或者--的地方我们就加锁保护起来这样在计数器或--的过程中即使是多线程也依旧是串行的而不是并行的下面我们运行起来看看能否解决刚刚的问题 经过多次的运行后我们发现是没问题的但是如果有细心的同学应该会发现我们的指针new了锁但是没有释放啊所以下面我们赶紧加上 void Release(){bool flag false;_pmtx-lock();if (--(*_pcount) 0){delete _ptr;delete _pcount;flag true;}_pmtx-unlock();if (flag){delete _pmtx;}} 我们再保护计数器的时候用了锁所以不能在if语句中释放ptr和pcount资源的时候将锁释放而是需要一个标志只要计数器为0需要释放资源了那么就将标志标记最后解锁后将锁释放就好了。 那么我们前面已经说过锁是保护计数器的那么指针指向的资源该如何被保护呢下面我们写个例子 struct Date {int _year 0;int _month 0;int _day 0; }; int main() {sxy::shared_ptrDate sp(new Date);int n 100000;thread t1([, n](){for (int i 0; i n; i){sxy::shared_ptrDate cp1(sp);cp1-_day;cp1-_month;cp1-_year;}});thread t2([, n](){for (int i 0; i n; i){sxy::shared_ptrDate cp2(sp);cp2-_day;cp2-_month;cp2-_year;}});t1.join();t2.join();cout sp.use_count() endl;cout sp-_year : sp-_month : sp-_day endl;return 0; } 我们可以看到正常的结果应该是200000才对对于这种情况我们只需要对资源进行加锁即可 int main() {sxy::shared_ptrDate sp(new Date);int n 100000;mutex mtx;thread t1([, n](){for (int i 0; i n; i){sxy::shared_ptrDate cp1(sp);mtx.lock();cp1-_day;cp1-_month;cp1-_year;mtx.unlock();}});thread t2([, n](){for (int i 0; i n; i){sxy::shared_ptrDate cp2(sp);mtx.lock();cp2-_day;cp2-_month;cp2-_year;mtx.unlock();}});t1.join();t2.join();cout sp.use_count() endl;cout sp-_year : sp-_month : sp-_day endl;return 0; } 加锁后我们运行多次答案依旧是正确的所以一定要注意shared_ptr的锁只保护引用计数不保护指针所指向的资源。 总结shared_ptr本身是线程安全的拷贝和析构时引用计数 --是线程安全的 shared_ptr管理资源的访问不是线程安全的需要用的地方自行保护。 下面我们讲一下shared_ptr的循环引用问题 struct ListNode {int val;/*ListNode* _next;ListNode* _prev;*/sxy::shared_ptrListNode _next;sxy::shared_ptrListNode _prev;~ListNode(){cout distory endl;} };int main() {sxy::shared_ptrListNode n1(new ListNode);sxy::shared_ptrListNode n2(new ListNode);//n1-_next n2;//n2-_prev n1;return 0; } 现在有两个链表我们用指针指针让他们在析构的时候可以自己释放节点的空间当我们不让n1和n2前后连接时运行可以正常释放 注意我们为了能在ListNode中将next和prev设为智能指在构造函数中加了缺省参数否则编译不过去 然后我们让n1和n2两个节点前后链接再看看是否可以可以成功释放 可以看到释放不了这就是循环引用循环引用会导致内存泄漏。下面我们讲讲原理 刚开始这两个节点引用计数为1是因为n1和n2都是智能指针初始化引用计数为1一点n1的next链接到n2,这个时候相当于n1的next管理了n2因为我们链表节点中prev和next也是智能指针这样的话n2就由n1的next和n2本身这个智能指针一起管理所以引用计数变成2n1也同理变成2.然后出了n1和n2作用域要析构的时候他们引用计数--变成了1如下图 这个时候n1这个资源由n2的prev管理n2这个资源由n1的next管理析构的时候没有办法析构了呀n1要析构那么next管理的资源的引用计数必须减为0但是n2的引用计数不为0n2要析构那么prev管理的资源的引用计数必须减为0但是n1的引用计数不为0谁都退出不了就导致了死循环所以最后就无法成功释放造成了内存泄漏。为了解决这个问题weak_ptr就应运而生了。我们刚刚最主要的问题在于next和prev这两个指针参与资源的管理了导致引用计数变了如果我们让这两个指针不参与资源的管理不就解决了吗实际上weak_ptr就是一个不参与资源管理的指针并且weak_ptr是配合shared_ptr使用的。 整体分析 循环引用分析 1. node1 和 node2 两个智能指针对象指向两个节点引用计数变成 1 我们不需要手动 delete 。 2. node1 的 _next 指向 node2 node2 的 _prev 指向 node1 引用计数变成 2 。 3. node1 和 node2 析构引用计数减到 1 但是 _next 还指向下一个节点。但是 _prev 还指向上 一个节点。 4. 也就是说 _next 析构了 node2 就释放了。 5. 也就是说 _prev 析构了 node1 就释放了。 6. 但是 _next 属于 node 的成员 node1 释放了 _next 才会析构而 node1 由 _prev 管理 _prev 属于 node2 成员所以这就叫循环引用谁也不会释放。 weak_ptr: 下面是没有用weak_ptr的n1和n2的退出前的引用计数 下面是用weak_ptr的n1和n2的退出前的引用计数 struct ListNode {int val;//weak_ptr可以指向资源访问资源不参与资源管理不增加引用计数weak_ptrListNode _next;weak_ptrListNode _prev;~ListNode(){cout distory endl;} }; 下面我们就实现一下weak_ptr: template class Tclass weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}weak_ptrT operator(const shared_ptrT sp){//由于_ptr是私有成员weak_ptr无法直接访问所以shared_ptr有一个get()接口是返回_ptr的_ptr sp.get();return *this;}//像指针一样.................private:T* _ptr;}; 首先weak_ptr不接受RAII操作也就是说单独使用weak_ptr起不到释放的作用是需要配合shared_Ptr解决循环引用问题的。我们前面说过weak_ptr不管理资源引用计数也不会所以这个指针只会指向shared_ptr指向的资源。 下面我们就用自己的weak_ptr解决一下循环引用问题 通过运行结果可以看到没有任何问题。 总结 1. C 98 中产生了第一个智能指针 auto_ptr. 2. C boost 给出了更实用的 scoped_ptr 和 shared_ptr 和 weak_ptr. 3. C TR1 引入了 shared_ptr 等。不过注意的是 TR1 并不是标准版。 4. C 11 引入了 unique_ptr 和 shared_ptr 和 weak_ptr 。需要注意的是 unique_ptr 对应 boost 的 scoped_ptr 。并且这些智能指针的实现原理是参考 boost 中的实现的。 下一篇文章中我们会讲到和智能指针相关的定制删除器定制删除器也有很多不一样的玩法。
http://www.dnsts.com.cn/news/56060.html

相关文章:

  • 洛阳建设三轮网站余杭网站建设
  • 找人帮你做PPT的网站做数据的网站
  • 网站流量统计模板公司宣传册封面图片
  • 多语言的网站陕西政务服务网注册公司流程
  • 地方门户网站系统不同网站建设特点
  • 做阿里巴巴网站装修要多久电子商务网站建设c
  • 给企业做网站的好处推广联盟有哪些平台
  • 画出网站和目录结构图泉州网站建设
  • 网站建设需要什么人员wordpress 喜欢分享插件
  • 校园网站建设先进企业网站建设费用账务处理
  • 全球最大购物网站建筑工程类人才招聘
  • 学雷锋 做美德少年网站上海旅游景点
  • 返利网站建设哪个公司好绿蜻蜓建设管理有限公司网站
  • 海洋cms做电影网站好做吗用.net做网站中含有论坛
  • 上海建设工程信息网站网站添加邮件发送怎么做
  • 网站首页栏目怎么做萧山网页设计
  • 服务器主机管理系统建好后如何用它搭建网站刚做的网站怎么
  • 现在做个网站要多少钱网站开发 c
  • 深圳坪山住房和建设局网站机械加工信息
  • 价格划算的常州做网站电脑全自动挂机赚钱
  • 网站建站建设公司平面设计师务所
  • 网站短期就业培训班网站的二级菜单怎么做
  • 重庆网站制作合作商做淘客网站 名字
  • 网站开发需要什么上海网站seo设计
  • 展开网站建设西安网站推广招聘网
  • 自助外贸网站建设网站建设企业需要准备资料
  • 网站宣传片的创意基层建设 官方网站
  • 做网站找哪家好要钱吗企业年金如何查询
  • 长春网站建设平台自己制作的网站如何发布
  • 移动端网站建设的好处电器工程东莞网站建设