当前位置: 首页 > news >正文

SLAM 岗位 C++ 面试速查手册

本文覆盖 SLAM 算法岗面试中最常见的 17 道 C++ 基础题,每题给出标准答案 + 面试官常见追问的应对策略。


面试速查(30秒版)

知识点一句话核心
面向对象 vs 面向过程OOP通过封装/继承/多态管理复杂度,POP以函数为中心更高效
指针 vs 引用引用是别名不可变不可空,指针是地址可重指可为空
多态运行时多态靠虚函数表(vtable)实现动态绑定
虚/纯虚函数虚函数有默认实现可覆盖,纯虚函数无实现必须覆盖
析构函数为虚基类指针delete派生对象时确保析构完整
4种类型转换static快但不安全,dynamic慢但安全(RTTI),const去常量,reinterpret底层位模式
智能指针unique独占,shared共享(引用计数),weak打破循环引用
mutex互斥,shared_mutex读写,condition_variable条件等待
堆 vs 栈栈自动管理快速连续,堆手动管理灵活离散
STL容器vector连续随机访问,list离散快速插删,map有序log(n),unordered_map哈希O(1)
vector<bool>是位压缩特化,非真正容器,不支持指针/引用
resize vs reserveresize改变size(可访问),reserve改变capacity(预分配)
左值/右值左值有地址可取&,右值是临时量;右值引用实现移动语义避免拷贝

1. 面向过程 vs 面向对象编程

答案

面向过程(POP):以函数/过程为核心组织代码,数据和操作分离。

  • 优点:执行效率高、逻辑直观、适合小程序
  • 缺点:数据和函数耦合松散,大型系统难维护

面向对象(OOP):以类/对象为核心,通过封装、继承、多态组织代码。

  • 优点:高内聚低耦合、代码复用、适合大型系统
  • 缺点:设计开销大、运行时有虚函数表等间接开销

SLAM 工程中的体现

  • PCL、OpenCV 大量使用面向对象(继承体系)
  • 但 FAST-LIO2 中so3_math.h的模板函数是面向过程风格(追求效率)
  • 实际工程中两者结合:框架用 OOP,核心计算用 POP/模板

2. 指针和引用的区别

答案(5个核心区别)

维度指针引用
本质存储变量地址的变量变量的别名
可否为空可以为 nullptr不能为空,声明时必须初始化
可否重新绑定可以指向其他对象一旦绑定不可更改
sizeof指针本身大小(64位=8字节)所引用对象的大小
多级有多级指针int**无多级引用
自增地址偏移值递增

追问:什么时候用指针什么时候用引用?

  • 参数可能为空 → 指针
  • 需要重新绑定/多态所有权 → 指针
  • 函数参数传递避免拷贝 → const引用
  • 运算符重载返回值 → 引用

3. 什么是多态?虚函数和纯虚函数的区别?

答案

多态:同一接口不同实现。分为:

  • 编译时多态:函数重载、模板
  • 运行时多态:虚函数 + 基类指针/引用

虚函数 vs 纯虚函数

维度虚函数纯虚函数
声明virtual void f() {}virtual void f() = 0;
基类能否实例化不能(抽象类)
是否必须重写不必须派生类必须重写
用途提供默认实现定义接口规范

虚函数表(vtable)机制

Base* p = new Derived(); p->func(); // 通过 vtable 查找实际调用的函数 对象内存布局: +------------------+ | vptr → vtable | ← 指向虚函数表的指针 | member data | +------------------+ vtable: +------------------+ | &Derived::func() | ← 被覆盖,指向派生类实现 | &Base::other() | ← 未覆盖,仍指向基类 +------------------+

4. 为什么基类析构函数建议定义为虚函数?

答案

Base*p=newDerived();deletep;// 如果~Base()不是虚函数,只调用Base的析构,Derived部分内存泄漏!

当通过基类指针delete派生类对象时:

  • 非虚析构:只调用基类析构函数 → 派生类资源泄漏
  • 虚析构:通过 vtable 找到派生类析构函数,正确释放所有资源

注意:纯虚析构函数virtual ~Base() = 0;也必须提供定义(因为派生类析构会隐式调用基类析构)。


5. C++ 的4种类型转换

答案

转换用途运行时开销安全性
static_cast编译时已知的类型转换(基本类型、上行转换)中(不检查运行时类型)
dynamic_cast多态类的安全下行转换有(RTTI)高(失败返回nullptr)
const_cast去除/添加 const/volatile低(滥用导致UB)
reinterpret_cast底层位模式重解释最低

6. 基类指针调用派生类对象时用哪种转换?

答案

下行转换(Base→ Derived)用dynamic_cast**:

Base*base=getObject();Derived*d=dynamic_cast<Derived*>(base);if(d!=nullptr){d->derivedMethod();}
  • 需要基类有虚函数(开启RTTI)
  • 失败时返回nullptr(指针)或抛出bad_cast(引用)

上行转换(Derived→ Base)用static_cast** 或隐式转换即可。


7. dynamic_cast vs static_cast 哪个效率高?

答案

static_cast效率更高

原因:

  • static_cast:编译时完成,零运行时开销
  • dynamic_cast:需要 RTTI(Run-Time Type Information),运行时遍历类继承链检查类型,有开销

static_cast的下行转换是不安全的(不检查实际类型),如果类型不匹配会导致未定义行为。

SLAM 工程实践

  • 高频调用(如每个点的处理)→ 用static_cast(确保类型正确的前提下)
  • 低频调用(如传感器类型判断)→ 用dynamic_cast(安全优先)

8. 智能指针有哪几种?区别是什么?

答案

智能指针语义引用计数典型场景
unique_ptr独占所有权工厂函数返回值、独占资源
shared_ptr共享所有权有(线程安全的原子计数)多处共用同一资源
weak_ptr弱引用(不增加计数)观察shared_ptr打破循环引用、缓存

SLAM 工程中的实例

// FAST-LIO2 中:shared_ptr<Preprocess>p_pre(newPreprocess());// 多处使用预处理器shared_ptr<ImuProcess>p_imu(newImuProcess());// 多处使用IMU处理器// 点云用裸指针(PCL的Ptr本质上是shared_ptr)PointCloudXYZI::Ptrfeats_undistort(newPointCloudXYZI());// PCL中 Ptr = boost::shared_ptr<PointCloud>

追问:shared_ptr 的性能问题?

  • 引用计数的原子操作有开销(多线程下尤其明显)
  • 内存布局:控制块(计数器)和数据可能不连续(除非用make_shared
  • make_shared一次分配控制块+对象,更高效

9. unique_ptr 什么时候允许赋值?

答案

unique_ptr禁止拷贝(删除了拷贝构造和拷贝赋值),但允许移动

unique_ptr<int>p1=make_unique<int>(42);// unique_ptr<int> p2 = p1; // ❌ 编译错误unique_ptr<int>p2=std::move(p1);// ✅ 移动(p1变为nullptr)

编译器允许的赋值场景:

  1. 显式std::move:如上
  2. 函数返回局部变量(NRVO/移动语义):
unique_ptr<Foo>createFoo(){autop=make_unique<Foo>();returnp;// ✅ 编译器自动move(返回值优化)}
  1. 临时对象赋值
unique_ptr<int>p=make_unique<int>(10);// ✅ 右值直接构造

10. C++ 中提供了哪些锁?

答案

锁/同步原语头文件特点适用场景
std::mutex<mutex>基本互斥锁,非递归通用互斥访问
std::recursive_mutex<mutex>可递归加锁(同线程多次lock)递归函数中的互斥
std::timed_mutex<mutex>支持超时的互斥锁避免死锁等待
std::shared_mutex(C++17)<shared_mutex>读写锁(多读单写)读多写少场景
std::condition_variable<condition_variable>条件等待生产者-消费者
std::atomic<atomic>无锁原子操作简单计数器/标志位

SLAM 工程实例

// FAST-LIO2 中的数据缓冲区保护:mutex mtx_buffer;condition_variable sig_buffer;voidimu_cbk(...){mtx_buffer.lock();imu_buffer.push_back(msg);mtx_buffer.unlock();sig_buffer.notify_all();// 通知主循环有新数据}

RAII锁管理

// 推荐用 lock_guard/unique_lock,不要手动 lock/unlock{std::lock_guard<std::mutex>lock(mtx_buffer);// 构造时加锁,析构时解锁imu_buffer.push_back(msg);}// 离开作用域自动解锁,异常安全

11. 堆与栈的区别

从内存管理角度

维度栈 (Stack)堆 (Heap)
分配/释放编译器自动管理(LIFO)程序员手动(new/delete)或智能指针
速度极快(移动栈指针)较慢(系统调用/内存碎片)
大小有限(通常1-8MB)很大(受物理内存限制)
地址增长向低地址增长向高地址增长
碎片有外部碎片
生命周期函数结束自动回收手动释放或引用计数为0

从数据结构角度

维度栈 (Stack)堆 (Heap)
结构LIFO(后进先出)完全二叉树(优先队列)
操作push/pop O(1)insert/extract O(log n)
应用函数调用、DFS、括号匹配优先队列、TopK问题
STLstd::stackstd::priority_queue

12. STL 容器有哪些?

答案

类别容器底层实现随机访问插入/删除
序列vector动态数组O(1)尾部O(1),中间O(n)
序列deque分段数组O(1)头尾O(1),中间O(n)
序列list双向链表O(n)任意位置O(1)(已定位)
关联map/set红黑树O(log n)
关联unordered_map/set哈希表平均O(1),最差O(n)
适配器stack/queue/priority_queue封装底层容器

13. vector<int> vs vector<bool> 的区别

答案

vector<bool>是一个特化版本,不是真正的 STL 容器:

维度vector<int>vector<bool>
存储每个元素占4字节每个元素占1 bit(位压缩)
operator[]返回int&(真正的引用)返回代理对象reference(非真引用)
取地址&v[0]合法&v[0]不合法
与C数组互操作v.data()可用不支持
迭代器随机访问迭代器特殊迭代器

替代方案

// 如果需要真正的bool容器:std::deque<bool>// 不做位压缩std::vector<char>// 用char模拟boolstd::bitset<N>// 编译时大小确定的位集合

14. resize 和 reserve 的区别

答案

vector<int>v;v.reserve(100);// capacity=100, size=0, 不能v[50]访问v.resize(100);// capacity≥100, size=100, 可以v[50]访问(值初始化为0)
操作改变 size改变 capacity可访问元素构造元素
reserve(n)是(≥n)不变
resize(n)是(=n)可能(如果n>capacity)增加/减少是(默认构造)

SLAM 实践

// FAST-LIO2 中预分配,避免频繁重新分配:Nearest_Points.resize(feats_down_size);// 需要访问每个元素PointToAdd.reserve(feats_down_size);// 只是预分配空间,后续push_back

15. map vs unordered_map

答案

维度std::mapstd::unordered_map
底层红黑树(自平衡BST)哈希表(拉链法/开放寻址)
查找/插入/删除O(log n)平均 O(1),最差 O(n)
有序性key有序遍历无序
内存每节点含左右指针+颜色bucket数组+链表
key要求需要operator<需要hash+operator==

时间效率选择

  • 绝大多数场景用unordered_map:O(1) vs O(log n)
  • 需要有序遍历时用map
  • 数据量小(<100)时map可能更快(cache友好、无哈希开销)

哈希冲突与性能退化

// 最差情况:所有key哈希到同一bucket → O(n)// 解决:// 1. 好的哈希函数// 2. 预留足够bucket: m.reserve(n)// 3. 控制负载因子: m.max_load_factor(0.7)

16. 频繁插入中间且频繁访问,用什么容器?

答案

这是一个经典的 tradeoff 问题:

容器中间插入随机访问
vectorO(n)(需要搬移)O(1)
listO(1)(已定位)O(n)(不支持随机访问)
dequeO(n)(中间插入)O(1)

推荐方案

  1. std::deque:如果插入主要在头尾附近,deque 是好选择
  2. std::list+ 缓存迭代器:如果确实需要频繁中间插入,用 list 并缓存常用位置的迭代器
  3. std::vector+ 标记删除:如果访问极频繁而插入不多,用 vector + 惰性删除(标记而非真正删除)
  4. 考虑std::dequeboost::container::flat_set:分段连续内存,折中方案

面试追问的"标准答案":如果同时需要快速随机访问和快速中间插入,考虑std::deque(折中)或根据实际访问模式选择。没有完美方案,需要根据具体的 读/写比例 权衡。


17. 左值、右值与右值引用

答案

左值 (lvalue):有持久身份的表达式,可以取地址&x

intx=10;// x 是左值int&ref=x;// 左值引用

右值 (rvalue):临时的、即将销毁的表达式

int&&rref=10;// 右值引用绑定到临时量int&&rref2=x+1;// x+1 的结果是临时量(右值)

右值引用的使用场景

1. 移动语义(避免深拷贝)

classPointCloud{vector<Point>points;public:// 移动构造函数:偷走临时对象的资源PointCloud(PointCloud&&other)noexcept:points(std::move(other.points)){}};PointCloudprocessCloud(){PointCloud cloud;// ... 处理returncloud;// 移动而非拷贝(或NRVO直接省略)}

2. 完美转发(模板编程)

template<typenameT>voidwrapper(T&&arg){// 万能引用realFunction(std::forward<T>(arg));// 完美转发:保持左/右值性}

3. STL容器的 emplace 系列

vector<Pose>poses;poses.emplace_back(rotation,translation);// 直接原地构造,无临时对象// 比 push_back(Pose(rotation, translation)) 少一次移动

SLAM 中的实际应用

// FAST-LIO2 中 set_pose6d 使用移动语义:returnmove(rot_kp);// 避免拷贝 Pose6D 结构体// Eigen 矩阵的移动(Eigen 3.4+ 支持移动语义)Eigen::MatrixXdcomputeH(){Eigen::MatrixXdH(n,12);// ... 计算returnH;// 移动返回,无需拷贝大矩阵}

附:SLAM工程中的C++最佳实践总结

场景推荐做法
点云传递PointCloud::Ptr(shared_ptr)
矩阵参数const Eigen::Ref<const MatrixXd>&
数据缓冲区std::deque+mutex+condition_variable
高频小对象栈分配 or 对象池
配置参数const &传递
回调函数std::functionor 模板(无虚函数开销)
并行计算OpenMP#pragma omp parallel for
内存对齐EIGEN_MAKE_ALIGNED_OPERATOR_NEW

面试真题与答题要点

Q: 面试官:“说说 SLAM 项目中你用到了哪些 C++ 特性?”

答题模板

在我的 FAST-LIO2 项目中:

  1. 模板编程:SO(3) 数学库全部用模板实现(so3_math.h),支持 float/double
  2. 智能指针:点云用shared_ptr管理生命周期,避免手动内存管理
  3. 多线程:IMU回调和主处理循环通过mutex+condition_variable同步
  4. STL容器:IMU缓冲区用deque(双端操作),最近邻结果用vector(随机访问)
  5. Eigen内存对齐EIGEN_MAKE_ALIGNED_OPERATOR_NEW避免SSE指令的段错误
  6. OpenMP并行:点云配准中的最近邻搜索和残差计算并行化

延伸阅读

  • 书籍:Scott Meyers, “Effective Modern C++”(C++11/14最佳实践)
  • 书籍:Anthony Williams, “C++ Concurrency in Action”(并发编程)
  • 参考:cppreference.com(权威语言参考)
  • 实践:阅读 PCL / Eigen / Sophus 源码学习模板和内存管理
http://www.rkmt.cn/news/1495207.html

相关文章:

  • 光学实验室必备技能:离线环境下用MetroPro和命令行生成Zemax兼容的zxg文件
  • 用树莓派4B搭建Matter智能家居中枢:从刷写Ubuntu Server到运行chip-tool全记录
  • Kinetis K64引脚配置与选型实战:从数据手册到硬件设计
  • 计算机网络(4) -- http协议
  • 护网必学日志分析
  • 2026桥梁工程公司实力榜:木桥以“诚信筑基”领跑行业,六家高潜力本土品牌深度解析 - 品牌发掘
  • 8 套毕业论文降重降 AIGC 工具实测对比,平衡双检测不翻车
  • 终极歌词获取指南:如何快速免费下载网易云和QQ音乐LRC歌词
  • 基于AI的微服务故障注入与混沌工程自动化:从手动演练到智能验证
  • 工业级RAG检索
  • 2026年6月 港澳台联考志愿填报实操与靠谱机构参考 - 起跑123
  • 多模态时代下,鲲鹏极致性能库KVCL重构高效视频数据处理
  • 终极指南:5分钟让Mac通过Android手机USB共享上网的完整解决方案
  • 2026财税代理记账十强品牌测评:六家本土财税服务商以智能税务系统与合规性优势领跑行业深度解析 - 品牌发掘
  • 新手到专家:2026 年 Chrome SEO 插件最优组合与避坑攻略开篇
  • 2026年6月广东港澳台联考志愿填报排名实用指南 - 起跑123
  • wxapkg-convertor:解密微信小程序包的技术实现与应用实践
  • 智能可观测性:基于LLM的日志异常模式挖掘与根因推理
  • i.MX RT1060X引脚配置与BGA封装PCB设计实战指南
  • MonkCode:2026年最值得用的免费AI编程工具
  • 别再手动解压了!用Docker一键部署Matlab 2018b到Linux服务器(含离线激活)
  • 从碎片到全景:用Python stitching库解决你的图像拼接难题
  • 【KOA三维路径规划】五种改进策略开普勒算法山地环境下无人机 3D路径规划【含Matlab源码 15605期】
  • 2026玉林市家里卫生间漏水、阳台漏水、楼顶漏水、阳台漏水、地下室渗水、阳光房漏水各种房屋漏水情况不用愁!本地防水补漏公司为您排忧解难!您附近的专业防水团队 - 企业资讯
  • 如何快速清理重复视频?Vidupe智能去重工具帮你一键搞定
  • JN5169 ZigBee模块选型、开发与低功耗设计实战指南
  • INP/CLS/LCP 优化神器!谷歌官方 Web Vitals 插件免费装
  • 2026海口市家里卫生间漏水、阳台漏水、楼顶漏水、阳台漏水、地下室渗水、阳光房漏水各种房屋漏水情况不用愁!本地防水补漏公司为您排忧解难!您附近的专业防水团队 - 企业资讯
  • 力扣算法面试150题——二分查找——个人笔记
  • 长沙GEO优化公司排行:5家服务商核心能力实测对比 - 起跑123