2010-03-30 24 views
5

Giả sử tôi có vector<int> myvec và tôi muốn lặp qua tất cả các phần tử ngược lại. Tôi có thể nghĩ ra một số cách để thực hiện việc này:Lặp lại lùi

for (vector<int>::iterator it = myvec.end() - 1; it >= myvec.begin(); --it) 
{ 
    // do stuff here 
} 

for (vector<int>::reverse_iterator rit = myvec.rbegin(); rit != myvec.rend(); ++rit) 
{ 
    // do stuff here 
} 

for (int i = myvec.size() - 1; i >= 0; --i) 
{ 
    // do stuff here 
} 

Vì vậy, câu hỏi của tôi là khi nào tôi nên sử dụng? Có sự khác biệt nào không? Tôi biết rằng điều đầu tiên là nguy hiểm bởi vì nếu tôi vượt qua trong một véc tơ trống, thì myvec.end() - 1 là không xác định, nhưng có bất kỳ mối nguy hiểm hoặc không hiệu quả nào khác với điều này không?

Trả lời

11

Phiên bản reverse_iterator thể hiện ý định và hoạt động trên tất cả các vùng chứa, bất kể nội dung của chúng.

Đầu tiên có sự thiếu hụt mà bạn mô tả. Nó cũng sử dụng >=, sẽ không hoạt động đối với các trình vòng lặp truy cập không ngẫu nhiên.

Thứ ba có vấn đề là iint. Nó sẽ không thể giữ nhiều như size() có khả năng có thể trở lại. Làm cho nó hoạt động không dấu (vector<int>::size_type), nhưng sau đó chúng tôi có cùng một vấn đề như một giải pháp. (0U - 1 ->Funky terminating checks ->:|)

+1

"Nó cũng sử dụng> =, sẽ không hoạt động đối với trình lặp không truy cập ngẫu nhiên". OK, không phải 'end() -1' ;-) –

+0

+1. Khi một thư viện cung cấp chức năng được thiết kế rõ ràng để thực hiện một tác vụ, bạn thường nên sử dụng nó khi cố gắng thực hiện cùng một nhiệm vụ. – Brian

+0

@Steve: Heh, đúng vậy. Tôi đã suy nghĩ 'end() -' hoạt động cho các trình vòng lặp hai chiều. – GManNickG

1

Luôn sử dụng thứ hai. Việc đầu tiên bạn loại trừ chính mình, và thứ ba không làm việc cho danh sách và như vậy.

3

Cá nhân, tôi sẽ đi với phiên bản thứ hai.

Khi bạn cho biết người đầu tiên yêu cầu bạn quấn vòng lặp trong một if (!myvec.empty()) để tránh hành vi không xác định.

Đối với người cuối cùng, có thể bạn đang sử dụng vector<int>::size_type hoặc size_t, trong trường hợp này >= 0 sai, bạn cần phải thực hiện != (size_t)-1 hoặc tương tự.

Vì vậy, phiên bản reverse_iterator sẽ sạch hơn.

2

Đối với phiên bản đầu tiên, bạn cũng chắc chắn sẽ kết thúc việc giảm trình phát lặp begin() ở cuối vòng lặp (hành vi không xác định).

reverse_iterator được thực hiện cho việc này.

Thứ ba có thể làm việc một chút tốt hơn nếu bạn sử dụng các hình thức phần nào gây tranh cãi hơn:

for (size_t i = vec.size(); i --> 0;) 

Đây có thể là một thành ngữ nếu mọi người sẽ ngừng kháng cự. Nó sử dụng một loại truy cập phù hợp (unsigned), và chứa các ghi nhớ để dễ dàng ghi nhớ và nhận ra.

+1

Xin chào! – GManNickG

+0

+1 cho cảnh báo chống lại _decrementing the begin() iterator_ –

7

Thường thì không có điều nào ở trên. Thay vào đó, bạn thường nên ngồi lại và thư giãn trong vài giây, tìm ra thuật toán nào bạn muốn áp dụng và quên tự viết một vòng lặp. Rất có thể bạn sẽ sử dụng reverse_iterator với nó, nhưng tùy thuộc vào những gì bạn đang cố gắng hoàn thành mà không phải lúc nào cũng đúng như vậy (ví dụ: xem std::copy_backwards).

1

Có tùy chọn thứ tư (không nhất thiết phải là một tùy chọn tốt, nhưng nó tồn tại).Bạn có thể sử dụng lặp hai chiều/truy cập ngẫu nhiên theo kiểu bắt chước cách lặp ngược lại được thực hiện để tránh những vấn đề với myvec.end()-1 trên một iterator trống:

for (vector<int>::iterator it = myvec.end(); it != myvec.begin(); --it) 
{ 
    // convert the loop controlling iterator to something that points 
    // to the item we're really referring to 

    vector<int>::iterator true_it = it; 
    --true_it; 


    // do stuff here 
    // but always dereference `true_it` instead of `it` 
    // this is essentially similar to the way a reverse_iterator 
    // generally works 

    int& x = *true_it; 
} 

hoặc thậm chí:

for (vector<int>::iterator it = myvec.end(); it != myvec.begin();) 
{ 
    // decrement `it` before the loop executes rather than after 
    // it's a bit non-idiomatic, but works 
    --it; 

    int& x = *it; 

    // do stuff... 
} 

Như tôi đã nói, điều này không nhất thiết là một lựa chọn tốt (tôi nghĩ rằng Jerry Coffin's answer là cách tiếp cận bạn nên xem trước), nhưng tôi nghĩ nó quan tâm vì nó cho thấy các trình vòng lặp ngược hoạt động như thế nào - và tránh được việc chuyển đổi một reverse_iterator thành một trình lặp những lúc bạn có thể muốn sử dụng trình lặp với thứ gì đó sẽ không chấp nhận một số reverse_iterator (chuyển đổi reverse_iterator s thành iterator s luôn làm tôi đau đầu, vì vậy tôi sẽ thường xuyên tránh reverse_iterators để tránh bị đau đầu). Ví dụ: nếu bạn muốn gọi insert() cho vị trí mà trình lặp ngược lại đề cập đến:

// if `it` is a reverse iterator, a call to insert might have to look something like: 

myvec.insert(--(it.base()), 42); // assume that you know the current vector capacity 
            // will avoid a reallocation, so the loop's 
            // iterators won't be invalidated 

// if `it` is a normal iterator... 

myvec.insert(it, 42);