当前位置: 首页 > news >正文

cpp中vector的push_back和emplace_back精简小结

push_back 和 emplace_back

网络上讲这两个操作差异的文章很多,这里仅从使用差异分析。

定义

在这里插入图片描述


在这里插入图片描述

假设:

  1. 控制变量:当前vector能够容下push_back和emplace_back的所有元素,没有触发扩容操作。 使用vector.reserve();
  2. push_back和emplace_back操作的对象类型:
    1. 普通变量、普通变量
    2. 普通变量、临时变量
    3. 临时变量、普通变量
    4. 临时变量、临时变量

实验的类Foo

#include <iostream>
#include <vector>class Foo {
public:// default ctorFoo(int value = 0) : value_(value) {std::cout << "Foo(int value = 0)" << std::endl;}// copy ctorFoo(const Foo& foo) : value_(foo.value_) {std::cout << "Foo(const Foo& foo)" << std::endl;}// move ctorFoo(Foo&& foo) : value_(foo.value_) {foo.value_ = 0;std::cout << "Foo(Foo&& foo)" << std::endl;}// copy assignment Foo& operator=(const Foo& foo) {value_ = foo.value_;std::cout << "Foo& operator=(const Foo& foo)" << std::endl;return *this;}// move assignmentFoo& operator=(Foo&& foo) {value_ = foo.value_;foo.value_ = 0;std::cout << "Foo& operator=(Foo&& foo)" << std::endl;return *this;}// dtor~Foo() {std::cout << "~Foo()" << std::endl;}private:int value_;
};

1. 普通变量、普通变量

int main() {std::vector<Foo> vFoos;vFoos.reserve(2);       // 防止vector扩容std::cout << vFoos.capacity() << std::endl;Foo foo(20);            // 普通变量std::cout << "----------------------------------" << std::endl;vFoos.push_back(foo);    std::cout << "----------------------------------" << std::endl;vFoos.emplace_back(foo);std::cout << "----------------------------------" << std::endl;
}

输出:

2
Foo(int value = 0)
----------------------------------
Foo(const Foo& foo)
----------------------------------
Foo(const Foo& foo)
----------------------------------
~Foo()
~Foo()
~Foo()

分析:
两者目标就是把普通变量foo的信息放到vector上,但是foo可能后续还得用,所以不能强行移动,只能使用拷贝构造函数


2. 普通变量、临时变量

int main() {std::vector<Foo> vFoos;vFoos.reserve(2);       // 防止vector扩容std::cout << vFoos.capacity() << std::endl;Foo foo(20);            // 普通变量std::cout << "----------------------------------" << std::endl;vFoos.push_back(foo);    std::cout << "----------------------------------" << std::endl;vFoos.emplace_back(1);std::cout << "----------------------------------" << std::endl;
}

输出:

2
Foo(int value = 0)
----------------------------------
Foo(const Foo& foo)
----------------------------------
Foo(int value = 0)
----------------------------------
~Foo()
~Foo()
~Foo()

分析:
可以看见emplace_back是直接在vector管理的堆上内存原地调用构造函数

3. 临时变量、普通变量

int main() {std::vector<Foo> vFoos;vFoos.reserve(2);       // 防止vector扩容std::cout << vFoos.capacity() << std::endl;Foo foo(20);            // 普通变量std::cout << "----------------------------------" << std::endl;vFoos.push_back({1});    std::cout << "----------------------------------" << std::endl;vFoos.emplace_back(foo);std::cout << "----------------------------------" << std::endl;
}

输出

2
Foo(int value = 0)
----------------------------------
Foo(int value = 0)
Foo(Foo&& foo)
~Foo()
----------------------------------
Foo(const Foo& foo)
----------------------------------
~Foo()
~Foo()
~Foo()

分析:
push_back先根据传入的实参{1}调用构造函数以创建一个栈上的临时对象,然后使用移动/拷贝构造函数将其信息放到vector管理的堆上。这里是使用移动还是拷贝构造比较讲究?换位思考,编译器是能用移动就绝不用拷贝。可能在这个实验的类里两个构造函数的工作类似,但是移动构造对于管理堆上内存的类而言是远比拷贝构造轻量的。深拷贝?

4. 临时变量、临时变量

int main() {std::vector<Foo> vFoos;vFoos.reserve(2);       // 防止vector扩容std::cout << vFoos.capacity() << std::endl;std::cout << "----------------------------------" << std::endl;vFoos.push_back({1});    std::cout << "----------------------------------" << std::endl;vFoos.emplace_back(1);std::cout << "----------------------------------" << std::endl;
}

输出:

2
----------------------------------
Foo(int value = 0)
Foo(Foo&& foo)
~Foo()
----------------------------------
Foo(int value = 0)
----------------------------------
~Foo()
~Foo()

分析:
个人认为是emplace_back真正彰显性能优势的场景。它只需要做一件事,通过用户提供的实参1在vector管理的堆上调用类的构造函数即可。而push_back还是避免不了地要构造临时对象,不过它也在尽力优化地调用移动构造而非拷贝构造(如果可以使用移动构造的话)。

总结

  1. 如果操作的是临时对象,那么这是emplace_back的用武之地(假设不会触发动态扩容)。
  2. push_back总是会构造临时对象,然后析构它。不过它也在尽力的优化:去使用移动构造函数而非拷贝构造函数。

待优化的地方

  1. 构造一个较重的类以量化两个操作的优势?

http://www.mrgr.cn/news/74834.html

相关文章:

  • 网络安全之内网安全
  • ThreadLocal 父子线程、线程池、数据传递
  • 亚信安全与飞书达成深度合作
  • 蓝桥杯介绍
  • window的wsl(Ubuntu)安装kafka步骤
  • [ubuntu]编译共享内存读取出现read.c:(.text+0x1a): undefined reference to `shm_open‘问题解决方案
  • LeetCode【0047】全排列II
  • HarmonyOS基础:选项卡组件(Tabs)
  • PostgreSQL 查看重复索引
  • 第一课-Rust入门
  • 数据结构查找-哈希表(创建+查找+删除)+(C语言代码)
  • Tofu识别跟踪变焦镜头控制接口与协议
  • 云服务器安装mysql8.0(阿里云或者腾讯云都可以)
  • 比高考还严?该地软考报考减少了5420人,工作人员却增加100多人!
  • 如何使用Jupyter
  • 【机器学习chp2】贝叶斯最优分类器、概率密度函数的参数估计、朴素贝叶斯分类器、高斯判别分析。万字超详细分析总结与思考
  • 真的别跟风了!PMP认证原来只对这些人有用...
  • leveldb存储token的简单实现
  • 理解 C++ 中的 `const` 关键字
  • 域名绑定服务器小白教程
  • [刷题]入门1.矩阵转置
  • MATLAB和Python及R瑞利散射
  • 37邮件服务器
  • Sorvall Legend Micro 17 微量离心机产品特性
  • 开放式耳机怎么戴?不入耳的蓝牙耳机推荐
  • 背景移除,主体物抠图模型 RMBG-2.0:最佳一键去背景模型