C++ 中emplace_back和push_back差异

前言

最近看rocskdb源码,发现了大量的设计模式和C++高级特性,特此补充一下,巩固基础。

问题描述

其中关于动态数组的元素添加,代码中基本将push_back抛弃掉了,全部替换为emplace_back进行元素的添加。

看了一下官网描述:
原来的push_back 一个右值元素的时候 过程分为如下几步:

  • 使用右值数据类型的构造函数 构造一个临时对象
  • 调用拷贝构造函数将临时对象放入 容器中
  • 释放临时对象

可以看到以上的三个步骤,临时对象的资源就没有用到,而且还多了一次拷贝构造的过程,效率大大降低

后来真针对push_back的优化是引入了右值引用,即在构造出临时对象之后直接调用转移构造函数std::move()的形态,将临时对象的值和地址全部转移到容器,临时对象直接就变为空了。
所以c++11的emplace_back是在以上基础上进一步优化的,在容器的尾部添加元素,而这个元素是原地构造的,不需要调用拷贝构造函数和转移构造函数了。

测试

测试代码如下:

#include 
#include 
#include 

using namespace std;

class Test {
private:
  int num;

public:
  Test(int x):num(x){std::cout << "constructed\n";}

  /*拷贝构造函数*/
  Test(const Test &obj) {
      num = obj.num;
      std::cout << "copy constructed\n";
  }
	
  /*C++11 支持的 转移构造函数*/
  Test (Test &&obj) {
      num = std::move(obj.num);
      std::cout << "moved constructed\n";
  }

};

int main() {
  vector<Test> arr;
  vector<Test> t_arr;

  std::cout << "push_back : Test(1)\n";
  arr.push_back(1);
  
  std::cout << "emplace_back : Test(1)\n";
  t_arr.emplace_back(1);
  return 0;
}

输出如下:

push_back : Test(1)
constructed
moved constructed
emplace_back : Test(1)
constructed

很明显,这里使用push_back进行了两次构造(临时对象的构造 和 转移构造函数),而empalce_back仅仅构造了临时对象。

所以emplace_back的性能会优于push_back。

源码分析

以下源码是boost库的最新源代码,所以push_back的实现也是经过优化的(将拷贝构造函数的实现变更为转移构造函数)
关于empalce_back的boost源码实现过程如下:

template <class _Tp, class _Allocator>
template <class... _Args>
inline
#if _LIBCPP_STD_VER > 14
typename vector<_Tp, _Allocator>::reference
#else
void
#endif
vector<_Tp, _Allocator>::emplace_back(_Args&&... __args)
{
    if (this->__end_ < this->__end_cap())
    {
        __RAII_IncreaseAnnotator __annotator(*this);
        /*传入可变长参数模版forward进行对象构造,并添加到this代表的容器的末尾*/
        __alloc_traits::construct(this->__alloc(),
                                  _VSTD::__to_raw_pointer(this->__end_),
                                  _VSTD::forward<_Args>(__args)...);
        __annotator.__done();
        ++this->__end_;
    }
    else
        __emplace_back_slow_path(_VSTD::forward<_Args>(__args)...);
#if _LIBCPP_STD_VER > 14
    return this->back();
#endif
}

push_back的源码如下:

template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
vector<_Tp, _Allocator>::push_back(value_type&& __x)
{
    if (this->__end_ < this->__end_cap())
    {
    	/*构造容器指针*/
        __RAII_IncreaseAnnotator __annotator(*this);

		/*使用move 的转移构造函数*/
        __alloc_traits::construct(this->__alloc(),
                                  _VSTD::__to_raw_pointer(this->__end_),
                                  _VSTD::move(__x));
        __annotator.__done();
        ++this->__end_;
    }
    else
        __push_back_slow_path(_VSTD::move(__x));
}

你可能感兴趣的:(#,编程语言C,#,编程语言:C++)