2011-12-21 40 views
16

Tôi đang lặp qua một vectơ có vòng lặp như for(int i = 0; i < vec.size(); i++). Trong vòng lặp này, tôi kiểm tra điều kiện trên phần tử tại chỉ mục vector đó và nếu một điều kiện nhất định là đúng, tôi muốn xóa phần tử đó.Làm thế nào để xóa một phần tử từ một vector trong khi lặp lại nó?

Làm cách nào để xóa phần tử vectơ trong khi lặp qua phần tử đó mà không bị lỗi?

+4

Bạn có thể sử dụng 'remove_if' và' erase'? http://en.wikipedia.org/wiki/Erase-remove_idiom – msandiford

+0

Xóa Xóa: http://stackoverflow.com/questions/4175896/safe-way-to-continuously-erase-from-a-stdvector –

Trả lời

31

Cách thành ngữ để xóa tất cả các phần tử khỏi vùng chứa STL đáp ứng một vị từ đã cho là sử dụng remove-erase idiom. Ý tưởng là để di chuyển vị (đó là các chức năng trong đó sản lượng đúng hay sai đối với một số phần tử) vào một chức năng nhất định, nói pred và sau đó:

static bool pred(const std::string &s) { 
    // ... 
} 

std::vector<std::string> v; 
v.erase(std::remove_if(v.begin(), v.end(), pred), v.end()); 

Nếu bạn nhấn mạnh vào việc sử dụng các chỉ số, bạn không nên tăng chỉ số cho mỗi yếu tố, nhưng chỉ dành cho những người mà không được loại bỏ:

std::vector<std::string>::size_type i = 0; 
while (i < v.size()) { 
    if (shouldBeRemoved(v[i])) { 
     v.erase(v.begin() + i); 
    } else { 
     ++i; 
    } 
} 

Tuy nhiên, đây không phải chỉ đang ngày càng ít thành ngữ (đọc: C lập trình viên ++ thực sự phải nhìn vào các mã trong khi 'xóa & loại bỏ 'thành ngữ ngay lập tức cho một số ý tưởng những gì đang xảy ra), nhưng cũng kém hiệu quả hơn vì vectơ lưu trữ thứ các phần tử eir trong một khối bộ nhớ liền kề, do đó việc xóa các vị trí khác với đầu vectơ cũng sẽ di chuyển tất cả các phần tử sau khi phân đoạn bị xóa sang vị trí mới của chúng.

+1

Làm thế nào để biết có bao nhiêu yếu tố bạn đã giết sau khi v.erase()? (Không đếm kích thước trước & sau?) – dynamic

+0

Vị từ không cần phải là hàm, nhưng có thể là hàm functor (đặc biệt nếu bạn cần lấy dữ liệu ngoài để quyết định có loại bỏ phần tử hay không). –

+1

@dynamic 'std :: vector' có các trình vòng lặp truy cập ngẫu nhiên, vì vậy bạn có thể biết có bao nhiêu phần tử được xóa đơn giản bằng cách trừ toán tử kết thúc mới được trả về bởi' std :: remove_if' từ trình lặp kết thúc hiện tại (ví dụ: 'v.end() '). –

1

Lặp lại vectơ về phía sau. Bằng cách đó, bạn không nuke khả năng để có được các yếu tố bạn chưa truy cập được nêu ra.

8

Sử dụng Erase-Remove Idiom, sử dụng remove_if với vị từ để chỉ định điều kiện của bạn.

+0

Chúng tôi có thể tiếp tục các liên kết thực tập cho SO. Ít có khả năng bị phá hoại ở đây. –

9

Nếu không thể sử dụng xóa/xóa (ví dụ bởi vì bạn không muốn sử dụng lambdas hoặc gửi thư một vị), sử dụng thành ngữ chuẩn để loại bỏ yếu tố container chuỗi:

for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */) 
{ 
    if (delete_condition) 
    { 
     it = v.erase(it); 
    } 
    else 
    { 
     ++it; 
    } 
} 

Nếu càng tốt, tuy nhiên, thích xóa/xóa:

#include <algorithm> 

v.erase(std::remove_if(v.begin(), v.end(), 
         [](T const & x) -> bool { /* decide */ }), 
     v.end()); 
+0

'cho (tự động nó .... Sẽ biên dịch này? – ThomasMcLeod

+0

Với trình biên dịch C++ 11, có. – Vortico

1

tôi nhận ra bạn đang yêu cầu cụ thể về loại bỏ từ vector, nhưng chỉ muốn chỉ ra rằng nó rất tốn kém để loại bỏ các mục từ một std :: vector vì tất cả các mặt hàng sau khi mục gỡ bỏ phải được sao chép sang vị trí mới. Nếu bạn định xóa các mục khỏi vùng chứa, bạn nên sử dụng danh sách std ::. Phương thức std :: list :: erase (item) thậm chí trả về iterator trỏ tới giá trị sau khi giá trị vừa xóa, vì vậy nó dễ sử dụng trong vòng lặp for hoặc while. Điều tốt đẹp quá với std :: list là các trình vòng lặp trỏ tới các mục không bị xóa vẫn còn giá trị trong suốt quá trình tồn tại của danh sách. Xem ví dụ: docs at cplusplus.com.

Điều đó nói rằng, nếu bạn không có lựa chọn, một mẹo có thể hoạt động chỉ đơn giản là tạo một véc tơ trống mới và thêm các mục vào vectơ từ vectơ đầu tiên, sau đó sử dụng std :: swap (oldVec, newVec), rất hiệu quả (không sao chép, chỉ thay đổi con trỏ nội bộ).

3
if(vector_name.empty() == false) { 
    for(int i = vector_name.size() - 1; i >= 0; i--) 
    { 
     if(condition) 
      vector_name.erase(vector_name.at(i)); 
    } 
} 

Điều này phù hợp với tôi. Và không cần phải suy nghĩ về các chỉ mục đã bị xóa.

+0

Nên xóa (vector_name.begin() + i); cũng kiểm tra xem véc-tơ có trống không là vô ích vì kích thước = = 0 sẽ dẫn đến không có vòng lặp nào trong vòng lặp cho phép truy cập ngẫu nhiên – log0

+0

@ log0, tại sao không? Và đồng ý với điều kiện sau: –

+0

'erase' lấy một vòng lặp làm đối số,' at' trả về giá trị vector ** ** (một chuỗi ví dụ) – log0

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