第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)★★★☆☆所有大厂:区分基础与进阶
智能指针回答框架 原理:RAII + 控制块 场景:资源获取/异常安全 注意事项:循环引用/性能 加分点:C++11/14 新特性 unique_ptr独占 / shared_ptr计数原子 工厂函数、动态数组、自定义删除器 weak_ptr打破循环、额外开销 移动语义/make_shared/final/override

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++ 的掌握,这正是大厂面试的侧重点。