1. 程式人生 > >C++11使用emplace_back代替push_back

C++11使用emplace_back代替push_back

一次 lease ptr 值引用 out 字符串 打印 clear push

最近在寫一段代碼的時候,突然很好奇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