独占智能指针(std::unique_ptr)

独占智能指针(std::unique_ptr)
枫在 C++ 中,std::unique_ptr 是 C++11 引入的智能指针,用于管理动态分配的资源,提供独占所有权语义。它确保一个资源在任一时刻仅由一个指针拥有,自动释放资源,避免内存泄漏。std::unique_ptr 是 std::auto_ptr 的现代替代品,性能高效且更安全。
1. 什么是 std::unique_ptr?
std::unique_ptr 是一个轻量级的智能指针,设计用于管理单一动态资源的独占所有权。它通过
特性:
- 独占所有权:资源只能由一个 std::unique_ptr 管理,无法复制。
- 零开销:与原始指针相比,几乎没有运行时性能损耗。
- 自定义删除器:支持自定义资源释放逻辑,适用于非内存资源(如文件、网络句柄)。
- 类型安全:支持移动语义,防止误用导致的内存问题。
2. std::unique_ptr 的初始化
在 C++ 中,std::unique_ptr 的初始化有多种方式,推荐使用安全且高效的方法。以下是 std::unique_ptr 的常见初始化方式及注意事项:
2.1 使用 std::make_unique 初始化(推荐)
- 方式:通过 std::make_unique(C++14 引入)创建 std::unique_ptr,这是首选方法。
- 优点:
- 更高效:一次性分配对象内存。
- 更安全:避免直接使用 new 可能导致的异常问题。
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct MyClass {
int value;
MyClass(int v) : value(v) {}
~MyClass() { std::cout << "Destroyed\n"; }
};
int main() {
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(42);
std::cout << ptr->value << "\n"; // 输出 42
return 0; // 自动释放
} - 适用场景:大多数情况下创建 std::unique_ptr 的首选方式。
2.2 通过构造函数和 new 初始化
- 方式:直接将 new 分配的指针传入 std::unique_ptr 构造函数。
- 缺点:
- 性能略低:new 和 unique_ptr 构造是分开的。
- 不安全:如果在构造 unique_ptr 前抛出异常,可能导致内存泄漏。
- 示例:
1
2
3
4
5
6
7
8
9
10
11
struct MyClass {
~MyClass() { std::cout << "Destroyed\n"; }
};
int main() {
std::unique_ptr<MyClass> ptr(new MyClass());
return 0; // 自动释放
} - 注意:尽量避免此方式,优先使用 std::make_unique。
2.3 通过移动语义初始化
- 方式:通过 std::move 从另一个 std::unique_ptr 转移所有权,源指针变为空。
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
struct MyClass {
~MyClass() { std::cout << "Destroyed\n"; }
};
int main() {
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
std::unique_ptr<MyClass> ptr2 = std::move(ptr1); // 转移所有权
std::cout << (ptr1 == nullptr) << "\n"; // 输出 1(ptr1 已为空)
return 0; // ptr2 销毁时释放资源
} - 注意:转移后,源 unique_ptr 变为空,无法访问原资源。
2.4 空 std::unique_ptr 初始化
- 方式:创建空的 std::unique_ptr,不管理任何资源。
- 示例:
1
2
3
4
5
6
7
8
9
10
int main() {
std::unique_ptr<int> ptr; // 空 unique_ptr
std::cout << (ptr == nullptr) << "\n"; // 输出 1
ptr = std::make_unique<int>(10); // 重新赋值
std::cout << *ptr << "\n"; // 输出 10
return 0;
}
3. 指定删除器
std::unique_ptr 支持指定自定义删除器(deleter),以在资源释放时执行特定清理操作。自定义删除器在管理非标准内存资源(如文件句柄、网络连接等)或需要特殊清理逻辑时非常有用。
3.1 使用函数指针作为删除器
1 |
|
输出:
1
2MyClass destroyed
Custom deleter called说明:删除器 customDeleter 在 unique_ptr 销毁时被调用,执行自定义清理逻辑。
3.2 使用 Lambda 表达式作为删除器
1 |
|
- 输出:
1
2MyClass destroyed
Lambda deleter called - 优点:Lambda 表达式更灵活,适合临时定义删除逻辑。
3.3 使用函数对象(Functor)作为删除器
1 |
|
- 输出:
1
2MyClass destroyed
Functor deleter called - 说明:函数对象适合需要重用或复杂逻辑的场景。
4. 核心特性与使用场景
4.1 独占所有权
- std::unique_ptr 确保资源仅由一个指针管理,禁止拷贝,防止多指针管理同一资源导致的双重释放。
- 场景:函数返回值、容器存储动态对象。
4.2 数组支持
- std::unique_ptr<T[]> 支持管理动态数组,自动调用 delete[ ] 。
1
2
3
4
5
6
7
8
9
10
11
int main() {
std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);
for (int i = 0; i < 5; ++i) {
arr[i] = i + 1;
}
std::cout << arr[2] << "\n"; // 输出 3
return 0; // 自动释放数组
}
4.3 异常安全
- 使用 std::make_unique 确保初始化过程中的异常安全,避免内存泄漏。