unique_ptr 智能指针

unique_ptr 智能指针

std::unique_ptr 是 C++ 标准库中的一部分,位于 <memory> 头文件中。它的“唯一性”(unique)意味着它是独占的,即一个资源(例如动态分配的内存)只能由一个 unique_ptr 管理。

std::unique_ptr 的特性

  1. 独占所****有权std::unique_ptr 通过“独占所有权”来管理资源。每个 unique_ptr 对象拥有它所指向的资源,且资源只能有一个 unique_ptr 对象来管理。这意味着不能存在两个 unique_ptr 同时指向同一个资源。
  2. 自动资源释放: 当 unique_ptr 被销毁时,它会自动释放所管理的资源。这消除了手动调用 deletedelete[] 的需要,大大减少了内存泄漏的风险。
  3. 不可复制、可移动: unique_ptr 不允许复制(copy),因此不能有多个 unique_ptr 指向同一个对象。它只支持移动(move),意味着你可以通过移动语义将资源的所有权从一个 unique_ptr 转移到另一个。
  4. 与裸指针兼容: 虽然 unique_ptr 不能直接复制,但它可以通过 get() 方法获取裸指针,并与其他裸指针函数兼容。get() 返回指向资源的原始指针,但不拥有资源的所有权。

初始化

std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。

// 创建一个unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>(10);

// 创建一个int 的 unique_ptr 并使用自定义的 deleter
std::unique_ptr<int, std::function<void(int*)>> ptr2(new int(10), [](int *p){ 
    cout<< "Deleting pointer: " << *p << endl;
    delete p; });

转移所有权(移动语义)

int main(){
    std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
    std::unique_ptr<int> ptr2 = std::move(ptr1);  // 转移所有权

    std::cout << *ptr2 << std::endl;  // 输出10

    // ptr1 现在为空,无法再访问
    if (!ptr1) {
        std::cout << "ptr1 is now null" << std::endl;
    }
    
}

通过 std::moveptr2 获得了 ptr1 所管理的资源的所有权,ptr1 成为一个空指针。

访问和修改资源

可以使用 * 操作符来解引用 unique_ptr,访问它所管理的资源。

int main(){
    std::unique_ptr<int> ptr = std::make_unique<int>(20);

    // 通过解引用访问资源
    *ptr = 30;

    // 或者使用指针成员函数 get()
    std::cout << *ptr << std::endl;  // 输出 30
}

删除器

unique_ptr指定删除器和shared_ptr指定删除器是有区别的,unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器,举例说明:

shared_ptr<int> ptr1(new int(10), [](int*p) {delete p; });	// ok
unique_ptr<int> ptr1(new int(10), [](int*p) {delete p; });	// error

int main()
{
    using func_ptr = void(*)(int*);
    unique_ptr<int, func_ptr> ptr1(new int(10), [](int*p) {delete p; });

    return 0;
}

在上面的代码中第7行,func_ptr的类型和lambda表达式的类型是一致的。在lambda表达式没有捕获任何变量的情况下是正确的,如果捕获了变量,编译时则会报错:

int main()
{
    using func_ptr = void(*)(int*);
    unique_ptr<int, func_ptr> ptr1(new int(10), [&](int*p) {delete p; });	// error
    return 0;
}

上面的代码中错误原因是这样的,在lambda表达式没有捕获任何外部变量时,可以直接转换为函数指针,一旦捕获了就无法转换了,如果想要让编译器成功通过编译,那么需要使用可调用对象包装器来处理声明的函数指针:

int main()
{
    using func_ptr = void(*)(int*);
    unique_ptr<int, function<void(int*)>> ptr1(new int(10), [&](int*p) {delete p; });
    return 0;
}
### 定义和使用全局 `unique_ptr` 的方法及注意事项 #### 方法说明 定义全局的 `std::unique_ptr` 需要注意其生命周期管理以及所有权语义。由于 `unique_ptr` 是独占所有权的智能指针,在作为全局变量时,应确保其正确初始化并合理处理资源释放。 对于全局 `unique_ptr` 的声明可以直接放在文件作用域内: ```cpp #include <iostream> #include <memory> // 全局 unique_ptr 声明 std::unique_ptr<int> globalPtr; void initializeGlobalPtr() { // 初始化全局 unique_ptr, 使用 new 或者 make_unique(C++14及以上) globalPtr.reset(new int(42)); // C++11 方式 // 对于支持 C++14 及以上的编译器可以这样写: // globalPtr = std::make_unique<int>(42); } int main() { initializeGlobalPtr(); if(globalPtr){ std::cout << *globalPtr << std::endl; } } ``` 当程序结束运行时,全局 `unique_ptr` 自动析构,并自动调用删除器来清理所指向的对象[^1]。 #### 注意事项 - **线程安全**:如果多个线程可能访问同一个全局 `unique_ptr` ,则需考虑同步机制以防止数据竞争。 - **初始化顺序**:静态对象之间的初始化顺序是一个复杂的话题,尤其是在跨翻译单元的情况下;因此建议尽早初始化全局 `unique_ptr` 并避免依赖其他未初始化完成的静态对象。 - **复制/赋值限制**:因为 `unique_ptr` 不允许被复制或赋值给另一个 `unique_ptr`(除非是移动),所以在设计涉及全局 `unique_ptr` 的接口时要特别小心[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liknana

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值