第17天:智能指针
学习目标
掌握C++11引入的智能指针,理解现代C++的内存管理方式,学会使用RAII原则避免内存泄漏。
核心知识点
1. 智能指针概述
为什么需要智能指针?
- 避免内存泄漏:自动释放内存
- 异常安全:即使发生异常也能正确释放资源
- 简化代码:减少手动内存管理
- 避免悬空指针:防止访问已释放的内存
RAII原则
- Resource Acquisition Is Initialization:资源获取即初始化
- 自动管理资源生命周期
- 参考资料:
2. unique_ptr
特点
- 独占所有权:同一时间只能有一个unique_ptr拥有对象
- 移动语义:支持移动,不支持复制
- 零开销:与原始指针性能相同
基本使用
1 2 3 4 5 6 7 8 9
| std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::unique_ptr<int> ptr2 = std::move(ptr);
auto deleter = [](int* p) { delete p; }; std::unique_ptr<int, decltype(deleter)> ptr3(new int(42), deleter);
|
参考资料
3. shared_ptr
特点
- 共享所有权:多个shared_ptr可以共享同一个对象
- 引用计数:自动管理引用计数
- 线程安全:引用计数操作是线程安全的
基本使用
1 2 3 4 5 6 7 8
| std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::shared_ptr<int> ptr2 = ptr1;
std::cout << ptr1.use_count() << std::endl;
|
循环引用问题
- 问题描述:两个对象相互持有shared_ptr会导致内存泄漏
- 解决方案:使用weak_ptr打破循环引用
参考资料
4. weak_ptr
特点
- 弱引用:不影响对象的生命周期
- 解决循环引用:打破shared_ptr的循环引用
- 安全访问:通过lock()方法安全访问对象
基本使用
1 2 3 4 5 6 7 8 9 10
| std::shared_ptr<int> shared = std::make_shared<int>(42); std::weak_ptr<int> weak = shared;
if (!weak.expired()) { if (auto locked = weak.lock()) { std::cout << *locked << std::endl; } }
|
参考资料
5. make_unique 和 make_shared
make_unique (C++14)
- 推荐使用:比new + unique_ptr更安全
- 异常安全:避免内存泄漏
- 简洁语法:减少代码重复
make_shared
- 性能优势:一次分配内存(对象+控制块)
- 异常安全:避免内存泄漏
- 推荐使用:优于new + shared_ptr
参考资料
6. 智能指针与容器
存储智能指针
1 2 3 4 5 6 7
| std::vector<std::unique_ptr<Base>> vec; vec.push_back(std::make_unique<Derived>());
std::vector<std::shared_ptr<Base>> sharedVec; sharedVec.push_back(std::make_shared<Derived>());
|
多态支持
- 基类指针:智能指针支持多态
- 虚析构函数:确保正确析构派生类对象
7. 智能指针最佳实践
选择原则
- 默认使用unique_ptr:大多数情况下的最佳选择
- 需要共享时使用shared_ptr:多个所有者的情况
- 打破循环引用使用weak_ptr:观察者模式等场景
性能考虑
- unique_ptr:零开销,与原始指针性能相同
- shared_ptr:有引用计数开销,但通常可接受
- make_shared:比new + shared_ptr更高效
参考资料
实用教程和文档
官方文档
优质教程
设计模式与智能指针
今日练习
基础练习
- 实现一个资源管理类,使用智能指针管理资源
- 编写一个观察者模式的实现,使用weak_ptr避免循环引用
- 实现一个树结构,使用智能指针管理节点
算法题推荐
- LeetCode 138. Copy List with Random Pointer - 使用智能指针管理链表节点
- LeetCode 146. LRU Cache - 使用智能指针实现LRU缓存
- LeetCode 208. Implement Trie (Prefix Tree) - 使用智能指针实现字典树
- LeetCode 341. Flatten Nested List Iterator - 使用智能指针实现迭代器
学习要点总结
- RAII原则:资源获取即初始化,自动管理资源生命周期
- 所有权语义:unique_ptr独占,shared_ptr共享,weak_ptr弱引用
- 循环引用:使用weak_ptr避免循环引用导致的内存泄漏
- 异常安全:智能指针确保异常情况下也能正确释放资源
- 性能考虑:make_shared比new + shared_ptr更高效
下一天预告
明天我们将学习Lambda表达式,包括捕获列表、参数列表、返回类型等现代C++的函数式编程特性。
上一天:STL算法 | 返回第三周总览 | 下一天:Lambda表达式