文章目录

  • 尽量使用make_shared初始化
  • make_shared初始化的优点
    • 1、提高性能
    • 2、 异常安全
  • 缺点
    • 构造函数是保护或私有时,无法使用 make_shared
    • 对象的内存可能无法及时回收

shared_ptr<string> p1 = make_shared<string>(10, '9');  shared_ptr<string> p2 = make_shared<string>("hello");  shared_ptr<string> p3 = make_shared<string>();

尽量使用make_shared初始化

C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::shared_ptr 的构造函数相比它能给我们带来什么好处呢 ?

make_shared初始化的优点

1、提高性能

shared_ptr 需要维护引用计数的信息:
强引用, 用来记录当前有多少个存活的 shared_ptrs 正持有该对象. 共享的对象会在最后一个强引用离开的时候销毁( 也可能释放).
弱引用, 用来记录当前有多少个正在观察该对象的 weak_ptrs. 当最后一个弱引用离开的时候, 共享的内部信息控制块会被销毁和释放 (共享的对象也会被释放, 如果还没有释放的话).
如果你通过使用原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块:

使用shred_ptr初始化

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

使用make_shared

std::make_shared(比起直接使用new)的一个特性是能提升效率。使用std::make_shared允许编译器产生更小,更快的代码,产生的代码使用更简洁的数据结构。考虑下面直接使用new的代码:

std::shared_ptr<Widget> spw(new Widget);

很明显这段代码需要分配内存,但是它实际上要分配两次。每个std::shared_ptr都指向一个控制块,控制块包含被指向对象的引用计数以及其他东西。这个控制块的内存是在std::shared_ptr的构造函数中分配的。因此直接使用new,需要一块内存分配给Widget,还要一块内存分配给控制块。

如果使用std::make_shared来替换

auto spw = std::make_shared<Widget>();

一次分配就足够了。这是因为std::make_shared申请一个单独的内存块来同时存放Widget对象和控制块。这个优化减少了程序的静态大小,因为代码只包含一次内存分配的调用,并且这会加快代码的执行速度,因为内存只分配了一次。另外,使用std::make_shared消除了一些控制块需要记录的信息,这样潜在地减少了程序的总内存占用。

对std::make_shared的效率分析可以同样地应用在std::allocate_shared上,所以std::make_shared的性能优点也可以扩展到这个函数上。

2、 异常安全

我们在调用processWidget的时候使用computePriority(),并且用new而不是std::make_shared:

    processWidget(std::shared_ptr<Widget>(new Widget),  //潜在的资源泄露 computePriority());

就像注释指示的那样,上面的代码会导致new创造出来的Widget发生泄露。那么到底是怎么泄露的呢?调用代码和被调用函数都用到了std::shared_ptr,并且std::shared_ptr就是被设计来阻止资源泄露的。当最后一个指向这儿的std::shared_ptr消失时,它们会自动销毁它们指向的资源。如果每个人在每个地方都使用std::shared_ptr,那么这段代码是怎么导致资源泄露的呢?

答案和编译器的翻译有关,编译器把源代码翻译到目标代码,在运行期,函数的参数必须在函数被调用前被估值,所以在调用processWidget时,下面的事情肯定发生在processWidget能开始执行之前:

表达式“new Widget”必须被估值,也就是,一个Widget必须被创建在堆上。
std::shared_ptr(负责管理由new创建的指针)的构造函数必须被执行。
computePriority必须跑完。
编译器不需要必须产生这样顺序的代码。但“new Widget”必须在std::shared_ptr的构造函数被调用前执行,因为new的结构被用为构造函数的参数,但是computePriority可能在这两个调用前(后,或很奇怪地,中间)被执行。也就是,编译器可能产生出这样顺序的代码:

执行“new Widget”。
执行computePriority。
执行std::shared_ptr的构造函数。

如果这样的代码被产生出来,并且在运行期,computePriority产生了一个异常,则在第一步动态分配的Widget就会泄露了,因为它永远不会被存放到在第三步才开始管理它的std::shared_ptr中。

使用std::make_shared可以避免这样的问题。调用代码将看起来像这样:

processWidget(std::make_shared<Widget>(),       //没有资源泄露computePriority());

在运行期,不管std::make_shared或computePriority哪一个先被调用。如果std::make_shared先被调用,则在computePriority调用前,指向动态分配出来的Widget的原始指针能安全地被存放到被返回的std::shared_ptr中。如果computePriority之后产生一个异常,std::shared_ptr的析构函数将发现它持有的Widget需要被销毁。并且如果computePriority先被调用并产生一个异常,std::make_shared就不会被调用,因此这里就不需要考虑动态分配的Widget了。

如果使用std::unique_ptr和std::make_unique来替换std::shared_ptr和std::make_shared,事实上,会用到同样的理由。因此,使用std::make_unique代替new就和“使用std::make_shared来写出异常安全的代码”一样重要。

缺点

构造函数是保护或私有时,无法使用 make_shared

make_shared 虽好, 但也存在一些问题, 比如, 当我想要创建的对象没有公有的构造函数时, make_shared 就无法使用了, 当然我们可以使用一些小技巧来解决这个问题, 比如这里 How do I call ::std::make_shared on a class with only protected or private constructors?

对象的内存可能无法及时回收

make_shared 只分配一次内存, 这看起来很好. 减少了内存分配的开销. 问题来了, weak_ptr 会保持控制块(强引用, 以及弱引用的信息)的生命周期, 而因此连带着保持了对象分配的内存, 只有最后一个 weak_ptr 离开作用域时, 内存才会被释放. 原本强引用减为 0 时就可以释放的内存, 现在变为了强引用, 若引用都减为 0 时才能释放, 意外的延迟了内存释放的时间. 这对于内存要求高的场景来说, 是一个需要注意的问题

【make_shared的使用】相关推荐

  1. 【c++】4.std::shared_ptr、std::make_shared、 .get() 、.data()、void *p 的用法、裸指针

    std::shared_ptr.std::make_shared. .get() ..data().void *p 的用法.裸指针 (1)shared_ptr能够记录对象被引用的次数,主要被用来管理动 ...

  2. 使用 C++0x 时 make_shared 完美转发构造函数参数的测试编译器

    使用 C++0x 时 make_shared 完美转发构造函数参数的测试编译器 实现功能 C++实现代码 实现功能 使用 C++0x 时 make_shared 完美转发构造函数参数的测试编译器 C+ ...

  3. make_shared和shared_ptr的区别

    struct A; std::shared_ptr<A> p1 = std::make_shared<A>(); std::shared_ptr<A> p2(new ...

  4. shared_ptrT make_shared( Args ... args );

    shared_ptr很好地消除了显式的delete调用,如果读者掌握了它的用法,可以肯定delete将会在你的编程字典中彻底消失. 但这还不够,因为shared_ptr的构造还需要new调用,这导致了 ...

  5. std::make_shared<T>/std::make_unique<T>与std::shared_ptr<T>/std::unique_ptr<T>的区别与联系

    (1).std::make_shared<T>与std::make_unique<T>相对于std::shared_ptr<T>/std::unique_ptr&l ...

  6. std::make_unique<T>和std::make_shared<T>

    更建议使用:std::make_unique<T>构造unique_ptr对象:std::make_shared<T>构造shared_ptr对象 std::make_shar ...

  7. 探究make_shared效率

    Why Make_shared ? C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::s ...

  8. std::make_unique和 std::make_shared区别

    区别1:分配除的指针,前者是不能赋值,只能move,后者可以赋值 区别2:前者使用智能指针数组,后者不行(因为后者分配出来的指针具有单一性) #include #include class A { p ...

  9. C++ make_shared() shared_ptr()用法

    shared_ptr很好地消除了显式的delete调用,如果读者掌握了它的用法,可以肯定delete将会在你的编程字典中彻底消失 .但这还不够,因为shared_ptr的构造还需要new调用,这导致了 ...

  10. C++11 std::make_shared 与 std::shared_ptr双剑合璧

    项目做完上线,发现存在内存泄漏.因为客户端链接到服务器时传统new出来对象,断开链接后没有进行释放. 一.定时检测对象释放情况 代码如下: //1.开启服务监听 bool CWebServer::St ...

最新文章

  1. 3没有样式重置_系统重置新增选项,99%的人不知道怎么选
  2. 在RHEL-4下半小时搭建Sendmail邮件服务器(下)
  3. 纯CSS实现多级菜单,兼容IE6
  4. Swift: 可变参数
  5. 算法9-5:最大流算法的Java代码
  6. android /data/data/数据作用,android 清除data/data/ 下其他应用的数据
  7. 【Python学习】内置函数(不断更新)
  8. Android Alarm自上而下 调试浅析
  9. MFC 最详细入门教程
  10. 一代上网人的记忆!百度浏览器将不再更新
  11. Github 标星 13K+!这可能是最好的 Java 博客系统
  12. NX/UG二次开发—CAM—刀轨转曲线(支持圆弧和直线)
  13. 企业微信开发实战:自建审批流引擎
  14. 跨界打劫!中医保健店用一招免费洗车,快速引流进店,月赚20万
  15. 计算机中计算平均数的函数是什么,Excel里怎么求平均数的?函数是什么?!excle2010怎么求平均数...
  16. 财会法规与职业道德【5】
  17. git pull报错:Your configuration specifies to...from the remote, but no such ref was fetched
  18. 如何快速打开北京健康宝小程序?
  19. Spring配置dbcp数据源
  20. 【虚幻引擎UE】UE5 三种模式调用API详解(案例基于免费Varest插件)

热门文章

  1. 续流二级管在电路的作用
  2. C语言 求一元二次方程ax2+bx+c=0的根
  3. linux菜鸟必学的60个命令,新手入门 Linux菜鸟必学的60个命令
  4. obsidian apk下载办法
  5. 计算机运算器进行什么运算,运算器能进行什么运算
  6. PC安装黑苹果 (macOS Sierra 10.12.6)上篇
  7. 用vue开发微信公众号H5移动端页面
  8. on error resume next用法
  9. C语言用字符串sex储存,《C语言》上机实验题及参考答案2
  10. java后端项目整体代码结构