C++11智能指针之std::shared_ptr

2023-11-20



        std::shared_ptr是在c++11中引入的一种智能指针,其特点是它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源。在c++中使用shared_ptr需要包含<memory>头文件。

一、定义并初始化一个共享指针的三种方式

class Investment
{
public:
      virtual void getObjectType() { cout<<"Base Investment"<< endl; }
      virtual ~Investment() { cout<<"Investmentdestruction!"<< endl; }
};

1通过new运算符或者普通指针

shared_ptr<Investment> sp(newInvestment());

Investment * pInvestment = new Investment();

shared_ptr<Investment> sp (pInvestment);

2通过make_shared方法

auto sp1 = std::make_shared<Investment>();

3通过拷贝另一个智能指针

shared_ptr<Investment> sp2(sp);
//或者
shared_ptr<Investment> sp3 = sp2;

二、shared_ptr的结构

       相比于普通指针,共享指针要占用多一倍的内存空间,其内部包含两个指针,一个指针指向它所管理的资源,第二个指针指向一个称为“Control Block”的控制块,如图所示:

       通过上图所示的结构,我们可以知道在控制块中有一个引用计数字段(Reference Count),用来记录指向Object的共享指针数量,当我们声明并初始化一个共享指针时,Reference Count的值为1,当有另一个共享指针指向该Object时(比如通过赋值或拷贝构造函数,将一个共享指针赋值给另一共享指针),Reference count 的值递增1

      第二个字段:Weak Count,也是一个引用计数,它用来计数指向该Objectstd::weak_ptr指针的数量,关于std::weak_ptr我们将在下一章节讨论。

      引用计数的存在使得std::shared_ptr有一些比较特别的特性:

  1. Std::shared_ptr的大小两倍于普通指针。

  2. 控制块必须是动态分配的,这是由于被shared_ptr所管理的对象并不知道有“引用计数”的存在,所以在被管理对象中并没有空间来存储这个引用计数。

  3. 引用计数的增减必须是原子性的,否则在多线程环境下,由于线程竞争的存在,会导致对Reference count的脏读、脏写。

三、资源释放

    shared_ptr默认情况下使用delete释放资源,但是用户也可以指定自己的资源释放函数,例如在下面这个例子中,pInvestdelete_Investment进行资源释放操作:

auto delete_Investment = [](Investment*pInv)
{
      pInv->getObjectType();
      delete pInv;
};

shared_ptr<Investment> pInvest(new Investment(),delete_Investment)

   资源释放器的地址存放于控制块(Control Block)中,因此增加用户自定义的资源释放器不会增加shared_ptr的大小,其大小仍然为两个指针的大小。

四、关于控制块(Control Block)的创建

   当代码第一次为一个对象创建shared_ptr指针指向这个对象的时候,控制块就会被创建,或者说它应该被创建。但是问题在于,一处代码在创建shared_ptr来指向一个对象时,这处代码并不知道它创建的这个shared_ptr所要指向的对象是否已经有别的shared_ptr指向了,即这个对象是否已经有一个控制块与之对应了。为了解决这个问题,控制块的创建遵循以下原则:

  1. std::make_shared总是会创建一个控制块。这是因为当调用std::make_shared创建智能指针的时候,智能指针所要指向的这个对象是第一次被创建的。

  2. 当通过一个普通指针作为参数构建一个智能指针的时候,会为这个指针所指向的对象创建一个控制块。

    在这种情况下,如果用户想要为一个已经有控制块的对象创建一个shared_ptr时,需要用shared_ptr或者weak_ptr作为共享指针构造函数的参数,而不能用普通指针作为参数。

   注意:使用普通指针作为参数构造一个shared_ptr有可能会导致多个控制块(control block)的创建。这种情形并不总是会很容易的被意识到,看下面的例子:

   假设投资类中有一个函数来处理投资,一个vector来追踪被处理过的投资,代码如下:

std::vector<shared_ptr<Investment>> m_ptrVec;

class Investment
{
public:
      void Handler(); // handle the investment 
};

void Investment::Handler()
{
      m_ptrVec.emplace_back(this);
}

   在投资处理函数Handler中,用this作为参数传给vectoremplace_back函数,但这样做其实蕴藏了风险,试想如果在代码中的其他地方有一个shared_ptr也指向了这个投资对象,这样就会有两个控制块被建立起来。那么,有没有一种办法,让我们可以使用this指针构造shared_ptr,但是又不会引起控制块的重复创建呢?

   幸运的是,在STL中有一个模板类正是为此而生,它就是std::enable_shared_from_this。代码要达到这个目的,必须继承std::enable_shared_from_this类,并将this指针用shared_from_this()接口代替,就像下面的代码一样:

 

class Investment :public std::enable_shared_from_this<Investment>
{
public:
      void Handler();
};

void Investment::Handler()
{
      m_ptrVec.emplace_back(shared_from_this());
}

shared_from_this()会查询当前对象的控制块,并创建一个新的shared_ptr关联到这个控制块。如果这个对象还没有与之对应的控制块,shared_from_this()会抛出异常。

五、最后一点:

C++17 以前,shared_ptr并不支持动态数组,没有std::shared_ptr<T[]>.所以shared_ptr只能管理单个对象,而不能管理对象数组。

C++17及以上,增加了shared_ptr 对动态数组的支持。比如,向下面这样:

std::shared_ptr<int[]> p(new int[10]);

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11智能指针之std::shared_ptr 的相关文章

  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • 从 WebBrowser 控件 C# 获取滚动值

    我试图在 WebBrowser 控件中获取网页的 Y 滚动索引 但无法访问内置滚动条的值 有任何想法吗 对于标准模式下的 IE 使用文档类型 正如你所说 scrollTop是的财产元素 而不是 HtmlDocument htmlDoc th
  • JDBC 时间戳和日期 GMT 问题

    我有一个 JDBC 日期列 如果我使用 getDate 则会得到 date 仅部分2009 年 10 月 2 日但如果我使用 getTimestamp 我会得到完整的 date 2009 年 10 月 2 日 13 56 78 890 这正
  • std::forward_as_tuple 将参数传递给 2 个构造函数

    我想传递多个参数以便在函数内构造两个对象 以同样的方式std pair
  • 如何从文本文件读取整数到数组

    这就是我想做的 我对此有些不满 但我希望你能容忍我 这对我来说是一个非常新的概念 1 在我的程序中 我希望创建一个包含 50 个整数的数组来保存来自文件的数据 我的程序必须获取用户的文档文件夹的路径 2 文件的名称为 grades txt
  • 为什么\0在java中不同系统中打印不同的输出

    下面的代码在不同的系统中打印不同的输出 String s hello vsrd replace 0 System out println s 当我在我的系统中尝试时 Linux Ubuntu Netbeans 7 1 它打印 When I
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • 在 EnvDTE 中调试时捕获 VS 局部变量

    是否可以使用 EnvDTE 进行 vsix Visual Studio 扩展来捕获本地和调试窗口使用的调试数据 或者可以通过其他方法吗 我想创建一个自定义的本地窗口 我们可以修改它以根据需要显示一些较重的内容 而无需为高级用户牺牲原始的本地
  • .NET Core 中的跨平台文件名处理

    如何处理文件名System IO以跨平台方式运行类以使其在 Windows 和 Linux 上运行 例如 我编写的代码在 Windows 上完美运行 但它不会在 Ubuntu Linux 上创建文件 var tempFilename Dat
  • 在java中以原子方式获取多个锁

    我有以下代码 注意 为了可读性 我尽可能简化了代码 如果我忘记了任何关键部分 请告诉我 public class User private Relations relations public User relations new Rela
  • 使用restsharp序列化对象并将其传递给WebApi而不是序列化列表

    我有一个看起来像的视图模型 public class StoreItemViewModel public Guid ItemId get set public List
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • 更改 Windows Phone 系统托盘颜色

    有没有办法将 Windows Phone 上的系统托盘颜色从黑色更改为白色 我的应用程序有白色背景 所以我希望系统托盘也是白色的 您可以在页面 XAML 中执行此操作
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack
  • 由 Servlet 容器提供服务的 WebSocket

    上周我研究了 WebSockets 并对如何使用 Java Servlet API 实现服务器端进行了一些思考 我没有花费太多时间 但在使用 Tomcat 进行一些测试时遇到了以下问题 如果不修补容器或至少对 HttpServletResp
  • 将 char[][] 转换为 char** 会导致段错误吗?

    好吧 我的 C 有点生疏了 但我想我应该用 C 来做我的下一个 小 项目 这样我就可以对其进行抛光 并且我已经有不到 20 行的段错误了 这是我的完整代码 define ROWS 4 define COLS 4 char main map
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于
  • 从 JavaScript 中的 OnClientClick 事件中阻止 C# 中的 asp:Button OnClick 事件?

    我有一个asp Button在我的网页上 它调用 JavaScript 函数和代码隐藏方法 后者进行调用以导航到另一个页面 在 JavaScript 函数中 我正在检查条件 如果不满足这个条件 我想中止导航 以便OnClick方法未被调用
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐

  • 基于FPGA的AHT10传感器温湿度读取

    文章目录 一 系统框架 二 i2c接口 三 i2c控制模块 状态机设计 状态转移图 START INIT CHECK INIT IDLE TRIGGER WAIT READ 代码 四 数据处理模块 串口 代码 五 仿真 testbench设
  • vue:实现锚点双向滚动/文章章节联动滚动效果

    文章目录 需求描述 实现思路 示例代码 参考网址 需求描述 需要实现类似doc中文档大纲的效果 点击对应章节的名称时定位到相应的正文 而当正文滚动时 高亮显示对应的章节名称 实现思路 其实笔者一开始想到的是利用a标签页内跳转 也就是 锚点
  • pandas学习笔记--增加行或列

    一 增加行 1 loc 想增加一行 行名称为 5 内容为 16 17 18 19 df loc 5 16 17 18 19 后面的序列是Iterable就行 2 at df at 5 16 17 18 19 3 set value df s
  • CTFShow web1-7——CTF秀WEB模块解题思路

    CTFShow WEB模块详细通关教程 受篇幅所限 通关教程分为上下两部分 第一部分为1 7关 第二部分为8 14关 本篇博客为1 7关的通关教程 从解题思路和原理剖析两个方面进行讲解 CTFShow web1 7关详细教程 解题思路 CT
  • 架构师必备技能之——MySQL数据库表设计

    好记忆不如烂笔头 能记下点东西 就记下点 有时间拿出来看看 也会发觉不一样的感受 目录 一 总体设计思想 二 字段相关设计原则 三 索引设计原则 四 SQL操作原则 五 其他原则 一 总体设计思想 1 不要在数据库做运算符操作 数据库服务器
  • FastJSON、Jackson、Gson性能测试

    起因是公司原先用的是阿里开源的FastJSON 大家用的也比较顺手 但是在出现了两次严重的漏洞后 公司决定放弃FastJSON 使用其他序列化 反序列化工具 考虑大家常用的无非就是FastJSON Jackson和Gson这三种 因此领导让
  • MyBatis 中如何使用多表查询

    MyBatis 中如何使用多表查询 MyBatis 是一款优秀的 ORM 框架 支持多表查询操作 在实际开发中 经常需要使用多表查询来获取业务数据 本文将介绍 MyBatis 中如何使用多表查询 包括使用嵌套查询 使用关联查询和使用动态 S
  • 六种进程间通信方式

    转载 六种进程间通信方式 LceChan的博客 CSDN博客 如何实现进程间通信
  • swiper 轮播 多行多列 横向排列

    一直没仔细研究过swiper 用到了swiper多行多列的展示效果 官网默认是纵向排列 想要做到横向排列 需添加一个一个属性 slidesPerColumnFill row slidesPerView 4 slidesPerColumn 4
  • SpringBoot 统一功能处理

    目录 一 统一用户登录权限验证 Spring 拦截器 统一访问前缀添加 二 统一异常处理 三 统一数据格式返回 String 格式的特殊处理 一 统一用户登录权限验证 在没有统一功能处理之前 对于用户登录权限验证 每个方法都需要单独写用户登
  • 编写函数void fun(int x,int *pp,int *n),它的功能是:求出x的偶数因子,并按从小到大的顺序放在pp所指的数组中,这些因子的个数通过形参n返回(假设pp指向足够大的空间)。

    编写函数void fun int x int pp int n 它的功能是 求出x的偶数因子 并按从小到大的顺序放在pp所指的数组中 这些因子的个数通过形参n返回 假设pp指向足够大的空间 如 当x的值为24 则有6个符合要求分别是2 4
  • QT编译环境配置,以及开发板移植的问题

    一 QT编译环境的设置 编译环境的配置 这个是真个系统构建的时候配置的问题 比较麻烦 后面在补这部分的知识 韦东山的开发板和乌班图的编译工具链里面是具有qt的编译工具链的 自己看的是正点原子的视频 所以按照正点正点原子的编译工具进行配置的
  • Linux下进程退出的几种形式

    进程退出 Linux 下进程的退出分为正常退出和异常退出两种 1 正常退出 a 在main 函数中执行return b 调用exit 函数 c 调用 exit 函数 2 异常退出 a 调用about函数 b 进程收到某个信号 而该信号使程序
  • Webpack构建多页应用Mpa(一):阐述设计概要

    应用场景 如果现在要做一个前后端分离的项目 可能第一反应就是使用市面上很火的三大MVVM框架 Vue React Angular 但如果团队没有专职前端 并且项目预留时间也很紧张 没足够时间去系统学习工程化Vue项目 但是却也想让html
  • 代码段中存放数据

    1 前面我们写的程序中 只有一个代码段 我们先来在代码段中使用数据 看看和单独一个数据段存放数据有什么差别 考虑这样一个问题 编程计算以下8个数据的和 结果存放在ax寄存器中 0123H 0456H 0789H 0abcH 0defH 0f
  • Unity3d 插件 系列——DoTweenPro介绍(图文详细+案例)

    Unity3d 插件 系列 DoTweenPro介绍 图文详细 案例 前言 一 DoTweenPro简介 二 DoTweenPro安装 三 DoTweenPro主要组件 1 DoTweenAnimation 2 DoTweenPath 3
  • python3.7在centos下安装pygame

    python编程从入门到实践 一书的第二部分开始 需要安装pygame 我用的是centos7 里面有内置的python2 我自己装了python3 7 pip安装pip3 安装过程网上教程很多 但是一般安装的不完全 需要注意的是安装ssl
  • 关于Yarn的一些个人总结

    文章目录 前言 一 Yarn是什么 二 Yarn由什么组成 三 Yarn用来做什么 四 Yarn的优势是什么 五 Yarn解决了什么问题 总结 前言 在前面我们可以得出Yarn是Hadoop生态圈中一个重要得组成部分 主管资源管理 但是具体
  • 2021赣网杯网络安全大赛_部分Writeup

    目录 Web 1 checkin 2 gwb web easypop 3 gwb web2 挖洞大师 misc 1 decodemaster 2 gwb misc lovemath 3 gwb misc3 testcat Web 1 che
  • C++11智能指针之std::shared_ptr

    std shared ptr是在c 11中引入的一种智能指针 其特点是它所指向的资源具有共享性 即多个shared ptr可以指向同一份资源 在c 中使用shared ptr需要包含