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

在 C++ 中,std::unique_ptr 是 C++11 引入的智能指针,用于管理动态分配的资源,提供独占所有权语义。它确保一个资源在任一时刻仅由一个指针拥有,自动释放资源,避免内存泄漏。std::unique_ptrstd::auto_ptr 的现代替代品,性能高效且更安全。

1. 什么是 std::unique_ptr

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
    #include <memory>
    #include <iostream>

    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 构造函数。
  • 缺点
    • 性能略低:newunique_ptr 构造是分开的。
    • 不安全:如果在构造 unique_ptr 前抛出异常,可能导致内存泄漏。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <memory>
    #include <iostream>

    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
    #include <memory>
    #include <iostream>

    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
    #include <memory>
    #include <iostream>

    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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <memory>
#include <iostream>

struct MyClass {
~MyClass() { std::cout << "MyClass destroyed\n"; }
};

// 自定义删除器函数
void customDeleter(MyClass* ptr) {
std::cout << "Custom deleter called\n";
delete ptr;
}

int main() {
std::unique_ptr<MyClass, void(*)(MyClass*)> ptr(new MyClass(), customDeleter);
return 0; // 资源释放时调用 customDeleter
}
  • 输出

    1
    2
    MyClass destroyed
    Custom deleter called
  • 说明:删除器 customDeleterunique_ptr 销毁时被调用,执行自定义清理逻辑。

3.2 使用 Lambda 表达式作为删除器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <memory>
#include <iostream>

struct MyClass {
~MyClass() { std::cout << "MyClass destroyed\n"; }
};

int main() {
std::unique_ptr<MyClass, std::function<void(MyClass*)>> ptr(
new MyClass(), [](MyClass* ptr) {
std::cout << "Lambda deleter called\n";
delete ptr;
});
return 0;
}
  • 输出
    1
    2
    MyClass destroyed
    Lambda deleter called
  • 优点:Lambda 表达式更灵活,适合临时定义删除逻辑。

3.3 使用函数对象(Functor)作为删除器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <memory>
#include <iostream>

struct MyClass {
~MyClass() { std::cout << "MyClass destroyed\n"; }
};

// 函数对象作为删除器
struct CustomDeleter {
void operator()(MyClass* ptr) const {
std::cout << "Functor deleter called\n";
delete ptr;
}
};

int main() {
std::unique_ptr<MyClass, CustomDeleter> ptr(new MyClass(), CustomDeleter());
return 0;
}
  • 输出
    1
    2
    MyClass 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
    #include <memory>
    #include <iostream>

    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 确保初始化过程中的异常安全,避免内存泄漏。