C++ 智能指针与内存管理面试题
1. C++11的三种智能指针是什么?
答案:
- unique_ptr独占所有权,不能拷贝,只能移动轻量级,无额外开销适合明确单一所有者的场景
- shared_ptr共享所有权,引用计数最后一个shared_ptr销毁时释放资源有引用计数开销
- weak_ptr不控制对象生命周期配合shared_ptr使用解决循环引用问题
- 已废弃auto_ptr:C++11已废弃,被unique_ptr替代
2. shared_ptr的实现原理是什么?
答案:
- 引用计数维护一个控制块,存储引用计数每次拷贝,引用计数+1每次析构,引用计数-1计数为0时释放资源
- 控制块内容强引用计数(shared_ptr数量)弱引用计数(weak_ptr数量)删除器分配器
- 线程安全引用计数的增减是线程安全的(原子操作)但对象本身的访问不是线程安全的
- 性能开销额外的内存(控制块)原子操作的开销间接访问的开销
3. unique_ptr和shared_ptr的区别?
答案:
- 所有权unique_ptr:独占,不能拷贝shared_ptr:共享,可以拷贝
- 性能unique_ptr:无额外开销,和原始指针一样shared_ptr:有引用计数开销
- 使用场景unique_ptr:明确单一所有者,工厂函数返回值shared_ptr:多个所有者,不确定谁最后释放
- 转换unique_ptr可以转换为shared_ptrshared_ptr不能转换为unique_ptr
- 数组支持unique_ptr支持数组:unique_ptr<int[]>shared_ptr需要自定义删除器
4. weak_ptr的作用是什么?
答案:
- 主要作用打破shared_ptr的循环引用观察对象但不影响生命周期临时访问shared_ptr管理的对象
- 使用方法从shared_ptr构造使用lock()获取shared_ptr使用expired()检查对象是否存在
- 循环引用示例
struct Node { shared_ptr<Node> next; // 循环引用 weak_ptr<Node> prev; // 使用weak_ptr打破};
- 注意事项weak_ptr不能直接访问对象必须先转换为shared_ptr对象可能已被销毁
5. 如何自定义智能指针的删除器?
答案:
- unique_ptr的删除器
auto deleter = [](FILE* fp) { fclose(fp); };unique_ptr<FILE, decltype(deleter)> fp(fopen("file.txt", "r"), deleter);
- shared_ptr的删除器
shared_ptr<FILE> fp(fopen("file.txt", "r"), [](FILE* fp) { fclose(fp); });
- 区别unique_ptr的删除器是类型的一部分shared_ptr的删除器不是类型的一部分
- 使用场景管理非new分配的资源文件句柄、网络连接等数组(shared_ptr需要)
6. make_shared和make_unique的优势?
答案:
- 异常安全
// 不安全:可能内存泄漏func(shared_ptr<int>(new int), other_func());// 安全func(make_shared<int>(), other_func());
- 性能优势make_shared一次分配(对象+控制块)直接构造减少一次内存分配提高缓存局部性
- 代码简洁不需要写new自动推导类型
- 注意事项make_shared不支持自定义删除器对象和控制块一起释放,可能延迟内存释放
7. 智能指针的循环引用问题如何解决?
答案:
- 问题描述两个对象互相持有shared_ptr引用计数永远不为0导致内存泄漏
- 解决方案使用weak_ptr打破循环一方使用shared_ptr,另一方使用weak_ptr通常父节点持有子节点的shared_ptr,子节点持有父节点的weak_ptr
- 示例
struct Parent { shared_ptr<Child> child;};struct Child { weak_ptr<Parent> parent; // 使用weak_ptr};
- 检测方法使用内存检测工具检查引用计数代码审查
8. 智能指针的线程安全性如何?
答案:
- 引用计数操作增减是线程安全的(原子操作)多线程可以安全地拷贝和销毁
- 对象访问不是线程安全的多线程访问同一对象需要同步
- 指针本身的修改不是线程安全的多线程修改同一智能指针需要同步
- 安全使用
// 安全:每个线程有自己的shared_ptr副本void thread_func(shared_ptr<Data> data) { // 使用data}// 不安全:多线程修改同一shared_ptrshared_ptr<Data> global_ptr;// 需要加锁保护
9. 什么时候不应该使用智能指针?
答案:
- 性能关键代码shared_ptr有引用计数开销频繁拷贝会影响性能考虑使用原始指针或引用
- 不拥有所有权只是观察或使用对象使用原始指针或引用避免不必要的引用计数
- 与C接口交互C接口需要原始指针使用get()获取原始指针注意生命周期管理
- 已有明确的生命周期管理栈对象容器管理的对象不需要额外的智能指针
10. 如何在类中使用智能指针?
答案:
- 成员变量
class MyClass { unique_ptr<Resource> resource; // 独占资源 shared_ptr<Data> shared_data; // 共享数据};
- enable_shared_from_this
class MyClass : public enable_shared_from_this<MyClass> { void register_callback() { // 获取指向自己的shared_ptr callback_manager.register(shared_from_this()); }};
- 工厂函数
unique_ptr<MyClass> create() { return make_unique<MyClass>();}
- 注意事项避免循环引用不要混用智能指针和原始指针管理同一对象优先使用unique_ptr,需要时再用shared_ptr
C++面试总结 文章被收录于专栏
本专栏系统梳理C++面试高频考点,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力。
