std::vector的emplace_back 与 push_back 比较

现在来对比一下emplace_backpush_back的区别,分别有移动构造函数移动赋值运算符以及没有这两个函数的对象进行比较

1.没有 移动构造函数移动赋值运算符
class Test
{
public:
    Test()
    {
        qDebug() << "construct test" << this;
    }
    Test (const Test& other)
    {
        qDebug() << "copy construct test" << this;
    }
    Test& operator=(const Test& other)
    {
        qDebug() << "assgin operator" << this;
    }
    ~Test()
    {
        qDebug() << "distruct test" << this;
    }
};
   std::vector<Test> v;
   
   //测试1, push_back 放入 左值
   Test t;
   v.push_back(t);			//调用拷贝构造
   
   // 测试1, 结果:
   construct 0x3c296f940
   copy construct 0x15db0916660
   distruct test 0x3c296f940
   distruct test 0x15db0916660

测试1结果:没有移动构造和移动赋值运算符的对象,push_back放入一个左值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数


	//测试2, push_back 放入右值,
    Test t;
    v.push_back(std::move(t));		//1.通过 move 将左值转换成 右值		//调用拷贝构造
  //  v.push_back(Test());			//2.构造临时对象为右值
    //1 和 2 的两种右值,其实是一样的;
    //第一种方式的 t对象,会在出了作用域之后析构
    //第二种生成一个临时对象,这一行结束之后,临时对象立即析构
    //在以下测试统一使用第一种方式生成右值
    
	//测试2,结果:
	construct 0xc8c64ffce0
	copy construct 0x1dd8d28af00
	distruct test 0xc8c64ffce0
	distruct test 0x1dd8d28af00

测试2结果:没有移动构造和移动赋值运算符的对象,push_back放入一个右值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数


    //测试3, emplace_back 放入 左值
    Test t;
    v.emplace_back(t);		//调用拷贝构造

	//测试3,结果:
	construct 0xe60db8fd20
	copy construct 0x2b7a4a35ab0
	distruct test 0xe60db8fd20
	distruct test 0x2b7a4a35ab0

测试3结果:没有移动构造和移动赋值运算符的对象, emplace_back放入一个左值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数


	//测试4, emplace_back放入右值
    Test t;
    v.emplace_back(std::move(t));	//调用拷贝构造
	
	//测试4,结果:
	construct 0xf1004ff850
	copy construct 0x2a4b16a79a0
	distruct test 0xf1004ff850
	distruct test 0x2a4b16a79a0

测试4结果:没有移动构造和移动赋值运算符的对象, emplace_back放入一个右值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数

2.有 移动构造函数移动赋值运算符
class Test
{
public:
    Test()
    {
        qDebug() << "construct" << this;
    }

    Test (const Test& other)
    {
        qDebug() << "copy construct" << this;
    }

    Test& operator=(const Test& other)
    {
        qDebug() << "assgin operator" << this;
    }

    Test (Test&& other)
    {
        qDebug() << "move construct" << this;
    }
    Test& operator=(Test&& other)
    {
        qDebug() << "move operator" << this;
    }
    ~Test()
    {
        qDebug() << "distruct test" << this;
    }
};

依次将上面的测试再进行一次:

	//测试5, push_back 放入左值
    Test t;
    v.push_back(t);		//调用拷贝构造

	//测试5,结果
	construct 0x73832ff720
	copy construct 0x1b70034d390
	distruct test 0x73832ff720
	distruct test 0x1b70034d390

测试5结果:移动构造和移动赋值运算符的对象, push_back放入一个左值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数


	//测试6, push_back放入右值
    Test t;
    v.push_back(std::move(t));		//调用移动构造

	//测试6,结果:
	construct 0x7dd78ff770
	move construct 0x243f1bc3f40
	distruct test 0x7dd78ff770
	distruct test 0x243f1bc3f40

测试6结果:移动构造和移动赋值运算符的对象,push_back放入一个右值,是调用一次构造函数生成对象t,一次移动构造函数,两次析构函数


	//测试7, emplace_back放入左值
    Test t;
    v.emplace_back(t);		//调用拷贝构造

	//测试7,结果:
	construct 0xbc301bf7c0
	copy construct 0x27f1caee380
	distruct test 0xbc301bf7c0
	distruct test 0x27f1caee380

测试7结果:移动构造和移动赋值运算符的对象,emplace_back放入一个左值,是调用一次构造函数生成对象t,一次拷贝构造函数,两次析构函数


	//测试8, emplace_back放入右值
    Test t;
    v.emplace_back(std::move(t));		//调用移动构造

	//测试8,结果:
	construct 0x95bbb8f930
	move construct 0x246db820f20
	distruct test 0x95bbb8f930
	distruct test 0x246db820f20

测试8结果:移动构造和移动赋值运算符的对象,emplace_back放入一个右值,是调用一次构造函数生成对象t,一次移动构造函数,两次析构函数

结论:

1.没有移动构造和移动赋值运算符

左值 右值
push_back 拷贝构造函数 拷贝构造函数
emplace_back 拷贝构造函数 拷贝构造函数

2.有移动构造和移动赋值运算符

左值 右值
push_back 拷贝构造函数 移动构造函数
emplace_back 拷贝构造函数 移动构造函数
  • 看起来,无论有移动构造和移动赋值运算符的对象,还是没有这两个函数,emplace_backpush_back表现出来的特征是相同的, 并未出现任何性能上的改善与提升
  • 实际,emplace_back真正能带来效率上的提升的用法是:直接在emplace_back里面传递参数原地调用构造函数,构造出一个对象,这样做至少可以节省一次 拷贝构造(针对左值) 或者 移动构造(针对右值) 带来的开销;而不是预先生成一个左值或右值类型的对象,然后再将其放入到emplace_back的参数中,这样的做法与push_back并无差异;

正确使用:

emplace_back(Args...);

相关推荐

  1. OpenStack优缺点并CloudStack比较

    2024-06-15 10:20:04       53 阅读
  2. ReactVue:前端框架比较

    2024-06-15 10:20:04       54 阅读
  3. Spring Data JPA MyBatisPlus比较

    2024-06-15 10:20:04       27 阅读
  4. PostgreSQLMySQL数据库层面比较

    2024-06-15 10:20:04       39 阅读
  5. Emacs 优点及 DE 比较

    2024-06-15 10:20:04       27 阅读
  6. ReadWriteLock 和 StampedLock 比较解析

    2024-06-15 10:20:04       64 阅读
  7. 3.2 Puppet 和 Chef 比较应用

    2024-06-15 10:20:04       41 阅读
  8. 软件定制开发标准化产品比较及选择

    2024-06-15 10:20:04       59 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-15 10:20:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-15 10:20:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-15 10:20:04       82 阅读
  4. Python语言-面向对象

    2024-06-15 10:20:04       91 阅读

热门阅读

  1. 数据结构 ->反转链表

    2024-06-15 10:20:04       33 阅读
  2. 程序员该有怎么样的职业素养

    2024-06-15 10:20:04       30 阅读
  3. 高等数学与初等数学的分水岭是什么?

    2024-06-15 10:20:04       25 阅读
  4. EventBus之Reactor实战

    2024-06-15 10:20:04       22 阅读
  5. 蓝色格调qss

    2024-06-15 10:20:04       18 阅读
  6. 185. 部门工资前三高的所有员工

    2024-06-15 10:20:04       27 阅读
  7. 动画的js动画于css3区别?

    2024-06-15 10:20:04       28 阅读
  8. Mybatis

    Mybatis

    2024-06-15 10:20:04      19 阅读
  9. Qt事件的详细介绍和原理

    2024-06-15 10:20:04       25 阅读
  10. Linux

    Linux

    2024-06-15 10:20:04      19 阅读
  11. Linux用户管理

    2024-06-15 10:20:04       28 阅读
  12. android用Retrofit进行网络请求和解析

    2024-06-15 10:20:04       17 阅读
  13. 爬山算法介绍

    2024-06-15 10:20:04       20 阅读