在
C++11
之后,vector
容器中添加了新的方法:emplace_back()
,和push_back()
一样的是都是在容器末尾添加一个新的元素进去,不同的是emplace_back()
在效率上相比较于push_back()
有了一定的提升。
push_back()
方法 首先分析较为简单直观的
push_back()
方法。对于push_back()
而言,最开始只有void push_back( const T& value );
这个函数声明,后来从C++11
,新加了void push_back( T&& value )
函数,以下为C++
中的源码实现:
/**
* 以下程序来自STL源码 bits/stl_vector.h
*
* @brief Add data to the end of the %vector.
* @param __x Data to be added.
*
* This is a typical stack operation. The function creates an
* element at the end of the %vector and assigns the given data
* to it. Due to the nature of a %vector this operation can be
* done in constant time if the %vector has preallocated space
* available.
*/
void push_back(const value_type &__x) {
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
// 首先判断容器满没满,如果没满那么就构造新的元素,然后插入新的元素
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish; // 更新当前容器内元素数量
} else
// 如果满了,那么就重新申请空间,然后拷贝数据,接着插入新数据 __x
_M_realloc_insert(end(), __x);
}
// 如果 C++ 版本为 C++11 及以上(也就是从 C++11 开始新加了这个方法),使用 emplace_back() 代替
#if __cplusplus >= 201103L
void push_back(value_type &&__x) {
emplace_back(std::move(__x));
}
#endif
emplace_back()
方法
emplace_back()
是从C++11
起新增到vector
中的方法,最初的函数声明为:
template< class... Args >
void emplace_back( Args&&... args );
之后在
C++14
之后,将无返回值void
改为了返回对插入元素的引用:
template< class... Args >
reference emplace_back( Args&&... args );
在
STL
源码中,可以看到emplace_back()
的实现是这样的:
/**
* 以下程序来自STL源码 bits/vector.tcc
*/
template
template
#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) {
// 同样判断容器是否满了,没满的话,执行构造函数,对元素进行构造,并执行类型转换
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish; // 更新当前容器大小
} else
// 满了的话重新申请内存空间,将新的元素继续构造进来,并且进行类型转换
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
#if __cplusplus > 201402L
return back(); // 在 C++14版本之后,添加返回值,返回最后一个元素的引用
#endif
}
#endif
将
emplace_back()
和push_back()
中区别最大的程序拎出来看:
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...); // emplace_back()
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x); // push_back()
对于
std::forward()
函数而言,本质上是一个类型转换函数,它的声明函数如下所示:
/**
* 以下程序来自STL源码 bits/move.h
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template
constexpr _Tp &&forward(typename std::remove_reference<_Tp>::type &__t) noexcept {
return static_cast<_Tp &&>(__t);
}
在强制类型转换中,将参数
__t
传递给对应类_Tp
的构造函数,然后调用了该类的构造函数从而完成对象创建过程。因此,在
emplace_back()
函数中,是支持直接将构造函数所需的参数传递过去,然后构建一个新的对象出来,然后填充到容器尾部的。
emplace_back()
函数在原理上比push_back()
有了一定的改进,包括在内存优化方面和运行效率方面。内存优化主要体现在使用了就地构造(直接在容器内构造对象,不用拷贝一个复制品再使用)+强制类型转换的方法来实现,在运行效率方面,由于省去了拷贝构造过程,因此也有一定的提升。以下程序源码:
/**
* Created by Xiaozhong on 2020/9/3.
* Copyright (c) 2020/9/3 Xiaozhong. All rights reserved.
*/
#include
#include
using namespace std;
class Person {
int _age;
public:
Person(int age) : _age(age) {
cout << "Construct a person." << _age << endl;
}
Person(const Person &p) : _age(p._age) {
cout << "Copy-Construct" << _age << endl;
}
Person(const Person &&p) noexcept: _age(p._age) {
cout << "Move-Construct" << _age << endl;
}
};
#define TEST_EMPLACE_BACK
//#define TEST_PUSH_BACK
int main() {
vector person;
auto p = Person(1); // >: Construct a person.1
#ifdef TEST_EMPLACE_BACK
person.emplace_back(move(p)); // >: Move-Construct1
person.emplace_back(2);
/**
* >: Construct a person.2 // 构建一个新的元素
* >: Move-Construct1 // 拷贝之前的元素过来,这个时候用的是 Person(const Person &&p)
*/
#endif
#ifdef TEST_PUSH_BACK
person.push_back(p);
/**
* >: Copy-Construct1 因为容器扩容,需要把前面的元素重新添加进来,因此需要拷贝
*/
#endif
}
对于 pair 类型的元素的添加,
emplace_back()
函数和push_back() 函数有所区别.
详情如下:
std::vector> a;
for (int i = 0; i < n; i++) {
int x, y;
std::cin >> x >> y;
a.emplace_back(x, y);
}
//emplace_back()
std::vector> a;
for (int i = 0; i < n; i++) {
int x, y;
std::cin >> x >> y;
a.push_back({x, y});
}
//push_back()