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. Linux Shell 常用命令 - 02篇

    系列文章: Linux Shell 常用命令 - 01篇 8. shell 中各种括号的作用 参考 https://www.jb51.net/article/123081.htm https://ww ...

  2. SpringCloud注册中心切换nacos

    SpringBoot与nacos版本对应关系 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF ...

  3. yb课堂 新版SSM-SpringBoot2.X 后端项目框架搭建 《二》

    在线创建 https://start.spring.io/ 依赖项 Spring Boot核心包 <dependency> <groupId>org.springframewo ...

  4. [oeasy]python0118_语言的发展_希腊字符_拼音文字_亚历山大大帝

    希腊字符回忆上次内容 尼罗河流域 的 埃及圣书体 是象形文字 两河流域 的 苏美尔楔形文字 不是象形文字 做生意的 腓尼基人 利用 埃及圣书体 的 字型 苏美尔楔形文字 的 写法 构造出 腓尼基字符 ...

  5. [oeasy]python0082_VT100_演化_颜色设置_VT选项_基础色_高亮色_索引色_RGB总结

    更多颜色 回忆上次内容 上次 了解了控制序列 背后的故事 一切标准 都是 从无到有 的 就连 负责标准的组织 也是 从无到有 的 VT-05 奠定了 基础颜色 黑底 绿字 隔行 扫描 但 多颜色设置 ...

  6. AT_abc180_d 题解

    洛谷链接&Atcoder 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 现有 \(STR\) 和 \(EXP\) 两个变量,初始化分别为 \(X\) 和 \( ...

  7. 计算机网络中的检验和(checksum)(包括计算文件的检验和附有c++代码)

    介绍: 检验和(checksum),在数据处理和数据通信领域中,用于校验目的地一组数据项的和.它通常是以十六进制为数制表示的形式.如果校验和的数值超过十六进制的FF,也就是255. 就要求其补码作为校 ...

  8. 机器学习:详解什么是端到端的深度学习?(What is end-to-end deep learning?)

    什么是端到端的深度学习? 深度学习中最令人振奋的最新动态之一就是端到端深度学习的兴起,那么端到端学习到底是什么呢?简而言之,以前有一些数据处理系统或者学习系统,它们需要多个阶段的处理.那么端到端深度学 ...

  9. php 开发规范

    ===========================框架========================= · 使用laravel框架,原因:tp的框架路由和orm没有laravel好用 · 使用强 ...

  10. 《最新出炉》系列入门篇-Python+Playwright自动化测试-53- 处理面包屑(详细教程)

    1.简介 面包屑(Breadcrumb),又称面包屑导航(BreadcrumbNavigation)这个概念来自童话故事"汉赛尔和格莱特",当汉赛尔和格莱特穿过森林时,不小心迷路了 ...