为什么使用std :: make_ *而不是构造函数更好?

时间:2015-07-05 15:51:50

标签: c++ c++11 std

STL中有一些函数以make_前缀开头,如std::make_pairstd::make_sharedstd::make_unique等。为什么更好的做法是使用它们代替简单地使用构造函数?

auto pair2 = std::pair< int, double >( 1, 2.0 );
auto pair3 = std::make_pair( 1, 2.0 );

std::shared_ptr< int > pointer1 = std::shared_ptr< int >( new int( 10 ) );
std::shared_ptr< int > pointer2 = std::make_shared< int >( 10 );
  • 我只是看到这些函数使代码缩短了一点,但这就是全部吗?
  • 还有其他优点吗?
  • 这些功能使用起来更安全吗?

3 个答案:

答案 0 :(得分:17)

除了启用参数演绎的好处(如其他答案中已经提到的),还有其他一些好处。

std::make_pair<T1, T2>注意只返回std::pair<T1, T2>。如果您使用std::ref传入值,则返回的对不会存储std::reference_wrapper,它会存储引用。

std::make_shared可以合并分配。 shared_ptr需要一些地方来保存诸如refcount,弱指针列表等无法直接存储在shared_ptr中的内容。这些可以与正在创建的对象组合在一个稍大的块中,而不是在两个单独的块中。

std::make_sharedstd::make_unique都会确保在抛出异常时不会遗漏任何对象。如果将函数调用为f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)),则编译器可能首先分配两个int对象,然后构造两个shared_ptr<int>对象。如果第二个分配失败,并且尚未设置shared_ptr<int>对象以在销毁时释放内存,则会出现内存泄漏。 std::make_sharedstd::make_unique合并了int的分配和std::shared_ptr<int>在函数调用中的构造,以及int的另一个分配和std::shared_ptr<int>的其他构造{1}}在另一个函数调用中。函数调用不能重叠,因此如果第二次分配失败,则已经存在一个将被销毁的共享指针,同时撤消第一次分配。

答案 1 :(得分:6)

虽然这可能是主观的,但这项技术的一个主要好处是:

  

针对接口编写代码,而不是实现

本质上,函数模板实例化基于您传递的参数执行类型推导,而类模板实例化则不会。因此,您不必像直接实例化类那样传递模板参数。

应该注意的是,这不是关于&#34;保存一些字符&#34;而是关于使代码更通用,并避免与函数调用中的具体类型绑定。

但是,情况并非总是如此,正如您的std::make_shared示例所示,仍然存在必须将类型作为模板参数传递的情况。但是,正如Herb Sutter points out一样,在使用std::make_shared时还有其他一些优点:

  1. 你应该首先写清楚和正确,std::make_shared实现这两者(主观,但我同意)

  2. 使用std::make_shared更有效率,因为它一次性分配您的对象以及shared_ptr对象,从而允许更低的分配开销和更好的缓存对齐。

答案 2 :(得分:1)

make_unique隐藏了你的“原始”指针,这通常是一件好事 - 它不易出错。 make_shared可以改善shared_ptr<X>个实例的内存分配。通常,当您使用shared_ptr<X>构造函数时,它将分配内存两次,首先是X,第二次是内部数据(例如引用计数器)。 make_shared启用优化 - 它将创建包含X和引用计数器的单个内部结构,因此它将执行单个内存分配。和之前一样,它隐藏了原始指针。