条款21:优先选用std::make_unique和std::make_shared,而非直接new

std::make_shared是C++11一部分,std::make_unique为C++14才引入,std::allocate_shared它的行为和std::make_shared一样,只不过它的第一个实参是个用以动态分配内存的分配器对象;

是否对make系列函数来创建一个智能指针坐最平凡的对比,也能揭示优先选用make系列函数的第一个原因,考虑如下代码:

auto upw1(std::make_unique<Widget>());		// 使用make系列函数
std::unique_ptr<Widget> upw2(new Widget);	// 不使用make系列函数
auto spw1(std::make_shared<Widget>());		// 使用make系列函数
std::shared_ptr<Widget> spw2(new Widget);	// 不使用make系列函数

使用了new的版本將被创建对象(Widget)的类型重复写了两遍,而make系列没有,这违反了软件工程避免代码冗余的原则。源代码中的重复会增加编译遍数,导致臃肿的目标代码。

优先使用make系列函数另一个原因是与异常安全相关。考虑有一个函数一句某种优先级来处理一个Widget对象:

 void processWidget(std:shared_ptr<Widget> spw, int priority);
 int computerPriority();		//计算优先级

processWdiget(std::shared_ptr<Widget>(new Widget), computerPriority()); // 潜在的资源泄漏

如注释,这段代码会因为使用了new运算符而导致内存泄漏,原因是因为编译器从源代码到目标代码的翻译有关。如下事件必须在processWidget开始执行前发生:

  1. 一个Widget对象必须线在堆上建立
  2. new产生的裸指针的托管对象std::shared_ptr<Widget>的构造函数必须执行
  3. computerPriority`必须运行

编译器不一定按照上述顺序来执行代码。1必须在2之前执行,但是3可以在1,2之前/之后/中间掉用

如果执行顺序为1->3->2,并且3在出现了异常,那么1就会产生内存泄漏,因为它永远不会被存储到std::shared_ptr中;使用std::make_shared可以避免该问题:

processWdiget(std::make_shared<Widget>(), computerPriority()); 

使用std::make_shared会让编译器有机会利用更简洁的数据结构产生更小更快的代码,考虑如下使用new的代码

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

每个std::shared_ptr会涉及一个控制块,控制块内存是std::shared_ptr的构造函数进行分配的,还有一次Widget的内存分配,上述代产生两次内存分配;

如果使用std::make_shared<Widget>()代替的话:

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

因为std::make_shared会分配单块内存,既保存Widget对象又保存与其相关的控制块

有以下情景不应该使make系列函数

  1. 所有make系列函数都不允许使用自定义析构器
  2. 无法使用大括号初始化

对于std::shared_ptr,不建议使用make系列函数的场景包括:

  1. 自定义管理内存的类
  2. 内存紧张的系统,非常大的对象,以及在比涉及到相同对象的std::shared_ptr生存期更久的std::weak_ptr
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值