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. PLSQL 无法查询带中文的WHERE条件

    今天遇到一个坑爹的问题,plsql无法查询带where条件的语句,是因为plsql中Oracle的客户端字符集和服务器上的不一样造成的,需要新增系统环境变量,特意记录下解决办法. 第一步:查询服务器上 ...

  2. Vscode连接虚拟机报错

    Vscode 连接虚拟机报错问题解决 问题解释 Permission denied, please try again.出现这个问题通常表示身份验证失败. 可能的原因有 SSH用户密码错误 SSH端口 ...

  3. windows内置账户

    参考文献: http://www.cnblogs.com/xianspace/archive/2009/04/05/1429835.html 转载自: https://www.cnblogs.com/ ...

  4. card 卡片 html

    {% extends 'base.html' %} {% block content %} <div class="container"> <h1>客户信息 ...

  5. [oeasy]python0052_ raw格式字符串_单引号_双引号_反引号_ 退格键

    转义字符 回忆上次内容 最近玩的是\n.\r 之外的转义序列 \a是 ␇ (bell) \t是 水平制表符 \v是 换行不回车 通过 16 进制数值转义 \xhh 把(hh)16 进制对应的 asci ...

  6. 封装的grid控件

    class CGridCtrl : public CWnd { DECLARE_DYNAMIC(CGridCtrl) public: void Create(CWnd* pParent, DWORD ...

  7. 加速 Mac 时间机器备份

    加速 Mac 时间机器备份速度教程,Time Machine 备份太慢的解决方法 @Pertim 2020-09-11 相信用过一段时间电脑的人,都知道经常备份 macOS 系统的重要性了.特别是最近 ...

  8. 《最新出炉》系列初窥篇-Python+Playwright自动化测试-60 - 判断元素是否显示 - 下篇

    1.简介 有些页面元素的生命周期如同流星一闪,昙花一现.我们也不知道这个元素在没在页面中出现过,为了捕获这一美好瞬间,让其成为永恒.我们就来判断元素是否显示出现过. 在操作元素之前,可以先判断元素的状 ...

  9. 【Linux】快速文件交互 lrzsz

    首先需要安装依赖: yum install -y lrzsz 没有此依赖,Linux提示找不到命令: [root@localhost ~]# rz -bash: rz: 未找到命令 [root@loc ...

  10. AI的发展需要有应用和落地场景 —— 李开复:传统公司看不懂技术,大模型落地B端阻碍多

    引自:https://baijiahao.baidu.com/s?id=1801826206644007472&wfr=spider&for=pc "我们投了七八家机器人企业 ...