百度 C++软件开发 一面 面经
1. 进程和线程有什么区别?
- 进程是操作系统资源分配的基本单位,拥有独立的地址空间、文件描述符、信号处理等资源
- 线程是 CPU 调度的基本单位,共享所属进程的内存空间和资源,拥有独立的栈和寄存器状态
- 创建和切换线程的开销远小于进程,线程间通信直接读写共享内存,进程间通信需要专门的 IPC 机制
- 一个线程崩溃可能导致整个进程崩溃;一个进程崩溃不影响其他进程,隔离性更好
- 多线程适合共享数据频繁的并发场景;多进程适合需要强隔离、稳定性要求高的场景
2. 为什么推荐用 make_shared 而不是直接 new 来构造 shared_ptr?
- 直接 new 会进行两次内存分配:一次分配对象本身,一次分配控制块(引用计数);make_shared 只做一次内存分配,将对象和控制块放在同一块内存中,减少分配开销和内存碎片
- 异常安全:
shared_ptr<T>(new T(), deleter)这种写法,如果构造函数和 shared_ptr 构造之间抛异常,可能导致内存泄漏;make_shared 不存在这个问题 - 缓存友好:对象和控制块相邻,访问时更可能在同一缓存行
缺点:
- make_shared 的内存只有在强引用和弱引用都归零后才释放,若存在大量 weak_ptr,对象销毁后内存仍被控制块占用,可能延迟内存释放
3. STL 迭代器失效在哪些场景下会发生?以 vector 为例
插入操作:
- 在末尾 push_back 时,若触发扩容(重新分配内存),所有迭代器、指针、引用全部失效
- 在中间 insert 时,插入位置之后的所有迭代器失效,若触发扩容则全部失效
删除操作:
- erase 删除某个元素后,该位置及之后的所有迭代器失效,之前的迭代器仍然有效
正确做法:
- erase 返回值是指向下一个有效元素的迭代器,删除时应使用返回值更新迭代器
- 遍历时删除元素用
it = vec.erase(it)而不是vec.erase(it); it++ - 其他容器:map/set 删除元素只失效被删除的迭代器;list 插入删除不影响其他迭代器
4. TCP 拥塞控制的机制是什么?
拥塞控制的目的是防止发送方发送过快导致网络拥塞,分四个阶段:
- 慢启动:连接建立后拥塞窗口(cwnd)从 1 开始,每收到一个 ACK 翻倍增长,指数增长直到达到慢启动阈值(ssthresh)
- 拥塞避免:cwnd 超过 ssthresh 后改为线性增长,每个 RTT 增加 1,避免增长过快
- 快重传:收到 3 个重复 ACK 时立即重传丢失的包,不等超时,减少等待时间
- 快恢复:快重传后将 ssthresh 设为当前 cwnd 的一半,cwnd 设为 ssthresh,继续拥塞避免而不是回到慢启动
发生超时时:ssthresh 减半,cwnd 重置为 1,重新进入慢启动,惩罚更重。
5. weak_ptr 除了解决循环引用,在 lambda 中如何安全访问可能已销毁的对象?
核心问题:lambda 捕获 shared_ptr 会延长对象生命周期;捕获裸指针或引用则对象销毁后访问是未定义行为。
正确做法是捕获 weak_ptr,在 lambda 内部 lock() 升级为 shared_ptr 再使用:
- lock() 成功说明对象还存在,返回有效的 shared_ptr
- lock() 返回空说明对象已销毁,直接返回不做任何操作
- 这样 lambda 不会延长对象生命周期,也不会出现悬空指针访问
典型场景:异步回调、定时器回调中访问发起请求的对象,对象可能在回调触发前已经析构,用 weak_ptr 可以安全地处理这种情况。
6. TCP 如何实现可靠传输?
TCP 通过以下机制保证可靠性:
- 序列号和确认号:每个字节都有序列号,接收方通过 ACK 确认收到的数据,发送方知道哪些数据已
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++八股文全集 文章被收录于专栏
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。
查看10道真题和解析