最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多。

首先,写了一个类用于计时,

//time_interval.h
#pragma once #include <iostream>
#include <memory>
#include <string>
#ifdef GCC
#include <sys/time.h>
#else
#include <ctime>
#endif // GCC class TimeInterval
{
public:
TimeInterval(const std::string& d) : detail(d)
{
init();
} TimeInterval()
{
init();
} ~TimeInterval()
{
#ifdef GCC
gettimeofday(&end, NULL);
std::cout << detail
<< 1000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000
<< " ms" << endl;
#else
end = clock();
std::cout << detail
<< (double)(end - start) << " ms" << std::endl;
#endif // GCC
} protected:
void init() {
#ifdef GCC
gettimeofday(&start, NULL);
#else
start = clock();
#endif // GCC
}
private:
std::string detail;
#ifdef GCC
timeval start, end;
#else
clock_t start, end;
#endif // GCC
}; #define TIME_INTERVAL_SCOPE(d) std::shared_ptr<TimeInterval> time_interval_scope_begin = std::make_shared<TimeInterval>(d)

使用方法就是在作用域中使用宏TIME_INTERVAL_SCOPE(d),d为打印用的字符串,输出作用域的耗时情况。

其次,看一下现在push到vector的5种方法的耗时情况对比:

#include <vector>
#include <string>
#include "time_interval.h" int main() { std::vector<std::string> v;
int count = 10000000;
v.reserve(count); //预分配十万大小,排除掉分配内存的时间 {
TIME_INTERVAL_SCOPE("push_back string:");
for (int i = 0; i < count; i++)
{
std::string temp("ceshi");
v.push_back(temp);// push_back(const string&),参数是左值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back move(string):");
for (int i = 0; i < count; i++)
{
std::string temp("ceshi");
v.push_back(std::move(temp));// push_back(string &&), 参数是右值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back(string):");
for (int i = 0; i < count; i++)
{
v.push_back(std::string("ceshi"));// push_back(string &&), 参数是右值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back(c string):");
for (int i = 0; i < count; i++)
{
v.push_back("ceshi");// push_back(string &&), 参数是右值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("emplace_back(c string):");
for (int i = 0; i < count; i++)
{
v.emplace_back("ceshi");// 只有一次构造函数,不调用拷贝构造函数,速度最快
}
}
}

vs2015 release下编译,运行结果:

push_back string:327 ms 
push_back move(string):213 ms 
push_back(string):229 ms 
push_back(c string):215 ms 
emplace_back(c string):122 ms

第1中方法耗时最长,原因显而易见,将调用左值引用的push_back,且将会调用一次string的拷贝构造函数,比较耗时,这里的string还算很短的,如果很长的话,差异会更大

第2、3、4中方法耗时基本一样,参数为右值,将调用右值引用的push_back,故调用string的移动构造函数,移动构造函数耗时比拷贝构造函数少,因为不需要重新分配内存空间。

第5中方法耗时最少,因为emplace_back只调用构造函数,没有移动构造函数,也没有拷贝构造函数。

为了证实上述论断,我们自定义一个类,并在普通构造函数、拷贝构造函数、移动构造函数中打印相应描述:

#include <vector>
#include <string>
#include "time_interval.h" class Foo {
public:
Foo(std::string str) : name(str) {
std::cout << "constructor" << std::endl;
}
Foo(const Foo& f) : name(f.name) {
std::cout << "copy constructor" << std::endl;
}
Foo(Foo&& f) : name(std::move(f.name)){
std::cout << "move constructor" << std::endl;
} private:
std::string name;
};
int main() { std::vector<Foo> v;
int count = 10000000;
v.reserve(count); //预分配十万大小,排除掉分配内存的时间 {
TIME_INTERVAL_SCOPE("push_back T:");
Foo temp("ceshi");
v.push_back(temp);// push_back(const T&),参数是左值引用
//打印结果:
//constructor
//copy constructor
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back move(T):");
Foo temp("ceshi");
v.push_back(std::move(temp));// push_back(T &&), 参数是右值引用
//打印结果:
//constructor
//move constructor
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back(T&&):");
v.push_back(Foo("ceshi"));// push_back(T &&), 参数是右值引用
//打印结果:
//constructor
//move constructor
} v.clear();
{
std::string temp = "ceshi";
TIME_INTERVAL_SCOPE("push_back(string):");
v.push_back(temp);// push_back(T &&), 参数是右值引用
//打印结果:
//constructor
//move constructor
} v.clear();
{
std::string temp = "ceshi";
TIME_INTERVAL_SCOPE("emplace_back(string):");
v.emplace_back(temp);// 只有一次构造函数,不调用拷贝构造函数,速度最快
//打印结果:
//constructor
}
}

结论:在C++11情况下,果断用emplace_back代替push_back

C++11使用emplace_back代替push_back的更多相关文章

  1. (转)C++11使用emplace_back代替push_back (其中有关于右值引用)

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 首先,写 ...

  2. 学习 emplace_back() 和 push_back 的区别 emplace_back效率高

    在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放 ...

  3. 编程杂谈——使用emplace_back取代push_back

    近日在YouTube视频上看到关于vector中emplace_back与push_back区别的介绍,深感自己在现代C++中还是有不少遗漏的知识点,遂写了段代码,尝试比较两者的差别. 示例代码 #i ...

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

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

  5. emplace_back与push_back的区别

    std::vector::emplace_back     C++   Containers library   std::vector   template< class... Args &g ...

  6. 【C/C++开发】emplace_back() 和 push_back 的区别

    在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放 ...

  7. emplace_back() 和 push_back 的区别(转)

    在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放 ...

  8. C++ std::vector emplace_back 优于 push_back 的理由

    #include <iostream> #include <vector> #include <chrono> #include <windows.h> ...

  9. emplace_back() 和 push_back 的区别

    在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放 ...

随机推荐

  1. Dockerfile创建镜像

    Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile来快速创建自定义的镜像. Dockerfile由一行行命令语句组成,并且支持易#开头的注释行. 一般而言Dockerfil ...

  2. iOS - 友盟集成QQ分享的AppID转换16进制的方法

    设置xcode的url scheme格式为“QQ”+腾讯QQ互联应用appId转换成十六进制(不足8位前面补0),例如“QQ41EE2B54”.生成十六进制方法:echo 'ibase=10;obas ...

  3. 思科SVI接口和路由接口区别

    Cisco多层交换中提到了一个SVI接口,路由接口.在多层交换机上可以将端口配置成不同类型的接口. 其中SVI接口 类似于  interface Vlan10ip address 192.168.20 ...

  4. Hibernate的10个常见面试问题及答案

    在Java J2EE方面进行面试时,常被问起的Hibernate面试问题,大多都是针对基于Web的企业级应用开发者的角色的.Hibernate框架在Java界的成功和高度的可接受性使得它成为了Java ...

  5. zabbix触发器表达式详解

    Zabbix触发器的语法如下: {<server>:<key>.<function>(<parameter>)}<operator>< ...

  6. 关于keyGenerator,KeyPairGenerator,SecretKeyFactory的解析

    Java加密的常用的加密算法类型有三种 1单向加密:也就是不可逆的加密,例如MD5,SHA,HMAC 2对称加密:也就是加密方和解密方利用同一个秘钥对数据进行加密和解密,例如DES,PBE等等 3非对 ...

  7. SQL Fundamentals || 多表查询(内连接,外连接(LEFT|RIGHT|FULL OUTER JOIN),自身关联,ON,USING,集合运算UNION)

    SQL Fundamentals || Oracle SQL语言 一.多表查询基本语法 在进行多表连接查询的时候,由于数据库内部的处理机制,会产生一些“无用”的数据,而这些数据就称为笛卡尔积. 多表查 ...

  8. 疯狂java讲义 第三版 笔记

      java7新加特性: 0B010101  二进制数 int c=0B0111_1111;   数值中使用下划线分隔 switch 支持String类型   字符串常量放在常量池 String s0 ...

  9. 获取linux工具命令源码

    总结: 通过先通过which找到命令路径path rpm -qf  path 获取源码名称n rpm -qi n   获取源码地址 [root@d mongoexport]# rpm --helpUs ...

  10. 如何修改WordPress网站默认登录地址wp-admin

    使用过WordPress程序建网站的学员都知道,我们使用Wordpress建好的网站,它的网站登录后台就是“网站域名/wp-admin”.如下图: 为了网站安全,如何修改Wordpress网站默认登录 ...