面灵AI→

元戎启行 软件工程师一面:C++ 八股与 01 串手撕

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

项目部分(约 15 分钟)

  1. 自我介绍,然后讲一个最拿得出手的项目
  2. 在项目里具体负责什么?
  3. 遇到过什么难点,怎么解决的?
  4. 项目中用过智能指针吗?什么场景下用的?

八股部分

  1. C++ 多态怎么实现?virtual 有什么作用?底层逻辑是什么?
  2. 右值引用和移动语义了解吗?std::move 是干什么的?
  3. C++ 编译流程:预处理、编译、汇编、链接分别做什么?
  4. 进程和线程有什么区别?进程间通信有哪些方式?
  5. epoll 和 poll 有什么区别?LT 和 ET 有什么区别?
  6. TCP 三次握手、四次挥手,TCP 和 UDP 的区别

手撕代码

  1. 给一个 01 串,保证开头是 0、结尾是 1,找到任意一个「01」子串的位置,要求复杂度尽可能低

反问环节

  1. 这个软件工程师岗具体是做哪一块?(JD 里写了硬件、驱动、算法、数据闭环好几个方向)

《参考解析》

  1. 智能指针这题考的是「用没用过」而不是「背没背过」:面试官追问使用场景,就是在验证真实性。按所有权讲最稳:unique_ptr 表达独占所有权、开销为零,适合资源持有;shared_ptr 用引用计数表达共享所有权,代价是控制块与原子计数,要注意循环引用必须靠 weak_ptr 打破;weak_ptr 只观察不持有,用 lock() 提升成 shared_ptr 再访问。再补一句工程口径:能用 unique_ptr 就别用 shared_ptr,跨线程传递时想清楚谁负责释放。

  2. 多态的回答要能落到虚表和对象布局:虚函数表随类生成、对象头里存虚表指针,通过基类指针调用时按运行时类型查表分派,这就是动态绑定。可以顺带讲清几个常被追问的点:构造函数里调虚函数不会分派到派生类(此时派生部分还没构造)、析构函数要声明为 virtual 否则 delete 基类指针不会调派生析构、RTTI 与 dynamic_cast 依赖同一套运行时信息。移动语义与 std::move 是纯右值转换,本身不移动任何数据,真正做事的是移动构造/移动赋值。

  3. 编译流程要讲清每步的输入输出:预处理展开宏、#include 和条件编译,产出翻译单元;编译做语法分析与优化,产出汇编或目标文件;汇编把汇编翻译成机器码目标文件;链接做符号解析与重定位,把多个目标文件和库拼成可执行文件或共享库。能被追问的点是静态库与动态库的差别、符号表与未定义符号报错怎么读。

  4. epoll 与 poll 的差别要落到内核数据结构:poll 每次调用都要把整个 fd 集合拷进内核并线性扫描;epoll 把注册和等待拆开,epoll_ctl 维护内核里的红黑树,epoll_wait 只返回就绪链表里的 fd,所以在高并发下少了很多无谓拷贝与扫描。LT 是水平触发,只要缓冲区还有数据就会一直通知,写起来简单不容易漏;ET 是边沿触发,只在状态变化时通知一次,必须配合非阻塞 fd 循环读到 EAGAIN,漏读就会卡住。选哪个看业务对实现复杂度和唤醒次数的取舍。

  5. 手撕 01 串有比暴力枚举更省的解法:题目保证开头是 0、结尾是 1,所以「01」子串一定存在。从前往后找第一个出现的 ‘1’,它的前一个位置就是答案——因为首个 ‘1’ 之前全是 ‘0’,边界处必然是 0 挨着 1;对称地,从后往前找最后一个出现的 ‘0’,它的后一个位置也是答案。两端同时向中间找,谁先碰到边界就用谁,代价是 O(min(前导 0 的个数, 后缀 1 的个数)),最坏才是 O(n)。答题时先讲这个观察,再写代码,最后说清复杂度和为什么不用滑动窗口或逐位比对。

  6. 项目题用「难点 → 定位 → 方案 → 验证」四步讲:这场面试的复盘很值得借鉴——面试官问项目细节时,模板很容易被问穿。挑一个自己真写过的项目,把当时的现象、你排查过哪些方向、为什么最后选这个方案、改完用什么指标验证讲清楚。项目本身简单没关系,讲不出「为什么这么写」才是减分项。