美团 C++开发 二面

1. 自我介绍

2. 说一下 C++ 里的多态,虚函数表是怎么工作的?

答案: 如果只答“多态就是父类指针指向子类对象”,会比较浅。 更完整的回答应该分成两部分:语言层面的多态,以及底层实现。

C++ 里常说的多态主要是运行时多态,也就是:

  • 基类定义虚函数
  • 子类重写虚函数
  • 通过基类指针或引用调用时,实际执行子类版本

底层上通常依赖虚函数表 vtable 和 虚函数表指针 vptr。 对象里通常会有一个隐藏的 vptr,指向该类型对应的虚表。调用虚函数时,程序先通过对象找到虚表,再从虚表里取出对应函数地址完成动态分发。

多态的优点是扩展性好,适合做接口抽象、框架回调、策略替换。 代价主要有:

  • 多一次间接调用
  • 对象里通常要多一个 vptr
  • 某些场景不利于内联优化

代码:

#include <iostream>using namespace std;​class Base {public:    virtual void run() { cout << "Base\n"; }    virtual ~Base() = default;};​class Derived : public Base {public:    void run() override { cout << "Derived\n"; }};​int main() {    Base* p = new Derived();    p->run();   // 运行时多态    delete p;}

3. 项目拷打

4. 如果我有一堆正整数需要频繁插入和删除,并且还要知道第几大的数,应该用什么数据结构?为什么?

答案: 如果需要同时支持:

  • 动态插入
  • 动态删除
  • 查询第 k 大

那比较合适的数据结构通常是:

  • 带顺序统计信息的平衡二叉搜索树
  • 或者离散化后用 树状数组 / 线段树
  • 如果数据范围固定且不大,用桶或计数结构也可以

为什么普通堆不够? 因为堆擅长维护最值,但不擅长支持任意删除和第 k 大查询。 为什么 set / multiset 也不完全够? 因为标准库里的 set 虽然能维护有序,但不能直接高效查询“第 k 大”,除非你自己额外维护秩信息。

如果值域很大、数据动态变化很多,工程上更常见思路是:

  • 用平衡树节点维护子树大小
  • 插入删除时更新 size
  • 查询第 k 大时根据右子树节点数递归判断

5. 如果值域已知而且很大,做第 k 大查询时为什么有时线段树比平衡树更合适?

答案: 如果值域已知,比如数字都落在 [1, 10^7] 或经过离散化后范围明确,那么线段树或树状数组会很有优势。

原因主要有:

  • 它们天然适合做频次统计
  • 插入删除本质上就是单点加减
  • 第 k 大查询可以通过区间计数往下二分
  • 复杂度稳定,不依赖随机数据分布

相比之下,平衡树更适合:

  • 值域非常分散
  • 不方便离散化
  • 需要保留原始有序结构

线段树的代价则是:

  • 值域过大时空间压力大
  • 实现复杂度比直接用 STL 高
  • 如果是动态开点,工程复杂度会进一步上升

6. 现在有四个连接请求,前面三个已经收到了,需要把第四个拼起来,要求高并发,应该怎么做?

答案: 如果一个完整请求被拆成多次到达,那么服务端不能假设每次 recv 就是一个完整包,而应该:

  • 为每个连接维护独立接收缓冲区
  • 收到数据后追加到缓冲区
  • 根据协议头或长度字段判断一个完整包是否已经到齐
  • 到齐后切包处理,剩余数据继续保留

高并发下比较关键的点有:

  • 缓冲区不能频繁扩容和拷贝
  • 协议设计最好有固定头部或长度字段
  • 一个连接一个状态机,不要把多个连接数据混在一起
  • 解析和业务处理尽量解耦,避免 IO 线程被重逻辑阻塞

比较典型的做法是:

  • IO 线程负责收包和分包
  • 完整消息投递到工作线程池
  • 每个连接维护 read buffer 和 parse state

代码:

struct Conn {    std::string buffer;};​void onRead(Conn& c, const char* data, size_t len) {    c.buffer.append(data, len);​    while (true) {        if (c.buffer.size() < 4) return; // 假设前4字节表示包长        uint32_t msgLen = *reinterpret_cast<const uint32_t*>(c.buffer.data());        if (c.buffer.size() < 4 + msgLen) return;​        std::string msg = c.buffer.substr(4, msgLen);        handleMessage(msg);        c.buffer.erase(0, 4 + msgLen);    }}

7. Reactor 和 Proactor 有什么区别?如果是高并发连接场景你会怎么选?

答案: 两者的核心区别在于:

  • Reactor:内核通知“你可以读/写了”,真正的读写由应用自己做
  • Proactor:应用先提交异步 IO 请求,内核完成后通知“已经做完了”

在 Linux 常见服务端开发里,更多接触到的是 epoll + 非阻塞 socket,这更偏 Reactor 模型。 原因很现实:

  • 生态成熟
  • 编程模型清晰
  • 和线程池、状态机结合方便
  • 在高并发连接场景里非常常见

8. 现在有一个很大的数需要得到相乘之后的结果,使用 int 肯定溢出,怎么做?思路以及时间复杂度?

答案: 最直接的思路就是字符串模拟大整数乘法。 可以把两个数字按字符存储,然后模拟我们手算乘法的过程:

  • 从最低位开始逐位相乘
  • 把乘积累加到结果数组的对应位置
  • 最后统一处理进位
  • 去掉前导零后输出结果

如果两个数长度分别是 n 和 m,那么这种做法的时间复杂度通常是:

  • 时间复

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

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

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

全部评论

相关推荐

zbk1:学院本找嵌入式我觉得不太行,不要被培训班忽悠了,老老实实读个研吧。
点赞 评论 收藏
分享
刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结:&nbsp;27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
哪些公司开暑期实习了?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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