南京予芯 C++应用开发 实习

1. 自我介绍

2. 介绍一下你的项目结构和整体流程

差不多讲了40多分钟, 重心都放在这了 无手撕

3. 说一下 TcpServer 的实现思路,核心流程是什么

答案:TcpServer 的核心作用通常不是“真的处理所有读写”,而是组织监听、分发连接和管理线程模型。它会持有监听 socket 和 Acceptor,当有新连接到来时,Acceptor 触发可读事件,调用 accept 拿到 connfd。然后 TcpServer 按照某种策略把新连接分配给一个 IO 线程对应的 EventLoop,在那个线程里创建 TcpConnection,再把这个连接的读写事件注册到对应 loop 的 epoll 上。后面连接上的读事件来了,就走读回调,把数据读入输入缓冲区,协议层做拆包和解析;写事件则从输出缓冲区尽量发送数据,直到写空或者遇到 EAGAIN。如果继续往下问,面试官一般会追线程切换、连接关闭时机、回调绑定和对象析构问题。

4. ChannelTcpConnectionEventLoopEventLoopThread 分别是做什么的

答案:这几个类如果讲不清楚,整个 Reactor 结构基本就讲不顺。EventLoop 是事件循环本体,内部通常封装 epoll_wait、待执行任务队列和线程归属检查,它负责“等事件 + 处理事件 + 执行跨线程投递任务”。Channel 更像是对一个 fd 感兴趣事件的抽象,里面会记录这个 fd 当前关心读还是写,以及发生事件时该调用哪个回调。TcpConnection 表示一个 TCP 连接对象,里面会维护 socket、连接状态、输入输出缓冲区以及读写关闭等回调。EventLoopThread 则是“一个线程 + 一个 loop”的封装,负责启动线程,并在这个线程里跑事件循环。这几个类之间的关系一般是:连接归属于某个 EventLoop,连接内部持有自己的 Channel,线程则负责承载 loop。

5. 为什么连接对象一般要固定归属于某个 IO 线程

答案:核心目的是减少并发复杂度。如果一个连接今天在这个线程读,明天在另一个线程写,那连接状态、缓冲区、回调执行顺序和关闭流程都会变得很难控制,最后很容易引出大量锁和竞态。更常见的做法是 one loop per thread,每个连接从建立开始就绑定到某个 IO 线程,后续对这个连接的大部分操作都在同一个线程上下文里完成。这样可以把“多线程问题”尽量收缩成“跨线程投递任务问题”,复杂度会低很多。

6. 说一下移动语义,为什么它在网络库或者容器里很重要

答案:移动语义的核心是把临时对象或者即将失效对象内部持有的资源转移出去,而不是再做一遍昂贵拷贝。像网络库里的缓冲区、消息对象、任务对象,如果每次投递都发生深拷贝,成本会很高。有了右值引用和移动构造之后,可以把资源所有权从一个对象直接转给另一个对象,尤其是在 vector 扩容、函数返回对象、任务入队这些地方收益很明显。所以移动语义不是语法题,它直接影响很多基础组件的性能表现。

代码:

#include <iostream>
#include <vector>
using namespace std;

class Buffer {
public:
    Buffer() = default;
    Buffer(const Buffer&) { cout << "copy\n"; }
    Buffer(Buffer&&) noexcept { cout << "move\n"; }
};

int main() {
    vector<Buffer> v;
    Buffer b;
    v.push_back(std::move(b));
    return 0;
}

7. 智能指针在连接管理里一般怎么用,为什么 TcpConnection 常配合 shared_ptr

答案:连接对象生命周期往往比较复杂,因为它既会被连接表持有,也可能被回调、定时器、任务队列临时引用。如果只用裸指针,很容易出现连接已经关闭销毁,但某个异步回调里还在访问它的情况。所以很多网络库会让 Tcp

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

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

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