C++智能指针

本文详细介绍了C++中的智能指针unique_ptr、shared_ptr和weak_ptr的概念、用法、初始化方法、注意事项以及它们在处理对象生命周期和线程安全方面的特性,以及智能指针的自定义删除器功能。
摘要由CSDN通过智能技术生成

智能指针

unique_ptr

unique_ptr独享它指向的对象,也就是说,同时只有一个 unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。

简单理解:智能指针就是一个类,成员就是其管理的普通的指针,当智能指针销毁时,会自动调用其析构函数,其析构函数会delete释放普通指针。

1)初始化

方法一:

unique_ptr<AA> p0(new AA("西施"));//分配内存并初始化。
方法二:

unique_ptr<AA>p0= make_unique<AA>("西施");//C++14 标准。
方法三:

AA* p= new AA("西施");

unique_ptr<AA> p0(p);//用已存在的地址初始化。

2)使用方法

智能指针重载了 * 和->操作符,可以像使用指针一样使用 unique_ptr

  • 不支持普通的拷贝和赋值。

    AA* p= new AA("西施");
    unique_ptr<AA> pu2 = p;//错误,不能把普通指针直接赋给智能指针
    unique_ptr<AA> pu3 = new AA("西施");//错误,不能把普通指针直接赋给智能指针
    unique_ptr<AA> pu2 = pu1;//错误,不能用其它 unique_ptr拷贝构造。
    unique_ptr<AA> pu3;< pu3 = pu1;//错误,不能用=对 unique_ptr进行赋值。
        
    //因为智能指针类中禁用了 拷贝构造函数 和 =赋值
    
  • 不要用同一个裸指针初始化多个unique_ptr

  • get()方法返回裸指针。

  • 不要用 unique_ptr管理不是 new 分配的内存。

3)用于函数的参数
  • 传引用(不能传值,因为unique_ptr禁用了拷贝构造函数)。
  • 裸指针。
4)不支持指针的运算(+、-、++、-)
5)更多用法
  1. 将一个unique_ptr赋给另一个时,如果源 unique_ptr是一个临时右值,编译器允许这样做;如果源 unique_ptr将存在一段时间,编译器禁止这样做。一般用于函数的返回值。

    unique_ptr<AA>p0;
    p0 = unique_ptr<AA>(new AA("西瓜)");
    //unique_ptr<AA>(new AA("西瓜)")是匿名对象是一个临时的右值,可以直接复制
    
  2. 用 nullptr给unique_ptr 赋值将释放对象,空的unique_ptr==nullptr。

  3. release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。(可用于把 unique_ptr传递给子函数,子函数将负责释放对象。

  4. std::move()函数可以转移对原始指针的控制权。(可用于把 unique_ptr传递给子函数,子函数形参也是unique ptr)。

  5. reset()释放对象。

    void reset(T*_ptr=(T*) nullptr);
    pp.reset(); //释放pp对象指向的资源对象。
    pp.reset(nullptr); //释放pp对象指向的资源对象
    pp.reset(new AA("bbb")); //释放pp指向的资源对象,同时指向新的对象。
    
  6. swap()交换两个unique_ptr的控制权。
    void swap(unique_ptr<T>&_Right);

  7. unique_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

  8. unique_ptr不是绝对安全,如果程序中调用 exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放。

shared_ptr

1)初始化

方法一:
shared_ptr<AA>p0(new AA("西施"));//分配内存并初始化.

方法二:
shared_ptr<AA>p0= make shared<AA>("西施");//C++11标准,效率更高.

方法三:
AA*p= new AA("西施");

<shared_ptr<AA>p0(p);//用已存在的地址初始化.

方法四:
shared_ptr<AA>p0(new AA("西施")); shared_ptr<AA>p1(p0);//用已存在的shared_ptr初始化,计数加1. shared_ptr<AA>p1=p0;//用已存在的shared_ptr初始化,计数加1.

2)使用方法

智能指针重载了*和->操作符,可以像使用指针一样使用 shared_ptr

  • use_count()方法返回引用计数器的值.
  • unique()方法,如果use_count()为1,返回true,否则返回false.
  • shared_ptr支持赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1.
  • get()方法返回裸指针.
  • 不要用同一个裸指针初始化多个shared_ptr.
  • 不要用 shared_ptr管理不是new分配的内存.
3)用于函数的参数

unique_ptr同理。

4)不支持指针的运算(+、-、++、-)
5)更多用法
  1. 将一个unique_ptr赋给另一个时,如果源 unique_ptr是一个临时右值,编译器允许这样做;如果源 unique_ptr将存在一段时间,编译器禁止这样做。一般用于函数的返回值。

  2. 用 nullptr给shared_ptr 赋值将释放对象,空的shared_ptr==nullptr。

  3. release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。

  4. std::move()函数可以转移对原始指针的控制权。还可以将unique_ptr转换为shared_ptr。

  5. reset()释放对象。

    pp.reset();//解除与资源的关系,资源的引用计数减1.
    pp.reset(new AA("bbb"));//解除与资源的关系,资源的引用计数减1.关联新资源.
    
  6. swap()交换两个shared_ptr的控制权。
    void swap(shared_ptr<T>&_Right);

  7. shared_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

  8. shared_ptr不是绝对安全,如果程序中调用 exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放。

  9. shared_ptr的线程安全性:

    • shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。
    • 多个线程同时读同一个shared_ptr对象是线程安全的。
    • 如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
    • 多线程读写 shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。
  10. 如果 unique_ptr能解决问题,就不要用 shared_ptrunique_ptr的效率更高,占用的资源更少。

weak_ptr

1)shared_ptr存在的问题

shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一个资源。
如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。

例如有两个类,AA和BB,AA类有一个成员是BB,BB类有一个成员是AA。

AA->m_p = BB;
BB->m_p = AA;

上述代码就导致了循环引用的问题。

2)weak_ptr是什么

weak_ptr是为了配合 shared_ptr而引入的,它指向一个由 shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,如果最后一个指向资源的 shared_ptr被销毁,资源就会被释放。
weak_ptr更像是 shared_ptr的助手而不是智能指针。类似于windows系统下的快捷方式或者linux系统下的软链接。

3)使用方法

weak_ptr没有重载->和*操作符,不能直接访问资源。

有以下成员函数:

operator=();//把 shared_ptr或 weak_ptr赋值给weak_ptr.
expired();//判断它指资源是否已过期(已经被销毁).
lock();//返回 shared_ptr,如果资源已过期,返回空的 shared_ptr.
reset();//将当前weak_ptr指针置为空.
swap();//交换.
4)注意事项

pa是一个shared_prt,pa所指的对象的成员变量里有一个weak_ptr类型的成员mp。

if(pa->mp.expired()== true)
    cout<<"pa->m_p已过期.\n";
else 
    cout <<"pa->m_p.1ock()->m_name="<<pa->m_p.1ock()->m_name <<end1;

上述代码在单线程里面是没问题的,但是对于多线程来说,是不安全的。因为判断mp是否过期和mp.lock()不是原子操作。会有其他线程在这个过程中释放mp。

weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。
lock()函数把它可以提升为 shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的 shared_ptr
提升的行为(lock())是线程安全的(原子操作)。

shared ptr<BB> pp = pa->m_p.lock(); //把weak_ptr提升为shared_ptr
if(pp == nullptr)
    cout<<"pa->m_p已过期.\n";
else 
    cout <<"pa->m_p.1ock()->m_name="<<pp->m_name <<end1;

智能指针的删除器

在默认情况下,智能指针过期的时候,用delete原始指针;释放它管理的资源。
程序员可以自定义删除器,改变智能指针释放资源的行为。
删除器可以是全局函数、仿函数和 Lambda表达式,形参为原始指针。

以上资料参考链接:https://www.bilibili.com/video/BV1gV4y1G7fH?p=1&vd_source=fc6bd12a18142f9247518e546641c48f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值