公司动态
异常处理try/catch:面试官问“机器人程序崩了怎么办“,我这样答直接加分
上篇聊了模板类C基础语法部分到这里基本收尾了。今天讲一个工程味很浓的话题——异常处理。异常处理在面试里不算高频考点但一旦问到区分度很大。因为大部分人只会写try/catch但对异常的安全机制、性能影响、最佳实践说不清楚。讲个真实场景。机器人跑着跑着激光雷达突然断连了。如果没有异常处理程序可能直接崩溃——指针解引用一个空数据段错误core dump。整个机器人停在那里一动不动。有了异常处理你可以优雅地降级检测到传感器断连抛异常上层捕获切换到备用传感器或者安全停车。这就是异常处理在机器人开发里的意义——不是让程序不犯错而是让程序犯错的时候不会死。try/catch的基本用法语法很简单try { // 可能出错的代码 double distance readLidar(); if (distance 0) { throw runtime_error(Lidar reading error: negative distance); } processDistance(distance); } catch (const runtime_error e) { // 处理runtime_error cerr Error: e.what() endl; useFallbackSensor(); } catch (const exception e) { // 处理其他标准异常 cerr Unexpected error: e.what() endl; } catch (...) { // 兜底捕获所有异常 cerr Unknown error! endl; emergencyStop(); }几个要点catch的顺序很重要。子类异常要放在前面父类异常放在后面。因为catch是按顺序匹配的一旦匹配到就不再往下走。如果把exception放最前面它会捕获所有标准异常后面的catch就永远不会执行了。catch(...)是兜底捕获所有异常包括非标准异常。但它的缺点是你拿不到任何异常信息。异常类继承体系C标准库定义了一套异常类层次exception ├── logic_error │ ├── invalid_argument │ ├── out_of_range │ └── length_error └── runtime_error ├── range_error ├── overflow_error └── system_error面试偶尔会让你区分logic_error和runtime_error。简单说logic_error是逻辑错误理论上可以通过代码检查避免比如参数越界runtime_error是运行时错误只有在程序跑起来之后才能发现比如硬件故障。在机器人开发里自定义异常类通常继承自runtime_errorclass SensorException : public runtime_error { string sensor_name_; int error_code_; public: SensorException(const string name, int code) : runtime_error(Sensor name error: to_string(code)), sensor_name_(name), error_code_(code) {} const string sensorName() const { return sensor_name_; } int errorCode() const { return error_code_; } }; // 使用 try { throw SensorException(lidar_front, 1001); } catch (const SensorException e) { cerr e.what() endl; cerr Sensor: e.sensorName() endl; // 可以根据error_code做不同处理 }这样做的优点是异常信息丰富——你知道是哪个传感器出了什么问题方便定位和恢复。异常安全机器人开发里的关键问题面试官如果深挖会问什么是异常安全。异常安全有三个等级。基本保证程序在抛异常后不会泄漏资源。比如new了一块内存在异常路径上也能释放。强保证异常发生后程序状态回滚到调用前的状态。要么完全成功要么什么都不变。不抛异常保证函数保证不抛异常。用noexcept关键字标记。void processData() noexcept { // 保证不抛异常 // 如果真抛了程序直接terminate }在机器人开发里运动控制相关的函数通常标记为noexcept。因为机器人正在走的时候你不能用异常来处理错误——你得用返回值或者状态码确保控制回路不会被打断。异常的争议用还是不用讲真C社区对异常的态度是分裂的。Google的C编码规范里明确禁止使用异常。理由是异常的性能开销不可预测而且大型项目里异常会让控制流变得很难追踪。但很多机器人框架包括ROS是依赖异常的。比如PCL的IO操作、Eigen的部分运算出错了就抛异常。你不用异常就得在每层都检查返回值代码写起来很啰嗦。讲个实际例子。我之前做一个导航模块的时候初始化阶段用了大量异常处理——加载地图文件可能失败、连接激光雷达可能超时、读取配置文件可能格式错误。这些都是一次性的操作用异常处理很合适。但在主控制循环里所有错误都用返回值处理因为控制循环跑100Hz你不能让它因为异常被中断。我的建议是在机器人开发里异常适合处理不太可能发生的错误——比如硬件初始化失败、配置文件格式错误。对于经常发生的错误——比如传感器读数偶尔超时——用返回值更合适。记住一个原则异常处理的是异常情况不是正常的错误流程。面试中的异常考点面试官考异常几个经典问题。异常和错误码相比有什么优缺点优点是异常不能忽略你不catch就往上抛最终程序崩溃逼着你处理缺点是影响性能、让控制流不直观。析构函数能抛异常吗最好不要。如果析构函数在栈展开过程中抛异常程序会直接terminate。这就是为什么析构函数通常标记为noexcept。catch的时候用值还是引用用const引用。用值会多一次拷贝而且可能发生对象切片slicing——子类异常被截成父类。还有一个面试深度追问异常安全的三个保证级别你知道吗第一层是基本保证——即使抛出异常程序的状态也是合法的没有资源泄漏但数据可能变了。第二层是强保证——操作要么成功要么完全回滚就像数据库的事务一样数据状态不变。第三层是无异常保证——函数承诺绝不抛出异常也就是noexcept。在机器人开发里控制指令的发送函数一般要做到强保证因为发了一半的指令比不发更危险。实现异常安全最靠谱的方法还是RAII——用智能指针和局部对象来管理资源让析构函数自动清理这样即使中途抛异常也不会泄漏。面试时能讲出异常安全的三个级别再结合RAII给出解决方案面试官基本就满意了。给正在准备面试的你一点建议异常处理在面试里不算高频但属于会了加分的知识点。记住三点就够了try/catch的基本语法和catch顺序自定义异常类的设计异常安全的基本概念。如果你能聊到Google为什么禁用异常以及机器人开发里异常和返回值的选择面试官会觉得你有工程思维不只是会写代码。再聊一个面试常考的延伸点noexcept关键字。C11引入了noexcept让你可以标记一个函数不会抛出异常。比如移动构造函数通常标记为noexcept因为STL容器在扩容时会优先选择noexcept的移动构造而不是可能抛异常的拷贝构造这对性能影响很大。如果你能在面试中解释为什么移动构造函数要标记noexcept面试官会觉得你对异常安全和性能优化都有理解。下篇讲命名空间——大型项目中避免冲突的规范。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第27篇 模板类——STL容器的底层设计思路 下一篇预告第29篇 命名空间——大型项目中避免冲突的规范有任何问题欢迎评论区留言我会尽量回复。