C++11使用emplace_back代替push_back
阿新 • • 發佈:2017-11-06
一次 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