跳转到内容

C++ unique ptr 详解

来自代码酷

C++ unique_ptr 详解[编辑 | 编辑源代码]

unique_ptr 是 C++11 引入的一种智能指针,用于管理动态分配的内存资源。它遵循独占所有权(exclusive ownership)模型,确保同一时间只有一个 unique_ptr 可以指向特定的内存资源。当 unique_ptr 超出作用域或被销毁时,它会自动释放所管理的内存,从而有效防止内存泄漏。

基本概念[编辑 | 编辑源代码]

unique_ptr 是标准库头文件 <memory> 中定义的模板类,其核心特性包括:

  • 独占所有权:同一时间只能有一个 unique_ptr 指向特定对象。
  • 自动释放:当 unique_ptr 被销毁时,会自动调用 delete 或自定义删除器释放资源。
  • 轻量高效:与裸指针相比,运行时开销几乎为零。

数学上,unique_ptr 可以表示为: 解析失败 (语法错误): {\displaystyle \text{unique\_ptr}(T) \rightarrow \text{owns}(p) \land \forall q \neq p, \neg\text{owns}(q) }

基本用法[编辑 | 编辑源代码]

创建 unique_ptr[编辑 | 编辑源代码]

#include <memory>
#include <iostream>

int main() {
    // 方式1:使用构造函数
    std::unique_ptr<int> ptr1(new int(42));
    
    // 方式2:使用 std::make_unique (C++14引入)
    auto ptr2 = std::make_unique<int>(100);
    
    // 访问指针内容
    std::cout << *ptr1 << ", " << *ptr2 << std::endl;  // 输出: 42, 100
    
    return 0;
}

所有权转移[编辑 | 编辑源代码]

unique_ptr 不能复制,但可以通过 std::move 转移所有权:

std::unique_ptr<int> source = std::make_unique<int>(200);
std::unique_ptr<int> destination;

// destination = source;  // 错误!不能复制
destination = std::move(source);  // 正确:所有权转移

if (!source) {
    std::cout << "source 不再拥有所有权" << std::endl;
}

高级特性[编辑 | 编辑源代码]

自定义删除器[编辑 | 编辑源代码]

unique_ptr 允许指定自定义删除器来处理特殊资源:

struct FileDeleter {
    void operator()(FILE* file) {
        if (file) {
            fclose(file);
            std::cout << "文件已关闭" << std::endl;
        }
    }
};

int main() {
    std::unique_ptr<FILE, FileDeleter> filePtr(fopen("test.txt", "w"));
    if (filePtr) {
        fprintf(filePtr.get(), "Hello, unique_ptr!");
    }
    // 文件会在 unique_ptr 销毁时自动关闭
    return 0;
}

数组支持[编辑 | 编辑源代码]

unique_ptr 可以管理动态数组:

auto arrPtr = std::make_unique<int[]>(5);  // 创建包含5个int的数组

for (int i = 0; i < 5; ++i) {
    arrPtr[i] = i * 10;
}

实际应用案例[编辑 | 编辑源代码]

工厂模式中的应用[编辑 | 编辑源代码]

unique_ptr 非常适合用于工厂函数返回对象:

class Product {
public:
    virtual ~Product() = default;
    virtual void use() = 0;
};

class ConcreteProduct : public Product {
public:
    void use() override {
        std::cout << "使用具体产品" << std::endl;
    }
};

std::unique_ptr<Product> createProduct() {
    return std::make_unique<ConcreteProduct>();
}

int main() {
    auto product = createProduct();
    product->use();
    return 0;
}

异常安全保证[编辑 | 编辑源代码]

unique_ptr 提供了强异常安全保证:

void processResource() {
    auto res = std::make_unique<Resource>();  // 即使后面抛出异常,资源也会被释放
    // 可能抛出异常的操作
    res->doSomething();
    // 不需要手动delete
}

与裸指针比较[编辑 | 编辑源代码]

flowchart TD A[内存管理方式] --> B[裸指针] A --> C[unique_ptr] B --> D[需要手动delete] B --> E[容易忘记释放] B --> F[可能双重释放] C --> G[自动释放] C --> H[独占所有权防止误用] C --> I[明确的语义]

最佳实践[编辑 | 编辑源代码]

1. 优先使用 std::make_unique 而非直接 new 2. 避免将 unique_ptr 转换为裸指针,除非必要 3. 使用 release() 谨慎,它会放弃所有权 4. 在 API 边界使用 unique_ptr 明确所有权转移

常见问题[编辑 | 编辑源代码]

能否在容器中使用 unique_ptr?[编辑 | 编辑源代码]

可以,但需要注意所有权语义:

std::vector<std::unique_ptr<int>> vec;
vec.push_back(std::make_unique<int>(1));
vec.push_back(std::make_unique<int>(2));

// 需要移动语义
auto ptr = std::make_unique<int>(3);
vec.push_back(std::move(ptr));

如何将 unique_ptr 用于多态?[编辑 | 编辑源代码]

unique_ptr 完全支持多态:

class Base { virtual ~Base() = default; };
class Derived : public Base {};

std::unique_ptr<Base> polyPtr = std::make_unique<Derived>();

性能考量[编辑 | 编辑源代码]

unique_ptr 的运行时开销与裸指针几乎相同:

  • 构造/析构:与手动 new/delete 相当
  • 访问操作:无额外开销
  • 所有权转移:仅涉及指针复制

总结[编辑 | 编辑源代码]

unique_ptr 是 C++现代内存管理的核心工具之一,它:

  • 提供自动内存管理,防止泄漏
  • 明确表达独占所有权语义
  • 几乎零开销
  • 支持自定义删除器和数组
  • 增强代码安全性和可读性

对于现代 C++ 开发,unique_ptr 应该成为动态内存分配的首选工具。