首先要知道, std::make_shared是C++11引入的一个make函数,而std::make_unique是C++14才引入,当然我们也可以用简单的几行代码在C++11中实现std::make_unique(不支持数组):
template <typename T, typename... Ts>
std::unique_ptr<T> my_make_unique(Ts&&... params) {
return std::unique<T>(new T(std::forward<Ts>(params)...));
}
使用 std::make_shared及std::make_unique代替new的好处:
- 代码量更小,不需要重复撰写型别
auto upw1(std::make_unique<Widget>());
//重复写了两次Widget型别
std::unique_ptr<Widget> upw2(new Widget);
- 避免因为异常可能导致内存泄漏
代码示例如下:
process的执行顺序可能为:(1) new Widget -> (2)badFun() -> (3) std::shared_ptr<Widget>(),若在(2)处发生异常,程序中断,则(1)中new的内存将无法释放,导致内存泄漏。当然我们也可以将std::shared_ptr<Widget>(new Widget)放到一个单独的语句中简单规避这个问题。代码如下:
// 使用new创建智能指针对象
process(std::shared_ptr<Widget>(new Widget),badFun());
// 使用new时,避免内存泄漏方案
auto spw = std::shared_ptr<Widget>(new Widget);
process(spw,badFun());
而如果直接使用 std::make_shared或std::make_unique,则可以直接避免此问题。
- 对std::shared_ptr执行std::make_shared时,性能上有进一步的提升
我们知道,每个std::shared_ptr都会指涉到一个控制块,这个控制块的内存是在std::shared_ptr的构造函数中分配的,所以,如果直接使用new来创建std::shared_ptr时,会有两次内存分配,一次是对象本身的内存,另一个则是std::shared_ptr的控制块内存。
但如果使用 std::make_shared或std::make_unique,只会执行一次内存分配,因为这一次分配的内存既包括对象本身,也包括控制块,它们的内存地址是相邻的,这种优化减小了程序的静态尺寸,增加了可执行代码的运行速度,更潜在的减少了程序的内存痕迹总量。示例代码如下:
//使用new将会执行两次内存分配,一次是new Widget,另一次是共享指针的控制块
std::shared_ptr<Widget>(new Widget);
//使用make_shared只会执行一次内存分配,即将Widget对象及控制块同时分配到一块内存上
auto spw = make_shared<Widget>();
当然std::make_shared及std::make_unique也有无法完成的工作:
- std::make_shared及std::make_unique不允许使用自定义析构器
- 不能直接接受大括号初始化物,即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中:
- 对于自定义了operator new或operator delete的类,不能使用std::make_shared,因为std::shared_ptr除了对象内存外还包括控制块内存,所以不能自定义new及delete运算符
- 对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出来的对象内存与控制块内存时分开的。