c++智能指针shared_ptr

1.为什么需要shared_ptr智能指针

我们在日常使用指针过程中一般标准的大致流程应该是这样的。

 void funcTest(){
    int *pI = new int;
    //doSomething
    delete pI;
}

但如果我们由于种种原因忘记在最后delete掉在堆栈上分配的对象,就会造成内存泄露。

还有这么一个情景

void funcTest0(int *pI){
    //doSomething
    delete pI;
}


void funcTest1(int *pI){
    //doSomething
    delete pI;
}

void func(){
    int *pI = new int;
    //Thread1
    funcTest0(pI);

    //Thread2
    funcTest1(pI);
}

pI在主线程中被构造出来,然后传给thread1和thread2两个线程。在这两个线程中分别执行funcTest0 和 funcTest1 这两个函数。 这样的话情况就很麻烦,任何一个函数执行完后,pI都会被析构掉,导致另一个函数执行出错。

为了解决上面这些问题,聪明的程序员们发明了智能指针,通过使用智能指针,我们可以优雅方便的处理上面的这种问题。

2.shared_ptr智能指针大概原理

普通指针的使用方法是一个指针直接指向一片内存区域,如下图
在这里插入图片描述
这样的话就会出现第一部分中的那些问题。智能指针为了解决这个问题,在指针和内存空间之间添加了一个中间层(任何问题都可以通过添加一个中间层解决,如果解决不了就再加一个 - -);所以添加过之后的情况大致是这样
在这里插入图片描述
当一个shared_ptr智能指针需要指向一片内存区域时,会先指向这个中间层,这个中间层再去指向真正需要指向的对象。 同时这个中间层中维护者一个引用计数,也就是说,只要发生一次上面的这个指向过程,这个引用计数就会加一,手动调用reset,或者超出作用域,这个计数就会自动减一。指向的对象会在这个计数为0,也就是说所有地方都用不到这一个对象时自动析构。

3.shared_ptr智能指针使用方法

使用方法也很简单。只要引入<memory.h>头文件即可, 大致使用方法如下

class Test{
public:
    Test(){
        i = QUuid::createUuid().toString().remove("{").remove("}");
        qDebug() << i << "已经被构造了"<<endl;
    }

    Test(QString i){
        this->i = i;
        qDebug() << i << "已经被构造了"<<endl;
    }

    ~Test(){
        qDebug() << i <<"已经被析构了" <<endl;
    };


private:
    QString i;

};


void simpleUse(){
    shared_ptr<Test> p1 = make_shared<Test>("1"); //p1->指向Test(1), Test(1)的引用计数变为1

    p1 = make_shared<Test>("2"); // pi->指向新对象 Test(2) Test(2)的引用计数变为1 ,原先指向的Test(1)计数
    //减一变为0,Test(1)被析构,
    
	shared_ptr<Test> p2(nwe Test("3"; //效率比make_shared低,一般建议使用make_shared
	
    p1.reset(); // p1 reset后 指向Test(2)对象计数减一,变为0;Test(2)也被析构
}

4.shared_ptr智能指针注意事项

4.1 不要两个不同的shared_ptr智能指针指向同一对象

void repeatPointsTo()
{
    Test *t = new Test("1");
    shared_ptr<Test> p1(t);
    shared_ptr<Test> p2(t);
}

这样的话就会造成重复析构,执行结果如下
指向同一对象后的输出

4.2 注意shared_ptr智能指针不能循环引用

智能指针千万不能循环引用,循环引用之后每个指针的引用计数都变成了2,析构时只会减一,导致无法正常析构,出现内存泄露。

代码如下

struct TestB;
struct TestA {
    std::shared_ptr<TestB> bPtr;
    ~TestA() { cout << "TestA is deleted!"<<endl; }
};

struct TestB {
    std::shared_ptr<TestA> APtr;
    ~TestB() { cout << "TestB is deleted!" << endl; }
};

void TestLoopReference()
{
    std::shared_ptr<TestA> ap(new TestA);
    std::shared_ptr<TestB> bp(new TestB);
    ap->bPtr = bp;
    bp->APtr = ap;
}

这样执行之后,结果如下
重复引用输出结果
被构造出来的两个对象都没有被析构,导致了内存泄露。

4.3 注意shared_ptr智能指针指向数组时的处理

当我们使用智能指针指向数组时也要注意书写的方式,如果我们使用方式不对,也会造成内存溢出

void customRemover()
{
    shared_ptr<Test> p1(new Test[5]);//未指定自定义删除器,只会删除第一个,导致内存泄露;
}

如果我们像这样书写的话,就会导致内存泄露,生成5个对象,却只能析构一个。

错误写法输出
正确写法如下

void customRemover()
{
    shared_ptr<Test[]> p1(new Test[5]);//未指定自定义删除器,只会删除第一个,导致内存泄露;
}

正确写法输出
也可以自定义删除器,写法如下

   shared_ptr<Test> p2(new Test[5],[](Test *t){
       delete []t;
   }); //自定义删除器,生成对象全部delete

这样写的话也可以析构掉所有生成的内容
在这里插入图片描述

4.3 shared_ptr智能指针线程安全

智能指针自己是线程安全的,跨线程可以放心使用,但是智能指针指向的内存空间可不是内存安全的,在跨线程使用使用过程中要注意加锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值