C++11智能指针之优先使用std::make_unique和std::make_shared而不是直接使用new

在初始化一个unique_ptr或者shared_ptr时,我们最好优先使用std::make_unique和std::make_shared。原因有一下几点:

异常安全性

假设有如下函数声明:

intcomputePriority();
void processInvestment(std::shared_ptr ptr,int priority);

调用processInvestment的代码如下所示:

processInvestment(std::shared_ptr(newInvestment()),computePriority());

 

由于在C++中函数参数的执行顺序是不固定的,所以在上面对函数processInvestment调用中,函数参数的执行顺序很可能是这样的:

new Investment

computePriority()

std::shared_ptr constructor

这种执行顺序的风险是,如果在第二步,执行computePriority的过程中出现异常,那么在第一步中new出来的对象将变得不可访问,从而造成内存泄漏。通过make_ptr的方式,将new操作和shared_ptr的构造放在一起执行,就不会出现内存泄漏问题了。

执行效率(对于shared_ptr而言)

std::shared_ptrptr(new Investment);   //方式1,new的方式

在方式1中,会涉及到两次动态内存分配:

第一次是new Investment时,为Investment对象分配空间。

第二次是为控制块(Control Block)分配空间。

auto pIn = std::make_shared();//方式2,make_shared的方式

在方式二中,一次动态内存分配就足够了,这是由于make_shared会为Investment对象和控制块(Control block)一次性分配一大块内存。由于只有一次内存分配,因而方式二提高了程序的执行效率。

二、make函数的弊端

  既然使用make_shared和make_unique有这么多好处,我们是否就应该处处使用make函数而完全放弃new的方式了呢?当然不是,make函数也存在一些缺点:

1 make函数不支持用户自定义释放器。由于make函数有自己的内存分配和析构规则,所以他不适用于那些有自定义分配器和释放器的对象。

2 make函数不支持大括号初始化方式。对于下面这句代码:

auto spv = std::make_shared>(10,20);

意为spv指向一个vector,这个vector有10个元素,每个元素的值都为20。如果你想实现的是这个vector有两个元素,分别为10,20的话,你只能用new的方式。

3内存释放不够灵活。

在使用new的方式中,有两块独立的堆内存,一块存放资源对象,一块存放控制块,当资源对象的引用计数为0的时候,资源对象会被销毁,它所占用的内存也会被随之销毁。

在使用make函数的方式中,make函数一次性为资源对象和控制块分配了一块内存,当资源对象的引用计数为0是,对象被销毁,但是资源对象占用的内存却不会被销毁,只有当控制块占用的内存被销毁是才会将资源对象所占内存一并释放。那么,控制块内存什么时候被释放呢?这就涉及到控制块中另一个引用计数了,这个引用计数被称为“Weak Count”,其作用是用来计数指向该资源的weak_ptr的数量。当这个weak count的值为0时,控制块才会被释放。当资源对象非常庞大时,使用make函数的方式将造成不小的资源浪费。

你可能感兴趣的:(c++)