最近在写一段代码的时候,突然很好奇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
<< * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) /
<< " 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 = ;
v.reserve(count); //预分配十万大小,排除掉分配内存的时间 {
TIME_INTERVAL_SCOPE("push_back string:");
for (int i = ; 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 = ; 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 = ; i < count; i++)
{
v.push_back(std::string("ceshi"));// push_back(string &&), 参数是右值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("push_back(c string):");
for (int i = ; i < count; i++)
{
v.push_back("ceshi");// push_back(string &&), 参数是右值引用
}
} v.clear();
{
TIME_INTERVAL_SCOPE("emplace_back(c string):");
for (int i = ; 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 = ;
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
}
}

2020 4/4 更新,以前对于emplace_back有误解,发现只有在传入临时变量时才会由于push_back

class A
{
public:
A(int i)
{
m_i=i;
printf("construct A %d\n",i);
} A(const A& aa)
{
m_i=aa.m_i;
printf("copy construct A %d\n",m_i);
} ~A()
{
printf("destruct A %d\n",m_i);
}
int m_i;
};
void testFun(A a)
{
printf("111\n");
} int main()
{
vector<A> vec;
vec.reserve();
for(int i=;i<;i++)
{ //vec.push_back(i); //这个和下面的效果一样
vec.push_back(A(i)); //vec.emplace_back(A(i));
// vec.emplace_back(i); //只有这条在调用的时候不会再调用拷贝构造!!!
printf("capacity %d\n",vec.capacity());
}
}

(转)C++11使用emplace_back代替push_back (其中有关于右值引用)的更多相关文章

  1. C++11新特性,对象移动,右值引用,移动构造函数

    C++11新标准中的一个最主要的特性就是移动而非拷贝对象的能力.接下来简要介绍一下相关概念. 右值引用 所谓右值引用就是必须绑定到右值的引用.通过 && 而不是 & 来获得右值 ...

  2. 【C/C++开发】C++11:右值引用和转发型引用

    右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如 ...

  3. C++ 11 中的右值引用

    C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream>    #include &l ...

  4. C++ 11 右值引用以及std::move

    转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,in ...

  5. 对C++11中的`移动语义`与`右值引用`的介绍与讨论

    本文主要介绍了C++11中的移动语义与右值引用, 并且对其中的一些坑做了深入的讨论. 在正式介绍这部分内容之前, 我们先介绍一下rule of three/five原则, 与copy-and-swap ...

  6. 【转载】C++ 11中的右值引用

    本篇随笔为转载,原博地址如下:http://www.cnblogs.com/TianFang/archive/2013/01/26/2878356.html 右值引用的功能 首先,我并不介绍什么是右值 ...

  7. C++ 11的右值引用

    目录 一.问题导入 二.右值和右值引用 2.1 左值(lvalue)和右值(rvalue) 2.2 左值引用和右值引用 总结 参考资料 C++11 引入了 std::move 语义.右值引用.移动构造 ...

  8. C++11使用emplace_back代替push_back

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

  9. C++11 的右值引用

    作者:Tinro链接:https://www.zhihu.com/question/22111546/answer/30801982来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

随机推荐

  1. Flutter编程:Flutter命令行的学习

    1.创建 Flutter 工程 flutter create <output directory> D:\notebook\flutter\projects\ui_tutorial\lay ...

  2. oled屏幕配套取字模软件使用

    oled屏幕配套取字模软件使用 作者:李剀 出处:https://www.cnblogs.com/kevin-nancy/p/10531368.html欢迎转载,但也请保留上面这段声明.谢谢! **P ...

  3. mongodb常用语句(集合操作)

    mongodb常用语句(集合操作) 查看集合帮助 db.songs.help(); 查看集合总数据量 db.songs.count(); 查看表空间大小 db.songs.dataSize(); 查看 ...

  4. mysql中字段类型是datetime时比较大小问题

    select sum(studychj) as tofflinejz from afterline where studybegin >= '2010-01-01 00:00:00' and s ...

  5. FZU 2213——Common Tangents——————【两个圆的切线个数】

    Problem 2213 Common Tangents Accept: 7    Submit: 8Time Limit: 1000 mSec    Memory Limit : 32768 KB ...

  6. 深入理解JavaScript系列(3):全面解析Module模式

    简介 Module模式是JavaScript编程中一个非常通用的模式,一般情况下,大家都知道基本用法,本文尝试着给大家更多该模式的高级使用方式. 首先我们来看看Module模式的基本特征: 模块化,可 ...

  7. SQL脚本整理系列一 分隔函数

    原来效果: fName Scroe 王某某 ,, 李某某 , 王某某 李某某 李某某 ,, 王某某 执行后效果: name score 李某某 李某某 李某某 王某某 王某某 王某某 王某某 王某某 ...

  8. 跨页面传值之QueryString

    跨页面传值常用方法 1.QueryString 2.Form-post控件传递 3.Cookies传递 4.Application传递 5.Session传递(灵活强大) 1.query传值 http ...

  9. Android开发过程中部分报错解决方法。

    初学Android,最近在使用zxing开发一个条码扫描解析的安卓项目中,遇到以下几个问题.贴出来以供参考. 1.Http请求错误    Android4.0以上要求不能把网络请求的操作放在主线程里操 ...

  10. 2.C#编程语言

    C#(sharp):是一种编程语言,可以开发基于.net平台的应用.   java即是一种平台,也是一名语言.   在.net平台当中,C#是主流语言.C#语言开发的应用不能脱离.net环境而独立运行 ...