美团 软件开发-C++ 一面
1. 自我介绍
2. 这里的数据量大小是多少,字幕使用mysql存储是不是不大合适,做分表操作必要性大吗?
答案:如果字幕数据量只是中等规模,而且查询模式比较明确,比如按视频 ID、语言、时间轴版本去查,MySQL 其实也不是完全不能用,关键在于表结构设计、索引设计和冷热数据策略是否合理。但如果字幕文本本身很长,写入频繁、版本多,而且还有检索、回溯、批量更新这类需求,那单纯放在 MySQL 里就不一定划算了。因为它更适合做结构化数据管理,不太适合承载高频大文本读写。更常见的做法会是 MySQL 存元数据和索引信息,大文本内容放对象存储或者专门的文档型存储里。至于分表有没有必要,要看几个点:单表行数、索引膨胀情况、写入压力、查询是否已经明显变慢,以及是否出现了归档压力。如果现在数据规模还远没到瓶颈,分表未必是第一选择,因为它会带来路由、扩容、聚合查询、运维复杂度这些额外成本。所以我会倾向于先做数据量级评估、热点分析和 SQL 画像,再决定是先优化索引和存储结构,还是真的上分表。
3. 我看你之前实习过测试, 怎么又接着做开发了
4. 你为什么封装这个网络接口,有了解原来接口怎么实现通信的吗?
答案:我封装网络接口,主要不是为了“再套一层”,而是为了把请求发送、连接管理、超时控制、错误处理这些共性逻辑收敛起来,避免业务层到处散落重复代码。如果业务代码直接去操作底层网络细节,短期开发可能快一点,但后面一旦协议变更、超时策略调整、重试逻辑变化,改动面会很大,也不利于排查问题。做接口封装之后,业务层更关注参数和结果,底层细节集中处理,可维护性会好很多。至于原来接口怎么通信,我是有了解过的。底层本质上还是基于 socket 做事件驱动通信,在 Linux 下通常会结合 epoll 去监听可读可写事件,然后在事件到来时触发回调,完成连接建立、数据收发和状态切换。像这种封装,真正要解决的不是“能不能发出去”,而是异常怎么处理、连接怎么复用、半包粘包怎么兼容、超时和重试怎么设计,以及接口如何对上层保持稳定。
5. libevent底层原理,回答了epoll的相关内容
答案:libevent 本质上是一个事件驱动库,它做的事情是把 I/O 复用、事件注册、事件分发和回调执行这一整套流程封装起来。在 Linux 下,它通常会优先使用 epoll 作为底层多路复用机制。epoll 负责监听大量文件描述符的事件状态,比如可读、可写、异常等;libevent 再在这个基础上维护事件集合、回调函数和事件循环。它的核心流程可以理解成几步:先把感兴趣的 fd 和事件注册到底层复用器里,然后进入 event loop,不断等待内核返回就绪事件;一旦有事件触发,libevent 会找到对应的回调并执行。相比传统的阻塞 I/O 或者简单轮询,这种方式的好处是可以在单线程下高效管理大量连接,避免线程数太多带来的切换开销。如果从工程角度看,libevent 解决的不只是监听事件,还包括定时器、信号、事件优先级这些能力,所以它比直接手写 epoll 用起来更完整。
代码:
#include <event2/event.h>
#include <iostream>
using namespace std;
void cb(evutil_socket_t fd, short events, void* arg) {
cout << "fd " << fd << " readable" << endl;
}
int main() {
event_base* base = event_base_new();
event* ev = event_new(base, 0, EV_READ | EV_PERSIST, cb, nullptr);
event_add(ev, nullptr);
event_base_dispatch(base);
event_free(ev);
event_base_free(base);
return 0;
}
6. 你能实习多久
7. C++98和C++11相比,多引入了哪些?
答案:C++11 相比 C++98 是一次很大的升级,不只是语法糖,而是让现代 C++ 的写法基本成型了。比较核心的变化包括右值引用和移动语义,这让对象转移资源变得更高效;auto、decltype 提高了类型推导能力;nullptr 解决了空指针语义不清的问题;范围 for、lambda 让代码表达更自然;智能指针进入标准库之后,资源管理也更规范。另外还有线程库、原子操作、unordered_map、static_assert、初始化列表、override/final 这些,基本把语言能力和工程开发体验都往前推了一大步。如果让我概括,C++98 更像传统 C with class,C++11 开始才真正进入现代 C++ 的阶段。
代码:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4};
for (auto x : nums) {
cout << x << " ";
}
cout << endl;
auto add = [](int a, int b) { return a + b; };
cout << add(3, 4) << endl;
return 0;
}
8. 智能指针你介绍一下
如果按现在实际开发的角度来说,我会重点讲 unique_ptr、shared_ptr 和 weak_ptr。unique_ptr 表示独占所有权,同一时间只能有一个指针拥有对象,不能拷贝,可以移动,适合明确资源归属的场景。shared_ptr 是共享所有权,通过引用计数来管理对象生命周期,只要还有一个强引用存在,对象就不会被释放。它适合多个对象共同持有一份资源的场景。weak_ptr 不拥有对象,只是观察 shared_ptr 管理的资源,主要用途是打破循环引用,或者在使用前先判断对象是否还存活。至于 auto_ptr,它是早期标准里的产物,拷贝时会转移所有权,语义很危险,后来已经被废弃了。循环引用是 shared_ptr 很典型的问题,比如 A 持有 B,B 又持有 A,两个对象的引用计数都不会归零,这时候就需要一边改成 weak_ptr。
代码:
#include <iostream> #include <memory> using namespace std;
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.
查看9道真题和解析