2011-08-03 74 views
7

實施例示出了如何遍歷一個std::map常常這樣:在地圖上迭代時,是++還是++ ++?

MapType::const_iterator end = data.end(); 
for (MapType::const_iterator it = data.begin(); it != end; ++it) 

即它使用++it代替it++。有什麼理由?如果我用it++代替,會有什麼問題嗎?

+0

看看[這] [1]。 [1]:http://stackoverflow.com/questions/24853/c-what-is-the-difference-between-i-and-i – sergio

+5

如果在C++代碼爲任意長的時間,'++ it'將提高效率。不是在你的代碼中,而是花在向人們解釋爲什麼你首先沒有做'++ it'的時候。既然它絲毫不重要,那麼你也可以這樣做,這樣做不會導致爭論。 –

+0

@丹尼斯:它無關緊要,經常不會被測量,但是在緊密的循環中,它可以有所作爲。 –

回答

10

把它測試,我提出三個源文件:

#include <map> 

struct Foo { int a; double b; char c; }; 

typedef std::map<int, Foo> FMap; 

### File 1 only ### 

void Set(FMap & m, const Foo & f) 
{ 
    for (FMap::iterator it = m.begin(), end = m.end(); it != end; ++it) 
    it->second = f; 
} 

### File 2 only ### 

void Set(FMap & m, const Foo & f) 
{ 
    for (FMap::iterator it = m.begin(); it != m.end(); ++it) 
    it->second = f; 
} 

### File 3 only ### 

void Set(FMap & m, const Foo & f) 
{ 
    for (FMap::iterator it = m.begin(); it != m.end(); it++) 
    it->second = f; 
} 

### end ### 

g++ -S -O3編譯之後,GCC 4.6.1,我發現,版本2和3產生相同組件,和版本1點的不同之僅在一條指令中,cmpl %eax, %esi vs cmpl %esi, %eax

因此,請選擇適合自己風格的東西。前綴增量++it可能是最好的,因爲它最準確地表達了您的要求,但不要掛掉它。

+1

很好的顯示優化,但只能用於足夠簡單和內聯'operator ++'。很高興看到g ++可以爲'std :: map'做到這一點。 –

+1

@Christopher:是的,你一定要始終使用'++ it'時,這就是你的意思。我只是想提供一些比較硬的比較兩個廣告點的真正影響(提升結束和使用前綴增量)。 –

20

it++返回前一個迭代器的副本。由於這個迭代器不被使用,這是浪費。 ++it返回對遞增迭代器的引用,避免複製。

請參閱Question 13.15以獲得更全面的解釋。

+4

這幾乎沒有關係。這個問題純粹是風格。 –

+1

@Dark - 由於沒有使用它,編譯器很可能會注意到這一點並優化拷貝。 –

+2

我知道這一點。然而,編譯器顯然不需要這樣做。 –

7

在使用預增加運算符與後增加運算符時,有一個小小的performance advantage。在設置使用迭代循環,可以選擇使用預增量:

for (list<string>::const_iterator it = tokens.begin(); 
    it != tokens.end(); 
    ++it) { // Don't use it++ 
    ... 
} 

的原因涉及到光,當你想想如何無論是運營商通常會是implemented.The預增量是非常簡單的。然而,爲了使後遞增工作,你需要首先使對象的副本,做原始對象的實際增量,然後返回副本:

class MyInteger { 
private: 
    int m_nValue; 

public: 
    MyInteger(int i) { 
     m_nValue = i; 
    } 

    // Pre-increment 
    const MyInteger &operator++() { 
     ++m_nValue; 
     return *this; 
    } 

    // Post-increment 
    MyInteger operator++(int) { 
     MyInteger clone = *this; // Copy operation 1 
     ++m_nValue; 
     return clone; // Copy operation 2 
    } 
} 

正如你所看到的,後增量實現涉及兩個額外的複製操作。如果所討論的對象體積龐大,這可能相當昂貴。話雖如此,一些編譯器可能足夠聰明,可以通過優化避開單個拷貝操作。重點在於後增加通常會涉及更多的工作而不是預增量,因此習慣於將「++」放在迭代器之前而不是之後是明智的。

(1)信用鏈接網站。

6

從邏輯的角度來看 - 它是一樣的,沒關係這裏

爲什麼前綴一被使用 - 因爲它更快 - 它改變迭代器並返回它的值,而後綴創建臨時對象,遞增當前迭代器,然後返回臨時對象(迭代器的副本,在遞增)。由於沒有人在這裏觀察這個臨時對象(返回值),所以它是相同的(邏輯上)。

有一個相當大的機會,編譯器會優化它。


另外 - 實際上,這應該是這樣的任何類型的。但它應該是。由於任何人都可以重載operator++ - 後綴和前綴,他們可以有副作用和不同的行爲。

嗯,這是一件可怕的事情,但仍有可能。

+0

確實是一件可怕的事情。就像濫用shift操作符將數據放入流中一樣,對嗎? ;-)(SCNR) –

+0

嗯,這是一個很好的點 - 畢竟,你可能最終被處理的是一個自定義的迭代器是複製無論出於何種原因非常昂貴,而且不能優化掉了,所以只是習慣性地寫你的意思( '++ it')而不是你習慣的習慣('C++')只是一個好習慣。 –

+0

@Kerrek SB - 正是:)我總是喜歡'++ bla_bla',當它的確定要使用的。 –

1

這不會引起任何問題,但使用++it更正確。隨着小各類它並沒有真正不管使用++ii++,但對於「大」類:

operator++(type x,int){ 
    type tmp=x; //need copy 
    ++x; 
    return tmp; 
} 

編譯器可以優化了其中的一部分,但它很難確定。

1

正如其他答案所說,寧願++它,除非它不會在上下文中工作。對於遍歷小型類型的容器,它確實沒什麼區別(如果編譯器將它優化掉)沒有什麼區別,但對於大型類型的容器,由於節省了製作副本的成本,它可以有所作爲。

沒錯,你可能知道在你的特定環境下,這個類型足夠小,所以你不用擔心它。但是稍後,您團隊中的其他人可能會將容器的內容更改爲重要的內容。另外,我認爲最好讓自己養成一個好習慣,並且只有當你知道你必須時才進行後期增量。