C++11使用emplace_back代替push_back

最近在写一段代码的时候,突然很好奇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

时间: 2024-12-11 10:33:35

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

emplace_back与push_back的区别

std::vector::emplace_back     C++   Containers library   std::vector   template< class... Args >void emplace_back( Args&&... args );   (since C++11)       Appends a new element to the end of the container. The element is constructed in-place

后台开发:核心技术与应用实践

后台开发:核心技术与应用实践 徐晓鑫 著 图书在版编目(CIP)数据 后台开发:核心技术与应用实践 / 徐晓鑫著. -北京:机械工业出版社,2016.8 ISBN 978-7-111-54339-8 I. 后- II. 徐- III. 网络-开发 IV. TP393.092 中国版本图书馆CIP数据核字(2016)第167884号 后台开发:核心技术与应用实践 出版发行:机械工业出版社(北京市西城区百万庄大街22号 邮政编码:100037) 责任编辑:李 艺 责任校对:董纪丽 印 刷: 版 次:

后台开发:核心技术与应用实践3.3.4 Vector类的简单实现

3.3.4 Vector类的简单实现 实现一个vector,绝对是C++中的重点知识.下面例3.13中提供了类的简单实现. [例3.17] vector类的简单实现. #include<algorithm> #include<iostream> #include <assert.h> using namespace std; template<typename T> class myVector { private:     /*walk length*/

C++11中的Lambda表达式

"Lambda 表达式"(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数.Lambda表达式可以表示闭包(注意和数学传统意义上的不同). ISO C++ 11 标准的一大亮点是引入Lambda表达式.基本语法如下: [capture list] (parameter list) ->return type { function

C++11时代的标准库快餐教程(2) - STL概览

STL概览 在进入STL的世界之前,我们先对其中的主要组件做一个鸟瞰: 先来一张层次图: 如果觉得层次图看不清的话,我们把它重新绘成思维导图吧: 从图中我们可以看到: STL的核心只有三个大组件: 容器 迭代器 算法 当然,这么大的一个包罗万象的C++标准库,还是有很多其他的组件,比如智能指针.字符串.正则表达式.流式I/O.并发处理等不是跟容器相关的.但是做为核心的容器库,就只有这三大组件. 容器的种类其实蛮少的,大体上分为基本容器和特殊容器两大类. 基本容器按照无序,有序,排序分成了三大类:

C++11 thread

windows系统中,需要vs2012才支持. 1.线程的创建C++11线程类std::thread,头文件include <thread>首先,看一个最简单的例子:   [cpp] view plain copy   void my_thread()   {       puts("hello, world");   }      int main(int argc, char *argv[])   {       std::thread t(my_thread);  

C++开发者都应该使用的10个C++11特性

在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛.不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备.你也许看到过许多类似介绍各种C++11特性的文章.下面是我总结的,C++开发者都需要学习和使用的C++11新特性.   auto 在C++11之前,auto关键字用来指定存储期.在新标准中,它的功能变为类型推断.auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型.各种作用域内声明变量都可以用到它.例如,名空间中,程序

C++11系列-什么是C++11

什么是C++0x? C++0x是C++最新标准标准化过程中的曾用名,在这一系列文章中我们将介绍最新标准添加的一系列新的语言特性.在2011年9月份,C++0x正式由官方发布并命名C++11,现在很多编译器已经支持了部分C++11特性. C++11包括大量的新特性:主要特征像lambda表达式和移动语义,实用的类型推导关键字auto,更简单的容器遍历方法,和大量使模板更容易使用的改进.这一系列教程将包含所以以上特性. 你该关注C++11吗? 很明显,C++11为C++带来了大量的新特性.C++11

C++11新特性:Lambda函数(匿名函数)

声明:本文参考了Alex Allain的文章http://www.cprogramming.com/c++11/c++11-lambda-closures.html 加入了自己的理解,不是简单的翻译   C++11终于知道要在语言中加入匿名函数了.匿名函数在很多时候可以为编码提供便利,这在下文会提到.很多语言中的匿名函数,如C++,都是用Lambda表达式实现的.Lambda表达式又称为lambda函数.我在下文中称之为Lambda函数. 为了明白Lambda函数的用处,请务必先搞明白C++中的