元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
C++基础
▶
类型系统
基本数据类型
类型推导
类型转换
▶
编译模型
翻译单元
ODR规则
头文件与实现分离
▶
作用域与生命周期
存储期类型
RAII机制
对象构造与析构
▶
面向对象编程
▶
类设计
成员访问控制
特殊成员函数
友元机制
▶
继承体系
虚函数表
多重继承
虚继承
▶
多态实现
动态绑定
RTTI机制
类型擦除
▶
模板与泛型编程
▶
模板基础
函数模板
类模板
可变参数模板
▶
模板元编程
SFINAE
类型萃取
编译期计算
▶
概念与约束
requires表达式
概念定义
约束模板
▶
内存模型
▶
内存管理
堆与栈
智能指针
移动语义
▶
对象模型
成员布局
虚函数实现
空基类优化
▶
并发内存
内存顺序
原子操作
内存屏障
▶
标准库核心
▶
容器
序列容器
关联容器
无序容器
▶
迭代器
迭代器类别
迭代器适配器
范围迭代
▶
函数对象
函数包装器
绑定表达式
lambda实现
发布时间:
2025-03-23 10:08
↑
☰
# C++智能指针详解 ## 概述 智能指针是C++中用于自动管理动态内存的工具。它们通过RAII机制,在对象生命周期结束时自动释放内存,有效防止内存泄漏。C++11引入了三种标准智能指针:unique_ptr、shared_ptr和weak_ptr。 ## unique_ptr ### 基本概念 unique_ptr实现独占式拥有,确保一个对象只能被一个指针拥有。 ```cpp #include <memory> class Resource { public: Resource() { std::cout << "Resource acquired\n"; } ~Resource() { std::cout << "Resource destroyed\n"; } void use() { std::cout << "Resource used\n"; } }; void uniquePtrExample() { // 创建unique_ptr std::unique_ptr<Resource> ptr1(new Resource()); // 推荐使用make_unique auto ptr2 = std::make_unique<Resource>(); // 编译错误:unique_ptr不能复制 // std::unique_ptr<Resource> ptr3 = ptr1; // 可以移动所有权 std::unique_ptr<Resource> ptr3 = std::move(ptr1); // ptr1现在为nullptr if (!ptr1) { std::cout << "ptr1 is null\n"; } // 使用->访问成员 ptr2->use(); // 自动释放内存 } // ptr2和ptr3的析构函数被调用 ``` ### 自定义删除器 ```cpp void customDeleterExample() { auto deleter = [](Resource* p) { std::cout << "Custom delete\n"; delete p; }; std::unique_ptr<Resource, decltype(deleter)> ptr(new Resource(), deleter); } ``` ## shared_ptr ### 基本概念 shared_ptr允许多个指针共享同一个对象,通过引用计数跟踪对象的生命周期。 ```cpp void sharedPtrExample() { // 创建shared_ptr auto ptr1 = std::make_shared<Resource>(); { // 创建ptr1的副本 std::shared_ptr<Resource> ptr2 = ptr1; std::cout << "引用计数: " << ptr1.use_count() << "\n"; // 输出2 // ptr2在作用域结束时销毁,引用计数减1 } std::cout << "引用计数: " << ptr1.use_count() << "\n"; // 输出1 } ``` ### 循环引用问题 ```cpp class Node { public: std::shared_ptr<Node> next; // 会导致循环引用 // std::shared_ptr<Node> prev; // 使用weak_ptr避免循环引用 std::weak_ptr<Node> prev; }; void circularReference() { auto node1 = std::make_shared<Node>(); auto node2 = std::make_shared<Node>(); node1->next = node2; node2->prev = node1; // weak_ptr不增加引用计数 } ``` ## weak_ptr ### 基本概念 weak_ptr用于观察shared_ptr管理的对象,但不影响其生命周期。 ```cpp void weakPtrExample() { auto resource = std::make_shared<Resource>(); std::weak_ptr<Resource> weak = resource; // 检查对象是否存在 if (auto shared = weak.lock()) { shared->use(); } else { std::cout << "Resource no longer exists\n"; } // 释放resource resource.reset(); // weak_ptr检测到对象已销毁 if (weak.expired()) { std::cout << "Resource has been deleted\n"; } } ``` ## 智能指针的实现原理 ### unique_ptr实现 ```cpp // 简化的unique_ptr实现 template<typename T, typename Deleter = std::default_delete<T>> class SimpleUniquePtr { T* ptr; Deleter deleter; public: explicit SimpleUniquePtr(T* p = nullptr) : ptr(p) {} ~SimpleUniquePtr() { if (ptr) deleter(ptr); } // 禁止复制 SimpleUniquePtr(const SimpleUniquePtr&) = delete; SimpleUniquePtr& operator=(const SimpleUniquePtr&) = delete; // 允许移动 SimpleUniquePtr(SimpleUniquePtr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } }; ``` ### shared_ptr实现 ```cpp // 简化的引用计数块 struct ControlBlock { size_t shared_count = 1; size_t weak_count = 0; }; // 简化的shared_ptr实现 template<typename T> class SimpleSharedPtr { T* ptr; ControlBlock* ctrl; public: explicit SimpleSharedPtr(T* p) : ptr(p), ctrl(new ControlBlock()) {} SimpleSharedPtr(const SimpleSharedPtr& other) : ptr(other.ptr), ctrl(other.ctrl) { ++ctrl->shared_count; } ~SimpleSharedPtr() { if (--ctrl->shared_count == 0) { delete ptr; if (ctrl->weak_count == 0) { delete ctrl; } } } }; ``` ## 最佳实践 ### 1. 优先使用make_函数 ```cpp // 不推荐 std::unique_ptr<Resource> ptr1(new Resource()); // 推荐 auto ptr2 = std::make_unique<Resource>(); ``` ### 2. 避免原始指针 ```cpp // 不推荐 Resource* ptr = new Resource(); // ... delete ptr; // 推荐 auto ptr = std::make_unique<Resource>(); ``` ### 3. 使用const引用传递shared_ptr ```cpp // 不推荐 - 增加引用计数 void process(std::shared_ptr<Resource> ptr); // 推荐 - 不增加引用计数 void process(const std::shared_ptr<Resource>& ptr); ``` ### 4. 合理选择智能指针类型 - 使用unique_ptr表示独占所有权 - 使用shared_ptr表示共享所有权 - 使用weak_ptr打破循环引用 - 优先使用unique_ptr,除非确实需要共享所有权 ### 5. 自定义删除器的使用 ```cpp // 文件句柄的自动管理 auto file_deleter = [](FILE* fp) { if(fp) fclose(fp); }; std::unique_ptr<FILE, decltype(file_deleter)> file(fopen("test.txt", "r"), file_deleter); ``` ## 性能考虑 1. unique_ptr - 零开销抽象 - 大小与原始指针相同 - 性能等同于手动管理 2. shared_ptr - 需要额外的控制块 - 引用计数操作有开销 - 线程安全的引用计数增加同步开销 3. weak_ptr - 需要检查有效性 - lock()操作有额外开销 ## 总结 1. 智能指针的选择: - 默认使用unique_ptr - 需要共享所有权时使用shared_ptr - 需要打破循环引用时使用weak_ptr 2. 使用建议: - 始终使用make_函数创建 - 避免显式使用new/delete - 注意避免循环引用 - 合理使用自定义删除器 3. 注意事项: - 不要用shared_ptr管理数组 - 注意shared_ptr的性能开销 - 正确处理异常安全 - 遵循RAII原则