智能指针之make_unique与make_shared

2023-05-16

make_unique的实现

std::make_shared是C++11的一部分,但是std::make_unique很可惜不是。它是在C++14里加入标准库的,但我们可以自己实现make_unique方法。

// 支持普通指针
template<class T,class... Args> inline
typename enable_if<!is_array<T>::value,unique_ptr<T>>::type
make_unique(Args&&... args){
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

// 支持动态数组
template<class T> inline
typename enable_if<is_array<T>::value && extent<T>::value == 0,unique_ptr<T>>::type
make_unique(size_t size){
    typedef typename remove_extent<T>::type U;
    return unique_ptr<T>(new U[size]());
}

// 过滤掉定长数组的情况
template<class T,class... Args>
typename enable_if<extent<T>::value != 0,void>::type
make_unique(Args&&...) = delete;

enable_if的作用

// Primary template.
/// Define a member typedef @c type only if a boolean constant is true.
template<bool, typename _Tp = void>
  struct enable_if
  { };

// Partial specialization for true.
template<typename _Tp>
  struct enable_if<true, _Tp>
  { typedef _Tp type; };

结合源码可知,当condition==true时,enable_if<condition,T>::type ≡ T,否则报错。

  • enable_if<!is_array<T>::value,unique_ptr<T>>::typeconditionT不是数组类型时为true
  • enable_if<is_array<T>::value && extent<T>::value == 0,unique_ptr<T>>::typeconditionT为数组类型且数组中元素个数为0时为true,由于对于非数组类型extent<U>::value也为0,语句is_array<T>::value是必要的
  • enable_if<extent<T>::value != 0,void>::typeconditionT类型中元素个数不为0时为true,即T为定长数组

std::forward的作用

std::forward在这里的作用是实现参数的完美转发,具体见《move和forward源码分析[转]》。

make函数的好处

1. 效率更高

shared_ptr需要维护引用计数的信息。如果你通过使用原始的new表达式分配对象,然后传递给shared_ptr(也就是使用shared_ptr的构造函数)的话,shared_ptr的实现没有办法选择,而只能单独的分配控制块:

这里写图片描述

如果选择使用make_shared的话,情况就会变成下面这样:

这里写图片描述

内存分配的动作,可以一次性完成。这减少了内存分配的次数,而内存分配是代价很高的操作。

2. 异常安全

看看下面的代码:

void F(const std::shared_ptr<Lhs>& lhs, const std::shared_ptr<Rhs>& rhs) { /* ... */ }

F(std::shared_ptr<Lhs>(new Lhs("foo")),
  std::shared_ptr<Rhs>(new Rhs("bar")));

C++是不保证参数求值顺序,以及内部表达式的求值顺序的,所以可能的执行顺序如下:

  1. new Lhs(“foo”))
  2. new Rhs(“bar”))
  3. std::shared_ptr
  4. std::shared_ptr

假设在第2步的时候,抛出了一个异常(比如out of memory,总之,Rhs的构造函数异常了),那么第一步申请的Lhs对象内存泄露了。这个问题的核心在于,shared_ptr没有立即获得裸指针。

我们可以用如下方式来修复这个问题:

auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);

当然,推荐的做法是使用std::make_shared来代替:

F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));

std::make_shared被调用,指向动态内存对象的原始指针会被安全的保存在返回的std::shared_ptr对象中,然后另一std::make_shared被调用。如果此时产生了异常,那std::shared_ptr析构会知道于是它所拥有的对象会被销毁。

使用std::make_unique来代替new在写异常安全的代码里和使用std::make_shared一样重要。

make函数的不足

  • make函数都不允许使用定制删除器,但是std::unique_ptrstd::shared_ptr的构造函数都可以。
  • make函数不能完美传递一个initializer_list
    替代方案:
// initializer_list<int> aa = {1,2,3}; // 或者
auto aa = {1,2,3};
auto a = make_shared<vector<int>>(aa);
// auto b = make_shared<vector<int>>({1,2,3}); // 错误
  • 对象的内存可能无法及时回收

虽然使用std::make_shared可以减少了内存分配的次数,提高效率,但由于控制块与对象都在同一块动态分配的内存上,所以当对象的引用计数变为0,对象被销毁(析构函数被调)后,该对象所占内存仍未释放,直到控制块同样也被销毁,内存才会释放。

我们知道,在控制块中包含两个计数:shared countweak count,分别表示std::shared_ptrstd::weak_ptr对对象的引用计数,只有当shared countweak count都为0时,控制块才会被销毁。

换句话说,只要有std::weak_ptr指向一个控制块(weak count大于0),那控制块就一定存在。只要控制块存在,包含它的内存必定存在。通过std::shared_ptrmake函数分配的内存在最后一个std::shared_ptr和最后一个std::weak_ptr被销毁前不能被释放。

  • 构造函数是保护或私有时,无法使用make_shared
    替代方案:
class A {
public:
    static std::shared_ptr<A> create() {
        return std::make_shared<A>();
    }
protected:
    A() {}
    A(const A &) = delete;
    const A &operator=(const A &) = delete;
};

std::shared_ptr<A> foo() {
    return A::create();
}

参考链接

c++11 条款21:尽量使用std::make_unique和std::make_shared而不直接使用new
Why Make_shared ?
通过new和make_shared构造shared_ptr的性能差异
How does weak_ptr work?
shared_ptr线程安全性分析
How do I call ::std::make_shared on a class with only protected or private constructors?

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

智能指针之make_unique与make_shared 的相关文章

  • 使用共享静态 WCF 代理客户端有哪些陷阱?

    我正在考虑将共享 读取静态 WCF 代理客户端用于高吞吐量应用程序 我相信这样做可以提高性能 但我还没有对此进行基准测试 这个想法有一些严重的缺陷吗 从我的研究中 我可以看到存在处理故障状态的问题 目前尚不清楚该状态对其他待处理请求的影响流
  • swift hashable 协议哈希函数是否需要返回唯一值?

    我正在学习 iOS swift 俄罗斯方块教程 并已完成并运行 但我对一个特定的方面感到困惑 Hashable 协议 功能 class Block Hashable Printable var hashValue Int return se
  • 如何获取 .net 中数组的唯一值?

    假设我有这个数组 MyArray 0 aaa MyArray 1 bbb MyArray 2 aaa 是否有一个 net 函数可以给我唯一的值 我想要这样的东西作为函数的输出 输出数组 0 aaa 输出数组 1 bbb 假设您有 Net 3
  • Excel - 计算与 ID 匹配的唯一值,针对 100,000 多个案例进行了优化

    参考下面的 Excel 屏幕截图 我正在寻找一个公式解决方案 用于计算 A 列中每个 ID 号的 B 列 颜色 中唯一值的数量 我已将所需结果归入 C 列 因此 例如 ID 1 A2 只有一种唯一颜色 灰色 B2 这将在 C2 中返回 1
  • 如何将 Tomcat 6 库管理到 %TOMCAT_HOME%/lib 下的子文件夹中?

    我使用 Tomcat 6 0 20 和 JDK 1 6 0 13 如何从子文件夹加载库 TOMCAT HOME lib 无需将 jar 从子文件夹中取出并直接放入 TOMCAT HOME lib 我想这样做的原因是因为许多应用程序将共享大量
  • Pandas GroupBy - 仅显示具有多个唯一特征值的组

    我有一个数据框df things看起来像这样 我想在训练之前预测分类的质量 A B C CLASS al1 bal1 cal1 Ship al1 bal1 cal1 Ship al1 bal2 cal2 Ship al2 bal2 cal2
  • 在一个逗号分隔的单元格中显示所有匹配的值

    我的 Excel 2010 电子表格中有两列数据 A 列中是一个类别 B 列中是一个值 对于 A 列中的每个唯一类别 B 列中将有多个值 我想在单独的工作表中实现的目标是在一个逗号 或分号等 分隔的单元格中显示每个唯一类别的所有值 例如 如
  • PHP - 使关联数组唯一,键 -> 值和值 -> 键

    我在 php 中遇到了一个小问题 我发现很难用语言解释 我有一个包含键值的关联数组 我想创建一个函数 或者如果已经有一个函数 它将接受一个数组作为输入并删除重复项 但两种方式都是如此 例如 在我的数组中 我有 a gt b a gt c b
  • C++ 使用 .o 链接和使用 .a 文件链接之间存在差异:行为不同,为什么?

    我期望 与 o 文件链接和与从 o 文件存档的 a 文件链接应该没有区别 但事实并非如此 我有2个源文件 每个都声明1个类 1个静态对象 1个函数 以及一个调用其中一个函数的main cpp cat First cpp include
  • CQRS 事件溯源:验证用户名唯一性

    我们以一个简单的 账户注册 为例 流程如下 用户访问网站 点击 注册 按钮并填写表格 点击 保存 按钮 MVC 控制器 通过读取 ReadModel 来验证用户名的唯一性 RegisterCommand 再次验证用户名唯一性 这是问题 当然
  • C++ 进程间通信的最佳方式

    我有两个进程 一个进程将查询另一个进程的数据 在有限的时间内 每秒 10000 个 将会有大量的查询 并且每秒将传输数据 gt 100 mb 数据类型将是整数类型 双精度 整数 我的问题是用什么方式连接这个过程 共享内存 消息队列 lpc
  • Javascript数组排序和唯一性

    我有一个像这样的 JavaScript 数组 var myData 237 124 255 124 366 255 我需要数组元素是唯一的并且已排序 myData 0 124 myData 1 237 myData 2 255 myData
  • MongoDB 多个字段上的唯一索引

    我正在使用 MongoDb 数据库 我需要使多个字段唯一 我需要的是 MongoDb 检查是否组合多个字段的值是唯一的 让我们举一个我需要的例子 如果我在添加索引后按此顺序在数据库中添加以下内容 name paul age 21 name
  • 在 SQL 数据库中保持 RSS 提要唯一的最佳实践

    我正在开发一个项目 该项目显示来自不同站点的 RSS 提要 我将它们保存在数据库中 我的程序每 3 小时获取一次并将它们插入到 SQL 数据库中 我希望提供者有独特的记录 不要显示重复的内容 但问题是一些提供商不提供 GUID 字段 而其他
  • 如何使用 codeigniter 生成 5 位字母数字唯一 ID?

    我有一个项目 需要为用户生成唯一的 5 位数字母数字 ID 我怎样才能使用 codeigniter 实现这一点 thanks 字符串助手中有一个名为 random string 的函数 this gt load gt helper stri
  • 为什么链接器要修改--defsym“绝对地址”

    目标 一个使用可执行文件中的函数 不导出符号 的共享库 Means gcc Wl defsym function 0x432238 手册页指出 defsym symbol expression Create a global symbol
  • 索引多列并匹配不同的值,返回跨列的唯一值列表

    我已经在漫长的几周内广泛寻找解决我的问题的方法了 我提出了一个部分有效的解决方案 我将其包含在底部 供那些可能知道如何修改 扩展它们以解决问题的人使用 这就是我想要完成的任务 以下描述参考此屏幕截图https i stack imgur c
  • 在多个不同线程之间共享变量

    我想在多个线程之间共享一个变量 如下所示 boolean flag true T1 main new T1 T2 help new T2 main start help start 我想分享flag在主线程和帮助线程之间 这是我创建的两个不
  • NFC标签唯一ID

    我正在开发一个包括 NFC 标签和 Android 手机的系统 使用 NFC 标签的唯一 ID 但不知道4种NFC标签之间有什么区别 我发现了这个 兼容 NFC 的标签可以采用以下技术 标准 他们每个人都有不同的 ID 概念 NFC Tag
  • PHP使用auto_increment生成短唯一ID?

    我想生成一个简短的 唯一的 ID 而不必检查冲突 我目前正在做类似的事情 但是我当前生成的 ID 是随机的 并且在循环中检查冲突很烦人 并且如果记录数量显着增加 将会变得昂贵 通常担心冲突不是问题 但我想要生成的唯一 ID 是一个由 5 8

随机推荐

  • 网易面试题(社招)

    转自http blog csdn net zhangpengju999 article details 11818941 时间 xff1a 2013年9月18日 地点 xff1a 网易大厦门户事业部 先是笔试题 xff0c 1小时 一 算法
  • Fast planner 基本原理学习(一)

    一 主题 xff1a Fast planner 基本原理学习 二 目标 xff1a 理解Fast planner轨迹规划处理流程理解hybrid A 的改进点B样条曲线定义 性质 以及所带来的便利 三 正文 xff1a 1 Fast pla
  • 阿里研发一面经历

    人生第一次找工作笔试 面试 xff0c 都碰上了阿里 9月暑假来了 xff0c 才开始准备找工作 xff0c 今年的宣讲来的早 所以现在有点措手不及 xff0c 只能边准备边找 废话不多了 xff0c 进入正题 笔试题 xff0c 下期再论
  • 腾讯2014校园招聘软件后台开发类笔试题

    转http www itmian4 com forum php mod 61 viewthread amp tid 61 3572
  • 2014暴风影音校招技术笔试题(长春站)

    转http www itmian4 com forum php mod 61 viewthread amp tid 61 3622 1 升序排列下列数值 xff1a 2 写出下列函数的返回值 int func int x 61 300 in
  • 计算100以内的素数的个数

    以下代码是用来计算100以内的素数的个数 xff0c 请把相应的空填上 struct prime number node int prime number prime number node next int calc prime numb
  • C语言除法运算符“/”和求余运算符“%”

    看似两个很简单的运算符 xff0c 却也真要掌握用好它也不容易 xff0c 本文作为关于此类运算符的各方面的问题的汇总 xff0c 希望对你我都有一些帮助 除法运算符 二元运算符 xff0c 具有左结合性 参与运算的量均为整型时 xff0c
  • 冒泡排序

    include lt stdio h gt define LENGTH 8 void main int tmp number LENGTH 61 95 45 15 78 84 51 24 12 for i 61 0 i lt LENGTH
  • 金山词霸C++笔试(北邮11月23号)

    金山词霸笔试题 xff08 北邮11月23号 xff09 xff08 一 xff09 以下输出什么 xff1f char ptr 61 34 hello world 34 cout lt lt ptr h ptr指向字符串的首地址 xff0
  • 关于计算机研究和写作的链接收集

    研究相关的资源 What is Research in Computer Science 计算科学的研究是什么 xff1f 翻译 Basic Research Skills in Computer Science 计算科学的基本研究方法 R
  • 实战笔记之C++里面双冒号和冒号及点号引用符

    标题党了额 xff0c mark一下 xff0c 有时间再整理 以下节选自多人博客 推荐博文一篇http blog csdn net gyymen article details 4962873 箭头 xff08 gt xff09 xff1
  • 在Linux中如何修改root帐户的登录用户名

    vi etc passwd 按i键进入编辑状态 修改第1行第1个root为新的用户名 按esc键退出编辑状态 xff0c 并输入 x保存并退出 vi etc shadow 按i键进入编辑状态 修改第1行第1个root为新的用户名 按esc键
  • Linux elasticsearch 安装超详细教程

    1 下载elasticsearch 7 11 2 linux x86 64 tar gz 之所以下载7 11 2版本是因为我使用了中文分词器 xff0c 对应中文分词器的版本号 2 可以官方下载 xff0c 会比较慢 xff0c 这里提供我
  • 程序员做一辈子?

    首先 xff0c 程序员真有必要干一辈子吗 xff1f 如果你是个搬砖的 xff0c 你会考虑一辈子搬砖吗 xff1f 你肯定会想着过几年挣钱了 xff0c 买个车跑运输 xff0c 或者自己做工头 对程序员来说 xff0c 真心没有必要干
  • ubuntu系统怎么使用ifconfig和开启远程连接

    在ubuntu系统刚安装好之后ifconfig命令和开启远程连接都是不起作用的 xff0c 如果需要使ifconfig起作用需要安装一个小工具 xff0c 如果想开启远程连接的话也需要安装open ssh工具 开启ifconfig xff1
  • LDMIA、LDMIB、LDMDB、LDMDA、STMIA、LDMFD、LDMFA、LDMED、LDMEA等指令详解

    关于多寄存器加载存储指令 1 LDMIA指令 LDMIB指令 LDMDB指令 LDMDA指令 xff08 1 xff09 LDMIA指令 xff0c IA表示每次传送后地址加4 xff08 2 xff09 LDMIB指令 xff0c 每次传
  • FreeRTOS-互斥信号量

    原文地址 xff1a http blog csdn net xukai871105 article details 43456985 0 前言 在嵌入式操作系统中互斥型信号量是任务间资源保护的重要手段 下面结合一个具体例子说明FreeRTO
  • 执行体线程--ETHREAD

    typedef struct ETHREAD KTHREAD Tcb 内嵌了KTHREAD对象作为第一个数据成员 LARGE INTEGER CreateTime 包含了线程创建时间 xff0c 他是在线程创建时被赋值的 union LAR
  • 显卡驱动的作用(本质作用)

    确切资料表明显卡不需要驱动也可以进行显示 xff0c 只需要将显示的内容存到对应的显存地址就可以 xff08 通过cpu直接或者间接的硬连线实现 xff09 也就是说单纯的显示像素的话不需要使用显卡驱动 xff0c 但是单纯的显示像素甚至某
  • 智能指针之make_unique与make_shared

    make unique的实现 std make shared是C 43 43 11的一部分 xff0c 但是std make unique很可惜不是 它是在C 43 43 14里加入标准库的 xff0c 但我们可以自己实现make uniq