2012-10-03 238 views
83

我是C++語言的新手。我已經開始使用向量,並且已經注意到,在我看到的所有代碼中,通過索引遍歷一個向量,for循環的第一個參數總是基於向量。在Java中我可能會做這樣的事情有一個ArrayList:使用'for'循環遍歷C++向量

for(int i=0; i < vector.size(); i++){ 
    vector[i].doSomething(); 
} 

是否有一個原因,我沒有看到這個在C++?這是不好的做法嗎?

+0

for循環不是一個函數,所以它沒有參數(或參數,這就是你傳入的)。你的意思是像'std :: vector :: size_type i = 0;',但是,或者也許是'std :: vector :: iterator it = vector.begin();'? – chris

+0

沒錯,我看到的所有例子都是這樣寫的。 – Flynn

+4

在Java中,我更喜歡for-each循環或使用迭代器。與C++非常相似,但語法略有不同。 –

回答

59

沒有任何理由我不認爲這在C++?這是不好的做法嗎?

號這不是一個不好的做法,但它使你的代碼的某些靈活性

通常,預C++ 11用於遍歷容器元素代碼使用迭代器,類似:

std::vector<int>::iterator it = vector.begin(); 

這是因爲它使代碼更加靈活。

所有標準庫容器都支持並提供迭代器,並且假如在開發的後期需要切換另一個容器,則不需要更改此代碼。

注意:編寫與每個可能的標準庫容器一起工作的代碼並不像看起來似的那樣容易。

+16

任何人都可以請解釋我爲什麼在這個特定的情況/代碼片斷,你建議迭代器索引?你在談論什麼是「靈活性」?就個人而言,我不喜歡迭代器,它們膨脹了代碼 - 只需輸入更多的字符即可達到相同的效果。特別是如果你不能使用'auto'。 –

+5

@VioletGiraffe:在使用迭代器就很難去錯某些情況下,像空的範圍和代碼更verbose.Ofcourse其問題或看法和選擇,因此,它可以無休止地進行辯論。 –

50

通過向量迭代的清潔方法是通過迭代:

for (auto it = begin (vector); it != end (vector); ++it) { 
    it->doSomething(); 
} 

或(相當於上面)

for (auto & element : vector) { 
    element.doSomething(); 
} 

在此之前的C++ 0x,你必須通過更換汽車迭代器類型和使用成員函數而不是全局函數開始和結束。

這可能是你所看到的。與您提到的方法相比,優點是您不會嚴重依賴於vector的類型。如果將vector更改爲不同的「集合類型」類,那麼您的代碼可能仍然有效。但是,您也可以在Java中執行類似的操作。概念上沒有太大差別;然而,C++使用模板來實現這一點(與Java中的泛型相比);因此該方法適用於定義了所有類型的beginend函數,即使對於非類類型(如靜態數組)也是如此。在這裏看到:How does the range-based for work for plain arrays?

+4

自動給出,自由開始/結束也是C++ 11。而且,在許多情況下,您應該使用++,而不是++。 – ForEveR

+0

是的,你是對的。然而,執行'begin'和'end'是單線程的。 – JohnB

+0

@JohnB它是一個不止一個班輪,因爲它也適用於固定大小的數組。另一方面'自動'將是相當棘手的。 – juanchopanza

27

做正確的做法是:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) { 
    it->doSomething(); 
} 

T爲載體內的類的類型。例如,如果該類是CActivity,只需編寫CActivity而不是T.

這種類型的方法可用於每個STL(不僅僅是向量,這比較好一點)。

如果你仍想使用的索引,方法是:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) { 
    v[i].doSomething(); 
} 
+0

不是'std :: vector :: size_type'總是'size_t'?這是我一直使用的類型。 –

+1

@VioletGiraffe我很確定你是對的(沒有真正檢查過),但是使用std :: vector :: size_type更好。 – DiGMi

+0

感謝您展示「禁止」的索引方式。 – Rosenthal

4

使用STL,程序員使用iterators遍歷容器,因爲迭代器是一個抽象的概念,在所有標準容器中實現。例如,std::list根本沒有operator []

67

你沒有看到這種做法的原因是很主觀的,不能有一個明確的答案,因爲我已經看到許多使用你提到的方式而不是iterator風格代碼的代碼。

下面就可以的人的原因不考慮循環的vector.size()方式:

  1. 總是擔心,每次打電話size()在循環 條件。然而無論它是一個非問題,或者它可以通過for環本身是平凡 固定
  2. 寧願std::for_each()
  3. 後來從std::vector改變容器到另一個(如 maplist)也將需求的變化循環機制, 因爲沒有循環

C++ 11的每一個集裝箱支撐size()風格提供了一個良好的機制,通過容器移動。這就是所謂的「基於循環的範圍」(或Java中的「增強for循環」)。

很少的代碼就可以通過全遍歷std::vector(強制性!):

vector<int> vi; 
... 
for(int i : vi) 
    cout << "i = " << i << endl; 
+4

只需要注意_range基於loop_的一個小缺點:你不能在'#pragma omp parallel for'中使用它。 – liborm

+1

我喜歡緊湊版本,因爲讀取的代碼較少。一旦你進行了心理調整,就會更容易理解,並且錯誤更加突出。當非標準的迭代發生時,這也使得它更加明顯,因爲有更多的代碼塊。 –

5

有一對夫婦的強烈理由使用迭代器,其中一些是這裏提到:

交換容器後不會使您的代碼無效。

即,如果您從std :: vector到std :: list或std :: set,則不能使用數字索引來獲取您的包含值。使用迭代器仍然有效。

運行無效迭代的醒目

如果修改容器在循環,下一次的中間您使用迭代器將拋出一個無效的迭代器例外。