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

server/2025/3/12 12:20:26/

文章目录

  • 迭代器失效
      • **常见的迭代器失效场景**
        • 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 允许在多个文件中定义相同的函数,而不会导致链接错误,并尝试优化函数调用(通过内联化)。

http://www.ppmy.cn/server/165174.html

相关文章

【黄啊码】常用AIGC办公工具大全

工具名称&#xff1a;办公小浣熊 功能简介&#xff1a;办公小浣熊是一款智能办公助手&#xff0c;专注于提升办公效率。它支持文档生成、数据分析、PPT制作等多功能&#xff0c;通过自然语言交互&#xff0c;帮助用户快速完成复杂任务。无论是撰写报告、整理数据&#xff0c;还…

寒假(一)

请使用消息队列实现2个终端之间互相聊天 终端一 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h&g…

LVGL+FreeRTOS实战项目:智能健康助手(lcd篇)

1.8寸彩色TFT显示屏简介 接线图 我们选用的是分辨率为128*160的彩色显示屏&#xff0c;采用的SPI接口&#xff0c;通过我们STM32的SPI外设&#xff0c;来和我们的屏幕进行通信&#xff0c;以显示我们需要显示的图片。 软件部分 #include "lcd_driver.h"//液晶IO初始…

DNS缓存详解(DNS Cache Detailed Explanation)

DNS缓存详解 清空DNS缓存可以让网页访问更快捷。本文将从什么是DNS缓存、为什么清空DNS缓存、如何清空DNS缓存、清空DNS缓存存在的问题四个方面详细阐述DNS缓存清空的相关知识。 一、什么是DNS缓存 1、DNS缓存的定义&#xff1a; DNS缓存是域名系统服务在遇到DNS查询时自动…

计算机网络中常见高危端口有哪些?如何封禁高危端口?

保障网络安全&#xff0c;从封禁高危端口开始&#xff01; 在计算机网络中&#xff0c;端口是设备与外界通信的“大门”&#xff0c;但某些端口因常被黑客利用而成为高危入口。封禁这些端口是防御网络攻击的关键一步。本文将详解 10个常见高危端口&#xff0c;并提供多平台封禁…

JVM监控和管理工具

基础故障处理工具 jps jps(JVM Process Status Tool)&#xff1a;Java虚拟机进程状态工具 功能 1&#xff1a;列出正在运行的虚拟机进程 2&#xff1a;显示虚拟机执行主类(main()方法所在的类) 3&#xff1a;显示进程ID(PID&#xff0c;Process Identifier) 命令格式 jps […

2024年终总结来了

忘记发CSDN的年度总结了&#xff0c;今天补上吧 说实话&#xff0c;今年过得不是特别好&#xff0c;感觉遇到了瓶颈&#xff0c;人生变得迷茫起来。不知道大家有没有同样的感受 刚毕业的时候人生充满了憧憬&#xff0c;慢慢的随着年龄变大后&#xff0c;就会觉得一事无成&…

RGB565转BITMAP[C#---2]

这是楼主在开发C#上位机的时候遇到的另一个问题&#xff0c;怎么把RGB565转为BITMAP&#xff0c;在CSDN上搜索&#xff0c;要么是安卓平台的&#xff0c;要么是2011年的古早代码&#xff08;还没排版&#xff09;&#xff0c;还是靠自己和DEEPSEEK的智慧解决了(●’◡’●) 当然…