长春网站设计哪家好,甘肃住房建设厅网站,珠海市香洲区建设局网站,vs2010网站开发示例目录
一#xff0c;为什么需要智能指针
二#xff0c;内存泄露的基本认识
1. 内存泄露分类
2. 常见的内存检测工具
3#xff0c;如何避免内存泄露
三#xff0c;智能指针的使用与原理
1. RAII思想
2. 智能指针
#xff08;1. unique_ptr
#xff08;2. shared_…目录
一为什么需要智能指针
二内存泄露的基本认识
1. 内存泄露分类
2. 常见的内存检测工具
3如何避免内存泄露
三智能指针的使用与原理
1. RAII思想
2. 智能指针
1. unique_ptr
2. shared_ptr weak_ptr shared_ptr的循环引用问题
3. weak_ptr解决循环引用问题
4. 定制删除器了解 嗨收到一张超美的风景图愿你每天都能顺心 一为什么需要智能指针 假设我们调用func函数我们会发现div函数操作如果抛异常则p1, p2就不会释放导致内存泄露。 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 ; } 二内存泄露的基本认识
什么是内存泄漏内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失而是应用程序分配某段内存后因为设计错误失去了对该段内存的控制因而造成了内存的浪费。 内存泄漏的危害长期运行的程序出现内存泄漏影响很大如操作系统、后台服务等等出现内存泄漏会导致响应越来越慢最终卡死。 1. 内存泄露分类
C/C程序中一般我们关心两种方面的内存泄漏 堆内存泄漏(Heap leak)本章关注点 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放那么以后这部分空间将无法再被使用就会产生Heap Leak。 系统资源泄漏 指程序使用 系统分配的资源比方套接字、文件描述符、管道等没有使用对应的函数释放掉导致系统资源的浪费严重可导致系统效能减少系统执行不稳定。 2. 常见的内存检测工具
在linux下内存泄漏检测 Linux下几款C程序中的内存泄露检查工具_c内存泄露工具分析-CSDN博客
在windows下使用第三方工具 VS编程内存泄漏VLD(Visual LeakDetector)内存泄露库_visual leak detector vs2020-CSDN博客
其他工具 内存泄露检测工具比较 - 默默淡然 - 博客园 (cnblogs.com)
3如何避免内存泄露
1. 采用 RAII思想或者智能指针来管理资源。 2. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带 内存泄漏检测的功能选项。 出问题了使用内存泄漏工具检测。ps不过很多工具都不够靠谱或者收费昂贵。 总结一下: 内存泄漏非常常见解决方案分为两种1、事前预防型如 RAII思想智能指针等。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之所以称为RAll思想而不是智能指针因为它还不具有指针的行为。指针可以解引用也可以通过-去访问所指空间中的内容因此 AutoPtr模板类中还得需要将* 、-重载下才可让其像指针一样去使用。 所以基本的框架就有了
// 功能像指针一样
template class T
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;}T operator*(){return *_ptr;}T* operator-(){return (*_ptr);}
private:T* _ptr;
};
这只是初步的框架我们知道这个SmartPtr类的拷贝构造是浅拷贝。如果两个该类对象指向同一个内容在析构时将会析构两次进而出现报错。换个方向这个类类似我们学习的迭代器但我们当时没有考虑析构的问题 答迭代器只是管理数据的机构数据析构是数据本身的事情。 说到拷贝我们要认识智能指针的发展史: C98的不好用在C11中引入了unique_ptr shared_ptrweak_ptr他们两来自Boost库 。 Boost的准标准库标准库就是C编译器就支持的准标准库需要外部引入源码库的怎么理解Boost? 可以理解为标准库的体验服。
1. unique_ptr 主旨简单粗暴禁止拷贝。auto_ptr栽在拷贝上 方法一将构造函数声明并放入private区中这样外部无法定义访问。 方法二强制禁用 2. shared_ptr weak_ptr 玩法引用计数并且支持拷贝 shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。 1. shared_ptr在其内部 给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。 2. 在 对象被销毁时(也就是析构函数调用)就说明自己不使用该资源了对象的引用计数减一。 3. 如果引用计数是0就说明自己是最后一个使用该资源的对象 必须释放该资源 4. 如果不是0就说明除了自己还有其他对象在使用该份资源 不能释放该资源否则其他对象就成野指针了。 下面是简单模拟实现的shared_ptr:
template class T
class share_ptr
{
public:share_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)),_mtx(new mutex){}~share_ptr(){destory();}void destory(){_mtx-lock();bool flag false;if (--(*_pcount) 0){cout delete : *_ptr endl;delete _ptr;delete _pcount;flag true;}_mtx-unlock();if (flag true){delete _mtx;}}void AddCount(const share_ptrT it){_mtx-lock();(*it._pcount);_mtx-unlock();}T operator*(){return *_ptr;}T* operator-(){return (*_ptr);}share_ptr(const share_ptrT it):_ptr(it._ptr),_pcount(it._pcount),_mtx(it._mtx){AddCount(it);}share_ptrT operator(const share_ptrT it){// 防止自己赋值自己导致数据丢失if (_ptr ! it._ptr){destory();_ptr it._ptr;_mtx it._mtx;_pcount it._pcount;AddCount(it); // 计数return *this;}}
private:T* _ptr;int* _pcount;mutex* _mtx;
}; shared_ptr的循环引用问题 特征互相使用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() ; // 打印链接数 cout node2.use_count() endl; node1-_next node2; node2-_prev node1; cout node1.use_count() ; cout node2.use_count() endl; return 0;} 3. weak_ptr解决循环引用问题 特征 1. 不支持RAII思想 2. 像指针 3. 专门用来辅助shared_ptr可以指向资源但不管理资源也不增加计数。 // 简化版本的weak_ptr实现
templateclass T
class weak_ptr
{
public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}weak_ptrT operator(const share_ptrT sp){_ptr sp.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}
private:T* _ptr;
}; 4. 定制删除器了解
我们注意到我们的析构函数底层都是delete但对象是数组文件指针delete析构就不合适所以我们的shared_ptr有了定制删除器的用法。 本质上就是类似仿函数下面是几个使用案例
template class T
struct DeleteArray
{void operator()(T* ptr){if (ptr ! nullptr){delete[] ptr;}}
};
int main()
{// 自己写仿函数法shared_ptrDate pt1(new Date[10], DeleteArrayDate());// 函数指针法这个过于简单的就不做演示// lambda法shared_ptrDate pt2(new Date[100], [](Date* ptr) { delete[] ptr; });// function对象法shared_ptrDate pt3(new Date[1000], functionvoid(Date*) (DeleteArrayDate()));// 文件管理shared_ptrFILE pt4(fopen(test.txt, r), [](FILE* ptr) { fclose(ptr); });cout endl;return 0;
} 结语 本小节就到这里了感谢小伙伴的浏览如果有什么建议欢迎在评论区评论如果给小伙伴带来一些收获请留下你的小赞你的点赞和关注将会成为博主创作的动力。