C++ 是技术面试中的高频考察测点,涉及语法特性、内存管理、面向对象、STL 等多个领域。以下整理了常见面试题及核心要点,帮助系统梳理知识点:
一、基础语法与关键字
1. const
关键字的用法
- 修饰变量:变量只读,初始化后不可修改(
const int a = 5;
)。 - 修饰指针:
const int* p
:指针指向的内容不可修改(*p 只读)。int* const p
:指针本身不可修改(p 只读)。const int* const p
:指针及其指向的内容均不可修改。
- 修饰函数参数:防止参数被意外修改(
void func(const int& x)
),常用于引用传递避免拷贝。 - 修饰类成员函数:函数不可修改类的非静态成员变量(
void print() const
),常搭配 mutable
关键字突破限制。
2. 指针与引用的区别
特性 | 指针 | 引用 |
---|
定义 | 存储地址的变量(int* p = &a; ) | 变量的别名(int& r = a; ) |
初始化 | 可空(int* p = nullptr; ) | 必须初始化且不可空 |
指向修改 | 可重新指向其他变量(p = &b; ) | 一旦绑定,无法更改指向 |
多级形式 | 支持多级指针(int**p ) | 无多级引用 |
sizeof 结果 | 地址长度(如 64 位系统为 8 字节) | 被引用变量的大小 |
操作 | 需解引用(*p = 5; ) | 直接使用(r = 5; ) |
3. static
关键字的作用
4. 函数重载(Overload)与重写(Override)的区别
5. inline
内联函数的特点
- 编译期替换:编译器将函数体嵌入调用处,减少函数调用开销(避免栈帧创建/销毁)。
- 适用场景:短小函数(如getter/setter),长函数会导致代码膨胀。
- 与宏的区别:内联函数有类型检查和作用域限制,宏仅为文本替换(无语法检查)。
二、面向对象(OOP)
1. 封装、继承、多态的概念
- 封装:将数据(成员变量)和操作(成员函数)捆绑,通过
public/private/protected
控制访问,隐藏实现细节。 - 继承:派生类复用基类的属性和方法,支持代码复用与扩展(如
class Derived : public Base
)。 - 多态:同一接口(基类指针/引用)调用不同派生类的实现,需通过虚函数实现动态绑定(运行时确定调用版本)。
2. 虚函数与纯虚函数
- 虚函数:基类中用
virtual
修饰的函数,允许派生类重写,实现多态。virtual void func() { }
- 纯虚函数:基类中声明但不实现的函数(
virtual void func() = 0;
),包含纯虚函数的类为抽象类,无法实例化,派生类必须实现纯虚函数才能实例化。
3. 虚析构函数的作用
4. 菱形继承问题及解决
- 问题:派生类间接继承同一基类多次(如
A→B
、A→C
,D
继承 B
和 C
),导致 D
包含 A
的多份数据,访问时歧义。 - 解决:虚继承(
class B : virtual public A
),确保基类 A
在派生类中仅存在一份实例。
三、内存管理
1. new/delete
与 malloc/free
的区别
特性 | new/delete | malloc/free |
---|
类型 | 运算符 | 标准库函数 |
类型检查 | 自动匹配类型,返回对应指针 | 返回 void* ,需手动强转 |
构造/析构 | 自动调用构造函数/析构函数 | 仅分配/释放内存,不处理对象生命周期 |
失败处理 | 抛出 bad_alloc 异常 | 返回 NULL |
重载 | 可重载 operator new/delete | 不可重载 |
2. 堆(Heap)与栈(Stack)的区别
- 栈:由编译器自动管理,存储局部变量、函数参数、返回地址等,大小固定(通常几 MB),操作速度快,遵循“先进后出”。
- 堆:由程序员手动管理(
new/malloc
分配,delete/free
释放),大小动态(可达 GB 级),操作速度慢,易产生内存碎片。
3. 智能指针(C++11)
4. 内存泄漏的常见原因
- 动态分配的内存未释放(
new
后未 delete
,malloc
后未 free
)。 - 指针被意外修改(如野指针、悬挂指针)。
- 容器或智能指针使用不当(如
shared_ptr
循环引用)。 - 检测工具:Valgrind(Linux)、AddressSanitizer、Visual Studio 内存诊断。
四、STL(标准模板库)
1. 常见容器的底层实现
容器 | 底层实现 | 特点 |
---|
vector | 动态数组 | 连续内存,支持随机访问,尾部插入/删除高效 |
list | 双向链表 | 非连续内存,任意位置插入/删除高效,无随机访问 |
deque | 分段连续内存(双端队列) | 头部/尾部操作高效,支持随机访问 |
map/set | 红黑树(自平衡BST) | 按键有序,插入/删除/查找复杂度 O(log n) |
unordered_map/set | 哈希表 | 无序,平均复杂度 O(1),最坏 O(n) |
2. vector
迭代器失效场景
- 插入元素:若触发扩容(重新分配内存),所有迭代器失效;未扩容时,插入位置及之后的迭代器失效。
- 删除元素:删除位置及之后的迭代器失效,可通过返回值更新迭代器(
it = vec.erase(it)
)。
3. map
与 unordered_map
的区别
- 底层结构:
map
基于红黑树,unordered_map
基于哈希表。 - 顺序:
map
按键升序排列,unordered_map
无序。 - 性能:
unordered_map
平均查找更快(O(1)),但内存开销大;map
在频繁插入删除时更稳定。
五、C++11 及以上新特性
1. 移动语义与完美转发
- 移动语义:通过右值引用(
&&
)转移资源所有权,避免不必要的拷贝(如 vector
扩容时的元素移动)。 - 完美转发:通过
std::forward
保持参数的左值/右值属性,用于模板函数传递参数。
2. Lambda 表达式
3. 其他特性
- 范围 for 循环:
for (auto& x : container) { ... }
。 - 线程库:
std::thread
支持多线程编程。 - 原子操作:
std::atomic
实现无锁同步。
六、总结
C++ 面试注重基础语法细节、内存管理能力、面向对象设计及 STL 应用。除理论知识外,需结合编程题练习(如链表操作、排序算法、动态规划等),理解时间/空间复杂度优化,才能在面试中应对自如。