2009-06-19 32 views
14

Tôi có một trình vòng lặp danh sách đi qua một danh sách và loại bỏ tất cả các số chẵn. Tôi có thể sử dụng trình vòng lặp danh sách để in ra các con số tốt nhưng tôi không thể sử dụng remove() của danh sách và chuyển vào trình lặp lặp lại không quan tâm.Danh sách Iterator Remove()

Tôi nhận thấy rằng khi lệnh remove() có hiệu lực, * itr bị hỏng? Ai đó có thể giải thích điều này?

#include <iostream> 
#include <list> 

#define MAX 100 

using namespace std; 

int main() 
{ 
    list<int> listA; 
    list<int>::iterator itr; 

    //create list of 0 to 100 
    for(int i=0; i<=MAX; i++) 
     listA.push_back(i); 

    //remove even numbers 
    for(itr = listA.begin(); itr != listA.end(); ++itr) 
    { 
     if (*itr % 2 == 0) 
     { 
      cout << *itr << endl; 
      listA.remove(*itr); //comment this line out and it will print properly 
     } 
    } 
} 

Trả lời

41

Có một số vấn đề với mã của bạn ở trên. Thứ nhất, remove sẽ làm mất hiệu lực bất kỳ trình lặp nào đang trỏ đến các phần tử đã xóa. Sau đó, bạn tiếp tục sử dụng trình lặp. Rất khó để biết (các) thành phần nào remove sẽ xóa trong trường hợp chung (mặc dù không phải của bạn) vì nó có thể xóa nhiều hơn một phần tử.

Thứ hai, có thể bạn đang sử dụng phương pháp sai. Xóa sẽ lặp qua tất cả các mục trong danh sách tìm kiếm bất kỳ yếu tố phù hợp nào - điều này sẽ không hiệu quả trong trường hợp của bạn vì chỉ có một mục. Có vẻ như bạn nên sử dụng phương thức erase, có thể bạn chỉ muốn xóa mục ở vị trí của trình lặp. Những điều tốt về erase là nó trả về một iterator mà là ở vị trí hợp lệ tiếp theo. Cách thành ngữ để sử dụng nó là một cái gì đó như thế này:

//remove even numbers 
for(itr = listA.begin(); itr != listA.end();) 
{ 
    if (*itr % 2 == 0) 
    { 
     cout << *itr << endl; 
     itr=listA.erase(itr); 
    } 
    else 
     ++itr; 
} 

Cuối cùng, bạn cũng có thể sử dụng remove_if để làm tương tự như bạn đang làm:

bool even(int i) { return i % 2 == 0; } 

listA.remove_if(even); 
2

Bạn không thể sử dụng trình lặp sau khi bạn xóa phần tử được nhắc đến.

Tuy nhiên, danh sách các trình vòng lặp tham chiếu đến các mục không bị xóa sau remove() sẽ vẫn hợp lệ.

-1

Vì trình vòng lặp phụ thuộc vào độ dài của cấu trúc còn lại, hầu hết các trình vòng lặp không cho phép thay đổi danh sách trong khi trình vòng lặp đang được sử dụng. Nếu bạn muốn đi qua và thay đổi danh sách, bạn sẽ phải sử dụng một vòng lặp độc lập với trình lặp.

+4

Tôi phải chỉ ra rằng các trình lặp STL không có bất kỳ sự phụ thuộc nào về độ dài của cấu trúc. Iterator khá thường xuyên cho phép bạn xóa các phần tử nhất định, ví dụ vòng lặp vector cho phép bạn xóa các mục qua trình vòng lặp và danh sách các trình vòng lặp cho phép bạn xóa bất kỳ thứ gì không được chỉ định bởi trình vòng lặp –

0

Chúng ta có thể sử dụng một cái gì đó như thế này:

container.erase(it++); 

tôi đã cố gắng vào ví dụ này:

int main(){ 

list<int>*a=new list<int>; 
a->push_back(1); 
a->push_back(2); 
a->push_back(3); 

list<int>::iterator I; 

I=a->begin(); ++I; 

a->erase(I++); 
cout<<*I<<endl; 
} 

và nó hiển thị 3, như tôi muốn. Bây giờ tôi không biết nếu điều này là hợp lệ hoặc một trong những người mà "đôi khi làm việc và đôi khi không".

EDIT: Có thể do trình biên dịch. Ví dụ, trình biên dịch tôi đang sử dụng (GNU gcc-g ++) là danh sách xử lý (std: :) như hình tròn, tức là nếu tôi tăng iterator sau khi list-> end() nó đặt bạn vào đầu.

+0

Điều này sẽ hoạt động cho các trình lặp vòng lặp 'std :: list' vì các trình vòng lặp đó chỉ bị vô hiệu khi phần tử mà chúng trỏ đến sẽ bị xóa. Nhưng điều này sẽ không hoạt động đối với các trình lặp 'std :: vector' bởi vì các trình vòng lặp đó bị vô hiệu khi phần tử mà chúng trỏ đến hoặc bất kỳ phần tử nào trước phần tử mà chúng đang trỏ tới sẽ bị xóa. – David

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