c++:智能指针(unique_ptr)

unique_ptr介绍:

unique_ptr是一种定义在<memory>中的智能指针(smart pointer)。它持有对对象的独有权

unique是独特的、唯一的意思,故名思议,unique_ptr可以“独占”地拥有它所指向的对象,它提供一种严格意义上的所有权。这一点和我们前面介绍的shared_ptr类型指针有很大的不同:shared_ptr允许多个指针指向同一对象,而unique_ptr在某一时刻只能有一个指针指向该对象。unique_ptr保存指向某个对象的指针,当它本身被删除或者离开其作用域时会自动释放其指向对象所占用的资源


注意:

1.两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作

2.unique_ptr在超出作用域,即以下情况时它指向的对象会被摧毁:

  • unique_ptr指向的对象被破坏
  • 对象通过operator=()或reset()被指定到另一个指针)

unique_ptr还可能没有对象,这种情况被称为empty。

例如:

std::unique_ptr<int>p1(new int(5));

std::unique_ptr<int>p2=p1;// 编译会出错

std::unique_ptr<int>p3=std::move(p1);// 转移所有权,那块内存归p3所有, p1成为无效的针.

p3.reset();//释放内存.

p1.reset();//无效


功能

  • 不管是正常退出还是异常退出,均可通过保证删除为处理拥有动态寿命的和函数提供额外的保护;
  • 将独有的持有动态寿命对象传递给函数;
  • 从函数获取持有动态寿命对象的所有权
  • 所有auto_ptr应该已经具有的功能

unique_ptr十分依赖于右值引用和移动语义。

下面是一段传统的会产生不安全的异常的代码:

X* f() {

X* p =new X; // 做一些事情 – 可能会抛出某个异常

return p;

}

解决方法是,用一个unique_ptr 来管理这个对象的所有权,由其进行这个对象的删除释放工作:

X* f() {

unique_ptr p(new X); // 或者使用{new X},但是不能 = new X

return p.release();

}

如果程序执行过程中抛出了异常,unique_ptr就会(毫无疑问地)删除释放它所指向的对象,这是最基本的RAII。但是,除非我们真的需要返回一个内建的指针,我们可以返回一个unique_ptr,让事情变得更好。

unique_ptr f() {

unique_ptr p(new X); // 或者使用{new X},但是不能 = new X

return p; // 对象的所有权被传递出f()

}

我们可以这样使用函数f():

void g() {

unique_ptr q = f(); // 使用移动构造函数(move constructor)

q->memfct(2); // 使用q

X x = *q; // 复制指针q所指向的对象

// …

} // 在函数退出的时候,q以及它所指向的对象都被删除释放

unique_ptr拥有“移动意义(move semantics)”,所以我们可以使用函数f() 返回的右值对q进行初始化,这样就简单地将所有权传递给了q。
  在那些要不是为了避免不安全的异常问题(以及为了保证指针所指向的对象都被正确地删除释放),我们不可以使用内建指针的情况下,我们可以在容器中保存unique_ptr以代替内建指针:

vector<unique_ptr<string>> vs {

new string{"Doug"}, new string{"Adams"}

};

unique_ptr可以通过一个简单的内建指针构造完成,并且与内建指针相比,两者在使用上的差别很小。特殊情况下,unique_ptr并不提供任何形式的动态检查(?)

来源自:百度


1.创建unique_ptr

创建一个unique_ptr,我们需要将一个new操作符返回的指针传递给unique_ptr的构造函数

示例:

int main()

{

   //创建一个unique_ptr实例

   unique_ptr<int> pInt(new int(5));

   cout << *pInt;

}

2. unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。

示例:

int main()

{

     //创建一个 unique_pt实例     

     unique_ptr<int> pInt(new int(5));

     unique_ptr<int> pInt2(pInt);//报错

     unique_ptr<int> pInt3 = pInt;//报错 

}

3.如果需要转移所有权通,通过std::move()函数将指针的所有权从一个unique_ptr转移给另外unique_ptr。

int main()

{

     unique_ptr<int> pInt(new int(5));

     unique_ptr<int> pInt2 = std::move(pInt);//转移所有权

     cout << *pInt2 <<endl;

     unique_ptr<int> pInt3(std::move(pInt2));

    //这个时刻可以打印*pInt3,但是不能打印*pInt,因为pInt为空

}

4.unique_ptr可以从函数返回

unique_ptr<int> clone(int p)
{
    unique_ptr<int> pInt(new int(p));
    return pInt;    // 返回unique_ptr
}

int main() {
    int p = 5;
    unique_ptr<int> ret = clone(p);
    cout << *ret << endl;
}


unique_ptr使用场景

  • 1、为动态申请的资源提供异常安全保证
  • 2、返回函数内动态申请资源的所有权
  • 3、在容器中保存指针
  • 4、管理动态数组
  • 5、作为auto_ptr的替代品

下面详细介绍下:

1、为动态申请的资源提供异常安全保证

传统写法:当我们动态申请内存后,有可能我们接下来的代码由于抛出异常或者提前退出(if语句)而没有执行delete操作。

void Func()
{
    int *p = new int(5);

    // ...(可能会抛出异常)

    delete p;
}
 

解决后的方法是使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数都会被调用。确保动态资源被释放。

void Func()
{
    unique_ptr<int> p(new int(5));

    // ...(可能会抛出异常)
}

2、返回函数内动态申请资源的所有权

unique_ptr<int> Func(int p)
{
    unique_ptr<int> pInt(new int(p));
    return pInt;    // 返回unique_ptr
}

int main() {
    int p = 5;
    unique_ptr<int> ret = Func(p);
    cout << *ret << endl;
    // 函数结束后,自动释放资源
}


3、在容器中保存指针

int main() {
    vector<unique_ptr<int>> vec;
    unique_ptr<int> p(new int(5));
    vec.push_back(std::move(p));    // 使用移动语义
}

4、管理动态数组

标准库提供了一个可以管理动态数组的unique_ptr版本。

int main() {
    unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
    p[0] = 0;   // 重载了operator[]
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值