C++11引入的智能指针是现代C++资源管理的核心。它们通过RAII(资源获取即初始化)机制,自动化了动态内存的管理,极大地减少了内存泄漏和悬空指针等问题。本文将重点深入讲解std::unique_ptr(独占指针)和std::shared_ptr(共享指针)。
智能指针基本使用和语法
1.1 为什么需要智能指针?
在原生指针的世界里,内存管理依赖于程序员:
- new/delete必须成对出现:忘记delete导致内存泄漏;过早delete导致悬空指针;重复delete导致未定义行为。
- 异常安全:如果在new和delete之间发生异常,delete将不会执行,导致内存泄漏。
智能指针通过将动态分配的内存与对象的生命周期绑定来解决这些问题。当智能指针对象离开作用域时,其析构函数会自动释放所管理的内存。
1.2 std::unique_ptr 独占所有权 核心思想:同一时间只有一个unique_ptr 拥有对对象的所有权。所有权可以通过std::move转移,但不能复制。 基本语法和使用:
1. 独占所有权,同一时间只有一个指针管对象
2. 不能拷贝,只能移动
3. 自动释放,无需delete
cpp
// 构造
unique_ptr<int> p1(new int(10));
// 推荐
unique_ptr<int> p2 = make_unique<int>(20);
// 移动转移所有权
unique_ptr<int> p3 = move(p1);
// 访问
*p3 = 30;
// 手动释放
p3.reset();
1.3 std::shared_ptr 共享所有权 核心思想: 多个shared_ptr 可以共享同一个对象的所有权。通过引用计数机制,当最后一个shared_ptr被销毁时,对象才会被释放。 基本语法和使用:
1. 共享所有权,引用计数
2. 拷贝计数+1,离开作用域-1,到0释放
3. 循环引用会内存泄漏
cpp
// 构造
shared_ptr<int> p1(new int(10));
// 推荐
shared_ptr<int> p2 = make_shared<int>(20);
// 拷贝,计数+1
shared_ptr<int> p3 = p2;
// 查看计数
p3.use_count();
// 手动释放
p3.reset();
通用常用方法
- *p :解引用
- p-> :访问成员
- p.get() :获取裸指针
- p.reset() :释放/重置
- if(p) :判断是否为空
智能指针的内部实现原理
2.1 std::unique_ptr 的实现原理 unique_ptr 是一个轻量级的包装器,其核心是:
- 独占所有权:通过删除拷贝构造函数和拷贝赋值运算符( = delete),只是保留移动语义来确保唯一所有权
- 资源释放: 在析构函数中调用删除器来释放资源。
2.2 std::shared_ptr 的实现原理 shared_ptr 的实现要复杂得多,核心是引用计数: 1.控制块(Control Block):shared_ptr在堆上维护一个控制块,包含: - 引用计数:当前有多少个shared_ptr共享对象 - 弱引用计数:用于weak_ptr - 删除器:用于释放资源的函数 - 被管理对象的指针
2.拷贝语义:当shared_ptr被拷贝时,引用计数加1;当shared_ptr被析构时,引用计数减1
3.原子操作:引用计数的增减是线程安全的原子操作。
总结
- std::unique_ptr 是一个轻量级的独占所有权包装器,通过删除拷贝语义、支持移动语义来确保资源唯一所有权,实现零开销的资源管理。
- std::shared_ptr 通过引用计数机制实现共享所有权,其核心是控制块的概念,包含了引用计数和删除器等元数据。