std::make_shared 是C++11的一部分,而 std::make_unique 是C++14才加入的。在C++11中可以轻松地写一个 std::make_unique :
template<typename T, typename... Ts>
std::unique_ptr<T> make_unique(Ts&&... params)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
}使用make系列函数来创建一个智能指针可以减少代码冗余,我们看下面的对比:
auto upw1(std::make_unique<Widget>());
std::unique_ptr<Widget> upw2(new Widget);
auto spw1(std::make_shared<Widget>());
std::shared_ptr<Widget> spw2(new WIdget);另一个好处是可以避免内存泄漏:
int computePriority();
void processWidget(std::shared_ptr<Widget> spw, int priority);
processWidget(std::shared_ptr<Widget>(new Widget),
computePriority());由于函数传参时求值顺序不定,有可能出现这样的情况:
- 执行
new Widget - 执行
computePriority() - 运行
std::shared_ptr构造函数
如果 computePriority() 抛出异常,那么第一步动态分配的 Widget 会被泄露,因为它无法被存储到第三步接管的 std::shared_ptr 中。
使用 std::make_shared 可以避免该问题:
processWidget(std::make_shared<Widget>(),
computePriority());由于动态分配内存在 std::make_shared 内部执行,所以一定会被 std::shared_ptr 接管,不会发生内存泄漏。
使用 std::make_shared 也可以提高代码的效率。
make函数的限制在于无法自定义析构器。条款7中指出,std::make_unique std::make_shared 在内部以圆括号的形式创建对象,无法提供对 std::initializer_list 的支持,下面的代码创建的vector对象是由10个元素20组成的。
auto upv = std::make_unique<std::vector<int>>(10, 20);变通的办法是使用大括号初始化器作为参数传入:
auto initList = {10, 20};
auto spv = std::make_shared<std::vector<int>>(initList);对于 std::shared_ptr 不建议使用make函数的额外场景包括:
- 自定义内存管理的类
- 内存紧张的系统,非常大的对象,以及存在比指向相同对象的
std::shared_ptr生存期更久的std::weak_ptr
当你必须使用 new 创建对象时,可以使用下面的保证异常安全又不失效率的方式:
std::shared_ptr<Widget> spw(new Widget, cusDel);
processWIdget(std::move(spw), computePriority());