面灵AI→

C++ 面经合集:海源泰和、芯极客、大华外包与摩西尔笔试

轮次
多轮面试合集
时间
2026-09
来源
牛客网

《面试题目》

  1. 讲一下封装、继承、多态。
  2. 讲一下静态多态和动态多态。
  3. 讲一下多继承、菱形继承以及相应的缺点。
  4. 请做一下自我介绍。
  5. 在职期间的工作内容是什么?
  6. 讲一下数据库的操作(增删改查)。
  7. 讲一下 Linux 的基础操作。
  8. 命名空间讲一下。
  9. 构造函数和析构函数讲一下。
  10. 构造和析构能不能重载?
  11. 引用和指针讲一下。
  12. 工作做了什么,怎么合理地说明自己没怎么写 C++ 代码?
  13. 你自己的项目写了什么东西?
  14. 简历里写的【音视频播放器】是怎么做的?
  15. 你开发的项目里面哪个有技术含量,难点在哪里?
  16. 你为什么选择去做这个 Sylar 服务器的项目?
  17. 讲一下多态是怎么实现的?
  18. 虚函数表里面存的什么?
  19. 在函数里,把变量写成引用和写成指针有什么区别?
  20. 对一个变量声明成 static 有什么效果?“全局只有一份”这个只有一份是什么意思?
  21. C++ 从写的代码到最终的可执行文件,经过了哪几步?每一步做了什么?
  22. 写过 makefile 吗?对 makefile 里面的规则清楚吗?
  23. 你了解哪些网络协议?
  24. TCP 协议和 IP 协议有什么关系?
  25. TCP 和 UDP 的区别是什么?
  26. TCP 为什么比 UDP 可靠?它是怎么实现可靠传输的,机制是什么?
  27. 拥塞控制是怎么实现的?
  28. 粘包是怎么产生的,解决方式有哪些?
  29. GDB 的命令你用过哪些?
  30. 用 GDB 把所有线程的堆栈都打出来,怎么写?
  31. Git 里把最近的三次提交顺序重新排一下,应该怎么操作?
  32. 程序崩溃了怎么调试?
  33. 什么样的信号会导致出现 core 文件?
  34. 什么是段错误?
  35. 用过 STL 的哪些容器?
  36. map 的底层实现是什么?红黑树的特点有哪些?
  37. 那为什么 map 的实现不用完全平衡二叉树?
  38. 指针和引用传参有什么区别?
  39. C++ 变量的命名规范是什么,哪些不能当变量名?
  40. Qt 里的 QVBoxLayout、QHBoxLayout、QGridLayout 分别能不能做横向、纵向和网格布局?
  41. *p++ 和 (*p)++ 有什么区别?
  42. 宏定义相关的计算题。
  43. 读一段 for 循环代码,写出打印的值。
  44. 用编程的方式说明 &&、||、%、/ 有什么区别。
  45. 用 Qt 的 QString::arg 把字符数组拼成字符串。
  46. 不用第三个变量交换 a、b 的值。

《参考解析》

静态多态与动态多态:静态多态在编译期就确定调用哪个函数——函数重载靠名字修饰区分参数列表,模板靠实例化生成不同的函数体,CRTP 靠把派生类当模板参数传进去,共同点是零运行时开销、但不能对”运行期才知道的类型”编程。动态多态在运行期靠 vptr 加虚表分发,代价是一次间接跳转和无法内联(除非编译器能去虚拟化),换来的是面向接口编程的能力。面试官顺着这题往下问多继承和菱形继承时要答出因果链:多继承下同一个基类子对象可能出现两份,数据冗余且访问有二义性;虚继承让被虚继承的基类只保留一份共享子对象,代价是对象布局里多出虚基类偏移信息、访问基类成员要间接寻址,而且初始化的责任落到最派生类身上。

虚函数表里存什么、多态怎么实现:虚表在编译期为每个多态类生成一张,按声明顺序存放虚函数地址,另外还带 RTTI 指针和 offset-to-top 等信息;对象里第一个(或多个)指针位置放 vptr。基类指针调用虚函数时通过 vptr 定位到表、再按固定槽位跳转,子类覆写就是把该槽位替换成自己的实现,多继承下对象持有多个 vptr,跨基类指针调用还需要 this 指针调整。两个高频追问:构造和析构期间 vptr 会被设置成当前正在构造/析构的类,所以在构造函数里调虚函数不会分发到子类;纯虚函数的槽位是一个会终止程序的占位实现,纯虚析构函数也必须给出定义。

map 为什么用红黑树而不是完全平衡二叉树:map 需要的是”有序 + 对数查找”,红黑树只保证近似平衡(最长路径不超过最短路径的两倍),树高仍是 O(log n) 量级,但换来的是插入删除时极少的旋转——红黑树插入最多两次旋转、删除最多三次,而 AVL 这类严格平衡树为了维持平衡因子在最坏情况下要沿路径旋转 O(log n) 次。map 的实际负载是读和写混合的,为每次写入付出严格平衡的代价不划算。加上红黑树实现里只需维护颜色这一位信息、比较函数只要求严格弱序,工程上更简单可靠,迭代器在插入时也不会失效——这些共同决定了 STL 的选择。

C++ 从源码到可执行文件经过哪几步:预处理、编译、汇编、链接。预处理阶段展开 #include 和宏、处理条件编译,产出纯 C++ 源码;编译阶段做词法语法语义分析和优化,生成汇编代码(模板在这一步真正实例化,这也是模板要实现到头文件里的原因);汇编阶段把汇编翻成机器码,产出带符号表和重定位信息的目标文件;链接阶段做符号解析与重定位,把多个目标文件和库拼成一个可执行文件,静态库直接拷入、动态库只记依赖,符号在装载时才解析。常见的追问点:为什么模板报错在编译期、为什么会有未定义符号、inline 与 ODR 规则、以及编译优化级别对调试体验的影响。

粘包与解决方式:TCP 是面向字节流的协议,没有消息边界的概念,应用层一次 send 的数据可能被拆成多个报文、也可能和下一次的粘在一起,所以粘包是应用层缺少分帧协议的表现,不是 TCP 的缺陷。解决方案四类:定长消息(实现简单、浪费带宽,适合固定结构的协议头)、分隔符(要处理转义,文本协议常用)、长度字段加负载(最通用,先读 4 字节长度再读 body)、以及自描述格式(protobuf、HTTP chunked)。工程上还要处理”半包”——读到的数据不足一条完整消息时要缓存起来等下次可读,并且必须给长度字段设上限,否则对端报一个超大长度就能让服务端按声明分配内存直接 OOM。

*p++ 与 (*p)++、编译链接之外的笔试小题:*p++ 因为后缀 ++ 优先级更高,等价于 *(p++),取的是当前 p 指向的值、然后 p 自增,表达式类型是指向对象的类型;(*p)++ 是先解引用再自增那个对象的值,指针不动。区别落到笔试题上就是”指针有没有移动、被指向的值有没有变”。不用第三个变量交换两个数有三种常用写法:加减法 a=a+b; b=a-b; a=a-b(可能溢出)、异或法 a^=b; b^=a; a^=b(要求两个变量不同地址,自己和自己换会清零)、以及 C++ 用 std::swap。至于 &&、|| 与 %、/ 的区别,要讲清短路求值:&& 和 || 返回布尔语义并且可能不计算右侧,%、/ 是算术运算且除数为 0 是未定义行为,负数取模的符号规则也常被拿来出题。