2008-10-13 43 views
6

Tôi có một dự án cũ được xây dựng bằng cách sử dụng visual studio 2003 và tôi biên dịch lại nó với vs2005 gần đây. Tuy nhiên, trong thời gian chạy, tôi nhận được lỗi sau:danh sách vòng lặp không gia tăng

danh sách iterator không incrementable

tôi bắt nguồn từ chương trình để chức năng này:

void InputQueue::update() 
{ 
    list<PCB>::iterator iter; 
    list<PCB>::iterator iterTemp; 
    for(iter = begin(); iter != end(); iter++) 
    { 
     if(iter->arrivalTime == 0) 
     {   
      ReadyQueue::getInstance()->add(*iter); 
      iterTemp = iter; 
      iter++; 
      erase(iterTemp); 
     } 
    } 
} 

Tôi không phải là một ++ chuyên gia C và đây là như xa khi trình gỡ rối VS nhận được tôi. Ai đó có thể giải thích cho tôi vấn đề là gì?

Cảm ơn

Trả lời

9

Chú ý rằng nếu iter->arrivalTime == 0, sau đó danh sách iterator được tăng lên hai lần: một lần trước khi loại bỏ yếu tố, và một lần nữa vào cuối vòng lặp.

Nếu mục cần xóa là mục cuối cùng trong danh sách, điều này rõ ràng sẽ không hoạt động chính xác. Tôi dám nói rằng nó không bao giờ làm việc một cách chính xác ngay cả trong VS2003, nhưng VS2005 cảnh báo bạn về nó tốt hơn. :-)

Hãy nhớ rằng, đó là hành vi không xác định để lặp qua end(). Tuyệt đối bất cứ điều gì có thể xảy ra, chẳng hạn như sự cố chương trình, hoặc (trong trường hợp này) là một thông báo lỗi.

1

tôi chỉ cần đi để bõ mẫu âm chót một vài dòng mã của bạn để hiển thị, nơi mà vấn đề nằm:

for(iter = begin(); iter != end(); iter++) // *** 
    { 
     if(iter->arrivalTime == 0) 
     {      

       iter++; // *** 

     } 
    } 

Trên hai dòng đánh dấu ***, bạn đang tăng iterator. Vấn đề là ở phần thứ hai của hai dòng, bạn không kiểm tra để thấy rằng bạn chưa đi đến cuối thùng chứa. Có hiệu quả, nếu bạn nhận được vào vòng lặp bên trong, bạn đang tăng gấp đôi, nhưng chỉ kiểm tra nếu bạn có thể tăng một lần.

Một giải pháp là kiểm tra xem bạn có đang ở số end() trước khi thực hiện lần tăng thứ hai không, nhưng có vẻ như tôi đang cố gắng thực hiện thao tác tương tự như tôi đang ở trong my question a while ago để làm với các mục lọc từ vùng chứa (bản đồ trong trường hợp đó, nhưng cũng áp dụng cho hầu hết các container STL).

0

Tôi tin rằng Chris nói đúng. Tuy nhiên, một vấn đề khác có thể xuất phát từ thực tế là bạn gán cho trình vòng lặp. - Các trình vòng lặp danh sách được đảm bảo có thể chuyển nhượng được không? Nếu không nhìn vào tiêu chuẩn, tôi không nghĩ vậy bởi vì khả năng chuyển nhượng không được đề cập trong tài liệu SGI của các trình vòng lặp.

+0

Có vẻ như từ http://www.sgi.com/tech/stl/Iterators.html các trình vòng lặp chuyển tiếp có thể gán được. std :: các trình vòng lặp của danh sách là các trình vòng lặp hai chiều (http://www.sgi.com/tech/stl/List.html, http://www.sgi.com/tech/stl/ReversibleContainer.html), và do đó cũng chuyển tiếp về phía trước. :-) –

+0

Hmm, đây có phải là ý nghĩa của từ “multi-pass” không? Bởi vì nếu không thì không có gì được nói về khả năng gán * của trình lặp * (trái ngược với giá trị của nó!). –

14

tôi sẽ viết lại vòng lặp của bạn là như sau:

while (iter != end()) 
{ 
    if (iter->arrivalTime == 0) 
    { 
    ReadyQueue::getInstance()->add(*iter); 
    iter = erase(iter); 
    } 
    else 
    { 
    ++iter; 
    } 
} 

Bây giờ bạn đang lặp một cách chính xác thông qua các danh sách kiểm tra tất cả các chỉ số.

+0

Bạn không gia tăng trình lặp trong phần đầu tiên của nếu –

+1

I am - iter = erase (iter). Hàm xóa trả về trình lặp mới sau lần lặp mới bị xóa. –

+0

Oh đúng không bao giờ nhớ tôi. Điều này không làm việc với một số loại container, hãy nhớ bạn –

0

Đây chỉ là một phần phụ, nhưng là phần quan trọng.

Tôi đoán bạn được kế thừa từ một số std::ist<PCB>. Tôi phải nói: kế thừa để tái sử dụng chức năng thường không bật ra tốt cho tôi. Nhưng kể từ khi bạn cũng 'kế thừa' dự án, không có gì nhiều để làm về nó ...

+0

Thực hiện thừa kế, trong khi không lý tưởng, có thể được tha thứ nếu nó chỉ là thừa kế riêng. :-) –

0

Nếu bạn nhận được "danh sách vòng lặp không tương thích" có thể là vì bên trong "ReadyQueue :: getInstance() -> add (* lặp lại); " bạn đang thay đổi một cái gì đó trong * lặp đó là làm cho các thuật toán băm trả về một giá trị khác nhau cho xóa hơn nó đã làm trong quá trình chèn.

0

Tôi có thể đề xuất một thuật toán đơn giản hơn không?

Chức năng miễn phí std::remove_if có thể được sử dụng để phân vùng danh sách của bạn thành 2, các phần tử khớp hoặc không khớp với biến vị ngữ (tức là thời gian đến == 0). Nó trả về trình vòng lặp phân tách các dãy. Sau đó, bạn có thể gọi ReadyQueue::getInstance()->add(subrange_begin, subrange_end)(bạn có tình trạng quá tải đó, phải không?) và xóa tùy chọn này sau đó.

Chỉ cần một trường hợp bạn có thể sử dụng thuật toán STL thay vì viết vòng của riêng bạn.

1

Nguyên nhân gốc là "list.erase()" sẽ thay đổi trình lặp. Ghi chính xác cho vòng lặp "for":

for (list<CMessage*>::iterator it=que.begin(); it!=que.end(); ++it) 
    { 
    if(m_type == (*it)->m_type) 
    { 
     delete *it; 
     it=que.erase(it); //"list.erase()" will change the iterator!!! 
     if(it==que.end()) break; //Check again!!! 
     //still has side effect here. --it? 
    } 
    } 

Nhưng nó vẫn có tác dụng phụ, vì vậy giải pháp trong khi Mark sẽ là tốt nhất.

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