首先要知道, std::make_shared是C++11引入的一个make函数,而std::make_unique是C++14才引入。

使用 std::make_shared及std::make_unique代替new的好处:

(1)代码量更小,不需要承父撰写型别

auto upw1(std::make_unique<Widget>());
//重复写了两次Widget型别
std::unique_ptr<Widget> upw2(new Widget);

(2)可能因为异常导致内存泄漏,例如在将new指针赋值智能指针前,程序发生异常,导致赋值失败,则刚刚new出来的指针将无法得到正常释放,从而内存泄漏。当然也可以通过间接规避掉,代码如下:

process(std::shared_ptr<Widget>(new Widget),badFun());
//此时process的执行顺序如果为:(1)new Widget (2)badFun() (3)std::shared_ptr<Widget>()
//若在(2)发生异常,程序中断,则(1)中new的内存将无法释放

//可以将std::shared_ptr<Widget>(new Widget)放到一个单独的语句中规避这个问题
auto spw = std::shared_ptr<Widget>(new Widget);
process(spw,badFun());

(3)对std::shared_ptr执行std::make_shared时,性能上有进一步的提升。

//使用new将会执行两次内存分配,一次是new Widget,另一次是共享指针的控制块
std::shared_ptr<Widget>(new Widget);

//使用make_shared只会执行一次内存分配,即将Widget对象及控制块同时分配到一块内存上
auto spw = make_shared<Widget>();

当然std::make_shared及std::make_unique也有无法完成的工作:

(1)std::make_shared及std::make_unique不允许使用自定义析构器

(2)不能直接接受大括号初始化物,即std::initializer_list对象。当然可以间接接受。

//不允许直接接受大括号初始化物
auto spv = make_shared<std::vector<int>>{10,20};


//可以间接接受
auto p = {10,20};
auto spv = make_shared<std::vector<int>>(p);

以下问题仅存在于std::make_shared中:

(1)对于自定义了operator new或operator delete的类,不能使用std::make_shared,因为std::shared_ptr除了对象内存外还包括控制块内存,所以不能自定义new及delete运算符

(2)对std::shared_ptr执行std::make_shared时,当std::shared_ptr析构后,其内存可能会延迟释放

前面说过,std::make_shared()分配的是一整款内存,其内容包括对象以及控制块,所以只能执行一次释放,但是因为控制块只有当std::weak_ptr引用次数为0时才会被允许释放,所以即使std::shared_ptr的引用次数为0了,但只要std::weak_ptr引用次数不为0,该对象就不会被释放。而使用new将可以规避这个问题,因为new出来的对象内存与控制块内存时分开的。