c++ push_back()和emplace_back()区别

References

  1. C++中push_back和emplace_back的区别
  2. push_back v.s. emplace_back

一、源码分析

(1)push_back()定义

// stl_vector.h

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
...
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(1);
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish;
_GLIBCXX_ASAN_ANNOTATE_GREW(1);
}
else
_M_realloc_insert(end(), __x);
} #if __cplusplus >= 201103L
void
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
}
  1. 需传入对应类型的对象(隐式构造的情况除外)
  2. C++11以及之后版本,传入右值需调用移动构造函数(若未定义,则调用拷贝构造函数)。

类的构造函数未添加explicit且只接受一个参数的情况,也可以传入单个参数进行构造。但涉及另一知识点:如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数(converting constructor)。

  1. push_back传入参数不涉及类型推断,以下x为rvalue reference。

若没有一个特定的std::vector实例化, push_back是不存在的。实例化的类型完全决定了push_back的声明。不涉及类型推断。


template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type => no type deduction;
... // && is rvalue reference
};

(2)emplace_back()定义

#if __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
template<typename... _Args>
#if __cplusplus > 201402L
typename vector<_Tp, _Alloc>::reference
#else
void
#endif
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(1);
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
_GLIBCXX_ASAN_ANNOTATE_GREW(1);
}
else
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
#if __cplusplus > 201402L
return back();
#endif
}
#endif
  1. emplace_back既可以传入相应类型对象,还可以传入元素的类构造函数的参数列表进行原地构造。参数列表通过std::forward进行完美转发。
  2. emplace_back的传入参数需要类型推断,以下Args为Universal Reference.emplace_back类型参数Args独立于vector模板类型参数T,所以Args类型在每次被调用的时候都必须被推断。
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types => type deduction;
... // && is universal references
};

二、对比push_back和emplace_back

(1)定义测试类


class Foo{
private:
int a;
public:
Foo(int a): a(a){
cout<<"Foo Constructor "<<a<<endl;
}
Foo(){
cout<<"Default Foo Constructor"<<endl;
}
~Foo() { cout << "Foo Destructor..." <<a<<endl; }
Foo(const Foo& foo){
cout<<"copy constructor"<<endl;
}
Foo(Foo&& foo){
cout<<"move constructor"<<endl;
}
};

(2)测试用例

  1. push_back拷贝构造,emplace_back原地构造.
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
Foo foo1(1);
vec1.push_back(foo1);
cout<<endl<<"push_back end"<<endl<<endl; cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(2);
cout<<endl<<"emplace_back end"<<endl<<endl;
}

输出:

push_back begin

Foo Constructor 1
copy constructor //调用拷贝构造 push_back end emplace_back begin Foo Constructor 2 //vector上原地构造,无需拷贝,也无需移动 emplace_back end Foo Destructor...2
Foo Destructor...1
Foo Destructor...0
  1. 拷贝构造函数和移动构造函数均已定义,用push_back/emplace_back向容器中加入左值元素,均将调用拷贝构造函数拷贝对象到容器中。
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
Foo foo1(1);
vec1.push_back(foo1);
cout<<endl<<"push_back end"<<endl<<endl; cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
Foo foo2(2);
vec2.emplace_back(foo2);
cout<<endl<<"emplace_back end"<<endl<<endl;
}

输出:

push_back begin

Foo Constructor 1
copy constructor push_back end emplace_back begin Foo Constructor 2
copy constructor emplace_back end Foo Destructor...2 //左值离开作用域销毁
Foo Destructor...0
Foo Destructor...1 //左值离开作用域销毁
Foo Destructor...0
  1. 拷贝构造函数和移动构造函数均定义,push_back/emplace_back向容器中加入右值元素, 那么均会调用移动构造函数在vector上进行构造。
  • 加入显式构造的右值元素
vec1.push_back(Foo(1));
vec1.emplace_back(Foo(1));
  • 通过std::move()转化而成的右值元素
Foo foo1;
vec1.push_back(std::move(foo1))
Foo foo2;
vec1.emplace_back(std::move(foo2));
  • 测试
#include <iostream>
#include <vector>
using namespace std;
int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
vec1.push_back(Foo(1));
cout<<endl<<"push_back end"<<endl<<endl; cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(Foo(2));
cout<<endl<<"emplace_back end"<<endl<<endl;
}

输出:

push_back begin

Foo Constructor 1
move constructor
Foo Destructor...1 //右值立马销毁 push_back end emplace_back begin Foo Constructor 2
move constructor
Foo Destructor...2 //右值立马销毁 emplace_back end Foo Destructor...0
Foo Destructor...0
  1. 若仅定义拷贝构造函数,未定义移动构造函数,push_back/emplace_back向容器中加入右值元素, 那么均会调用拷贝构造函数构造对象在vector上。

#include <iostream>
#include <vector>
using namespace std;
class Foo{
private:
int a;
public:
Foo(int a): a(a){
cout<<"Foo Constructor "<<a<<endl;
}
Foo(){
cout<<"Default Foo Constructor"<<endl;
}
~Foo() { cout << "Foo Destructor..." <<a<<endl; }
Foo(const Foo& foo){
cout<<"copy constructor"<<endl;
}
// Foo(const Foo&& foo){
// cout<<"move constructor"<<endl;
// }
// Foo(Foo&& foo)=delete;
}; int main() {
cout<<endl<<"push_back begin"<<endl<<endl;
vector<Foo> vec1;
vec1.push_back(Foo(1));
cout<<endl<<"push_back end"<<endl<<endl; cout<<endl<<"emplace_back begin"<<endl<<endl;
vector<Foo> vec2;
vec2.emplace_back(Foo(2));
cout<<endl<<"emplace_back end"<<endl<<endl;
}

输出:

push_back begin

Foo Constructor 1
copy constructor
Foo Destructor...1 //右值立马销毁 push_back end emplace_back begin Foo Constructor 2
copy constructor
Foo Destructor...2 //右值立马销毁 emplace_back end Foo Destructor...0
Foo Destructor...0

c++ push_back()和emplace_back()区别的更多相关文章

  1. 3个例子详解C++ 11 中push_back 和 emplace_back差异

    本文首发于个人博客https://kezunlin.me/post/b83bc460/,欢迎阅读最新内容! cpp11 push_back and emplace_back Guide case1 # ...

  2. push_back和emplace_back的区别

    emplace_back能就地通过参数构造对象,不需要拷贝或者移动内存,相比push_back能更好地避免内存的拷贝与移动,使容器插入元素的性能得到进一步提升.在大多数情况下应该优先使用emplace ...

  3. 论Java的ArrayList.add(e)和C++的vector.push_back(e)的区别

    Java的ArrayList和C++的vector很类似,都是很基本的线性数据结构.但是他们的表现却不同. 在工作中碰到一个问题就是,搞不清楚到底传进去的是一个新对象,还是当前对象的引用! 经过实战分 ...

  4. 编程杂谈——使用emplace_back取代push_back

    近日在YouTube视频上看到关于vector中emplace_back与push_back区别的介绍,深感自己在现代C++中还是有不少遗漏的知识点,遂写了段代码,尝试比较两者的差别. 示例代码 #i ...

  5. emplace_back与push_back

    资料参考: https://blog.csdn.net/p942005405/article/details/84764104 实际精华在评论中,转载如下: STL的实现版本很多,VS.GCC版本不同 ...

  6. 你想知道的 std::vector::push_back 和 std::vector::emplace_back

    引言 C++ 11 后,标准库容器 std::vector 包含了成员函数 emplace 和 emplace_back.emplace 在容器指定位置插入元素,emplace_back 在容器末尾添 ...

  7. STL用法整理

    百度百科 STL是Standard Template Library的简称,中文名标准模板库,惠普实验室开发的一系列软件的统称.从根本上说,STL是一些“容器”的集合,这些“容器”有list,vect ...

  8. STL容器vector应用注意事项

    [1]提前分配足够空间以免不必要的重新分配和复制代价 关于vector容器重新分配和复制及析构释放的代价,请参见随笔<STL容器之vector>. 应用示例对比代码如下: #include ...

  9. vector使用小结

    1.创建vector容器: std::vector<int> data; std::vector<int> data(20);大小20,自动赋值为0 std::vector&l ...

  10. c++11新特性总结(转)

    1.类型与变量相关 1.1.nullptr: 取代了NULL,专用于空指针 1.2.constexpr: 近似const, 可以修饰变量,也可以修饰函数, 修饰变量如: const int globa ...

随机推荐

  1. yb课堂之登陆校验Json Web Token实战之封装通用方法 《九》

    引入相关依赖并开发JWT工具类,开发生产token和校验token的方法 加入相关依赖 <dependency> <groupId>io.jsonwebtoken</gr ...

  2. 推荐一款功能强大、界面优美的开源SSH跨平台终端软件WindTerm

    WindTerm是一款开源免费且功能强大的终端软件,相比 MobaXterm自带中文支持.无论是在Windows.macOS还是Linux操作系统上,WindTerm都能提供出色的性能和稳定性.Win ...

  3. 宇宙最强开发工具VScode快速搭建前后端分离环境【VUE+Springboot】

    VS Code 的全称是 Visual Studio Code,是一款开源的.免费的.跨平台的.高性能的.轻量级的代码编辑器.它在性能.语言支持.开源社区方面,都做得很不错,是这两年非常热门的一款开发 ...

  4. Stopwatch 类来测量时间间隔

    使用 Stopwatch 类来测量时间间隔. 以下是一个示例代码,展示如何记录 Excel 导入的用时: ' 创建 Stopwatch 实例 Dim stopwatch As New Stopwatc ...

  5. 【游记】CSP 2023

    day 0 和 printfmingren 整理了一下不会的知识点,发现有点多,遂开摆 音游真的太好玩了 对着<算法竞赛进阶指南>复习了下对拍器的写法,把部分算法的模板又打了一遍 感觉前途 ...

  6. 再读vue

    app.vue是项目的主组件,页面的入口文件 main.js是项目的入口文件 vue.config.js是vue-cli的配置文件//用这个配置代理,端口号 例如 const { defineConf ...

  7. vue3 + ts 中出现 类型“typeof import(".........../node_modules/vue/dist/vue")”的参数不能赋给类型“Component<any, any, any, ComputedOptions, MethodOptions>”的参数。

    错误示例截图 解决方法 修改shims-vue.d.ts中的内容 declare module "*.vue" { import { defineComponent } from ...

  8. 人形机器人|星动纪元开源端到端强化学习训练框架“Humanoid-Gym”,实现「sim-to-real」 功能

    相关: https://www.leiphone.com/category/robot/cJo6GYgVkx8iQ9T7.html 开源的 Humanoid-Gym 框架,主要实现的技术有: 通过精心 ...

  9. 支持NVIDIA GPU —— 如何运行docker环境下的Jax环境

    项目地址: https://github.com/NVIDIA/JAX-Toolbox 具体的安装命令: 地址: https://github.com/NVIDIA/JAX-Toolbox/pkgs/ ...

  10. Ubuntu22.04系统安装DeepMind Lab

    相关资料: DeepMind Lab的一些python例子-----(Ubuntu22.04系统安装DeepMind Lab)后续 ================================== ...