C++ STL的vector相信大家一定都知道,它是一个一般用来当做可变长度列表的类。在C++11之前,一般给vector插入新元素用得都是push_back函数,比如下面这样:

std::vector<std::string> list;
list.push_back(std::string("6666"));

这种写法事实上有很多的冗余计算,我们来分析下,调用这句push_back一共做了哪些操作:

1.执行了std::string的构造函数,传入"6666"构造出一个std::string,这是一个临时变量,我们称它为temp;

2.执行了std::string的拷贝构造函数,将temp的内容拷贝到list的空间中;

3.执行了std::string的析构函数,析构临时变量temp。

从中可以看到,我们需要执行std::string的两个构造函数和一个析构函数,还是比较耗费时间的。仔细分析下可以发现,构造函数还是必须要有一个的(因为必须要为list创建一个std::string对象),但我们完全可以省略掉创建和销毁临时对象temp的操作,只要我们能保证将"6666"直接传入为list构造的std::string对象中。

为了实现这个目标,C++11引入了emplace_back函数,它通过完美转发实现了在vector中插入时直接在容器内构造对象,省略了创建临时对象的操作。我们看下它的代码就不难理解:

template<class _Ty,
class _Alloc = allocator<_Ty>>
class vector
: public _Vector_alloc<_Vec_base_types<_Ty, _Alloc>>
{
...
public:
template<class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val)
{ // insert by perfectly forwarding into element at end, provide strong guarantee
if (_Has_unused_capacity())
{
return (_Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...));
} _Ty& _Result = *_Emplace_reallocate(this->_Mylast(), _STD forward<_Valty>(_Val)...); return (_Result);
}
...
};

对于上面案例中的list(vectorstd::string)来说,_Ty是std::string,调用list.emplace_back("6666"),则_Valty就是const char*,通过完美转发机制(forward<_Valty>)最终将传入的参数_Val(本例中就是"6666")传入std::string的构造函数中,实现了直接从list中一步到位构造对象,省略了创建临时对象的过程,从而减少了创建的时间。

下面是我做的实验,首先定义一个类A,它可以接受一个const char*来构造:

class A
{
public:
A(const char* c)
{
int count = strlen(c);
content = new char[count];
memcpy(content, c, count);
} ~A()
{
if (content)
{
delete[] content;
}
} A(const A& a)
{
int count = strlen(a.content);
content = new char[count];
memcpy(content, a.content, count);
} A(A && a)
{
content = a.content;
a.content = nullptr;
}
private:
char * content = nullptr;;
};

通过以下代码来测试运行时间:

int main()
{
std::vector<A> str_list;
std::vector<A> str_list2;
std::vector<A> str_list3;
std::vector<A> str_list4; int count = 100000;
//提前留好空间,防止创建内存干扰
str_list.reserve(count * 2);
str_list2.reserve(count * 2);
str_list3.reserve(count * 2);
str_list4.reserve(count * 2); clock_t start = clock();
for (int i = 0; i < count; i++)
{
A a = "hahahaah";
str_list.push_back(a);
}
clock_t end = clock();
std::cout << end - start << std::endl; start = clock();
for (int i = 0; i < count; i++)
{
str_list2.push_back("hahahaah");
}
end = clock();
std::cout << end - start << std::endl; start = clock();
for (int i = 0; i < count; i++)
{
str_list3.emplace_back("hahahaah");
}
end = clock();
std::cout << end - start << std::endl; start = clock();
for (int i = 0; i < count; i++)
{
A a = "hahahaah";
str_list4.emplace_back(a);
}
end = clock();
std::cout << end - start << std::endl; system("pause");
return 0;
}

以上四种情况输出的运行时间如下(VS2017、Debug、Win32模式下编译):

125
84
78
106

可以看到,最耗时的就是第一种:

for (int i = 0; i < count; i++)
{
A a = "hahahaah";
str_list.push_back(a);
}

因为它就是本文前面说的“创建临时对象->创建list中的对象->销毁临时对象”过程。

str_list2的做法相对好一些,因为虽然也会生成临时对象,但因为传入的是右值引用,因此调用的是移动构造函数,相对来说节省一些。

str_list3就是本文上面提到的做法,直接在vector上构造对象,自然是时间最短的。

str_list4还是构造了对象,因此虽然调用的是emplace_back,但传入的是左值引用A&,所以仍然比较耗时间。不过这也说明,emplace_back也可以像push_back一样传入要插入的对象(而不是构造对象的参数),这样的话走的还是类似push_back的流程,通过调用复制构造函数创建新对象。这种用法从试验结果上看,起码不会比push_back性能更差。

C++ vector的emplace_back函数的更多相关文章

  1. C++标准库之vector(各函数及其使用全)

    原创作品,转载请注明出处:http://www.cnblogs.com/shrimp-can/p/5280566.html iterator类型: iterator:到value_type的访问,va ...

  2. C++11 vector使用emplace_back代替push_back

    C++11中,针对顺序容器(如vector.deque.list),新标准引入了三个新成员:emplace_front.emplace和emplace_back,这些操作构造而不是拷贝元素.这些操作分 ...

  3. map 和 vector 的erase函数说明

    1. map的erase函数使用 这里首先要注意,C++针对map的erase函数有不同的函数原型,这往往是出现问题的关键所在.根据参考文献1: 在C++98中: (1) void erase (it ...

  4. list map vector set 常用函数列表

    #include <stdio.h> #include <iostream>//cin,cout #include <sstream>//ss transfer. ...

  5. vector的成员函数解析

    vector是线性容器,它的元素严格的依照线性序列排序,和动态数组非常相似,和数组一样,它的元素存储在一块连续的存储空间中,这也意味着我们不仅能够使用迭代器(iterator)訪问元素,还能够使用指针 ...

  6. vector 利用swap 函数进行内存的释放 vector<int>().swap

    首先,vector与deque不同,其内存占用空间只会增长,不会减小.比如你首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个.所有 ...

  7. std::vector利用swap()函数进行内存的释放【转】

    首先,vector与deque不同,其内存占用空间只会增长,不会减小.比如你首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个.所有 ...

  8. 实战c++中的vector系列--emplace_back造成的引用失效

    上篇将了对于struct或是class为何emplace_back要优越于push_back,可是另一些细节没有提及.今天就谈一谈emplace_back造成的引用失效. 直接撸代码了: #inclu ...

  9. vector利用swap()函数进行内存的释放

    首先,vector与deque不同,其内存占用空间只会增长,不会减小.比如你首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个.所有 ...

  10. QVector 和vector的比较(QVector默认使用隐式共享,而且有更多的函数提供)

    QVector和vector的比较: Qvector默认使用隐式共享,可以用setSharable改变其隐式共享.使用non-const操作和函数将引起深拷贝.at()比operator[](),快, ...

随机推荐

  1. 记录自己在对订单进行按日期查询时使用的一种查询的方法,这里的orders是订单表,你也可以改成别的什么表对于最终数据不会造成影响,除非你那个表的数据只有几条那样就会出现查不到日期的情况

    SELECT @date := DATE_ADD(@date, INTERVAL + 1 DAY) days FROM ( SELECT @date := DATE_ADD("2019-06 ...

  2. HarmonyOS基础

    目录 自适应布局 自适应拉伸布局 自适应缩放 自适应延伸 组件多态 ArkUI开发框架 基础组件 Text组件和Span组件 参考 参考:harmonyos3: 鸿蒙ArkUI eTS教程配套源码 参 ...

  3. COM 对象的利用与挖掘4

    作者:Joey@天玄安全实验室 前言 本文在FireEye的研究Hunting COM Objects[1]的基础上,讲述COM对象在IE漏洞.shellcode和Office宏中的利用方式以及如何挖 ...

  4. Linux出现Read-only file system错误解决方法

    执行命令时遇到如下错误 这个问题是文件系统受损导致得,fstab文件未正确配置 解决方法: df -hT #查看一下分区及挂载信息 fsck -a /dev/sda3 -a :检查文件系统,有异常便自 ...

  5. CSP-J入门组

    setw(2) cout<<setw(2) //设置后面显示字符的宽度为2 cout<<fixed<<setprecision(6)<<变量名;//设置 ...

  6. 解决idea不能自动下载maven配置文件pom.xml下的jar包依赖的问题

    表现:无法下载pom配置文件中的依赖包,或只能下载少数包,各项配置都正确的情况 理由未知: 百度了很长一段时间,网上给出比较精准的解决之一是 setting>>maven>>去 ...

  7. Vmware 虚拟机Ubuntu系统,解决忘记用户名和密码解决办法

    1.在开机界面按住shift,会加载grub的启动界面,找到Advaced options for Ubuntu选项.按"e" 进入编辑模式. 2.光标移动至ro,改为rw,(Li ...

  8. 2.对于script标签的放置位置

    使用<script>元素有两种方式 一种是在页面嵌入JavaScript代码 ,一种就是包含外部JavaScript文件 包含在<script>标签内部的JavaScript代 ...

  9. Knowledge Fusion例子

    #! NX/KF 5.0 # #************************************************************************************ ...

  10. 什么是spring框架

    一bai.概念:1. spring是开源的轻量级框架2 spring核心主要两部分:(1)aop:面向切面编程,扩展功能不是修改源代码实现(2)ioc:控制反转,- 比如有一个类,在类里面有方法(不是 ...