C++ -- 面试考题(一)
·
1. const 与 #define 的区别
- 处理阶段:
#define在预处理阶段进行文本替换;const在编译阶段处理。 - 类型检查:
#define没有类型,不进行类型安全检查;const有具体数据类型,编译器会进行类型检查。 - 调试支持:
const常量可以被调试器跟踪;#define宏在预处理后消失,难以调试。 - 内存分配:
const通常会分配内存(存储在数据段);#define只是代码展开,不分配内存。
2. sizeof 和 strlen 的区别
sizeof是运算符,计算的是变量或类型所占用的内存字节数,在编译时确定。对于数组,它返回整个数组的大小。strlen是库函数,计算的是字符串的有效长度(不包含结尾的\0),在运行时确定。
3、引用与指针的区别
- 本质:指针是一个实体,存放地址,需要分配内存;引用是变量的别名,底层通常由指针实现,但不单独分配内存空间。
- 初始化:引用必须在定义时初始化,且一旦绑定不能改变指向;指针可以不初始化,且可以随时改变指向。
- 多级:有多级指针(如
int**),但没有多级引用。 - 自增运算:指针自增指向下一个内存地址;引用自增则是所绑定变量的值增加。
- 空值:指针可以为
nullptr;引用必须绑定有效对象,不存在空引用。
4、malloc/free 与 new/delete 的区别
- 属性:
malloc/free是 C 语言标准库函数;new/delete是 C++ 运算符。 - 构造/析构:
new在分配内存后会自动调用构造函数,delete在释放内存前会自动调用析构函数;malloc/free仅负责内存分配与释放,不涉及对象生命周期管理。 - 返回值:
malloc返回void*,需强制类型转换;new返回指定类型的指针,无需转换。 - 失败处理:
malloc失败返回NULL;new失败抛出std::bad_alloc异常。 - 大小计算:
malloc需手动指定字节数;new自动计算类型大小。
5、delete 与 delete[] 的区别
delete用于释放单个对象内存,调用一次析构函数。delete[]用于释放数组内存,会为数组中的每个元素调用析构函数,然后释放内存。- 注意:对于基本数据类型(如
int),两者效果通常相同;但对于自定义类对象,混用会导致未定义行为(如内存泄漏或析构不完全)。
6、如何避免野指针?
- 指针定义时初始化为
nullptr。 - 指针释放后立即置为
nullptr。 - 避免返回局部变量的地址或引用。
- 使用智能指针(
shared_ptr,unique_ptr)管理资源,利用 RAII 机制自动释放。
7、内存泄漏与智能指针
- RAII (Resource Acquisition Is Initialization):资源获取即初始化,利用对象生命周期管理资源。
-
unique_ptr:独占所有权,不可拷贝,可移动。轻量级,性能开销小。 -
shared_ptr:共享所有权,通过引用计数管理。当计数为 0 时释放资源。注意循环引用问题。 -
weak_ptr:配合shared_ptr使用,不增加引用计数,用于解决循环引用。可通过expired()判断对象是否存活,通过lock()获取shared_ptr。
8、多态的实现原理
- 静态多态:通过函数重载和模板实现,在编译期确定。
- 动态多态:通过虚函数实现,在运行期确定。
- 底层机制:包含虚函数的类会有一个虚函数表 (vtable),对象头部有一个虚指针 (vptr) 指向该表。调用虚函数时,通过 vptr 找到 vtable,再找到对应的函数地址进行调用。
9、虚函数与纯虚函数
- 虚函数:允许子类重写,基类可以提供默认实现。
- 纯虚函数:声明为
virtual void func() = 0;,基类无实现,包含纯虚函数的类称为抽象类,不能实例化,强制子类实现该接口。
10、构造函数与析构函数
- 构造函数:不能是虚函数(因为对象尚未完全构建,vptr 未初始化)。
- 析构函数:基类析构函数应声明为虚函数。如果基类析构函数不是虚函数,通过基类指针删除派生类对象时,只会调用基类析构函数,导致派生类部分资源泄漏。
- 初始化列表:以下情况必须使用初始化列表:
- 成员是引用类型。
- 成员是
const类型。 - 成员类没有默认构造函数。
- 调用父类带参构造函数。
11、vector 与 list 的区别
-
vector:动态数组,内存连续。支持随机访问(O(1)),尾部插入删除高效(O(1)),中间插入删除低效(O(n),需移动元素)。扩容时可能涉及内存重新分配和数据拷贝。 -
list:双向链表,内存不连续。不支持随机访问(O(n)),任意位置插入删除高效(O(1),只需修改指针)。
12、map 与 unordered_map 的区别
-
map:基于红黑树实现,键值对有序,查找、插入、删除时间复杂度为 O(log n)。 -
unordered_map:基于哈希表实现,键值对无序,平均查找、插入、删除时间复杂度为 O(1),最坏情况 O(n)。
13、迭代器失效
-
vector/deque:执行erase或insert后,指向被操作元素及其之后元素的迭代器失效。vector扩容会导致所有迭代器失效。 -
list:erase仅使指向被删除元素的迭代器失效,其他迭代器依然有效。 -
map/set:erase仅使指向被删除元素的迭代器失效,其他迭代器有效(因为红黑树节点独立分配)。
14、右值引用与移动语义 (move)
- 左值:有名称、可取地址的对象。
- 右值:临时对象、字面量等。
- 移动语义:通过
std::move将左值转换为右值引用,触发移动构造函数或移动赋值运算符,从而“窃取”资源(如指针指向的内存),避免深拷贝,提升性能。
15、完美转发 (forward)
- 在模板编程中,为了保持参数的左右值属性不变地传递给内部函数,使用
std::forward<T>(arg)。结合万能引用 (T&&) 使用。
更多推荐


所有评论(0)