一、C++基础语法与语言机制
1.1 指针与引用
1. 指针和引用的本质区别?
答案:
- 指针是独立变量,占用内存空间,存储地址,可为空,支持算术运算(++、--、±整数)和多级间接寻址(**p)。
- 引用是目标变量的别名,不占独立内存(编译器实现为常量指针),必须在定义时初始化且不能重新绑定到其他对象,不能为空,不支持算术运算。
【字节跳动 Linux C/C++ 后端一面;网易有道C++软件开发实习生一面】
2. 什么情况下用指针,什么情况下用引用?
答案:
- 用引用:函数参数传递(避免拷贝)、运算符重载(如[]、=需返回引用)、返回对象本身(链式调用)。
- 用指针:动态分配内存(new/delete)、需要表示"无对象"(nullptr)、需要改变指向、需要遍历数组(指针算术)。
【腾讯 C++/Go 后台开发 通用高频】
1.2 const 与 static
3. const的各种用法?顶层const与底层const区别?const成员函数的意义?
答案:
- 顶层const:指针本身是常量(int *const p);底层const:指针指向的对象是常量(const int *p)。
- const成员函数承诺不修改对象成员(mutable成员除外),可被常对象调用。
【小米 C++ 软件开发一面;阿里云 C++ 研发】
4. static关键字的三种使用场景?
答案:
- 文件/全局作用域:限制变量/函数的作用域为本文件(内部链接)。
- 局部静态变量:生命周期为整个程序,只在首次执行时初始化,函数退出后值保留。
- 类静态成员:属于类而非对象,所有对象共享一份,可在未创建对象时访问。
【小米 C++ 软件开发一面;百度 C++ 岗】
5. volatile关键字的作用及应用场景?
答案:
- 作用:告诉编译器不要优化对该变量的访问(每次从内存读取/写入),防止指令重排。
- 场景:信号处理函数中访问全局变量、内存映射I/O、多核无锁编程中的标志位。
【小米 C++ 软件开发一面】
1.3 内存管理
6. 堆与栈的区别?
答案:
| 特性 | 栈 | 堆 |
|---|---|---|
| 生命周期 | 由编译器自动管理,函数返回即销毁 | 手动new/delete,直至delete |
| 分配速度 | 快(移动栈指针) | 慢(查找空闲块、可能系统调用) |
| 大小限制 | 较小(通常1~8MB,可调整) | 较大(受虚拟内存限制) |
| 碎片 | 无碎片 | 容易产生碎片 |
【百度 C++ 岗一面;网易有道C++软件开发实习生一面】
7. new/delete 与 malloc/free 的核心差异?
答案:
- new会调用构造函数,delete会调用析构函数;malloc/free仅分配/释放内存。
- new是类型安全的,返回具体类型指针;malloc返回void*。
- new失败抛出bad_alloc异常;malloc失败返回NULL。
- new可以被重载(per-class或全局);malloc不可重载。
【阿里 C++ 研发(杭州站)】
8. 什么是内存对齐?对性能有何影响?
答案:
- 内存对齐:将数据存放在其自然边界(如4字节int放在地址%4==0处),提高CPU访问效率。
- 性能影响:对齐时CPU一次读取可完成;未对齐可能需要多次内存访问,甚至总线错误。
- 可通过#pragma pack或alignas控制对齐。
【腾讯后台开发】
9. 内存碎片产生原因及优化策略?
答案:
- 原因:频繁分配释放不同大小内存导致不连续空闲块。
- 优化:使用内存池(如tcmalloc、jemalloc)、分配器定期合并相邻空闲块、采用伙伴系统或slab分配器。
【腾讯后台开发;华为 C++ 后端开发】
1.4 移动语义与完美转发
10. 移动语义是什么?移动构造相比拷贝构造的性能优势?
答案:
- 移动语义:通过右值引用(T&&)将资源(如堆内存、文件句柄)"窃取"到新对象,避免深拷贝。
- 优势:对管理资源的类(如vector、string),移动构造仅需复制指针/句柄,时间复杂度O(1);拷贝构造需完整复制数据,O(n)。
【字节跳动 C++ 后端】
11. std::move 做了什么?
答案:
- std::move本质是static_cast<T&&>(),将左值强制转换为右值引用,不移动任何东西,仅使对象可被移动。
【字节跳动 C++ 后端】
12. 完美转发的实现原理及使用场景?
答案:
- 原理:利用万能引用(T&&)+std::forward,保持实参的左右值属性传递给内层函数。
- 场景:工厂函数、包装器(如std::make_unique、std::async)等需要保留参数原始类型的模板。
【字节跳动 C++ 后端;阿里 C++ 研发】
1.5 深拷贝与浅拷贝
13. 手写 String 类(简述关键点)?
答案:
- 三/五法则:析构函数、拷贝构造函数、拷贝赋值运算符(以及移动构造、移动赋值)。
- 深拷贝:分配新内存并复制内容;浅拷贝仅复制指针(导致双重释放)。
- 现代实现可使用写时复制(COW)或移动语义优化性能,需注意线程安全。
【阿里 C++ 研发(杭州站);腾讯 C++ 后台;字节跳动 C++ 后端】
二、面向对象与对象模型
2.1 多态与虚函数
14. 虚函数的实现原理?
答案:
- 每个含虚函数的类有一个虚函数表(vtable),存放虚函数指针(按声明顺序)。
- 每个对象有一个虚指针(vptr),指向所属类的 vtable。
- 动态绑定:通过 vptr 查表调用实际函数。多继承下对象有多个 vptr。
【阿里 C++ 研发(杭州站);网易有道C++软件开发实习生】
15. 纯虚函数作用?抽象类与接口区别?
答案:
- 纯虚函数(
=0)使类成为抽象类,不能实例化,要求派生类必须实现。 - 接口是只有纯虚函数(无成员变量)的抽象类,C++中可通过纯虚类模拟。
【腾讯 C++/Go 后台开发】
16. 虚析构函数的作用?什么时候必须使用?
答案:
- 作用:当通过基类指针 delete 派生类对象时,若析构函数非虚,则只调用基类析构,导致派生部分未释放(资源泄漏)。
- 必须使用场景:任何作为多态基类的类都应定义虚析构函数。
【阿里/腾讯 C++ 通用】
17. 菱形继承问题及虚继承解决方案?
答案:
- 菱形继承:B和C继承A,D继承B和C,导致D中包含两份A的成员,访问二义性。
- 解决:使用虚继承(virtual继承),A成为虚基类,D中只包含一份A,通过偏移量表访问。
【阿里 C++ 研发;字节跳动 C++ 后端】
18. 虚函数表存放在哪个内存段?
答案:
- 通常存放在只读数据段(.rodata),或者可执行代码段,因平台/编译器而异。
【字节/腾讯 C++ 开发】
19. 能否有虚构造函数?为什么?
答案:
- 不能。构造函数调用时对象尚未完全构建,vptr还未正确设置,无法动态绑定。
【阿里 C++ 研发】
2.2 类与对象
20. this 指针的本质?
答案:
- this 是非静态成员函数的隐含形参,指向当前对象,类型为 ClassType* const(在const成员函数中为 const ClassType* const)。
【腾讯/字节 C++ 通用】
21. 面向对象三大特性及落地细节?
答案:
- 封装:访问限定符(public/protected/private),隐藏内部实现。
- 继承:复用基类代码,支持多态。
- 多态:虚函数实现动态类型行为。
【华为 C++ 后端开发】
三、STL 与容器
22. vector 底层实现及扩容机制?
答案:
- 底层连续内存,维护 start、finish、end_of_storage 三个指针。
- 扩容:当 size == capacity 时重新分配新内存(通常为原capacity的2倍或1.5倍),拷贝/移动旧元素,释放原内存。
- 时间复杂度:push_back 摊还 O(1),单次扩容 O(n)。
【阿里 C++ 研发(杭州站)】
23. list 底层实现及 size 函数复杂度?
答案:
- 双向循环链表,每个节点含前后指针。
- C++11 前 size 为 O(n)(遍历),C++11 后标准要求 O(1),通常用成员变量保存大小。
【阿里 C++ 研发】
24. vector vs list 应用场景?
答案:
- vector:随机访问频繁,尾部插入删除多。
- list:中间任意位置插入删除频繁,不需要随机访问。
【阿里 C++ 研发校招】
25. 迭代器失效场景及避免方法?
答案:
- vector:插入/删除导致重新分配或元素移动时,所有迭代器失效。
- list:删除当前迭代器失效,其他不变;插入不失效。
- map/unordered_map:插入不失效(红黑树/哈希表重新哈希时可能失效)。
- 避免:插入后重新获取迭代器,使用返回值(如 erase 返回下一个迭代器)。
【阿里 C++ 研发杭州站 必问】
26. map 与 unordered_map 底层及时间复杂度?
答案:
- map:红黑树,有序,增删查改 O(log n)。
- unordered_map:哈希表(桶+拉链法),无序,平均 O(1),最坏 O(n)。
【小米 C++ 软件开发;阿里校招】
27. 哈希冲突的解决方式及哈希函数影响?
答案:
- 解决:拉链法(链表/红黑树)、开放地址法(线性探测/二次探测/双重哈希)。
- 哈希函数质量影响:冲突率、性能,需分布均匀。
【蚂蚁集团 基础架构/数据库方向】
四、智能指针与 RAII
28. C++11 智能指针与 auto_ptr 的区别?
答案:
- auto_ptr 使用拷贝语义实现转移(已弃用),不安全。
- unique_ptr 独占所有权,禁止拷贝,支持移动;shared_ptr 引用计数;weak_ptr 配合打破循环引用。
【字节跳动 C++ 后端】
29. unique_ptr、shared_ptr、weak_ptr 使用场景?
答案:
- unique_ptr:明确独占资源的场景(如工厂函数返回值)。
- shared_ptr:多个所有者需要共享同一资源(如图缓存)。
- weak_ptr:观察 shared_ptr 资源但不增加引用计数,解决循环引用。
【网易有道C++软件开发实习生;小米 C++ 软件开发】
30. unique_ptr 为何不支持拷贝构造?
答案:
- 设计意图:独占所有权,拷贝会导致多个指针管理同一块内存,析构时重复释放。
- 可通过移动(std::move)转移所有权。
【蚂蚁C++岗】
31. shared_ptr 引用计数为何设计为原子类型?
答案:
- 保证多线程环境下引用计数的线程安全(不同线程拷贝/销毁同一个 shared_ptr 时)。
- 注意:管理的对象本身不是线程安全的。
【蚂蚁C++岗;字节客户端(剪映)】
32. shared_ptr 循环引用如何解决?
答案:
- 使用 weak_ptr 打破:一个成员用 shared_ptr,另一个用 weak_ptr。
- weak_ptr 可以从 shared_ptr 构造,不增加引用计数,使用 lock() 获取临时 shared_ptr。
【阿里巴巴 C++ 后端;腾讯 C++ 后端】
33. 什么是 RAII?举例说明。
答案:
- RAII(Resource Acquisition Is Initialization):资源获取即初始化,利用对象生命周期管理资源。
- 例子:智能指针、std::lock_guard(构造函数加锁,析构函数解锁)。
【蚂蚁C++岗;华为 C++ 后端开发】
五、C++ 新特性(11/14/17/20/23)
34. C++11 的 auto 与 decltype?
答案:
- auto:根据初始化表达式自动推导类型,会忽略顶层 const 和引用。
- decltype:从表达式推导精确类型(保留 const 和引用)。
【网易有道C++软件开发实习生】
35. Lambda 表达式及捕获方式?
答案:
- 形式:
[capture](params) mutable -> ret { body } - 捕获:值捕获 [=]、引用捕获 [&]、混合([=, &x])、this 捕获([this])、泛化捕获(C++14)。
【网易有道C++软件开发实习生】
36. C++11 的并发库有哪些?
答案:
- thread、mutex、condition_variable、future/promise、async、atomic。
【腾讯 C++/Go 后台开发】
37. C++17 结构化绑定?
答案:
- 将结构体/元组/数组拆包到独立变量,如
auto [a, b] = std::make_pair(1, 2);
【字节跳动 C++ 后端】
38. C++20 协程的关键字及作用?
答案:
- co_await 挂起等待、co_yield 产出值、co_return 返回。
- 协程实现无栈异步编程,比线程更轻量(状态保存在堆上)。
【字节客户端(剪映)】
六、并发编程
39. 互斥锁、读写锁、自旋锁的区别及适用场景?
答案:
- 互斥锁:最通用,等待线程阻塞(适合临界区较长)。
- 读写锁:允许多个读或一个写,读多写少场景。
- 自旋锁:忙等待,适用于临界区极短且不允许睡眠(如中断上下文)。
【小米 C++ 软件开发;蚂蚁集团基础架构】
40. 手写生产者-消费者模型(使用条件变量)核心代码?
答案:
cpp
std::mutex m;
std::condition_variable cv;
std::queue<int> q;
// 生产者
{
std::lock_guard lk(m);
q.push(data);
cv.notify_one();
}
// 消费者
std::unique_lock lk(m);
cv.wait(lk, []{ return !q.empty(); });
int val = q.front(); q.pop();
【通用高频】
41. 死锁产生原因及避免方法?
答案:
- 原因:多个线程互相持有对方需要的资源且不释放。
- 避免:按固定顺序加锁、使用 std::lock 一次性锁定多个 mutex、超时尝试等。
【腾讯 C++/Go 后台开发】
42. C++ 内存顺序模型(memory_order)?
答案:
- relaxed:仅保证原子性,无同步。
- acquire:读操作,后续读写不可重排到此操作之前。
- release:写操作,之前读写不可重排之后。
- acq_rel:同时满足 acquire 和 release。
- seq_cst:全顺序一致性(默认)。
【字节 C++ 后端】
43. i++ 是原子操作吗?为什么?
答案:
- 不是。i++ 包含读、修改、写三步,存在竞争条件。
- 多线程环境中需要使用原子操作(std::atomic)或加锁。
【百度 C++ 岗】
44. 协程与线程的区别?
答案:
- 协程用户态调度,切换代价极低(无需陷入内核),但并发是主动让出(非抢占)。
- 线程内核调度,抢占式,切换开销大。
【腾讯后台开发(腾讯云)】
七、网络编程
45. TCP 服务端编程流程?
答案:
socket() → bind() → listen() → accept() → recv()/send() → close()
【腾讯后台开发;网易游戏开发】
46. select/poll/epoll 区别?
答案:
- select:fd上限1024,每次扫描全部fd,数据需拷贝。
- poll:链表无上限,仍要扫描全部fd。
- epoll:事件驱动,只返回就绪fd,mmap 减少拷贝,支持 ET/LT。
【腾讯后台开发;华为 C++ 后端】
47. epoll 的 ET 与 LT 区别?
答案:
- LT(水平触发):未处理完的事件下次仍会通知。
- ET(边缘触发):仅状态变化时通知一次,需一次性读写完,否则丢失。
【华为 C++ 后端开发】
48. 三次握手过程?半连接/全连接队列是什么?
答案:
- 第一次 client SYN,第二次 server SYN+ACK(进入半连接队列),第三次 client ACK(进入全连接队列)。
- 队列溢出可调整 backlog 或 /proc/sys/net/ipv4/tcp_max_syn_backlog。
【腾讯 C++/Go 后台开发】
49. 四次挥手,为什么需要四次?TIME_WAIT 作用?
答案:
- 四次:因为 TCP 是全双工,每个方向单独关闭。
- TIME_WAIT:等待 2MSL 保证 ACK 到达,让旧重复报文消亡,防止对新连接干扰。
【腾讯 C++/Go 后台开发】
50. TCP 如何保证可靠传输?
答案:
- 序列号、确认应答、超时重传、校验和、滑动窗口(流量控制)、拥塞控制(慢启动、拥塞避免、快速重传/恢复)。
【腾讯 C++/Go 后台开发】
51. 粘包/半包原因及解决?
答案:
- 原因:TCP 字节流协议,无边界。
- 解决:固定长度、分隔符(如\r\n)、长度字段(TLV)。
【腾讯/字节 C++ 后端】
52. UDP 上如何实现可靠传输?
答案:
- QUIC(基于UDP)、KCP、RUDP:增加序列号、确认、重传、流量控制等机制。
【小米 P2P 传输开发】
53. HTTP 版本演进及 HTTPS 加密原理?
答案:
- HTTP1.0 短连接,1.1 长连接,2.0 多路复用+HPACK,3.0 QUIC。
- HTTPS:TLS 握手交换对称密钥(使用非对称加密认证),后续对称加密。
【字节跳动 Linux C/C++ 后端;阿里云后端】
八、操作系统与 Linux
54. 进程与线程的区别?
答案:
- 进程是资源分配单位,线程是 CPU 调度单位。
- 进程有独立地址空间,线程共享进程地址空间。
- 线程切换开销更小。
【小米 C++ 软件开发;腾讯后台开发】
55. C++ 进程内存布局?
答案:
高地址 → 低地址:栈 → 堆 → 数据段(.data/.bss)→ 代码段(.text)
【网易iOS开发;百度 C++ 岗】
56. 进程间通信方式有哪些?
答案:
管道(匿名/命名)、共享内存、消息队列、信号量、信号、套接字。
【百度 C++ 岗】
57. 僵尸进程如何产生及处理?
答案:
- 子进程先于父进程结束,但父进程未调用 wait/waitpid 回收,子进程变成僵尸。
- 处理:父进程调用 wait,或者忽略 SIGCHLD 并设置 SA_NOCLDWAIT。
【腾讯后台开发】
58. 如何调试 Core Dump?
答案:
ulimit -c unlimited开启 core,gdb program core,使用 bt(backtrace)、frame、print 等。
【字节 C++ 后端】
九、数据库与中间件
59. 事务隔离级别及解决的问题?
答案:
- 读未提交:脏读。读已提交:不可重复读。可重复读(MySQL默认):幻读。串行化:无并发问题。
【字节后端实习;阿里校招】
60. MVCC 实现原理?
答案:
- 每行记录隐藏字段:DB_TRX_ID(最后修改事务ID)、DB_ROLL_PTR(回滚指针指向前版本)。
- Read View 判断可见性,实现快照读。
【阿里校招 MySQL】
61. B+树索引 vs 哈希索引?
答案:
- B+树:支持范围查询,有序,磁盘友好。
- 哈希:仅等值查询 O(1),不支持范围。
【阿里校招;网易游戏开发】
62. Redis 缓存穿透、雪崩及布隆过滤器?
答案:
- 穿透:查询不存在的数据,绕过缓存直接打 DB → 布隆过滤器。
- 雪崩:大量缓存同时过期,请求击穿 DB → 不同过期时间、互斥锁、异步更新。
- 布隆过滤器:位数组 + 多个哈希函数,存在一定误判(不存在一定不存在,存在不一定存在)。
【阿里校招;字节抖音电商】
63. 分布式 CAP 理论?
答案:
- 一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance) 只能同时满足两个。
- 多数分布式系统选择 AP(如 Eureka)或 CP(如 Zookeeper)。
【腾讯分布式系统题】
十、项目与系统设计
64. 如何优化 QPS 从 1k 到 10w?
答案:
- 代码层:缓存(CDN、Redis)、异步(消息队列)、池化(连接池、线程池)。
- 架构层:负载均衡、水平扩容、读写分离、分库分表。
- 数据库:索引优化、SQL 改写。
【阿里/字节/腾讯】
65. 线程池设计核心要素?如何设置线程数?
答案:
- 核心参数:核心线程数、最大线程数、任务队列、拒绝策略。
- CPU 密集型:线程数 = CPU 核数 + 1;IO 密集型:线程数 = CPU 核数 * (1 + IO时间/CPU时间)。
【字节二面 -- 1000 万短信 1 小时发完】
66. 设计短链系统(常见系统设计题)?
答案:
- 核心:长链 → 唯一短码(如发号器 + Base62 编码)→ 存储(KV 数据库:Redis 缓存 + DB 持久化)。
- 重定向:302 临时重定向(可统计点击)。
- 高并发:缓存预加载,读写分离,限流。
【字节跳动后端二面;腾讯系统设计】
67. 限流器设计(令牌桶 vs 漏桶)?
答案:
- 令牌桶:固定速率放令牌,允许突发流量。
- 漏桶:恒定速率流出,平滑流量。
- 分布式限流可用 Redis + Lua 脚本或 Sentinel。
【字节Java后端开发;阿里 P7】
十一、大厂面试风格总结
| 公司 | 核心考察 | 特色题示例 |
|---|---|---|
| 阿里巴巴 | STL 深挖、系统设计、基础原理 | vector 扩容因子为什么是2?迭代器失效所有场景 |
| 蚂蚁集团 | 金融一致性、基础架构 | 己方交易成功但渠道失败怎么发现处置 |
| 腾讯 | 网络、操作系统、LRU | TIME_WAIT 状态详述;手写 LRU 缓存 |
| 字节跳动 | 新特性、高并发、算法 | C++20 协程使用;线程池业务场景设计 |
| 华为 | OS、网络基础 | 守护进程实现;僵尸进程 |
| 百度 | 内存、多线程 | C++ 内存分区;i++ 原子性 |
| 小米 | 分方向(服务端/嵌入式/客户端) | 自旋锁在中断中的使用;UDP 可靠传输设计 |
备考建议:
- 通用题(手写 String、单例模式、生产者消费者)所有公司必掌握。
- 针对目标公司强化其特色模块(阿里看 STL+系统设计;腾讯看网络+OS;字节看新特性+高并发)。
- 面试答题时不仅要给出结论,更要解释"为什么"和底层原理,必要时画图。
祝面试顺利!
更多资源
C++面试题
C语言/C++入门教程