在 C++ STL 中,使用迭代器删除容器元素时需要注意迭代器的失效问题。不同容器的删除操作对迭代器的影响不同,以下是常见容器的删除方法及注意事项:
---
### **1. 序列容器(`vector`、`deque`、`list`)**
#### **(1) `std::vector` 和 `std::deque`**
- **删除单个元素**:
- 使用 `erase(iterator)`,返回指向被删除元素下一个位置的迭代器。
- **注意**:删除后原迭代器失效,必须使用返回的迭代器继续操作。
```cpp
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2; // 指向 3
it = vec.erase(it); // 删除 3,it 指向 4
```
- **删除范围**:
- `erase(first, last)` 删除 `[first, last)` 范围内的元素,返回指向 `last` 的迭代器。
```cpp
auto first = vec.begin() + 1; // 指向 2
auto last = vec.begin() + 4; // 指向 5
first = vec.erase(first, last); // 删除 2,3,4,first 指向 5
```
#### **(2) `std::list`**
- **删除单个元素**:
- `erase(iterator)` 返回下一个有效迭代器,原迭代器失效。
```cpp
std::list<int> lst = {1, 2, 3, 4, 5};
auto it = lst.begin(); // 指向 1
std::advance(it, 2); // 指向 3
it = lst.erase(it); // 删除 3,it 指向 4
```
- **删除范围**:
- 同 `vector`,`erase(first, last)` 返回 `last`。
```cpp
auto first = lst.begin(); // 指向 1
auto last = lst.begin();
std::advance(last, 3); // 指向 4
first = lst.erase(first, last); // 删除 1,2,3,first 指向 4
```
---
### **2. 关联容器(`set`、`map`、`multiset`、`multimap`)**
- **删除单个元素**:
- `erase(iterator)` 不返回迭代器(C++11 前),或返回 `void`(C++11 后)。
- **安全做法**:先递增迭代器再删除。
```cpp
std::set<int> s = {1, 2, 3, 4, 5};
auto it = s.find(3);
if (it != s.end()) {
s.erase(it++); // 先复制 it 并递增,再删除原 it
}
```
- **通过键删除**:
- `erase(key)` 直接删除键对应的元素,无需迭代器。
```cpp
std::map<std::string, int> m = {{"Alice", 25}, {"Bob", 30}};
m.erase("Alice"); // 删除键为 "Alice" 的元素
```
- **删除范围**:
- `erase(first, last)` 删除 `[first, last)` 范围内的元素。
```cpp
auto first = s.begin();
auto last = s.begin();
std::advance(last, 2);
s.erase(first, last); // 删除最小的 2 个元素
```
---
### **3. 无序关联容器(`unordered_set`、`unordered_map`)**
- 行为与关联容器类似,但删除元素可能导致哈希表重组,**所有迭代器失效**(除非是删除的当前元素)。
- **安全做法**:
```cpp
std::unordered_map<std::string, int> um = {{"Alice", 25}, {"Bob", 30}};
auto it = um.find("Alice");
if (it != um.end()) {
um.erase(it); // 仅 it 失效,其他迭代器仍有效
}
```
---
### **4. 通用删除模式**
#### **(1) 循环中删除元素**
- **错误示例**(`vector` 中直接删除导致迭代器失效):
```cpp
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it % 2 == 0) {
vec.erase(it); // 错误!it 失效后继续递增会导致未定义行为
}
}
```
- **正确做法**:
```cpp
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it % 2 == 0) {
it = vec.erase(it); // 使用返回值更新迭代器
} else {
++it;
}
}
```
#### **(2) 使用 `erase_if`(C++20)**
- 直接通过谓词删除元素,避免手动管理迭代器:
```cpp
std::vector<int> vec = {1, 2, 3, 4, 5};
std::erase_if(vec, [](int x) { return x % 2 == 0; }); // 删除所有偶数
```
---
### **5. 关键注意事项**
1. **迭代器失效**:
- `vector`/`deque`:删除后所有指向被删除元素之后的迭代器失效。
- `list`:仅被删除的迭代器失效,其他不受影响。
- 关联/无序容器:通常仅被删除的迭代器失效(但无序容器可能因哈希表重组影响其他迭代器)。
2. **性能考虑**:
- `vector`/`deque` 的 `erase` 是 O(n) 操作(需要移动元素)。
- `list` 的 `erase` 是 O(1)。
- 关联容器的 `erase` 是 O(1)(平均)。
3. **C++11 后的改进**:
- `map`/`set` 的 `erase` 返回下一个有效迭代器(C++11 起)。
---
### **6. 代码示例汇总**
#### **(1) `vector` 中删除偶数**
```cpp
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5, 6};
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it % 2 == 0) {
it = vec.erase(it);
} else {
++it;
}
}
for (int x : vec) std::cout << x << " "; // 输出:1 3 5
}
```
#### **(2) `map` 中删除特定键**
```cpp
#include <map>
#include <iostream>
int main() {
std::map<std::string, int> m = {{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};
auto it = m.find("Bob");
if (it != m.end()) {
m.erase(it);
}
for (const auto& [name, age] : m) {
std::cout << name << ": " << age << std::endl; // 输出 Alice 和 Charlie
}
}
```
#### **(3) C++20 `erase_if` 简化操作**
```cpp
#include <unordered_set>
#include <iostream>
int main() {
std::unordered_set<int> s = {1, 2, 3, 4, 5};
std::erase_if(s, [](int x) { return x > 3; }); // 删除大于 3 的元素
for (int x : s) std::cout << x << " "; // 输出顺序可能为 1 2 3
}
```
---