最佳实践案例:std::make_unique与std::unique_ptr的结合使用
发布时间: 2024-10-23 11:35:40 阅读量: 35 订阅数: 50 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![C++的std::make_unique](https://img-blog.csdnimg.cn/20210620161412659.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1bnllX2RyZWFt,size_16,color_FFFFFF,t_70)
# 1. 智能指针std::unique_ptr简介
智能指针是现代C++资源管理的关键工具之一,其中`std::unique_ptr`作为最常用的智能指针,提供了自动的资源管理功能,保证了资源的生命周期得到妥善管理。与传统的原生指针不同,`std::unique_ptr`不允许复制操作,以确保资源的唯一所有权。
本章节将介绍`std::unique_ptr`的基本概念及其背后的原理。我们将从定义和用法开始,逐步展开讨论其在现代C++编程中的重要性。通过了解`std::unique_ptr`,开发者可以编写出更安全、更高效的代码,避免诸如内存泄漏这类常见问题。
阅读本章节后,您将对如何在代码中有效地使用`std::unique_ptr`来管理资源有一个清晰的认识。
```cpp
#include <iostream>
#include <memory>
// 示例:使用std::unique_ptr管理动态分配的内存
void unique_ptr_example() {
std::unique_ptr<int> myPointer = std::make_unique<int>(42); // 创建一个std::unique_ptr指向动态分配的int
std::cout << *myPointer << std::endl; // 输出:42
} // myPointer超出作用域时,其指向的内存将自动释放
int main() {
unique_ptr_example();
return 0;
}
```
上述代码展示了`std::unique_ptr`的基本使用方法,同时体现了其自动释放所管理资源的特性,使得资源管理更加简洁和安全。
# 2. ```
# 第二章:std::make_unique的介绍与使用
## 2.1 std::make_unique的基本语法和优势
### 2.1.1 std::make_unique的定义和用法
`std::make_unique` 是C++14标准中引入的一个辅助函数,它在单个对象的分配中提供了一个异常安全的创建方法。这个函数的主要目的是简化动态内存分配和提高代码的安全性。
`std::make_unique` 的基本语法如下:
```cpp
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
```
这个函数模板使用完美转发(perfect forwarding),能够接受任意数量的参数,并将它们转发给对象的构造函数。使用 `std::make_unique`,开发者可以创建一个 `std::unique_ptr`,它在其析构函数中释放所管理的资源,这通常是通过调用 `delete` 关闭动态分配的对象。
下面是一个简单的用法示例:
```cpp
auto uptr = std::make_unique<int>(42); // 创建一个int类型的std::unique_ptr
```
在这个例子中,我们创建了一个指向 `int` 类型的 `std::unique_ptr`,并通过参数 `42` 初始化了它。这种方式比直接使用 `new` 操作符更加简洁和安全。
### 2.1.2 std::make_unique与new的对比
让我们更深入地比较一下 `std::make_unique` 和 `new` 操作符的用法和差异:
- **异常安全**:当使用 `new` 操作符直接分配内存时,如果构造函数抛出异常,则无法释放已分配的内存,从而可能导致内存泄漏。然而,`std::make_unique` 在这种情况下是异常安全的,因为它会使用局部变量来创建对象,并且确保在构造函数抛出异常时能够自动释放内存。
- **代码简洁性**:与 `new` 操作符相比,`std::make_unique` 提供了一种更加简洁、易于理解和维护的代码风格。它减少了源代码中出现的 `new` 关键字的数量,从而降低了出错的可能性。
- **安全性**:`std::make_unique` 提升了安全性,因为它通过 `std::unique_ptr` 管理了动态分配的内存,这样就不会忘记释放内存或者在多个地方手动释放内存。
- **默认构造函数**:如果需要使用默认构造函数创建对象,`std::make_unique` 可以更简洁地表示这一点,例如:
```cpp
auto uptr = std::make_unique<T>(); // 创建一个指向T类型的std::unique_ptr,并使用默认构造函数初始化
```
这比 `new T()` 更为直观,并且依旧保持异常安全。
在实际编码中,推荐使用 `std::make_unique` 而不是直接使用 `new` 操作符,除非需要非常特殊地控制内存分配行为。
## 2.2 std::make_unique在现代C++中的地位
### 2.2.1 现代C++编程风格的演变
现代C++强调代码的安全性和易用性。现代C++编程风格倾向于使用智能指针如 `std::unique_ptr` 和 `std::shared_ptr`,以减少直接使用裸指针时出现的资源管理错误。智能指针自动管理资源的分配和释放,因此可以减少内存泄漏和野指针的风险。
`std::make_unique` 是现代C++编程风格的一个典型例子,它使得智能指针的使用更加方便,并且鼓励开发者远离直接的内存分配操作。
### 2.2.2 std::make_unique对资源管理的影响
`std::make_unique` 提供了一个简洁而强大的方式来创建 `std::unique_ptr` 对象。由于 `std::unique_ptr` 是一种拥有所有权的智能指针,它确保了资源会在适当的时候被自动释放。这与 `std::shared_ptr` 相比,后者允许多个指针共享同一资源的所有权。
`std::make_unique` 的使用在资源管理方面引入了以下变化:
- **使用模式**:它鼓励使用局部变量来存储 `std::unique_ptr`,这样当变量作用域结束时,`std::unique_ptr` 会自动释放其管理的资源。
- **异常安全性**:`std::make_unique` 在构造对象时,如果对象的构造函数抛出异常,由于它是函数作用域内的局部变量,异常会自动展开栈,而局部变量在退出作用域时会自动析构,因此可以保证异常安全。
- **减少冗余代码**:使用 `std::make_unique` 可以减少代码中与内存管理相关的冗余代码,从而提高代码的可读性和可维护性。
通过使用 `std::make_unique`,开发者可以更容易地编写出既简洁又安全的代码,这对于提高整个项目代码质量和维护性都有着积极的影响。
```
# 3. std::unique_ptr的高级用法
## 3.1 std::unique_ptr与自定义删除器
### 3.1.1 删除器的定义和作用
智能指针std::unique_ptr在C++中是一种独特的资源管理工具,它不仅能够自动释放所拥有的资源,还能通过自定义删除器来增强资源管理的灵活性和安全性。当std::unique_ptr的实例离开作用域或者被重置时,它的默认行为是使用delete运算符来释放其管理的资源。然而,在某些情况下,标准的delete可能并不适用,比如:
- 当资源不是通过new分配的,比如是通过malloc分配的。
- 当资源释放需要执行额外的操作,比如释放互斥锁、关闭文件句柄等。
- 当使用自定义的内存池进行资源分配。
此时,std::unique_ptr允许我们提供一个自定义的删除器来处理资源的释放。删除器可以是一个函数、一个函数对象,或者是一个lambda表达式,它定义了释放资源的具体行为。这使得std::unique_ptr的适用场景大大扩展,可以用于几乎任何类型的资源管理。
### 3.1.2 自定义删除器的实例和注意事项
下面是一个使用自定义删除器的示例代码:
```cpp
#include <iostream>
#include <memory>
// 自定义删除器函数
void customDeleter(int* p) {
std::cout << "Deleting an int." << std::endl;
delete p;
}
int main() {
// 创建一个unique_ptr,关联一个int指针,并指定自定义删除器
std::unique_ptr<int, void(*)(int*)> myUniquePtr(new int(42), customDeleter);
// 使用自定义删除器释放资源
myUniquePtr.reset();
return 0;
}
```
在这个例子中,我们定义了一个名为`customDeleter`的函数,它接受一个int类型的指针作为参数,并使用`delete`释放它。然后,我们在创建`std::unique_ptr`时,通过一个模板参数将`customDeleter`作为删除器传递给`std::unique_ptr`。这样,当`myUniquePtr`离开作用域或者调用`reset()`方法时,`customDeleter`将被调用。
注意事项:
- 自定义删除器应该在创建`std::unique_ptr`实例时就确定,不能在之后更改。
- 自定义删除器不应该抛出异常。
- 当使用lambda表达式作为删除器时,应确保捕获的数据在lambda生命周期内有效,否则可能会造成悬空指针的
0
0
相关推荐
![pdf](https://img-home.csdnimg.cn/images/20241231044930.png)
![doc](https://img-home.csdnimg.cn/images/20241231044833.png)
![pdf](https://img-home.csdnimg.cn/images/20241231044930.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)