第2章 C++核心基础面试:内存管理、智能指针、虚函数
本章聚焦 C++ 面试最高频的三大基础点:内存管理(new/delete 与 RAII)、智能指针(unique_ptr / shared_ptr)和虚函数(虚表、虚析构、多态)。回答时需遵循 原理 → 场景 → 注意事项 的框架,并结合 C++11/14 新特性(移动语义、override、final)展示深度。以下表格梳理了各考点的考试频率与公司侧重点。
| 高频考点 | 面试频率 | 公司侧重点 |
| new/delete vs malloc/free + RAII | ★★★★★ | 字节、腾讯:考察异常安全与资源管理 |
| 智能指针(unique_ptr/shared_ptr) | ★★★★★ | 阿里、美团:C++11 特性、引用计数线程安全 |
| 虚函数与虚析构 | ★★★★☆ | 百度、华为:多态实现与性能代价 |
| C++11/14 新特性(override/final) | ★★★☆☆ | 所有大厂:区分基础与进阶 |
Q1:new/delete 与 malloc/free 的区别?如何用智能指针避免内存泄漏?
| 考察点 | 理解 RAII 与手动管理的本质区别;是否掌握 C++11 智能指针的核心优势。 |
| 回答框架 | ① 原理:new 调用构造/析构,malloc 仅分配内存;② 场景:对象生命周期复杂时优先 RAII;③ 注意事项:避免混用、数组用 unique_ptr<T[]>。 |
| 示例回答 | new/delete 自动调用构造和析构函数,malloc/free 仅操作内存。使用 unique_ptr 管理动态对象,离开作用域自动释放,无需手动 delete。例如 auto p = make_unique<Foo>() 保证异常安全。 |
| 可能追问 | 如何为智能指针指定自定义删除器?数组型 unique_ptr 的释放方式? |
| 避坑提醒 | 不要用 delete 释放 malloc 分配的内存,反之亦然;不要用 shared_ptr 管理数组(除非传入自定义删除器)。 |
Q2:shared_ptr 和 unique_ptr 的线程安全性?引用计数如何实现?
| 考察点 | 是否理解智能指针内部控制块机制;对多线程下引用计数的原子操作有正确认知。 |
| 回答框架 | ① 原理:控制块含 ref_count(原子变量),shared_ptr 拷贝时 +1,析构时 -1;② 场景:共享所有权时用 shared_ptr,独占用 unique_ptr;③ 注意事项:引用计数线程安全,但所管理对象本身不保证,需外部加锁。 |
| 示例回答 | shared_ptr 引用计数通过 std::atomic 操作实现线程安全,但多个线程同时读写指向的对象需互斥锁。unique_ptr 不支持拷贝,移动语义保证唯一所有权,零额外开销。 |
| 可能追问 | weak_ptr 如何解决循环引用?为什么 make_shared 比直接 new 更推荐? |
| 避坑提醒 | 不要将同一原生指针传给多个 shared_ptr 构造,会导致多个独立控制块造成 double free;优先使用 make_shared 以合并内存分配。 |
Q3:虚函数表如何工作?为什么基类析构函数必须为虚?C++11 的 override/final 有何作用?
| 考察点 | 虚函数动态绑定原理;多态正确释放的重要性;C++11 新增关键词对代码可维护性的提升。 |
| 回答框架 | ① 原理:每个含虚函数的类维护 vtable,对象首部有 vptr 指向对应 vtable;② 场景:需要通过基类指针/引用调用派生类方法时;③ 注意事项:构造函数/析构函数中虚函数非多态,避免在内部调用未完成初始化的派生类函数。 |
| 示例回答 | 基类析构非虚时,delete 基类指针只会调用基类析构,导致派生类资源泄漏。C++11 的 override 让编译器检查重写签名匹配,final 禁止进一步继承或重写,减少设计错误。 |
| 可能追问 | 虚表是否线程安全?纯虚函数与虚函数的区别? |
| 避坑提醒 | 不要在构造函数或析构函数中调用虚函数(实际调用当前类的版本);不要遗漏基类虚析构,否则派生类持有 unique_ptr 资源时无法释放。 |
💡 加分提示: 回答时主动提及 C++11/14 新特性(如 make_unique C++14、override C++11、移动语义、lambda 与智能指针结合),可体现对现代 C++ 的掌握,这正是大厂面试的侧重点。