STL版本如下:
Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
分析__construct_one_at_end()
我们首先分析该函数:
void __construct_one_at_end(_Args&& ...__args) {
_ConstructTransaction __tx(*this, 1);
__alloc_traits::construct(this->__alloc(), _VSTD::__to_address(__tx.__pos_),
_VSTD::forward<_Args>(__args)...);
++__tx.__pos_;
}
用于在 vector 容器的末尾构造一个新的元素。该函数利用完美转发(perfect forwarding),将参数直接传递给元素的构造函数,从而在容器的存储空间中直接构造对象。
具体细节感兴趣可以自行查看
emplace_back()和push_back()
对于emplace_back我们只分析其核心代码:
template <class ..._Args>
vector<_Tp, _Allocator>::emplace_back(_Args&&... __args)
{
if (this->__end_ < this->__end_cap())
{
__construct_one_at_end(_VSTD::forward<_Args>(__args)...);
}
else
__emplace_back_slow_path(_VSTD::forward<_Args>(__args)...);
#if _LIBCPP_STD_VER > 14
return this->back();
#endif
}
关于这段代码,我在【对象优化(三)|move移动语义和forward类型完美转发】的【使用forword类型完美转发】部分已经实现的push_back即通过该形式进行实现。
template <class _Tp, class _Allocator /* = allocator<_Tp> */>
class _LIBCPP_TEMPLATE_VIS vector {
typedef _Tp value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
vector<_Tp, _Allocator>::push_back(const_reference __x)
{
if (this->__end_ != this->__end_cap())
{
__construct_one_at_end(__x);
}
else
__push_back_slow_path(__x);
}
vector<_Tp, _Allocator>::push_back(value_type&& __x)
{
if (this->__end_ < this->__end_cap())
{
__construct_one_at_end(_VSTD::move(__x));
}
else
__push_back_slow_path(_VSTD::move(__x));
}
这两个函数其实仍然能实现emplace的主要功能,唯一的区别就是传入参数,那么现在我们一起来分析一下传参的不同
分析三个函数的形参列表
1. 对于 push_back(const_reference __x)
:
这个版本的 push_back 接收一个类型为 const _Tp& 的常量引用参数。
2. 对于push_back(value_type&& __x)
:
这个版本的 push_back 接收一个右值引用参数 _Tp&&。
3. 对于emplace_back(_Args&&... __args)
:
可以接受任意类型的参数,通过模版类型推导和类型的完美转发实现插入
如果你看过这篇文章:【对象优化(三)|move移动语义和forward类型完美转发】
他们在效率上是绝对没区别的,因为底层的实现不就是定位new和类型完美转发吗?只不过是实现的方法不一样,但是结果绝对都是一样的。
那么唯一的区别就在传参上了,emplace_back是可变参,push_back只能传入一个参数。
所以综上所述,直接给出一个结论吧。(我也不知道为什么很多人都这样做结论)
- 当你要直接传入构造类对象的参数列表时,请使用emplace_back
- 如果你已经有了类对象的实例,或者传入临时对象,请使用push_back
文章分享:
David Stone的回答,这篇文章中简单讨论了他俩真正的区别在于:隐式构造函数和显式构造函数。更具体的可以移步文章
重大疑问
#include <vector>
#include <iostream>
using namespace std;
class CMyString {
public:
CMyString(const char* str = nullptr) {
cout << "CMyString(const char*)" << endl;
if (str != nullptr) {
mptr = new char[strlen(str) + 1];
strcpy(mptr, str);
} else {
mptr = new char[1];
*mptr = '\0';
}
}
~CMyString() {
cout << "~CMyString()" << endl;
delete[] mptr;
mptr = nullptr;
}
//带左值引用参数的拷贝构造
CMyString(const CMyString& str) {
cout << "CMyString(const CMyString&)" << endl;
mptr = new char[strlen(str.mptr) + 1];
strcpy(mptr, str.mptr);
}
//带右值引用参数的拷贝构造
CMyString(CMyString &&str) { //str引用的就是一个右值
cout << "CMyString(CMyString &&str)" << endl;
mptr = str.mptr;
str.mptr = nullptr;
}
//带左值引用参数的赋值重载函数
CMyString& operator=(const CMyString& str) {
cout << "operator=(const CMyString&)" << endl;
if (this == &str) {
return *this;
}
delete[] mptr;
mptr = new char[strlen(str.mptr) + 1];
strcpy(mptr, str.mptr);
return *this;
}
//带右值引用参数的赋值重载函数
CMyString& operator=(CMyString &&str) {
cout << "operator=(CMyString&&)" << endl;
if (this == &str)
return *this;
delete[] mptr;
mptr = str.mptr;
str.mptr = nullptr;
return *this;
}
const char* c_str() const { return mptr; }
private:
char* mptr;
friend CMyString operator+(const CMyString &lhs,
const CMyString &rhs);
friend ostream& operator<<(ostream &out, const CMyString &str);
};
CMyString operator+(const CMyString &lhs,
const CMyString &rhs){
//char *ptmp = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
CMyString tempStr;
tempStr.mptr = new char[strlen(lhs.mptr) + strlen(rhs.mptr) + 1];
strcpy(tempStr.mptr, lhs.mptr);
strcat(tempStr.mptr, rhs.mptr);
//delete []ptmp;
return tempStr;
//return CMyString(ptmp);
}
ostream& operator<<(ostream &out, const CMyString &str) {
out << str.mptr;
return out;
}
我实现了MyString类,并做了如下三个实验,这是为什么呢?
实验一
![](https://img-blog.csdnimg.cn/direct/82609081777949a2b6ecbd2fa19b7df1.png#pic_center)
实验二
![](https://img-blog.csdnimg.cn/direct/0501ad97189c49038e9a8c260ae2eaee.png#pic_center)
实验三
![](https://img-blog.csdnimg.cn/direct/31d4466229ba48139dc94e0e983f4f12.png#pic_center)
这是什么情况??为什么emplace_back效率会低这么多??发生了什么??