C++11中有std::make_shared,C++14中增加了std::make_unique,它们把任意集合的参数完美转发给动态分配对象的构造函数,然后返回一个指向那对象的智能指针。那么它们与new的区别是什么呢?
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);
本质上的区别是:使用new的版本需要重复创建的类型(即出现了两次Widget),而使用make函数不需要。
第二个原因异常安全。
std::make_shared的一个特点(相比于直接使用new)是提高效率。使用std::make_shared允许编译器生成更小、更快的代码。考虑当我们直接使用new时:
std::shared_ptr<Widget> spw(new Widget);
很明显这代码涉及一次内存分配,不过,它实际上分配两次。每个std::shared_ptr内都含有一个指向控制块的指针,这控制块的内存是由std::shared_ptr的构造函数分配的,那么直接使用new,需要为Widget分配一次内存,还需要为控制块分配一次内存。
如果用std::make_shared呢,
auto spw = std::make_shared<Widget>();
一次分配就够了,因为std::make_shared会分配一大块内存来同时持有Widget对象和控制块。这种优化减少了程序的静态尺寸,因为代码只需要调用一次内存分配函数,增加了代码执行的速度,因为只需要分配一次内存。而且,使用std::make_shared能避免一些控制块的信息,潜在地减少了程序占用的内存空间。
结论:
- 相比于直接使用new,make函数可以消除代码重复,提高异常安全,而且std::make_shared生成的代码更小更快。
- 不适合使用make函数的场合包括需要指定自定义删除器和想要传递大括号初始值。
- 对于std::shared_ptr,使用make函数可能是不明智的场合包括(1)自定义内存管理函数的类、(2)内存紧张的系统中,有非常大的对象,然后std::weak_ptr比std::shared_ptr长寿。