智能指针 in c++【C++学习笔记】

智能指针 in c++

智能指针会自动的释放内存,实现自动化

用智能指针,当调用new时,而不需要调用delete,而甚至在很多情况下使用智能指针,我们甚至不需要调用new

而智能指针本质上是一个原始指针的包装,当创建一个智能指针,它会调用new并为你分配内存,然后这些内存会在某一时刻自动释放

🍅最简单的智能指针:unique_ptr

unique_ptr作用域指针,超出作用域时,它就会被销毁,然后调用delete

为什么叫unique_ptr
  • 因为它必须是unique,它必须是唯一的。你不能复制一个unique_ptr,因为如果你复制一个unique_ptr,那么就会有两个unique_ptr指向同一个内存块,而其中一个指针无了,则它会释放那段内存,那么第二个unique_ptr就会指向了已经被释放了的内存。故不能复制unique_ptr
  • unique_ptr是你想要有一个作用域指针的时候,它是你唯一的参考
用法:std::unique_ptr<模板参数> 名字
  • 包括memory头文件,#include <memory>
  • //一般智能指针
    std::unique_ptr<int> temp1 = new int;
    //声明类的智能指针
    std::unique_ptr<Entity> temp2(new Entity());  //这里小懵 
    //初始化的方式可以有:1.用=赋值运算符初始化  2.用()小括号初始化
    //3.用{}大括号初始化
    //在一些不可以复制的对象的初始化上,只能用小括号来初始化(比如智能指针)
    

    但是在unique_ptr中调用new可能会有异常安全(很迷,总之就是不推荐调用new

    💡然而,另外一个更好的办法是std大法好,一个函数叫make_unique<>()make_pair<>()有异曲同工之妙,好处是不用再起变量名,直接造一个对象出来

    #include <memory>
    class Entity {};
    int main() 
    {
        std::unique_ptr<Entity> entity = std::make_unique<Entity>();//直接造一个指向Entity内存空间的指针
        return 0;
    }
    

    这个函数对于unique_ptr<>()很重要,主要原因是异常安全,故最好的方式就是调用make_unique,因为如果构造函数恰好抛出异常,它会稍微安全一些,写程序的人最终不会得到一个没用引用的悬空指针,从而造成内存泄漏(注:make_unique<>()是在C++14引入的,C++11并不支持)

    效果:这样用智能指针会创造一个Entity类的内存空间然后让指针指向它。如果离开了智能指针的作用域,智能指针就会把自己创建的这个类的内存空间删除(原理是智能指针是一个类,在离开作用域时,智能指针会调用自己的析构函数删除自己,而析构函数里的内容还有一个就是delete新创建的内存空间,这样子就完成了自动化)

unique_ptr很简单,用处很大,开销也很小(几乎没有),但是有个致命的缺点是无法分享,无法复制,那如果我想完成这两个操作又想有智能指针的自动化流程呢?

🍅共享指针:shared_ptr

shared_ptr的工作方式是通过引用计数,引用计数基本上是一种方法,可以跟踪你的指针有多少个引用,一旦引用计数到达零,它就会被删除(两个共享指针,则引用计数为2,一个无了,则引用计数为1,而再无了最后一个,引用计数归零,则会释放掉内存)

用法:std::shared_ptr<模板参数> 名字

  • 首先也要包含头文件#include <memory>

  • 💡相仿上面,共享指针也有一个std的函数:std::make_shared<>()
    #include <memory>
    class Entity {};
    int main() {    
        int temp = 1;
        std::shared_ptr<int> a = std::make_shared<int>(temp);//注意这里不是&temp,好像只有在赋值运算符用的时候&才会被识别成取地址运算符
        //比如 a = &b;这个才是取地址
        //而在这里&temp会被识别成引用,所以要注意啊
        //上面也可以写成
        //std::shared_ptr<int> a = std::make_shared<int>();
        //a = &temp;
        //只不过在括号内可以直接初始化了
        std::shared_ptr<Entity> b = std::make_shared<Entity>();
        return 0;
    }
    
  • unique_ptr中不建议用new是因为异常安全,而在共享指针里同样不建议用new,但在二者原因有所不同,因为共享指针需要分配另一块内存,叫做控制块,用来储存引用计数。如果用new,那他必须要做两次内存分配:先做一次new Entity的分配,然后是shared_ptr的控制内存块的分配。而如果用make_shared<>(),就能把这两步操作组合起来,这样更有效率

  • {
        std::shared_ptr<Entity> temp1;
        {
            std::shared_ptr<Entity> temp2 = std::make_shared<Entity>();
            temp1 = temp2;//此时temp1和temp2指向同一块内存!引用计数
        }//当这个作用域结束,temp2无了,但是temp1还在,所以那块内存还是会保留 
    }//当这个作用域结束了,temp1无了,则这块内存就会被释放!
    //当所有的引用都消失了,则底层的Entity就会被删除
        
    

共享指针是有一点开销的,因为它的引用计数系统

🍅弱指针:weak_ptr(搭配共享指针)

用法:std::weak_ptr<模板参数> 名字

💡💡💡当用一个共享指针赋值给另一个共享指针时,引用计数会增加但是共享指针赋值给弱指针时,引用计数并不会增加

std::shared_ptr<Entity> temp1 = std::make_shared<Entity>();
std::weak_ptr<Entity> temp2;
temp2 = temp1;//把temp1的值赋给弱指针,引用计数不会增加!

使用目的:

std::weak_ptr<Entity> temp2;
{
    std::shared_ptr<Entity> temp1 = std::make_shared<Entity>();
    temp2 = temp1;//把temp1的值赋给弱指针,引用计数不会增加!
}//作用域结束,因为引用计数只有一个,所以释放Entity内存空间
//在这个时候,弱指针会指向一个无效的Entity内存空间

弱指针的思想就是:如果底层对象还活着,那就可以拿弱指针干想做的事,但是弱指针不会让底层对象保持存活!因为弱指针不会增加引用计数(并不关心Entity是否存活,我只是需要储存它的一个引用罢了)

使用总结

当想声明一个堆分配的对象,但是我不希望我自己亲自来清理它(或者显式的用delete来清理),则去用智能指针。如果用,就尽量使用unique_ptr,因为它有一个较低的开销。但如果需要在对象之间共享,就用shared_ptr

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值