欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 培训 > 【CPP】迭代器失效问题 static和inline

【CPP】迭代器失效问题 static和inline

2025/5/8 14:00:26 来源:https://blog.csdn.net/LHRan_ran_/article/details/145443173  浏览:    关键词:【CPP】迭代器失效问题 static和inline

文章目录

  • 迭代器失效
      • **常见的迭代器失效场景**
        • 1. **`std::vector`**
        • 2. **`std::deque`**
        • 3. **`std::list`**
        • 4. **`std::map` / `std::set`**
        • 5. **`std::unordered_map` / `std::unordered_set`**
      • **总结:迭代器失效场景**
      • **如何避免迭代器失效?**
  • static 和 inline
      • 1. `static`
      • 2. `inline`
      • `static` vs `inline`
      • 总结

迭代器失效

在C++中,迭代器失效指的是在容器进行某些操作后,原先获取的迭代器不再指向有效的元素或位置。迭代器失效可能会导致未定义行为(如访问无效内存、程序崩溃等)。不同的容器在特定操作下会有不同的迭代器失效行为。


常见的迭代器失效场景

1. std::vector
  • 失效场景

    1. 插入元素
      • 如果插入导致vector重新分配内存(即容量不足),所有迭代器都会失效。
      • 如果没有重新分配内存,插入点之后的迭代器会失效。
    2. 删除元素
      • 删除点及其之后的迭代器会失效。
    3. resizereserve
      • 如果resizereserve导致内存重新分配,所有迭代器都会失效。
    4. clear
      • 清空容器后,所有迭代器都会失效。
  • 示例

    std::vector<int> vec = {1, 2, 3, 4};
    auto it = vec.begin() + 2;  // 指向3
    vec.push_back(5);           // 可能导致内存重新分配,it失效
    std::cout << *it;           // 未定义行为
    

2. std::deque
  • 失效场景

    1. 插入元素
      • 在两端(push_frontpush_back)插入元素不会使任何迭代器失效。
      • 在中间插入元素会使所有迭代器失效。
    2. 删除元素
      • 在两端(pop_frontpop_back)删除元素不会使任何迭代器失效。
      • 在中间删除元素会使所有迭代器失效。
    3. clear
      • 清空容器后,所有迭代器都会失效。
  • 示例

    std::deque<int> dq = {1, 2, 3, 4};
    auto it = dq.begin() + 2;  // 指向3
    dq.push_back(5);           // it仍然有效
    dq.insert(dq.begin() + 1, 0);  // 所有迭代器失效
    std::cout << *it;          // 未定义行为
    

3. std::list
  • 失效场景

    1. 插入元素
      • 插入元素不会使任何迭代器失效。
    2. 删除元素
      • 只有被删除元素的迭代器会失效,其他迭代器仍然有效。
    3. clear
      • 清空容器后,所有迭代器都会失效。
  • 示例

    std::list<int> lst = {1, 2, 3, 4};
    auto it = ++lst.begin();  // 指向2
    lst.erase(it);            // it失效,但其他迭代器仍然有效
    std::cout << *it;         // 未定义行为
    

4. std::map / std::set
  • 失效场景

    1. 插入元素
      • 插入元素不会使任何迭代器失效。
    2. 删除元素
      • 只有被删除元素的迭代器会失效,其他迭代器仍然有效。
    3. clear
      • 清空容器后,所有迭代器都会失效。
  • 示例

    std::map<int, int> mp = {{1, 10}, {2, 20}, {3, 30}};
    auto it = mp.find(2);  // 指向{2, 20}
    mp.erase(it);          // it失效,但其他迭代器仍然有效
    std::cout << it->second;  // 未定义行为
    

5. std::unordered_map / std::unordered_set
  • 失效场景

    1. 插入元素
      • 如果插入导致重新哈希(rehash),所有迭代器都会失效。
      • 否则,插入不会使迭代器失效。
    2. 删除元素
      • 只有被删除元素的迭代器会失效,其他迭代器仍然有效。
    3. clear
      • 清空容器后,所有迭代器都会失效。
    4. rehash
      • 重新哈希后,所有迭代器都会失效。
  • 示例

    std::unordered_map<int, int> ump = {{1, 10}, {2, 20}, {3, 30}};
    auto it = ump.find(2);  // 指向{2, 20}
    ump.rehash(100);        // 所有迭代器失效
    std::cout << it->second;  // 未定义行为
    

总结:迭代器失效场景

容器类型插入元素删除元素其他操作
std::vector可能全部失效(重新分配内存时)删除点及其后失效resizereserve可能导致失效
std::deque中间插入使全部失效,两端插入不失效中间删除使全部失效,两端删除不失效clear使全部失效
std::list不失效只有被删除元素失效clear使全部失效
std::map / std::set不失效只有被删除元素失效clear使全部失效
std::unordered_map / std::unordered_set可能全部失效(重新哈希时)只有被删除元素失效rehashclear使全部失效

如何避免迭代器失效?

  1. 谨慎操作:在插入或删除元素后,尽量避免使用之前的迭代器。
  2. 更新迭代器:在插入或删除操作后,重新获取迭代器。
  3. 使用返回值:某些操作(如inserterase)会返回新的有效迭代器,可以利用这些返回值。
    auto it = vec.erase(it);  // erase返回下一个有效迭代器
    

理解迭代器失效的场景和规则,可以帮助你编写更安全、更健壮的C++代码。

static 和 inline

在C++中,staticinline 是两种常用于避免函数定义在头文件中多次引用导致链接错误(通常是“多重定义”错误)的方式。它们各自有不同的作用和使用场景:

1. static

  • 当函数或变量声明为 static 时,它的作用域被限制在当前的源文件中(即文件内可见)。因此,即使该头文件被多个源文件引用,每个源文件内部会有自己独立的函数副本,避免了多个源文件之间的符号冲突。
  • static 关键字保证了函数在其他源文件中不可见,不会影响到其他源文件中的相同函数名。

使用示例

// 在头文件中定义 static 函数
static void myFunction() {// 函数实现
}
  • 每个包含该头文件的源文件都会有 myFunction 的独立副本。即使它被多个源文件引用,也不会导致链接错误。

2. inline

  • inline 关键字用于告诉编译器尝试将函数的调用替换为函数体的内容,从而消除函数调用的开销。对于头文件中的函数,使用 inline 可以避免多个定义导致的链接错误,因为编译器会为每个引用的源文件提供该函数的定义,而不会在链接时产生多重定义。
  • 尽管 inline 表示希望进行内联,但更重要的是它能解决多文件中函数多次定义的问题。

使用示例

// 在头文件中定义 inline 函数
inline void myFunction() {// 函数实现
}
  • 使用 inline 后,每个源文件中对该函数的引用都会引入该函数的实现,从而避免了多个源文件之间的链接冲突。

static vs inline

  • static 解决的问题是作用域问题:每个源文件都会有自己的副本,不会发生不同源文件间的符号冲突。
  • inline 主要解决的是性能问题(通过内联化)以及链接问题(避免多重定义)。

常见的用法

  • 如果函数的实现非常简单且短小,通常会同时使用 inlinestatic,这样不仅避免了链接错误,也可能提高效率:
// 在头文件中使用 inline 和 static
static inline void myFunction() {// 函数实现
}

这样,每个源文件在引用头文件时都会有 myFunction 的独立副本,并且编译器可能会将其内联,从而避免了链接错误并提高性能。

总结

  • static 保证函数仅在当前源文件内有效,避免了多文件链接冲突。
  • inline 允许在多个文件中定义相同的函数,而不会导致链接错误,并尝试优化函数调用(通过内联化)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词