Lặp lại qua Vector C ++ bằng vòng lặp 'for'


139

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

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

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


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

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

4
Trong Java, tôi thích một vòng lặp for-every hoặc sử dụng các trình vòng lặp. Khá giống với C ++ mặc dù cú pháp hơi khác nhau.
Jesse Tốt


10
Hầu hết các câu trả lời ở đây không chính xác giả sử Q là: Cách tốt nhất / ngắn nhất để lặp lại là std::vectorgì? , Q thực sự đang được hỏi ở đây là: Có lý do nào khiến tôi không thấy điều này trong C ++ không? Có phải là thực hành xấu? aka Tại sao tôi luôn thấy mã trong C ++ sử dụng các trình vòng lặp trong khi lặp lại std::vector?
Alok Lưu

Câu trả lời:


92

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

Không. Đó không phải là một thực tiễn xấu, nhưng cách tiếp cận sau đây cho thấy mã của bạn linh hoạt nhất định .

Thông thường, tiền C ++ 11 mã để lặp qua các phần tử container sử dụng các trình vòng lặp, đại loại như:

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

Điều này là do nó làm cho mã linh hoạt hơn.

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

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


25
Bất cứ ai có thể vui lòng giải thích cho tôi tại sao trong trường hợp cụ thể / đoạn mã này, bạn khuyên các trình lặp qua lập chỉ mục? "Tính linh hoạt" 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, họ viết mã - đơn giản là thêm nhiều 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.
Hươu cao cổ Violet

8
@VioletGiraffe: Trong khi sử dụng các trình vòng lặp, thật khó có thể sai với một số trường hợp nhất định như phạm vi trống và mã dài hơn. Đó là vấn đề hoặc nhận thức và lựa chọn, vì vậy nó có thể được tranh luận không ngừng.
Alok Lưu

9
Tại sao bạn chỉ hiển thị cách khai báo iterator mà không sử dụng nó để thực hiện vòng lặp ...?
gạch dưới

115

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ì iteratormã kiểu.

Sau đây có thể là lý do của những người không xem xét vector.size()cách lặp:

  1. Bị hoang tưởng về việc gọi size()mỗi lần trong điều kiện vòng lặp. Tuy nhiên, đó không phải là vấn đề hoặc nó có thể được sửa chữa một cách tầm thường
  2. Thích std::for_each()hơn forvòng lặp chính nó
  3. Sau này thay đổi container từ std::vectormột cái khác (ví dụ map, list) cũng sẽ yêu cầu thay đổi cơ chế lặp, bởi vì không phải mọi size()kiểu hỗ trợ container của vòng lặp

C ++ 11 cung cấp một cơ sở tốt để di chuyển qua các container. Đó đượ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 ít mã bạn có thể duyệt qua toàn bộ (bắt buộc!) std::vector:

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

12
Chỉ cần lưu ý một nhược điểm nhỏ của phạm vi dựa trên vòng lặp : bạn không thể sử dụng nó với #pragma omp parallel for.
liborm

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

87

Cách lặp sạch nhất trong một vectơ là thông qua các 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ằng loại lặp và sử dụng các hàm thành viên thay vì các hàm toàn cầu bắt đầu và kết thúc.

Đây có lẽ là những gì bạn đã thấy. So với cách tiếp cận bạn đề cập, ưu điểm là bạn không phụ thuộc nhiều vào loại vector. Nếu bạn thay đổi vectorthành một lớp "kiểu 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ũng có thể làm một cái gì đó tương tự trong Java. Không có nhiều khác biệt về mặt khái niệm; C ++, tuy nhiên, sử dụng các khuôn mẫu để thực hiện điều này (so với các khái quát trong Java); do đó cách tiếp cận sẽ hoạt động cho tất cả các loại beginvà các endhàm được định nghĩa, ngay cả đối với các loại không phải là lớp như mảng tĩnh. Xem ở đây: Làm thế nào để phạm vi dựa trên phạm vi làm việc cho mảng đơn giản?


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

Vâng bạn đã đúng. Thực hiện beginend, tuy nhiên, là một lót.
JohnB

@JohnB nó là nhiều hơn một, vì nó cũng hoạt động cho các mảng kích thước cố định. automặt khác sẽ khá khó khăn
juanchopanza

Nếu bạn cần nó cho vector chỉ là một lớp lót.
JohnB

Tuy nhiên, ví dụ đầu tiên là sai lệch, vì nó không thể hoạt động trong C ++ 03, trong khi cụm từ của bạn cho thấy rằng nó có.
juanchopanza

35

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

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

Trong đó T là loại của lớp bên trong vectơ. Ví dụ: nếu lớp là CActivity, chỉ cần viết CActivity thay vì T.

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

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

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

không phải std::vector<T>::size_typeluôn luôn size_tsao? Đó là loại tôi luôn sử dụng cho nó.
Hươu cao cổ Violet

1
@VioletGiraffe Tôi khá chắc chắn rằng bạn đã đúng (chưa thực sự kiểm tra), nhưng nên sử dụng std :: vector <T> :: size_type.
DiGMi

8

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

Chuyển đổi container 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ừ std :: vector sang std :: list hoặc std :: set, bạn không thể sử dụng các chỉ số bằng số để lấy giá trị chứa trong đó. Sử dụng một iterator vẫn còn hiệu lực.

Thời gian chạy bắt lặp không hợp lệ

Nếu bạn sửa đổi vùng chứa của mình ở giữa vòng lặp, lần sau khi bạn sử dụng trình vòng lặp của mình, nó sẽ đưa ra một ngoại lệ trình vòng lặp không hợp lệ.


1
bạn có thể chỉ ra một số bài viết / bài đăng của chúng tôi giải thích các điểm trên với mã ví dụ không? sẽ rất tuyệt! hoặc nếu bạn có thể thêm một :)
Anu

5

Tôi đã ngạc nhiên không ai đề cập rằng việc lặp qua một mảng với một chỉ số nguyên giúp bạn dễ dàng viết mã bị lỗi bằng cách đăng ký một mảng với chỉ mục sai. Ví dụ, nếu bạn có các vòng lặp lồng nhau bằng cách sử dụng ijnhư các chỉ mục, bạn có thể đăng ký không chính xác một mảng jthay vì ivà do đó đưa ra một lỗi vào chương trình.

Ngược lại, các hình thức khác được liệt kê ở đây, cụ thể là forvòng lặp dựa trên phạm vi và các vòng lặp, ít bị lỗi hơn rất nhiều. Ngữ nghĩa của ngôn ngữ và cơ chế kiểm tra loại của trình biên dịch sẽ ngăn bạn vô tình truy cập vào một mảng sử dụng chỉ mục sai.


4

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


3

Sử dụng toán tử tự động thực sự giúp bạn dễ dàng sử dụng vì người ta không phải lo lắng về kiểu dữ liệu và kích thước của vectơ hoặc bất kỳ cấu trúc dữ liệu nào khác

Lặp lại vector bằng auto và for loop

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

Đầu ra:

1 2 3 4 5

Bạn cũng có thể sử dụng phương pháp này để lặp lại các bộ và danh sách. Sử dụng tự động tự động phát hiện loại dữ liệu được sử dụng trong mẫu và cho phép bạn sử dụng nó. Vì vậy, ngay cả khi chúng tôi đã có một vectorsố stringhoặc charcú pháp tương tự sẽ chỉ làm việc tốt


1

Cách lặp chính xác của vòng lặp và in các giá trị của nó như sau:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}

1

Đây là một cách đơn giản hơn để lặp và in các giá trị trong vector.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 

0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.