欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++ STL 容器vector添加元素函数emplace_back()和push_back()的使用差异

程序员文章站 2022-03-21 14:29:00
...

在对这两个函数进行比较之前,先提下vector这个容器的一个特点:
vector底层数据结构是一个动态数组,就是在实例化一个vector对象时,如果不手动对其容量进行设置的话,vector的默认容量为0,即capacity()返回值为0。随着向容器中添加元素,容器对象按照1 2 4 8 16 32 二倍扩容。GCC是按照二倍扩容,VS13是1.5倍扩容。
扩容后是一段新的内存空间,此时就需要将旧的内存空间里的所有元素拷贝到新的内存空间中去(如果元素为一个类的对象的话,此时会触发拷贝构造函数),然后再在新的内存空间中的原数据元素后面继续插入新的元素,且旧的内存空间会被释放,如果元素为一个类的对象的话,则会触发析构函数。
因此,在测试emplace_back()和push_back()函数时,需要对容器进行分配初始容量,避免多次多次插入触发扩容,影响测试效果。可以使用vector的成员函数reserve()初始化容器容量。

push_back()和emplace_back()函数的主要区别在于两个函数的底层实现机制不同。push_back()函数在向容器末尾添加新元素时,会先创建该元素,然后再将该元素移动或者拷贝到容器中;emplace_back()函数的底层实现是直接在容器尾部创建该新元素,不存在拷贝或者移动元素的过程。
为了更好的阐述两者的区别,来编写一个实现了构造函数、拷贝构造、移动构造的类,使用该类的对象作为元素插入容器,来查看具体区别,示例代码如下:

#include <iostream>
#include <vector>
// #include <unistd.h>

using namespace std;
class CPPTest{
  public:
    CPPTest(int num) : m_pNum(num) { 
      cout << "执行构造函数 ,m_pNum = " << m_pNum << " ! " << endl; 
    };
    CPPTest(const CPPTest& obj) : m_pNum(obj.m_pNum) {
      cout << "执行拷贝构造函数 ,m_pNum = "  << m_pNum << " ! " << endl;
    };
    CPPTest(const CPPTest&& obj) : m_pNum(obj.m_pNum) {
      cout << "执行移动构造函数 ,m_pNum = " << m_pNum << " ! " << endl;
    };

    ~CPPTest() {
      cout << "执行析构函数 ,m_pNum = " << m_pNum << " ! "<< endl;
    }

  private:
    int m_pNum;
};

int main(int argc, char *argv[]) {
  
  vector<CPPTest> pVec;
  //初始化容器的容量,如果不对容器进行初始化,则容器的容量初始值为0,之后随着增加元素,容量为按照2倍的形式增长。即0、1、2、4...
  //在此过程中,容器再重新分配空间时,会出发元素的拷贝函数,影响比对效果。
  //在此,利用reserve函数初始化容器容量为5.
  pVec.reserve(5); 

  cout << "emplace_back  function execute!" << endl;
  pVec.emplace_back(1);
  cout << endl;
  cout << "push_back function execute!" << endl;
  pVec.push_back(2); 
  cout << endl;
  // pause();
  return 0;
}

编译后运行结果如下:

emplace_back  function execute!
执行构造函数 ,m_pNum = 1 ! 

push_back function execute!
执行构造函数 ,m_pNum = 2 ! 
执行移动构造函数 ,m_pNum = 2 ! 
执行析构函数 ,m_pNum = 2 ! 

执行析构函数 ,m_pNum = 1 ! 
执行析构函数 ,m_pNum = 2 !

从运行结果输出来看,push_back() 底层实现时,优先选择调用移动构造函数,如果类中没有实现移动构造函数,则调用拷贝构造函数。

从以上分析可以得出结论,emplace_back函数和push_back函数在效率方面相比,emplace_back函数较优。