unique_ptr的使用和陷阱

本文详细介绍了C++中unique_ptr的使用方法,包括内存分配、操作、陷阱等内容,并探讨了如何正确地传递和返回unique_ptr。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

unique_ptr的使用


分配内存

与shared_ptr不同,unique_ptr没有定义类似make_shared的操作,因此只可以使用new来分配内存,并且由于unique_ptr不可拷贝和赋值,初始化unique_ptr必须使用直接初始化的方式。

unique_ptr<int> up1(new int());    //okay,直接初始化
unique_ptr<int> up2 = new int();   //error! 构造函数是explicit
unique_ptr<int> up3(up1);          //error! 不允许拷贝

不可 拷贝和赋值

与shared_ptr不同,unique_ptr拥有它所指向的对象,在某一时刻,只能有一个unique_ptr指向特定的对象。当unique_ptr被销毁时,它所指向的对象也会被销毁。因此不允许多个unique_ptr指向同一个对象,所以不允许拷贝与赋值。


unique_ptr的操作

  1. unique_ptr<T> up
    空的unique_ptr,可以指向类型为T的对象,默认使用delete来释放内存

  2. unique_ptr<T,D> up(d)
    空的unique_ptr同上,接受一个D类型的删除器d,使用删除器d来释放内存

  3. up = nullptr
    释放up指向的对象,将up置为空

  4. up.release()
    up放弃对它所指对象的控制权,并返回保存的指针,将up置为空,不会释放内存

  5. up.reset(…)
    参数可以为 内置指针先将up所指对象释放,然后重置up的值


传递unique_ptr参数和返回unique_ptr

前面说了unique_ptr不可拷贝和赋值,那要怎样传递unique_ptr参数和返回unique_ptr呢?
事实上

不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr (C++ Primer 5th p418)

//从函数返回一个unique_ptr
unique_ptr func1(int a)
{
    return unique_ptr<int> (new int(a));
}

//返回一个局部对象的拷贝
unique_ptr func2(int a)
{
    unique_ptr<int> up(new int(a));
    return up;
}

传unique_ptr参数可以使用引用避免所有权的转移,或者暂时的移交所有权

void func1(unique_ptr<int> &up){
    cout<<*up<<endl;
}
unique_ptr<int> func2(unique_ptr<int> up){
    cout<<*up<<endl;
    return up;
}

//使用up作为参数
unique_ptr<int> up(new int(10));

//传引用,不拷贝,不涉及所有权的转移
func1(up);
//暂时转移所有权,函数结束时返回拷贝,重新收回所有权
up = func2(unique_ptr<int> (up.release()));
//如果不用up重新接受func2的返回值,这块内存就泄漏了

向unique_ptr传递删除器

类似shared_ptr,用unique_ptr管理非new对象、没有析构函数的类时,需要向unique_ptr传递一个删除器。不同的是,unique_ptr管理删除器的方式,我们必须在尖括号中unique_ptr指向类型后面提供删除器的类型,在创建或reset一个这种unique_ptr对象时,必须提供一个相同类型的可调用对象(删除器),这个删除器接受一个T*参数。



unique_ptr的陷阱

不要与裸指针混用

unique_ptr不允许两个独占指针指向同一个对象,在没有裸指针的情况下,我们只能用release获取内存的地址,同时放弃对对象的所有权,这样就有效避免了多个独占指针同时指向一个对象。
而使用裸指针就很容器打破这一点

int *x(new int());
unique_ptr<int> up1,up2;
//会使up1 up2指向同一个内存
up1.reset(x);
up2.reset(x);

要避免写这样的程序

记得使用u.release()的返回值

在调用u.release()时是不会释放u所指的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了

在 C++ 中,`std::unique_ptr` `std::shared_ptr` 是 C++11 引入的两种智能指针,它们都用于自动管理动态分配的内存,避免内存泄漏。但它们的设计目标、使用场景行为有显著区别。 --- ## ✅ `unique_ptr` 与 `shared_ptr` 的主要区别 | 特性 | `unique_ptr` | `shared_ptr` | |------|----------------|----------------| | **所有权** | 独占所有权(只能有一个指针拥有对象) | 共享所有权(多个指针可以共享一个对象) | | **复制** | 不可复制(只能移动) | 可复制 | | **性能** | 更高效(没有引用计数开销) | 稍慢(需要维护引用计数) | | **适用场景** | 资源独占使用,如局部对象、工厂返回的对象 | 多个对象共享资源,如容器、回调函数 | | **内存开销** | 无额外内存开销 | 需要额外内存存储引用计数 | | **删除器支持** | 支持自定义删除器 | 支持自定义删除器 | --- ## ✅ 示例代码对比 ### 🟢 `unique_ptr` 示例 ```cpp #include <iostream> #include <memory> class MyClass { public: MyClass() { std::cout << "构造\n"; } ~MyClass() { std::cout << "析构\n"; } void foo() { std::cout << "foo\n"; } }; int main() { std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(); // std::unique_ptr<MyClass> ptr2 = ptr1; // 错误!不能复制 std::unique_ptr<MyClass> ptr2 = std::move(ptr1); // 正确:移动所有权 if (ptr1 == nullptr) { std::cout << "ptr1 已经被移动,不再拥有对象\n"; } ptr2->foo(); return 0; } ``` ### 🟢 `shared_ptr` 示例 ```cpp #include <iostream> #include <memory> class MyClass { public: MyClass() { std::cout << "构造\n"; } ~MyClass() { std::cout << "析构\n"; } void foo() { std::cout << "foo\n"; } }; int main() { std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(); std::shared_ptr<MyClass> ptr2 = ptr1; // 正确:共享所有权 std::cout << "引用计数: " << ptr1.use_count() << std::endl; // 输出 2 ptr1.reset(); // ptr1 放弃所有权 std::cout << "引用计数: " << ptr2.use_count() << std::endl; // 输出 1 ptr2->foo(); return 0; } ``` --- ## ✅ 使用建议 | 场景 | 推荐指针 | |------|----------| | 对象生命周期清晰、不共享 | `unique_ptr` | | 多个对象共享资源 | `shared_ptr` | | 需要传递所有权 | `unique_ptr`(使用 `std::move`) | | 存入容器中需要复制 | `shared_ptr` | | 避免循环引用 | `shared_ptr` + `weak_ptr` | --- ## ✅ 循环引用问题(`shared_ptr` 的陷阱) ```cpp struct B; struct A { std::shared_ptr<B> b_ptr; ~A() { std::cout << "A 析构\n"; } }; struct B { std::shared_ptr<A> a_ptr; // 会导致循环引用 ~B() { std::cout << "B 析构\n"; } }; int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // 循环引用:无法释放 } ``` > 🔴 上述代码中,`A` `B` 相互持有 `shared_ptr`,引用计数永远不为 0,导致内存泄漏。 ✅ **解决方法**:将其中一个改为 `std::weak_ptr` ```cpp struct B; struct A { std::shared_ptr<B> b_ptr; ~A() { std::cout << "A 析构\n"; } }; struct B { std::weak_ptr<A> a_ptr; // 改为 weak_ptr ~B() { std::cout << "B 析构\n"; } }; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值