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. 详解Web应用安全系列(10)文件上传漏洞

    文件上传漏洞(File Upload Vulnerabilities)是Web攻击中常见的一种安全漏洞,它允许攻击者上传并执行恶意文件,从而可能对Web服务器造成严重的安全威胁. 一.定义与原理 文件 ...

  2. 敏捷开发(Scrum)

    ​ 一.敏捷的背景与动机 1.1 软件危机及软件工程的出现 速度是企业竞争致胜的关键因素,软件项目的最大挑战在于,一方面要应付变动中的需求,一方面要在紧缩的时程内完成项目,传统的软件工程难以满足这些要 ...

  3. 类、事件与对象---Dad&Mom简单练习

    目的: 模拟一个家庭日常发生的场景:妈妈做好饭,说:"开饭了!",这是爸爸听到了妈妈的喊话就立马动身开始饭吃.而儿子此时正在打游戏,于是他就说:"等我打完这把游戏再吃!& ...

  4. 解决方案 | vb记住上次打开的文件夹

      Private Sub Button_ImportBasicData_Click(sender As Object, e As EventArgs) Handles Button_ImportBa ...

  5. 网易数帆实时数据湖 Arctic 的探索和实践

    作者 | 蔡芳芳 采访嘉宾 | 马进 网易数帆平台开发专家 数据中台也要从离线为主走向实时化,湖仓一体是第一步. 数据从离线到实时是当前一个很大的趋势,但要建设实时数据.应用实时数据还面临两个难题.首 ...

  6. 【JavaScript高级01】JavaScript基础深入

    1,数据类型 JavaScript将数据分为六大类型,分别为数值类型(number).字符串类型(string).布尔类型(boolean).undefined(定义未赋值).null(赋值为空值). ...

  7. MySQL之DCL

    DCL * 一个项目创建一个用户!一个项目对应的数据库只有一个! * 这个用户只能对这个数据库有权限,其他数据库你就操作不了了! 1. 创建用户   * CREATE USER 用户名@IP地址 ID ...

  8. 机器学习:详解多任务学习(Multi-task learning)

    详解多任务学习 在迁移学习中,步骤是串行的,从任务\(A\)里学习只是然后迁移到任务\(B\).在多任务学习中,是同时开始学习的,试图让单个神经网络同时做几件事情,然后希望这里每个任务都能帮到其他所有 ...

  9. 滑块解锁-scratch编程作品

    程序说明: <滑块解锁>是一款基于Scratch平台制作的益智类小游戏.游戏中存在多个黄色滑块阻挡红色滑块通往出口的路径.玩家需要通过逻辑思考和精确操作,滑动黄色滑块以开辟道路,使红色滑块 ...

  10. ceph 001 存储类型 传统存储与分布式存储 分布式文件系统 集群与分布式

    ceph 存储类型 块存储:裸磁盘 未被格式化的磁盘 DAS(直连存储,usb,硬盘插到电脑):scsi接口 接口数量有限 传输距离有限 SAN(存储区域网络):ip-san 网络(iscsi) 以太 ...