std::vector的幾種遍歷方式比較
std::vector是我在標準庫中實用最頻繁的容器。總結一下在遍歷和建立vector時需要注意的一些地方。
在不考慮執行緒安全問題的前提下,在C++11中有五種遍歷方式。
方式一
for (size_t i =0; i < vec.size(); i ++) {
int d = vec[i];
}
方式二
size_t len = vec.size();
for (size_t i =0; i < len; i ++) {
int d = vec[i];
}
方式三for (auto
int d = *it;
}
方式四for (int i:vec) {
int d = i;
}
方式五
for_each(vec.begin(), vec.end(), [](int i){
int d = i;
});
程式碼很簡單,如果不實際測試的話。你能推測出哪種方式效率最高嗎?
我在mac下面用xcode測試了這五種遍歷方式。
////h檔案
void testBianli();
void testBianli1(const
void testBianli2(conststd::vector<int>& vec);
void testBianli3(conststd::vector<int>& vec);
void testBianli4(const std::vector<int>& vec);
void testBianli5(const std::vector<int>& vec);
////end h檔案
///cpp檔案
void testBianli()
{
size_t
std::vector<int> vec(counter);
testBianli1(vec);
testBianli2(vec);
testBianli3(vec);
testBianli4(vec);
testBianli5(vec);
}
void testBianli1(conststd::vector<int>& vec)
{
MEARSURE_DURATION(all);
for (size_t i =0; i < vec.size(); i ++) {
int d = vec[i];
}
}
void testBianli2(conststd::vector<int>& vec)
{
MEARSURE_DURATION(all);
size_t len = vec.size();
for (size_t i =0; i < len; i ++) {
int d = vec[i];
}
}
void testBianli3(conststd::vector<int>& vec)
{
MEARSURE_DURATION(all);
for (auto it = vec.begin(); it != vec.end(); it ++) {
int d = *it;
}
}
void testBianli4(conststd::vector<int>& vec)
{
MEARSURE_DURATION(all);
for (int i:vec) {
int d = i;
}
}
void testBianli5(conststd::vector<int>& vec)
{
MEARSURE_DURATION(all);
for_each(vec.begin(), vec.end(), [](int i){
int d = i;
});
}
其中需要定義一個測試函式執行時間的類和一個巨集
#define MEARSURE_DURATION(fun) CFunctionDuration fun(std::string(std::string(__FUNCTION__) +" " +std::string(#fun)).c_str() );
class CFunctionDuration {
public:
CFunctionDuration(const char* funname)
{
m_start_df = clock();
sprintf(m_funname,"%s",funname);
}
~CFunctionDuration()
{
double du = (clock() -m_start_df)*0.001;
if (du >= 0) {
printf("%s running duration:%f(ms) \n",m_funname,du);
}
}
private:
double m_start_df;
char m_funname[256];
};
最後輸出結果如下:testBianli1 all running duration:3.440000(ms)
testBianli2 all running duration:2.854000(ms)
testBianli3 all running duration:11.009000(ms)
testBianli4 all running duration:5.109000(ms)
testBianli5 all running duration:7.637000(ms)
很明顯了,第二種方式是最快的。個人覺得原因如下:第一種方法每次都要呼叫size()函式去計算vec的長度。通過檢視size(),其實現如下:
_LIBCPP_INLINE_VISIBILITY
size_type size() const_NOEXCEPT
{return static_cast<size_type>(this->__end_ -this->__begin_);}
指標地址相減,再來一個強制轉換最後得到size()。就這這裡稍微費點時間。
第三種方式是最費時間的。使用迭代器it迴圈,迭代器本身不是內部資料,它的各種操作(比較,偏移,取值操作)都是一系列行內函數操作,暗地裡乾的事遠比看到的複雜。這個迭代器給自己套上偽裝,讓你可以像使用指標一樣利用它去訪問物件,但是畢竟中間隔了一層。個人覺得迭代器的實用主要是便於stl中演算法的實現,有一種通用的資料型別來訪問各種容器中的元素。
第四種方式從形式上看非常簡潔,可幹活卻沒有長相利索。這是C++11的新特性。這篇博文中有詳細介紹http://www.cnblogs.com/h46incon/archive/2013/06/02/3113737.html.
第五種方式也是c++的新特性。其中包括大受推崇的lambda特性。這種特性是向其它語言學習的結果。在這裡也跑的不是最快的。
其實這些執行時間結果的差別也只有在遍歷過程中對元素操作的過程很簡短的時候才會顯現出來。當對每個元素操作花費的時間跟純粹遍歷vector所費時間不是一個數量級時,這些區別就不重要了。另外這個時間在不同裝置上執行的時間肯定是不一樣的。不同編譯器下得到的結果也不一樣,但是其相對執行效率還是有參考意義的。