滴滴 C++后端开发 二面 面经

1. 介绍一下你参加过的竞赛,遇到的最大挑战是什么?

二面通常会深挖竞赛经历,面试官想了解的不只是"拿了什么奖",而是你在其中的思考过程和解决问题的能力。

回答思路:说清楚竞赛背景(什么比赛、什么方向)、你承担的角色、遇到的核心技术难点、你是如何分析和解决的、最终结果。重点放在"你做了什么"而不是"团队做了什么"。

例如:参加某届全国大学生嵌入式系统设计竞赛,负责底层驱动和通信模块,遇到的挑战是实时性不达标,通过分析发现是中断响应延迟过高,最终通过调整中断优先级和减少临界区长度解决,系统响应时间从8ms降到1.2ms。

2. 竞赛项目中你是如何做技术选型的?为什么选这个方案而不是其他方案?

这道题考察你的技术判断力和工程思维,面试官想看你是否真正理解自己做的东西,而不是照着教程堆砌。

回答要体现对比思维:列出你考虑过的备选方案,分析各自的优缺点,结合项目的实际约束(时间、硬件资源、团队能力、可维护性)说明为什么最终选了这个方案。

例如:通信协议选型时对比了 MQTT、HTTP、自定义二进制协议,考虑到设备资源有限(RAM 64KB)且需要低延迟,最终选择自定义轻量二进制协议,牺牲了通用性换取了更小的包头开销和更快的解析速度。

3. 你的项目中是如何保证代码质量和可维护性的?

这是二面常见的工程素养题,考察你是否有良好的开发习惯。

可以从以下几个维度回答:代码规范(命名、注释、模块划分)、版本管理(Git分支策略、commit规范)、代码审查(是否有 code review 流程)、测试(单元测试覆盖率、集成测试)、文档(接口文档、设计文档)。

重点说你实际做到的,不要泛泛而谈。例如:项目中使用 Git Flow 管理分支,feature 分支开发完成后必须经过至少一人 review 才能合并,关键模块写了单元测试,覆盖率维持在70%以上。

4. 项目中遇到过内存泄漏或崩溃问题吗?你是怎么定位和解决的?

这道题考察实际调试能力,是区分有没有真正做过项目的关键题。

定位内存泄漏的常用手段:Valgrind(Linux下检测内存泄漏和非法访问)、AddressSanitizer(编译期插桩,运行时检测,开销小)、自定义内存分配器统计分配/释放次数。

定位崩溃的手段:分析 core dump 文件(gdb core dump 定位崩溃栈帧)、在关键路径加日志、使用 backtrace 打印调用栈。

回答时结合具体案例,说清楚现象、定位过程、根因、修复方式,比泛泛介绍工具更有说服力。

5. C++ 虚函数的底层实现原理是什么?虚函数表是如何工作的?

每个含有虚函数的类都有一张虚函数表(vtable),表中按声明顺序存放各虚函数的函数指针。每个该类的对象在内存布局的最开头有一个隐藏的虚指针(vptr),指向所属类的 vtable。

调用虚函数时,编译器生成的代码是:取对象的 vptr,找到 vtable,按偏移量取出函数指针,再调用。这就是运行时多态的实现机制,比普通函数调用多了两次内存间接寻址,有轻微性能开销。

继承时,子类的 vtable 是在父类 vtable 基础上覆盖(override)对应槽位,新增的虚函数追加在后面。多重继承时每个基类对应一张 vtable,对象内存中有多个 vptr。

纯虚函数在 vtable 中对应槽位通常填 0 或一个报错函数,调用纯虚函数是未定义行为。

6. std::move 和右值引用的本质是什么?什么时候用移动语义?

右值引用(T&&)是C++11引入的,用于绑定临时对象(右值)。std::move 本身不移动任何东西,它只是一个强制类型转换,将左值转换为右值引用,告诉编译器"这个对象的资源可以被转移走"。

移动语义的本质是资源所有权的转移而非拷贝。以 std::vector 为例,拷贝构造需要分配新内存并逐元素复制,而移动构造只需要把内部指针、大小、容量三个字段复制过来,再把原对象的指针置空,时间复杂度从 O(n) 降到 O(1)。

适合使用移动语义的场景:函数返回局部对象(编译器会自动触发 NRVO 或移动)、将对象插入容器时(emplace_back 优于 push_back)、独占资源的转移(unique_ptr 只能移动不能拷贝)。

注意:被 move 之后的对象处于"有效但未指定"状态,不应再使用其值。

7. epoll 的 ET 模式下如何正确处理读事件?为什么必须循环读到 EAGAIN?

ET(边缘触发)模式下,epoll 只在 fd 状态从无数据变为有数据时通知一次。如果这次通知后没有把缓冲区数据全部读完,epoll 不会再次通知,剩余数据就会永远滞留在缓冲区,导致请求丢失或连接假死。

正确做法是:收到可读事件后,在一个循环中持续调用 read/recv,直到返回 -1 且 errno 为 EAGAIN(表示缓冲区已空)或 EWOULDBLOCK 为止。

同时 fd 必须设置为非阻塞模式,否则最后一次 read 会阻塞住整个事件循环线程,导致其他连接无法处理。

ET 模式的优势是减少了 epoll_wait 的唤醒次数,在高并发场景下系统调用开销更小,但编程复杂度更高,需要仔细处理 EAGAIN、EINTR、连接关闭等各种情况。

8. MySQL 事务的四个隔离级别分别解决了什么问题?InnoDB 默认是哪个?

四个隔离级别从低到高:

读未提交(Read Uncommitted):可以读到其他事务未提交的数据,存在脏读问题,实际几乎不用。

读已提交(Read Committed):只能读到已提交的数据,解决了

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务