2012-10-03 97 views
83

Tôi mới sử dụng ngôn ngữ C++. Tôi đã bắt đầu sử dụng vectơ, và đã nhận thấy rằng trong tất cả các mã tôi thấy để lặp đi lặp lại mặc dù một vector thông qua chỉ số, tham số đầu tiên của vòng lặp for luôn luôn là một cái gì đó dựa trên vector. Trong Java, tôi có thể làm một cái gì đó như thế này với một ArrayList:Lặp lại qua C++ Vector bằng cách sử dụng vòng lặp 'for'

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

Có lý do nào tôi không thấy điều này trong C++? Có thực hành xấu không?

+0

Vòng lặp for không phải là một hàm, do đó, nó không có tham số (hoặc đối số, đó là những gì bạn chuyển vào). Bạn có nghĩa là một cái gì đó như 'std :: vector :: size_type i = 0;', mặc dù, hoặc có lẽ 'std :: vector :: iterator it = vector.begin();'? – chris

+0

Chính xác, tất cả các ví dụ mà tôi thấy được viết như thế. – Flynn

+4

Trong Java, tôi thích vòng lặp for-each hoặc sử dụng vòng lặp. Khá giống với C++ mặc dù hơi khác cú pháp. –

Trả lời

59

Có bất kỳ lý do tôi không thấy điều này trong C++? Có thực hành xấu không?

số Nó không phải là một thực tế xấu, nhưng nó ám mã của bạn nhất định linh hoạt.

Thông thường, trước C++ 11 mã cho iterating trên các yếu tố chứa sử dụng vòng lặp, một cái gì đó như:

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

này là bởi vì nó làm cho mã linh hoạt hơn.

Tất cả các bộ chứa thư viện chuẩn hỗ trợ và cung cấp trình lặp và cho rằng nếu tại điểm phát triển sau này, bạn cần chuyển sang vùng chứa khác thì mã này không cần phải thay đổi.

Lưu ý: Mã viết hoạt động với mọi thùng chứa thư viện chuẩn có thể không dễ dàng như nó có vẻ dường như.

+16

Bất cứ ai có thể vui lòng giải thích cho tôi lý do tại sao trong trường hợp cụ thể/đoạn mã này bạn tư vấn cho các trình vòng lặp về lập chỉ mục? "Tính linh hoạt" này mà bạn đang nói đến là gì? Cá nhân, tôi không thích các trình vòng lặp, chúng làm nổi bật mã - chỉ cần thêm các ký tự để nhập cho cùng một hiệu ứng. Đặc biệt là nếu bạn không thể sử dụng 'auto'. –

+5

@VioletGiraffe: Trong khi sử dụng trình lặp, rất khó để đi sai với một số trường hợp như phạm vi trống và mã là chi tiết hơn.Theo dõi của nó một vấn đề hoặc nhận thức và sự lựa chọn, Vì vậy, nó có thể được tranh luận vô tận. –

50

Cách rõ ràng nhất để lặp qua một vector là thông qua vòng lặp:

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

hoặc (tương đương với trên)

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

Trước C++ 0x, bạn phải thay thế tự động bởi loại trình vòng lặp và sử dụng các hàm thành viên thay vì các hàm toàn cục bắt đầu và kết thúc.

Đây có thể là những gì bạn đã thấy. So với cách tiếp cận bạn đề cập, lợi thế là bạn không phụ thuộc nhiều vào loại vector. Nếu bạn thay đổi vector thành một loại "loại bộ sưu tập" khác, mã của bạn có thể vẫn hoạt động. Tuy nhiên, bạn có thể làm một cái gì đó tương tự trong Java. Không có nhiều khác biệt về khái niệm; Tuy nhiên, C++ sử dụng các mẫu để thực hiện điều này (so với các generic trong Java); do đó cách tiếp cận sẽ làm việc cho tất cả các loại mà các hàm beginend được xác định, ngay cả đối với các loại không thuộc lớp như mảng tĩnh. Xem ở đây: How does the range-based for work for plain arrays?

+4

tự động, bắt đầu/kết thúc miễn phí cũng là C++ 11. Và bạn cũng nên sử dụng ++, thay vào đó là ++ trong nhiều trường hợp. – ForEveR

+0

Vâng, bạn nói đúng. Tuy nhiên, việc triển khai 'begin' và' end' là một lớp lót. – JohnB

+0

@JohnB nó là nhiều hơn một lớp lót, bởi vì nó hoạt động cho mảng kích thước cố định quá. Mặt khác, 'auto' sẽ khá phức tạp. – juanchopanza

27

Cách đúng để làm điều đó là:

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

đâu T là kiểu của lớp bên trong vector. Ví dụ: nếu lớp là CActivity, chỉ cần viết CActivity thay vì T.

Loại phương pháp này sẽ hoạt động trên mọi STL (Không chỉ vectơ, tốt hơn một chút).

Nếu bạn vẫn muốn sử dụng các chỉ số, đường đi là:

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

không phải là 'std :: vector :: size_type' luôn' size_t'? Đó là loại tôi luôn sử dụng cho nó. –

+1

@VioletGiraffe Tôi khá chắc chắn bạn là đúng (chưa thực sự kiểm tra), nhưng nó là một thực hành tốt hơn để sử dụng std :: vector :: size_type. – DiGMi

+0

cảm ơn vì đã hiển thị cách chỉ mục "bị cấm". – Rosenthal

4

Với STL, các lập trình viên sử dụng iterators để duyệt qua các vùng chứa, vì trình vòng lặp là một khái niệm trừu tượng, được triển khai trong tất cả các vùng chứa tiêu chuẩn. Ví dụ: std::list hoàn toàn không có operator [].

67

Lý do tại sao bạn không thấy thực hành như vậy là khá chủ quan và không thể có câu trả lời rõ ràng, bởi vì tôi đã thấy nhiều mã sử dụng cách được đề cập của bạn thay vì mã kiểu dáng iterator.

Tiếp theo có thể có lý do của những người không xem xét vector.size() cách của vòng lặp:

  1. Là hoang tưởng về gọi size() mỗi lần trong vòng lặp điều kiện . Tuy nhiên một trong hai đó là một vấn đề không hoặc nó có thể được trivially cố định
  2. thích std::for_each() qua for vòng lặp tự
  3. Sau đó thay đổi các container từ std::vector một khác (ví dụ map, list) cũng sẽ đòi hỏi sự thay đổi của looping cơ chế, bởi vì không phải mỗi container hỗ trợ size() phong cách của Looping

C++ 11 cung cấp một cơ sở tốt để di chuyển qua các container. Điều đó được gọi là "phạm vi dựa trên vòng lặp" (hoặc "tăng cường cho vòng lặp" trong Java).

Với chút mã bạn có thể đi qua thông qua đầy đủ std::vector (bắt buộc):

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

Chỉ cần lưu ý một bất lợi nhỏ của _range dựa trên loop_: bạn không thể sử dụng nó với '#pragma omp song song cho'. – liborm

+1

Tôi thích phiên bản nhỏ gọn vì có ít mã để đọc hơn. Khi bạn thực hiện điều chỉnh về tinh thần, việc hiểu và lỗi dễ hiểu hơn nhiều sẽ dễ dàng hơn nhiều. Nó cũng làm cho nó rõ ràng hơn nhiều khi có một sự lặp lại không chuẩn xảy ra bởi vì có một đoạn mã lớn hơn nhiều. –

5

Có một vài lý do mạnh mẽ để sử dụng vòng lặp, một số trong đó được đề cập ở đây:

container Switching sau không làm mất hiệu lực mã của bạn.

tức là, nếu bạn đi từ một tiêu chuẩn :: vectơ thành std :: list hoặc std :: set, bạn không thể sử dụng chỉ mục bằng số để nhận giá trị chứa của bạn. Sử dụng một trình lặp vẫn hợp lệ.

Runtime bắt lặp lại không hợp lệ

Nếu bạn sửa đổi container của bạn ở giữa vòng lặp của bạn, lần sau khi bạn sử dụng iterator của bạn nó sẽ ném một iterator ngoại lệ không hợp lệ.

Các vấn đề liên quan