手机网站建设 jz.woonl,汉中建设工程招标信息网,网站开发制作软件,php sqlite 做网站#x1f4d8;北尘_#xff1a;个人主页 #x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上#xff0c;不忘来时的初心 文章目录 一、为什么需要智能指针#xff1f;二、内存泄漏1、 什么是内存泄漏#xff0c;内存泄漏的危… 北尘_个人主页 个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上不忘来时的初心 文章目录 一、为什么需要智能指针二、内存泄漏1、 什么是内存泄漏内存泄漏的危害2、内存泄漏分类3、如何避免内存泄漏 三、智能指针的使用及原理1、RAII2、智能指针的原理 四、智能指针的分类1、std::auto_ptr2、std::unique_ptr3、std::shard_ptr 五、C11和boost中智能指针的关系 一、为什么需要智能指针
下面我们先分析一下下面这段程序有没有什么内存方面的问题提示一下注意分析MergeSort 函数中的问题。
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;
}问题分析上面的问题分析出来我们发现有什么问题 会出现内存泄漏p1和p2没有释放因为抛异常了会执行流调转。 二、内存泄漏
1、 什么是内存泄漏内存泄漏的危害
什么是内存泄漏内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对该段内存的控制因而造成了内存的浪费。 内存泄漏的危害长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现内存泄漏会导致响应越来越慢最终卡死。
2、内存泄漏分类
C/C程序中一般我们关心两种方面的内存泄漏 堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放那么以后这部分空间将无法再被使用就会产生Heap Leak。 系统资源泄漏 指程序使用系统分配的资源比方套接字、文件描述符、管道等没有使用对应的函数释放掉导致系统资源的浪费严重可导致系统效能减少系统执行不稳定。
3、如何避免内存泄漏
内存泄漏非常常见解决方案分为两种1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具。 三、智能指针的使用及原理
1、RAII
RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源如内存、文件句柄、网络连接、互斥量等等的简单技术。 在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源。借此我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处
不需要显式地释放资源。采用这种方式对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
templateclass T
class SmartPtr {
public:SmartPtr(T* ptr nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};
int div()
{int a, b;cin a b;if (b 0)throw invalid_argument(除0错误);return a / b;
}
void Func()
{ShardPtrint sp1(new int);ShardPtrint sp2(new int);cout div() endl;
}
int main()
{try {Func();}catch(const exception e){coute.what()endl;}return 0;
}2、智能指针的原理
上述的SmartPtr还不能将其称为智能指针因为它还不具有指针的行为。指针可以解引用也可以通过-去访问所指空间中的内容因此AutoPtr模板类中还得需要将* 、-重载下才可让其像指针一样去使用。
templateclass T
class SmartPtr {
public:
SmartPtr(T* ptr nullptr): _ptr(ptr){}
~SmartPtr(){if(_ptr)delete _ptr;}
T operator*() {return *_ptr;}
T* operator-() {return _ptr;}
private:
T* _ptr;
};总结一下智能指针的原理
具有RAll特性重载operator*和operator-具有和指针一样的行为。 四、智能指针的分类
1、std::auto_ptr
C98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。 其原理是管理权转移。
namespace zsc
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptrT sp):_ptr(sp._ptr){// 管理权转移sp._ptr nullptr;}auto_ptrT operator(auto_ptrT ap){// 检测是否为自己给自己赋值if (this ! ap){// 释放当前对象中资源if (_ptr)delete _ptr;// 转移ap中资源到当前对象中_ptr ap._ptr;ap._ptr NULL;}return *this;}~auto_ptr(){if (_ptr){cout delete: _ptr endl;delete _ptr;}}// 像指针一样使用T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
}
// 结论auto_ptr是一个失败设计很多公司明确要求不能使用auto_ptr
//int main()
//{
// std::auto_ptrint sp1(new int);
// std::auto_ptrint sp2(sp1); // 管理权转移
//
// // sp1悬空
// *sp2 10;
// cout *sp2 endl;
// cout *sp1 endl;
// return 0;
//}2、std::unique_ptr
C11中开始提供更靠谱的unique_ptr. 其原理是防止拷贝。
namespace zsc
{templateclass Tclass unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout delete: _ptr endl;delete _ptr;}}// 像指针一样使用T operator*(){return *_ptr;}T* operator-(){return _ptr;}unique_ptr(const unique_ptrT sp) delete;unique_ptrT operator(const unique_ptrT sp) delete;private:T* _ptr;};
}
//int main()
//{
// /*bit::unique_ptrint sp1(new int);
// bit::unique_ptrint sp2(sp1);*/
//
// std::unique_ptrint sp1(new int);
// //std::unique_ptrint sp2(sp1);
//
// return 0;
//}3、std::shard_ptr
C11中开始提供更靠谱的并且支持拷贝的shared_ptr。 shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
shared_ptr在其内部给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。在对象被销毁时(也就是析构函数调用)就说明自己不使用该资源了对象的引用计数减1。如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源如果不是0就说明除了自己还有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了。
templateclass Tclass shared_ptr{public:shared_ptr(T* ptr nullptr):_ptr(ptr), _pcount(new int(1)){}templateclass Dshared_ptr(T* ptr, D del): _ptr(ptr), _pcount(new int(1)), _del(del){}void release(){if (--(*_pcount) 0){_del(_ptr);delete _pcount;}}~shared_ptr(){release();}shared_ptr(const shared_ptrT sp):_ptr(sp._ptr), _pcount(sp._pcount){(*_pcount);}// sp1 sp3shared_ptrT operator(const shared_ptrT sp){if (_ptr ! sp._ptr){release();_ptr sp._ptr;_pcount sp._pcount;(*_pcount);}return *this;}// 像指针一样T operator*(){return *_ptr;}T* operator-(){return _ptr;}int use_count() const{return *_pcount;}T* get() const{return _ptr;}private:T* _ptr;int* _pcount;functionvoid(T*) _del [](T* ptr) {delete ptr; };};std::shared_ptr的循环引用问题 struct ListNode
{int _data;shared_ptrListNode _prev;shared_ptrListNode _next;~ListNode(){ cout ~ListNode() endl; }
};
int main()
{shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);cout node1.use_count() endl;cout node2.use_count() endl;node1-_next node2;node2-_prev node1;cout node1.use_count() endl;cout node2.use_count() endl;return 0;
}循环引用分析 1. node1和node2两个智能指针对象指向两个节点引用计数变成1我们不需要手动
delete。
2. node1的_next指向node2node2的_prev指向node1引用计数变成2。
3. node1和node2析构引用计数减到1但是_next还指向下一个节点。但是_prev还指向上
一个节点。
4. 也就是说_next析构了node2就释放了。
5. 也就是说_prev析构了node1就释放了。
6. 但是_next属于node的成员node1释放了_next才会析构而node1由_prev管理_prev
属于node2成员所以这就叫循环引用谁也不会释放。// 解决方案在引用计数的场景下把节点中的_prev和_next改成weak_ptr就可以了
// 原理就是node1-_next node2;和node2-_prev node1;时weak_ptr的_next和
_prev不会增加node1和node2的引用计数。
struct ListNode
{int _data;weak_ptrListNode _prev;weak_ptrListNode _next;~ListNode(){ cout ~ListNode() endl; }
};
int main()
{shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);cout node1.use_count() endl;cout node2.use_count() endl;node1-_next node2;node2-_prev node1;cout node1.use_count() endl;cout node2.use_count() endl;return 0;}如果不是new出来的对象如何通过智能指针管理呢其实shared_ptr设计了一个删除器来解决这个问题 代码同上 五、C11和boost中智能指针的关系
C 98 中产生了第一个智能指针auto_ptr.C boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.C TR1引入了shared_ptr等。不过注意的是TR1并不是标准版。C 11引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。