米哈游 服务器开发方向 C++ 一面 面经

1. 协程和线程的本质区别是什么?协程的调度是由谁控制的?

线程由操作系统内核调度,切换需要陷入内核态,开销大(保存/恢复寄存器、栈、上下文)。协程是用户态的轻量级执行单元,调度由程序自身控制(协作式),切换只需保存少量寄存器,无系统调用开销。协程共享线程栈,适合高并发 IO 密集型场景,如游戏服务器的大量连接处理。

2. epoll 的 LT 和 ET 模式有什么区别?ET 模式下如何避免数据丢失?

LT(水平触发):只要 fd 可读/可写就持续通知,未处理完下次还会触发,使用简单但效率略低。ET(边缘触发):只在状态变化时触发一次,必须一次性把数据读完,否则不再通知。ET 下避免丢失的做法:将 fd 设为非阻塞,循环 read 直到返回 EAGAIN/EWOULDBLOCK 为止。

3. Reactor 和 Proactor 模式有什么区别?游戏服务器一般用哪种?

Reactor 是同步非阻塞模型,事件就绪后由应用层自己去读写数据(如 epoll + 非阻塞 IO)。Proactor 是异步模型,内核完成 IO 后才通知应用层(如 Windows IOCP)。游戏服务器在 Linux 上通常用 Reactor 模式,配合线程池处理业务逻辑,典型实现如 muduo、libevent。

4. TCP 四次挥手中,为什么 TIME_WAIT 状态需要等待 2MSL?

MSL 是报文最大生存时间。等待 2MSL 有两个原因:① 确保最后一个 ACK 能到达对端,若对端没收到会重发 FIN,2MSL 内能收到并重传 ACK;② 让本次连接的所有报文在网络中消亡,防止旧连接的延迟报文被新连接误收。

5. TCP 的滑动窗口和拥塞控制有什么关系?拥塞控制的四个阶段是什么?

滑动窗口是流量控制机制,由接收方通告窗口大小限制发送速率。拥塞控制是发送方根据网络状况自适应调整发送量,实际发送量 = min(拥塞窗口, 接收窗口)。四个阶段:① 慢启动:cwnd 指数增长;② 拥塞避免:cwnd 线性增长;③ 快重传:收到 3 个重复 ACK 立即重传;④ 快恢复:ssthresh = cwnd/2,cwnd = ssthresh 后进入拥塞避免。

6. UDP 如何实现可靠传输?说说 KCP 的核心思路。

UDP 本身不可靠,要实现可靠传输需在应用层加:序列号、ACK 确认、超时重传、滑动窗口、乱序重排。KCP 的核心思路:以带宽换延迟,取消 Nagle 算法,降低 RTO 计算的保守程度(1.5x 而非 2x),支持选择性重传(SACK),减少不必要的等待,适合游戏实时对战场景。

7. std::movestd::forward 的区别是什么?

std::move 无条件将左值转为右值引用,触发移动语义,转移资源所有权,避免深拷贝。std::forward 是完美转发,保留参数的原始值类别(左值还是右值),用于模板函数中将参数原封不动地传递给下一层,避免多余拷贝。本质上两者都只是 static_cast,不产生任何运行时代码。

8. 虚函数表(vtable)的布局是怎样的?多继承时 vtable 有几张?

每个含虚函数的类有一张 vtable,存放虚函数指针数组。对象内存开头存放 vptr 指向该表。单继承时子类复用父类 vtable 并覆盖重写的槽位。多继承时每个有虚函数的基类对应一张 vtable,子类对象内存中有多个 vptr,调用不同基类的虚函数时使用对应的 vptr,存在 thunk 调整 this 指针偏移。

9. weak_ptr 解决了什么问题?它是如何判断对象是否还活着的?

weak_ptr 解决 shared_ptr 循环引用导致内存泄漏的问题(如 A 持有 B 的 shared_ptr,B 持有 A 的 shared_ptr,引用计数永远不为 0)。weak_ptr 不增加强引用计数,通过控制块中的弱引用计数跟踪。判断对象是否存活:调用 lock() 尝试提升为 shared_ptr,若强引用计数已为 0 则返回空指针。

10. 内存池的设计思路是什么?游戏服务器为什么需要内存池?

内存池预先向系统申请一大块内存,按固定大小或分级管理,分配时直接从池中取,释放时归还到池而非还给系统。游戏服务器需要内存池的原因:① 频繁 new/delete 导致内存碎片;② 系统调用开销大,影响帧率和延迟;③ 内存池分配/释放是 O(1),且局部性好,缓存命中率高。常见实现:固定块内存池、tcmalloc、jemalloc。

11. std:: 和 在游戏服务器中如何选择?

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

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

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

全部评论

相关推荐

03-31 14:46
已编辑
门头沟学院 Web前端
励志成为双港第一ja...:这其实很正常,离的太远了,他认为你不会来,就为了混个面试,而且成本很高,实习生都优先选本地高校。吃了地域的亏,所有很多时候地域可能比院校层次更重要。
点赞 评论 收藏
分享
题目都答出来了,但是表述弯弯绕绕,还得练表达啊。代码题创建线程不会创建,直接露馅。其他似乎应该也答出来了。只有三十分钟,应该是寄了项目相关1. 你用 Caffeine + Redis 构建了两级缓存,这两级缓存的数据如何保持一致?2. 你简历上写了"通过乐观锁解决支付回调与关单任务的并发冲突",能介绍一下具体是怎么用乐观锁解决的?假设关单任务已经开始执行,这时候支付成功的回调过来了,是一个怎样的处理过程?3. (追问)如果支付回调过来,但乐观锁没抢到(update 失败),后续流程是什么?(用户已经付了钱,但订单被关闭了,怎么处理?)4. 你的扣减库存是通过 Kafka 异步处理的,那消费 Kafka 消息进行库存扣减时,如何保证幂等性?(即一个订单不会被扣多次)5. 你设计了 AI CodeReview 提示词,提高了代码缺陷识别率和 AI 输出质量,中间做了哪些优化?有没有一个递进改进的过程?6. 在做 RAG 系统时,知识库是以什么方式进行切分(分块)的?代码题1. 锁竞争:给一段代码,多线程并发调用同一个对象的 Method1 和 Method2(两个方法都加了 synchronized,锁对象分别是两个不同变量 A、B,但 A 和 B 指向同一个对象),它们之间的锁是否会产生竞争?2. Spring 事务失效:给一段代码,是通过 Spring 管理的 Bean 实例调用 Method1,Method1 内部用 this 调用 Method2,Method2 上的 @Transactional 注解是否会生效?为什么?3. 线程访问局部变量:要求写代码实现:有一个局部变量 int x = 5,不能移动它的定义位置,要在线程里对它加 5,最终输出 10,如何实现?4. (上一题不会,我说平时了解线程池更多,面试官换了一题八股)线程池参数:线程池的 corePoolSize、maxPoolSize、workQueue 这几个核心参数的关系是什么?假设使用无界队列,有新任务提交进来时,这几个参数的行为是怎样的?5. SQL 索引优化题:有一张员工表,包含若干字段,给出一段查询 SQL(WHERE 条件中有对字段使用函数的情况,还有 ORDER BY),不考虑其他查询条件,想通过建索引来优化,应该在哪些字段上建索引?(应当是考察索引失效)6. (追问)为什么在有函数的字段上建索引会失效?7. 算法题:自定义三叉树节点结构,实现三叉树的广度优先遍历。反问:1. 业务和技术栈2. AI 的使用
发面经攒人品
点赞 评论 收藏
分享
03-30 00:09
吉林大学 C++
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

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