要确保用 new 动态分配的内存空间在程序的各条执行路径都能被释放是一件麻烦的事情。C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared _ptr 模板,就是用来部分解决这个问题的。

只要将 new 运算符返回的指针 p 交给一个 shared_ptr 对象“托管”,就不必担心在哪里写delete p语句——实际上根本不需要编写这条语句,托管 p 的 shared_ptr 对象在消亡时会自动执行delete p。而且,该 shared_ptr 对象能像指针 p —样使用,即假设托管 p 的 shared_ptr 对象叫作 ptr,那么 *ptr 就是 p 指向的对象。

通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:

shared_ptr<T> ptr(new T);  // T 可以是 int、char、类等各种类型

此后,ptr 就可以像 T* 类型的指针一样使用,即 *ptr 就是用 new 动态分配的那个对象。

多个 shared_ptr 对象可以共同托管一个指针 p,当所有曾经托管 p 的 shared_ptr 对象都解除了对其的托管时,就会执行delete p

例如下面的程序:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class A
  5. {
  6. public:
  7. int i;
  8. A(int n):i(n) { };
  9. ~A() { cout << i << " " << "destructed" << endl; }
  10. };
  11. int main()
  12. {
  13. shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
  14. shared_ptr<A> sp2(sp1); //A(2)同时交由sp2托管
  15. shared_ptr<A> sp3;
  16. sp3 = sp2; //A(2)同时交由sp3托管
  17. cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
  18. A * p = sp3.get(); // get返回托管的指针,p 指向 A(2)
  19. cout << p->i << endl; //输出 2
  20. sp1.reset(new A(3)); // reset导致托管新的指针, 此时sp1托管A(3)
  21. sp2.reset(new A(4)); // sp2托管A(4)
  22. cout << sp1->i << endl; //输出 3
  23. sp3.reset(new A(5)); // sp3托管A(5),A(2)无人托管,被delete
  24. cout << "end" << endl;
  25. return 0;
  26. }

程序的输出结果如下:
2,2,2
2
3
2 destructed
end
5 destructed
4 destructed
3 destructed

可以用第 14 行及第 16 行的形式让多个 sharecLptr 对象托管同一个指针。这多个 shared_ptr 对象会共享一个对共同托管的指针的“托管计数”。有 n 个 shared_ptr 对象托管同一个指针 p,则 p 的托管计数就是 n。当一个指针的托管计数减为 0 时,该指针会被释放。shared_ptr 对象消亡或托管了新的指针,都会导致其原托管指针的托管计数减 1。

第 20、21 行,shared_ptr 的 reset 成员函数可以使得对象解除对原托管指针的托管(如果有的话),并托管新的指针。原指针的托管计数会减 1。

输出的第 4 行说明,用 new 创建的动态对象 A(2) 被释放了。程序中没有写 delete 语句,而 A(2) 被释放,是因为程序的第 23 行执行后,已经没有 shared_ptr 对象托管 A(2),于是 A(2) 的托管计数变为 0。最后一个解除对 A(2) 托管的 shared_ptr 对象会释放 A(2)。

main 函数结束时,sp1、sp2、sp3 对象消亡,各自将其托管的指针的托管计数减为 0,并且释放其托管的指针,于是会有以下输出:
5 destructed
4 destructed
3 destructed

只有指向动态分配的对象的指针才能交给 shared_ptr 对象托管。将指向普通局部变量、全局变量的指针交给 shared_ptr 托管,编译时不会有问题,但程序运行时会出错,因为不能析构一个并没有指向动态分配的内存空间的指针。

注意,不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:

  1. A* p = new A(10);
  2. shared_ptr <A> sp1(p), sp2(p);

sp1 和 sp2 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp2 无法知道 p 已经被 sp1 托管过)。这样,当 sp1 消亡时要析构 p,sp2 消亡时要再次析构 p,这会导致程序崩溃。

C++11 shared_ptr(智能指针)详解的更多相关文章

  1. C++11 unique_ptr智能指针详解

    在<C++11 shared_ptr智能指针>的基础上,本节继续讲解 C++11 标准提供的另一种智能指针,即 unique_ptr 智能指针. 作为智能指针的一种,unique_ptr ...

  2. [转]C++ 智能指针详解

    转自:http://blog.csdn.net/xt_xiaotian/article/details/5714477 C++ 智能指针详解 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每 ...

  3. C++ 智能指针详解(转)

    C++ 智能指针详解   一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常 ...

  4. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  5. 【C++】智能指针详解(一):智能指针的引入

    智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...

  6. C++11 shared_ptr智能指针(超级详细)

    在实际的 C++ 开发中,我们经常会遇到诸如程序运行中突然崩溃.程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当造成的.比如: 有些内存资源已经被释放,但指向它的指针并没 ...

  7. [C++11新特性] 智能指针详解

    动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极为困难的.有时我们会忘记释放内存产生内存泄漏,有时提前释放了内存,再使用指针去引用内存就会报错. 为了更容易(同时也更安全)地使用动态内存, ...

  8. C++智能指针详解

    本文出自http://mxdxm.iteye.com/ 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最 ...

  9. 【转】C++ 智能指针详解

    一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...

  10. 【C++】智能指针详解

    转自:https://blog.csdn.net/flowing_wind/article/details/81301001 参考资料:<C++ Primer中文版 第五版>我们知道除了静 ...

随机推荐

  1. Springboot中Rest风格请求映射如何开启并使用

    问题引入 因为前端页面只能请求两种方式:GET请求和POST请求,所以就需要后台对其进行处理 解决办法:通过springmvc中提供的HiddenHttpMethodFilter过滤器来实现 而由于我 ...

  2. python根据正则表达式生成指定规律的网址

    import os def file_name(file_dir): for root, dirs, files in os.walk(file_dir): print(root) #当前目录路径 p ...

  3. Leetcode No.14 Longest Common Prefix最长公共前缀(c++实现)

    1. 题目 1.1 英文题目 Write a function to find the longest common prefix string amongst an array of strings ...

  4. 【记录】如何造一个vite插件(1)

    在看文章前,先做个定位,这不是一篇纯粹的技术性文章,可以把它理解成一个叙述文章,记录我开发插件的过程. 开始前简单的吹个牛 vue2 也写了很多年了,多人合作始终避不开用到别人的组件.关键是有些组件没 ...

  5. SLAM十四讲第二版项目代码总结

    github地址:https://github.com/gaoxiang12/slambook2/tree/master/ch13 双目视觉里程计 头文件 所有的类都在myslam命名空间中 1.co ...

  6. 【Mysql】InnoDB 引擎中的数据页结构

    InnoDB 是 mysql 的默认引擎,也是我们最常用的,所以基于 InnoDB,学习页结构.而学习页结构,是为了更好的学习索引. 一.页的简介 页是 InnoDB 管理存储空间的基本单位,一个页的 ...

  7. Spring RestTemplate 之put、delete请求

    ●PUT请求:在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已.举一个简单的例子, ...

  8. 简单配置nginx反向代理,实现跨域请求

    简单配置nginx去做反向代理,实现跨域请求 简单介绍nginx的nginx.conf最核心的配置,去做反向代理,实现跨域请求. 更多详细配置,参考nginx官方文档 先介绍几个nginx命令 打开n ...

  9. Kubernetes全栈架构师(基本概念)--学习笔记

    目录 为什么要用Kubernetes? K8s控制节点-Master概念 K8s计算节点-Node概念 什么是Pod? 为什么要引入Pod? 创建一个Pod 零宕机发布应用必备知识:Pod三种探针 零 ...

  10. 队列Queue:任务间的消息读写,安排起来~

    摘要:本文通过分析鸿蒙轻内核队列模块的源码,掌握队列使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十三 消息队列Queue>,作者:zhushy . 队列(Queue)是 ...