unique_ptr的常规使用

文章详细介绍了C++中的unique_ptr智能指针用法,展示了如何创建、转移所有权、以及使用make_unique和reset操作。特别强调了移动语义和资源管理的重要性。

在这里插入图片描述

#include <memory>
#include <iostream>
using namespace std;

class Widget{
    int m_x;
    int m_y;
    int m_z;

public:
    Widget(int x,int y , int z):
        m_x(x), m_y(y),m_z(z)
    {}

    void print(){
        cout<<m_x<<","<<m_y<<","<<m_z<<endl;
    }

    ~Widget()
    {
        cout<<"Widget dtor"<<endl;
    }
};


int main()
{

    {
        //unique_ptr<Widget> w1=new Widget(1,2,3); 错误
        unique_ptr<Widget> w1(new Widget(1,2,3));
        w1->print();

        auto w2= unique_ptr<Widget>(new Widget(10,20,30));
        //auto w2=w1;
         auto w2=std::move(w1);
         //if(w1==nullptr) cout<<"w1 is nullptr"<<endl;
        // w2->print();

        //w1.swap(w2);
        //w1->print();
        //w2->print();

        //w2.reset();
        w2.reset(new Widget(11,22,33)); 
        //w2=nullptr; //等价

        if(w2!=nullptr)
        {
            cout<<"w2 is not null"<<endl;
        }
        else 
        {
            cout<<"w2 is null_ptr"<<endl;
        }

    }
    cout<<"----------"<<endl;


    {
        auto w3 = make_unique<Widget>(100,200,300);
        w3->print();

        //auto w4=w3;// 错误

        auto w4=std::move(w3); //OK,w3不能用
       
        unique_ptr<Widget> w5{std::move(w4)};

        Widget* rawp1=w5.get(); 

        //process(w5.get());
        w5->print();
        //delete rawp1;

         Widget* rawp=w5.release();//释放所有权
         if(w5==nullptr) cout<<"w5 is null_ptr"<<endl;
         delete rawp; //必须负责释放d

    }
    
}
<think>嗯,用户现在问的是关于`unique_ptr`的赋值问题,之前已经讨论过使用`unique_ptr`管理从函数返回的原始指针的情况。用户现在看到示例代码中有一个`unique_ptr<int> ptr = Fun();`,而根据之前的了解,`unique_ptr`是不能被赋值的,因为它独占所有权,不可复制。这看起来似乎矛盾,所以用户提出了疑问。 首先,我需要确认用户的问题所在。他们可能认为赋值操作涉及到复制构造函数或赋值运算符的使用,而`unique_ptr`确实删除了复制构造函数和复制赋值运算符,所以常规的赋值是不允许的。但是示例中的代码是`unique_ptr<int> ptr = Fun();`,这看起来像是一个初始化而不是赋值操作。需要检查这里是否触发了移动语义或者隐式的移动操作。 接下来,要回忆C++中关于构造函数和赋值操作的区别。当使用`=`初始化一个对象时,实际上调用的是构造函数,而不是赋值运算符。对于`unique_ptr`来说,虽然不能复制,但移动构造函数是允许的。因此,如果函数返回的是一个临时`unique_ptr`对象,那么在初始化新`unique_ptr`时,会调用移动构造函数而不是复制构造函数,这样是合法的。 然后,需要验证示例代码中的`Fun()`返回的是一个右值,因此当使用`unique_ptr<int> ptr = Fun();`时,实际上触发了移动构造,而不是复制构造。这符合`unique_ptr`只能移动不能复制的特性。因此,这种情况下代码是合法的,而用户可能混淆了初始化和赋值的概念。 另外,用户可能对`=`符号在这里的作用有误解,认为这是赋值操作,但实际上在变量声明时使用`=`是初始化,调用的是构造函数。如果是后续的赋值,比如`ptr = Fun();`,这同样可能有效,如果`unique_ptr`定义了移动赋值运算符的话。需要确认`unique_ptr`是否支持移动赋值,即是否允许将右值赋值给已存在的`unique_ptr`对象。 可能还需要指出,在C++中,返回值优化(RVO)和移动语义的结合使得返回`unique_ptr`的函数非常高效,不会有额外的开销,这也是推荐使用`make_unique`来返回`unique_ptr`的原因之一。 总结起来,用户的问题在于混淆了初始化和赋值操作,同时可能没有意识到移动语义在其中的作用。需要详细解释这两种操作的区别,以及`unique_ptr`如何通过移动构造函数和移动赋值运算符来管理所有权的转移,而不是复制。</think>以下是关于 `unique_ptr` 的赋值和所有权的分步解释: --- ### 1. **`unique_ptr` 的核心特性** - **独占所有权**:同一时刻只能有一个 `unique_ptr` 拥有某个资源。 - **不可复制**:**拷贝构造函数**和**拷贝赋值运算符**被显式删除(`= delete`)。 - **支持移动**:可以通过**移动构造函数**和**移动赋值运算符**转移所有权。 --- ### 2. **代码分析:`unique_ptr<int> ptr = Fun();`** ```cpp unique_ptr<int> Fun() { return make_unique<int>(42); // 返回一个临时 unique_ptr(右值) } unique_ptr<int> ptr = Fun(); // 这里发生的是移动构造,而非拷贝! ``` #### 关键点: 1. **返回值是右值**:`Fun()` 返回的 `unique_ptr` 是一个临时对象(右值)。 2. **移动构造函数生效**: - `unique_ptr` 的**移动构造函数**会被调用(因为右值可以安全“转移”所有权)。 - 原临时 `unique_ptr` 的所有权转移给 `ptr`,原指针变为 `nullptr`。 3. **不是赋值操作**:`=` 在这里是初始化(构造),不是赋值运算符。 --- ### 3. **验证所有权转移** ```cpp unique_ptr<int> ptr1 = make_unique<int>(42); unique_ptr<int> ptr2 = std::move(ptr1); // 显式移动构造:合法 // ptr1 现在为空(ptr1.get() == nullptr) // ptr2 拥有资源 ``` - **显式移动**:必须使用 `std::move` 标记左值为右值,才能转移所有权。 - **隐式移动**:函数返回临时对象时(如 `Fun()`),编译器自动优化为移动操作。 --- ### 4. **为何不允许直接赋值?** 如果尝试拷贝(而非移动),会直接编译报错: ```cpp unique_ptr<int> ptr1 = make_unique<int>(42); unique_ptr<int> ptr2 = ptr1; // 错误:拷贝构造函数被删除 ``` - **错误原因**:`unique_ptr` 的设计禁止共享所有权,因此拷贝语义被禁用。 --- ### 5. **赋值与初始化的区别** - **初始化**(构造): ```cpp unique_ptr<int> ptr = Fun(); // 移动构造(合法) ``` - **赋值操作**(需移动语义): ```cpp unique_ptr<int> ptr; ptr = Fun(); // 移动赋值(合法,因为 Fun() 返回右值) ``` - 这里调用 `unique_ptr` 的**移动赋值运算符**,同样合法。 --- ### 6. **总结规则** | 操作类型 | 是否合法 | 原因 | |-------------------------|------------------------------|----------------------------------------------------------------------| | `unique_ptr<T> a = b;` | 非法(编译错误) | `b` 是左值,尝试拷贝 | | `unique_ptr<T> a = Fun()` | 合法 | `Fun()` 返回右值,触发移动构造 | | `a = b` | 非法(编译错误) | `b` 是左值,尝试拷贝赋值 | | `a = Fun()` | 合法 | `Fun()` 返回右值,触发移动赋值 | | `a = std::move(b)` | 合法 | 显式转移所有权 | --- ### 7. **最佳实践** - **优先返回 `unique_ptr`**:如示例中的 `Fun()`,直接返回 `unique_ptr` 可避免手动管理资源。 - **明确移动语义**:需要转移所有权时,使用 `std::move`。 - **避免悬挂指针**:所有权转移后,原 `unique_ptr` 不再持有资源,无需(也不应)再访问它。 --- 通过这种设计,C++ 在保证安全性的同时,提供了高效的资源管理机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

发如雪-ty

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

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

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

打赏作者

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

抵扣说明:

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

余额充值