前言

那么这里博主先安利一些干货满满的专栏了!

首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。

高质量干货博客汇总https://blog.csdn.net/yu_cblog/category_12379430.html?spm=1001.2014.3001.5482


使用指针如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要智能指针来管理才有保证。

  2. 采用RAII思想或者智能指针来管理资源。

  3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

  4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵。

最简单的例子,malloc之后我们就要free,但是如果程序还没有跑到free的地方就因为出问题而终止呢?如果有一个东西,可以像C++到类一样,自己会调用析构,那么我们是不是就不用自己手动去free了?

智能指针RAII思想

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句 柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的 候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 不需要显式地释放资源。 采用这种方式,对象所需的资源在其生命期内始终保持有效。

C++STL维护的智能指针有四种:

  • auto_ptr
  • unique_ptr
  • shared_ptr
  • weak_ptr

auto_ptr

auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了
C++98中设计的auto_ptr问题是非常明显的,所以实际中很多公司明确规定了不能使用auto_ptr

unique_ptr

unique_ptr是C++11标准引入的一种独占所有权的智能指针。它提供了独特的所有权,即一个资源只能被一个unique_ptr拥有。它不能与其他unique_ptr共享或复制。这使得unique_ptr非常轻量和高效。当unique_ptr超出范围或被显式地释放时,它将自动删除其拥有的资源。

shared_ptr

shared_ptr是C++11标准引入的一种共享所有权的智能指针。它可以被多个shared_ptr实例共享,每个实例都持有一个引用计数。引用计数跟踪资源的当前拥有者数量,当最后一个shared_ptr超出范围时,它会自动释放资源。shared_ptr通过引用计数来管理资源,可以在多个地方共享和传递所有权,使其非常适合动态资源管理和循环引用的情况。

weak_ptr

weak_ptr也是C++11标准引入的一种弱引用智能指针。它与shared_ptr一起使用,但不会增加引用计数。weak_ptr只有资源的观察权,没有所有权。weak_ptr用于解决shared_ptr之间的循环引用问题,因为循环引用可能导致资源无法释放。通过检查weak_ptr是否有效,可以判断被观察的资源是否已被释放。

四种智能指针的代码实现

里面注释还提到了很多细节,大家可以都看一下。

  1. #pragma once
  2. #include<iostream>
  3. using namespace std;
  4. class A
  5. {
  6. public:
  7. ~A()
  8. {
  9. cout << "A::~A()" << endl;
  10. }
  11. int _a = 0;
  12. int _b = 0;
  13. };
  14. namespace yufc
  15. {
  16. template<class T>
  17. class auto_ptr
  18. {
  19. private:
  20. T* _ptr;
  21. public:
  22. auto_ptr(T* ptr = nullptr)
  23. :_ptr(ptr) {}
  24. auto_ptr(auto_ptr<T>& ap)
  25. :_ptr(ap._ptr)
  26. {
  27. ap._ptr = nullptr;
  28. }
  29. ~auto_ptr()
  30. {
  31. if (_ptr)
  32. {
  33. cout << "Delete:" << _ptr << endl;
  34. delete _ptr;
  35. }
  36. }
  37. //ap1 = ap2;
  38. auto_ptr<T>& operator =(auto_ptr<T>& ap)
  39. {
  40. if (this != &ap)
  41. {
  42. if (_ptr)
  43. {
  44. cout << "Delete:" << _ptr << endl;
  45. delete _ptr;
  46. }
  47. _ptr = ap._ptr;
  48. ap._ptr = nullptr;
  49. }
  50. return *this;
  51. }
  52. T& operator*()
  53. {
  54. return *_ptr;
  55. }
  56. T* operator->()
  57. {
  58. return _ptr;
  59. }
  60. };
  61. //unique_ptr
  62. template<class T>
  63. class unique_ptr
  64. {
  65. private:
  66. T* _ptr;
  67. public:
  68. unique_ptr(T* ptr = nullptr)
  69. :_ptr(ptr) {}
  70. //仿拷贝
  71. unique_ptr(unique_ptr<T>& ap) = delete;
  72. unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;
  73. ~unique_ptr()
  74. {
  75. if (_ptr)
  76. {
  77. cout << "Delete:" << _ptr << endl;
  78. delete _ptr;
  79. }
  80. }
  81. T& operator*()
  82. {
  83. return *_ptr;
  84. }
  85. T* operator->()
  86. {
  87. return _ptr;
  88. }
  89. };
  90. //shared_ptr
  91. //我们还是提供一个默认的删除器,不然删啥都得传
  92. template<class T>
  93. struct DefaultDeleter
  94. {
  95. void operator()(T* ptr)
  96. {
  97. delete ptr;//默认是delete
  98. }
  99. };
  100. template<class T, class D = DefaultDeleter<T>> //传多一个定制删除器
  101. class shared_ptr
  102. {
  103. protected:
  104. T* _ptr;
  105. //static int _count;
  106. int* _pCount;
  107. public:
  108. shared_ptr(T* ptr = nullptr)
  109. :_ptr(ptr)
  110. , _pCount(new int(1)) {}
  111. shared_ptr(shared_ptr<T>& sp)
  112. :_ptr(sp._ptr)
  113. , _pCount(sp._pCount)
  114. {
  115. (*_pCount)++;
  116. }
  117. void release()
  118. {
  119. cout << "Delete:" << _ptr << endl;
  120. D()(_ptr);//用定制删除器释放
  121. //delete _ptr;
  122. delete _pCount;
  123. }
  124. shared_ptr<T>& operator=(shared_ptr<T>& sp)
  125. {
  126. if (_ptr == sp._ptr)
  127. {
  128. return *this;
  129. }
  130. if (--(*_pCount) == 0)
  131. {
  132. release();
  133. }
  134. //共管新资源,++记数
  135. _ptr = sp._ptr;
  136. _pCount = sp._pCount;
  137. (*_pCount)++;
  138. return *this;
  139. }
  140. ~shared_ptr()
  141. {
  142. if (--(*_pCount) == 0)
  143. {
  144. release();
  145. }
  146. }
  147. T& operator*()
  148. {
  149. return *_ptr;
  150. }
  151. T* operator->()
  152. {
  153. return _ptr;
  154. }
  155. int use_count()
  156. {
  157. return *_pCount;
  158. }
  159. T* get() const
  160. {
  161. return _ptr;
  162. }
  163. };
  164. //template<class T>
  165. //int shared_ptr<T>::_count = 0;
  166. //weak_ptr不是一个功能型的智能指针,是辅助型的
  167. //它的发明是为了解决shared_ptr的循环引用问题
  168. //STL的实现比我们这个复杂很多,他还处理了很多其他的问题
  169. //STL的weak_ptr其实保存了引用计数,用于处理过期的问题,具体看杭哥解释
  170. //STL中的它不增加引用计数,但是它要保存一下
  171. template<class T>
  172. class weak_ptr
  173. {
  174. protected:
  175. T* _ptr;
  176. public:
  177. weak_ptr()
  178. :_ptr(nullptr) {}
  179. weak_ptr(const shared_ptr<T>& sp)
  180. :_ptr(sp._ptr) {}
  181. weak_ptr(const weak_ptr<T>& wp)
  182. :_ptr(wp._ptr) {}
  183. weak_ptr<T>& operator=(const shared_ptr<T>& sp)
  184. {
  185. _ptr = sp.get();
  186. return *this;
  187. }
  188. T& operator*()
  189. {
  190. return *_ptr;
  191. }
  192. T* operator->()
  193. {
  194. return _ptr;
  195. }
  196. //库里面其实还提供get,提供原生指针
  197. T* get() const
  198. {
  199. return _ptr;
  200. }
  201. };
  202. }
  203. //想要实现库里面那一套,还得复杂不少
  204. //其实shared_ptr还涉及到线程安全问题,要加锁的
  205. void test_auto_ptr()
  206. {
  207. std::auto_ptr<A>ap1(new A);
  208. ap1->_a++;
  209. std::auto_ptr<A>ap2(ap1);
  210. //ap1->_a++;//err
  211. //会出现拷贝对象的悬空问题
  212. //很多公司明确规定不能使用它
  213. std::auto_ptr<A>ap3(new A);
  214. //注意 -- 这里不是交换 -- 是把ap3的资源转移给ap2,然后把ap2的释放掉,ap3悬空
  215. ap2 = ap3;
  216. //ap3->_a++;
  217. }
  218. void test_unique_ptr()
  219. {
  220. yufc::unique_ptr<A> up1(new A);
  221. //不准拷贝了
  222. //std::unique_ptr<A> up2(up1);
  223. up1->_a++;
  224. yufc::unique_ptr<A> up2;
  225. //up2 = up1;//err
  226. }
  227. #if 1
  228. void test_shared_ptr()
  229. {
  230. yufc::shared_ptr<A> sp1(new A);
  231. yufc::shared_ptr<A> sp2(sp1);
  232. yufc::shared_ptr<A> sp3(sp1);
  233. yufc::shared_ptr<int> sp4(new int(1));
  234. yufc::shared_ptr<A>sp5(new A);
  235. }
  236. #endif
  237. /**
  238. * 能否用静态变量来计数?
  239. * 我们期望是 针对不同空间,我们希望有多个记数,但是如果我们使用了静态变量来记数
  240. * 所有类型,所有空间的记数都堆在一起了.
  241. */
  242. //weak_ptr
  243. //循环引用
  244. #if 0
  245. struct Node
  246. {
  247. int _val;
  248. yufc::shared_ptr<Node> _next;
  249. yufc::shared_ptr<Node> _prev;
  250. ~Node()
  251. {
  252. cout << "Node::~Node()" << endl;
  253. }
  254. };
  255. struct Node2
  256. {
  257. int _val;
  258. yufc::weak_ptr<Node2> _next;
  259. yufc::weak_ptr<Node2> _prev;
  260. ~Node2()
  261. {
  262. cout << "Node::~Node()" << endl;
  263. }
  264. };
  265. #endif
  266. #if 0 //stl的weak_ptr测试
  267. void test_weak_ptr()
  268. {
  269. cout << "before use weak_ptr to construct the Node" << endl;
  270. std::shared_ptr<Node>n1(new Node);
  271. std::shared_ptr<Node>n2(new Node);
  272. //tips:shared_ptr的构造是加了explicit的 -- 不允许隐式类型转换,所以不能这样写
  273. //std::shared_ptr<Node>n2 = new Node; //err
  274. //我们看这种情况 -- 此时不能正确释放了 -- 为什么?
  275. cout << n1.use_count() << endl;
  276. cout << n2.use_count() << endl;
  277. n1->_next = n2;
  278. n2->_prev = n1;
  279. cout << n1.use_count() << endl;
  280. cout << n2.use_count() << endl;
  281. //这里就是循环引用的问题
  282. //shared_ptr是无法解决这个问题的
  283. //weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源
  284. //weak_ptr就是shared_ptr的小跟班 -- 专门帮忙处理shared_ptr的剩余问题
  285. //weak_ptr主要用shared_ptr构造 -- 处理循环引用问题
  286. //当我们把Node里面的_prev和_next改成weak_ptr之后,_prev和_next,就不增加记数
  287. //它不参与资源释放管理,可以访问和修改资源,不存在循环引用问题
  288. cout << "after use weak_ptr to construct the Node" << endl;
  289. std::shared_ptr<Node2>n11(new Node2);
  290. std::shared_ptr<Node2>n21(new Node2);
  291. cout << n11.use_count() << endl;
  292. cout << n21.use_count() << endl;
  293. n11->_next = n21;
  294. n21->_prev = n11;
  295. cout << n11.use_count() << endl;
  296. cout << n21.use_count() << endl;
  297. }
  298. //yufc的weak_ptr测试
  299. void test_weak_ptr2()
  300. {
  301. cout << " ----- before use weak_ptr to construct the Node ----- " << endl;
  302. yufc::shared_ptr<Node>n1(new Node);
  303. yufc::shared_ptr<Node>n2(new Node);
  304. //tips:shared_ptr的构造是加了explicit的 -- 不允许隐式类型转换,所以不能这样写
  305. //std::shared_ptr<Node>n2 = new Node; //err
  306. //我们看这种情况 -- 此时不能正确释放了 -- 为什么?
  307. cout << n1.use_count() << endl;
  308. cout << n2.use_count() << endl;
  309. n1->_next = n2;
  310. n2->_prev = n1;
  311. cout << n1.use_count() << endl;
  312. cout << n2.use_count() << endl;
  313. //这里就是循环引用的问题
  314. cout << endl << endl;
  315. //shared_ptr是无法解决这个问题的
  316. //weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源
  317. //weak_ptr就是shared_ptr的小跟班 -- 专门帮忙处理shared_ptr的剩余问题
  318. //weak_ptr主要用shared_ptr构造 -- 处理循环引用问题
  319. //当我们把Node里面的_prev和_next改成weak_ptr之后,_prev和_next,就不增加记数
  320. //它不参与资源释放管理,可以访问和修改资源,不存在循环引用问题
  321. cout << " ----- after use weak_ptr to construct the Node ----- " << endl;
  322. yufc::shared_ptr<Node2>n11(new Node2);
  323. yufc::shared_ptr<Node2>n21(new Node2);
  324. cout << n11.use_count() << endl;
  325. cout << n21.use_count() << endl;
  326. n11->_next = n21;
  327. n21->_prev = n11;
  328. cout << n11.use_count() << endl;
  329. cout << n21.use_count() << endl;
  330. }
  331. #endif
  332. //定制删除器
  333. struct Node
  334. {
  335. int _val;
  336. std::shared_ptr<Node> _next;
  337. std::shared_ptr<Node> _prev;
  338. ~Node()
  339. {
  340. cout << "Node::~Node()" << endl;
  341. }
  342. };
  343. void test5()
  344. {
  345. std::shared_ptr<Node> n1(new Node[5]); //这里报错,因为delete[]没有匹配上
  346. std::shared_ptr<Node> n2(new Node);
  347. //这其实和new的底层实现是有关系的
  348. //new[]但是delete没有[] -- 会不会报错?为什么会报错?其实这和平台有关系
  349. //new Node[5] 这里涉及到Node的构造和析构
  350. //此时 new Node[5] --> 5次malloc和5次构造函数
  351. //如果是delete --> 1次析构函数+free
  352. //delete[] --> 5次析构函数+free
  353. //此时如果编译器发现我们的析构函数没写 -- 他会认为这个Node不需要析构 -- 直接free就行
  354. //他会做优化 -- 因此如果析构函数不写 -- 有时候上面的情况不报错
  355. //假设Node大小是12,现在new Node[5]
  356. //在VS下,其实开的不是60字节,而是64字节 -- 4个字节放在头部,用来存个数
  357. //因为delete[]其实规定不传个数 -- 所以new Node[]的时候会偷偷记录
  358. //所以调用delete[] 就知道到底要调用多少次析构了
  359. //所以new []返回来的指针其实比真实malloc出来的地址向后偏移了4个字节
  360. //delete[]的时候,其实指针就会往前偏移四个字节,先找到个数
  361. //所以delete[] 其实是free((char*)ptr - 4)这个位置的指针
  362. //如果直接delete 首先5个Node只析构一个,其次头上的4个字节没人管了
  363. //所以我们delete的时候要匹配
  364. //我们用这个智能指针的时候的场景会非常复杂
  365. std::shared_ptr<Node> n3(new Node[6]);
  366. std::shared_ptr<int> n4((int*)malloc(sizeof(int) * 12));
  367. //所以我们需要定制删除器
  368. //一般是传一个仿函数或匿名参数
  369. }
  370. template<class T>
  371. struct DeleteArray
  372. {
  373. void operator()(T* ptr)
  374. {
  375. cout << "delete[] " << ptr << endl;
  376. delete[] ptr;
  377. }
  378. };
  379. template<class T>
  380. struct Free
  381. {
  382. void operator()(T* ptr)
  383. {
  384. cout << "free() " << ptr << endl;
  385. free(ptr);
  386. }
  387. };
  388. void test6()
  389. {
  390. //这里要看清楚文档 -- shared_ptr的定制删除器是在构造函数里面传的
  391. //unique_ptr的地址删除器是现在模板参数里面传的
  392. std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());
  393. std::shared_ptr<int> n4((int*)malloc(sizeof(int) * 12), Free<int>());
  394. //这样就不会出问题了
  395. std::shared_ptr<Node> n3(new Node[5], [](Node* ptr) {delete[] ptr; });//这里用匿名函数也是很好用的
  396. std::shared_ptr<int> n2((int*)malloc(sizeof(int) * 12), [](int* ptr) {free(ptr); });
  397. //还可以这么玩
  398. std::shared_ptr<FILE> n5(fopen("test.txt", "w"), [](FILE* ptr) {fclose(ptr); });
  399. //unique_ptr不能用匿名函数了,因为要在模板参数里面传对象
  400. std::unique_ptr<Node, DeleteArray<Node>>up(new Node[5]);
  401. }
  402. void test7()
  403. {
  404. //测试我们自己定义的shared_ptr的删除器
  405. yufc::shared_ptr<Node, DeleteArray<Node>>up(new Node[5]);
  406. }
  407. //如果我们要像库里面一样,可以从构造函数传,我们目前实现的架构是做不到的
  408. //因为STL里面的计数器其实是封装成了一个类的
  409. //我们传定制删除器的时候其实还往下传了一层
  410. //现在我们如果要通过构造函数传,这个删除器就只能在构造函数里面用了,外面用不了。
  411. //所以我们写一个从模板参数里面传的
  412. //定制删除器在开始里面很少
  413. //不过平时使用还是地用到的

auto_ptr|unique_ptr|shared_ptr|weak_ptr|你都搞明白了吗?的更多相关文章

  1. stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...

  2. auto_ptr,unique_ptr,shared_ptr,weak_ptr

    http://mojijs.com/2016/08/218129/index.html http://www.cnblogs.com/lanxuezaipiao/p/4132096.html

  3. auto_ptr, unique_ptr, shared_ptr and weak_ptr智能指针讲解

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...

  4. 相机拍的图,电脑上画的图,word里的文字,电脑屏幕,手机屏幕,相机屏幕显示大小一切的一切都搞明白了!

    相机拍的图,电脑上画的图,word里的文字,电脑屏幕,手机屏幕,相机屏幕显示大小一切的一切都搞明白了! 先说图片X×dpi=点数dotX是图片实际尺寸,简单点,我们只算图片的高吧,比如说拍了张图片14 ...

  5. 这20个常规Python语法你都搞明白了吗?

    Python简单易学,但又博大精深.许多人号称精通Python,却不会写Pythonic的代码,对很多常用包的使用也并不熟悉.学海无涯,我们先来了解一些Python中最基本的内容. Python的特点 ...

  6. 就想搞明白,component-scan 是怎么把Bean都注册到Spring容器的!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 忒复杂,没等搞明白大促都过去了! 你经历过618和双11吗?你加入过大促时候那么多复 ...

  7. c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr

    c++智能指针的使用 官方参考 普通指针的烦恼:内存泄漏,多次释放,提前释放 智能指针 负责自动释放所指向的对象. 三种智能指针 shared_ptr,unique_ptr,weak_ptr: 将sh ...

  8. shared_ptr & weak_ptr

    shared_ptr <1> 类模板说明 namespace boost { class bad_weak_ptr: public std::exception; template< ...

  9. auto_ptr与shared_ptr ZZ

    http://blog.csdn.net/rogeryi/article/details/1442700 Part(1) 这篇文章试图说明如何使用auto_ptr和shared_ptr,从而使得动态分 ...

  10. LIN、CAN、FlexRay、MOST,三分钟搞明白四大汽车总线

    LIN.CAN.FlexRay.MOST,三分钟搞明白四大汽车总线 2016-09-21 13:09 汽车中的电子部件越来越多,光是ECU就有几十个,这么多的电子单元都要进行信息交互.传统的点对点通信 ...

随机推荐

  1. 蓝桥杯历年省赛试题汇总 C/C++ A组

    A组 省赛 B 组的题目可以在这里查看 → 刷题笔记: 蓝桥杯 题目提交网站:Here 2013 第四届 高斯日记 排它平方数 振兴中华 颠倒的价牌 前缀判断 逆波兰表达式 错误票据 买不到的数目 剪 ...

  2. Vue中使用el-menu高亮显示问题

    https://blog.csdn.net/weixin_43336525/article/details/132541500 <template> <el-menu :defaul ...

  3. Https 原理与工作流程及证书链校验

    本文为博主原创,未经允许不得转载: 目录 HTTP传输三大风险 安全通信原则 HTTPS定义 TLS/SSL 协议及加密算法 HTTPS工作流程 HTTPS协议和HTTP协议的区别 CA机构 证书链校 ...

  4. Visual Studio实用的搜索、查找、替换技巧

    前言 对于.NET开发者而言Visual Studio是我们日常工作中比较常用的开发工具,掌握一些Visual Studio实用的搜索.查找.替换技巧可以帮助我们大大提高工作效率从而避免996. Vi ...

  5. SD协议-基本概念

    1.SD协议版本 SD 1.1 SD 2.0 SD 3.0 在看协议的时候,需要注意协议的版本,注意版本之间的差别 SD协议是常见的数据通信和存储卡之间的协议 HDMI是显示相关的协议,遵循HDMI协 ...

  6. ORA-65140: 无效的通用配置文件名称

    1.问题 CREATE PROFILE PM_Profile LIMIT SESSIONS_PER_USER 100 PASSWORD_LIFE_TIME 90; 在创建概要文件时,报错:ORA-65 ...

  7. android studio 如何把依赖导出成 jar

    反编译工具 dex-tools-2.1-SNAPSHOT 第一步 用一个普通的app工程,引用所有的库,然后生成apk文件 第二步 把apk文件,改扩展名为zip,解压后,里面有几个*.dex文件,拷 ...

  8. MyBatis05——一对多和多对一处理

    多对一处理 1.数据库表的设计 CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRI ...

  9. [转帖]Kafka—配置SASL/PLAIN认证客户端及常用操作命令

    介绍   SASL/PLAIN 是一种简单的 username/password安全认证机制,本文主要总结服务端开启该认证后,命令行客户端进行配置的操作流程. 配置 增加jaas.properties ...

  10. ESXi6.5 登录后出现错误 必须 退出的解决办法