C++智能指针以及循环引用的解决办法

• 1:为什么要使用智能指针
我们知道C++的内存管理是让很多人头疼的事情,当我们写一个new语句时,一般就会立即把delete语句直接也写了,但是我们不能避免程序还未执行到delete时就跳转了或者在函数中没有执行到最后的delete语句就返回了,如果我们不在每一个可能跳转或者返回的语句前释放资源,就会造成内存泄露。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。
• 2:auto_ptr

class Test
{
public:
    Test(string s)
    {
        str = s;
       cout<<"Test creat\n";
    }
    ~Test()
    {
        cout<<"Test delete:"<<str<<endl;
    }
    string& getStr()
    {
        return str;
    }
    void setStr(string s)
    {
        str = s;
    }
    void print()
    {
        cout<<str<<endl;
    }
private:
    string str;
};
int main()
{
    auto_ptr<Test> ptest(new Test("123"));
    ptest->setStr("hello ");
    ptest->print();
    ptest.get()->print();
    ptest->getStr() += "world !";
    (*ptest).print();
    ptest.reset(new Test("123"));
    ptest->print();
    return 0;
}

智能指针可以像类的原始指针一样访问类的public成员,成员函数get()返回一个原始的指针,成员函数
reset()重新绑定指向的对象,而原来的对象会被释放。
当对智能指针进行赋值时,如ptest2 = ptest,ptest2会接管原来的内存控制权,ptest会变成空指针,如果ptest2原来不为空,则他会释放掉原来的资源,基于这个原因,应该避免把auto_ptr放在容器中,因为算法对容器操作时,很难避免STL内部对容器实现了赋值操作,这样会使容器中很多元素被置为NULL,判断一个智能指针是否为空不能使用if(ptest == null),应该使用if(ptest.get() == null)。
• 3:unique_ptr
uniqu_ptr是一个独享所有权的智能指针,它提供了严格意义上的所有权,包括:
(1)拥有它指向的对象
(2)无法进行复制构造,无法进行复制赋值操作,他将复制构造函数和等号操作符设为私有。即无法使两个unique_ptr指向同一个对象。但是可以进行移动构造和移动赋值操作
(3)保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象

unique_ptr<Test> fun()
{
    return unique_ptr<Test>(new Test("789"));
}
int main()
{
    unique_ptr<Test> ptest(new Test("123"));
    unique_ptr<Test> ptest2(new Test("456"));
    ptest->print();
    ptest2 = std::move(ptest); //不能直接ptest2 = ptest
    if(ptest == NULL)cout<<"ptest = NULL\n";
    Test* p = ptest2.release();
    p->print();
    ptest.reset(p);
    ptest->print();
    ptest2 = fun(); //这里可以用=,因为使用了移动构造函数
    ptest2->print();
    return 0;
}

• 4:share_ptr
shared_ptr允许多个指针指向同一个对象。
从名字可以看出资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。可以通过new来构造。
*share_ptr出现的循环引用的问题
循环引用简单来说就是:类A中有类B的shared_ptr智能指针,类B中有类A的shared_ptr智能指针

class B;
class A
{
public:
  shared_ptr<B> m_b;
};
 
class B
{
public:
  shared_ptr<A> m_a;
};
 
int main()
{
  {
    shared_ptr<A> a(new A); //new出来的A的引用计数此时为1
    shared_ptr<B> b(new B); //new出来的B的引用计数此时为1
    a->m_b = b; //B的引用计数增加为2
    b->m_a = a; //A的引用计数增加为2
  }
 
  //b先出作用域,B的引用计数减少为1,不为0,所以堆上的B空间没有被释放,且B持有的A也没有机会被析构,A的引用计数也完全没减少
 
  //a后出作用域,同理A的引用计数减少为1,不为0,所以堆上A的空间也没有被释放
}

如此一来,A和B都互相指着对方吼,“放开我的引用!“,“你先发我的我就放你的!”,于是悲剧发生了。
所以在使用基于引用计数的智能指针时,要特别小心循环引用带来的内存泄漏,循环引用不只是两方的情况,只要引用链成环都会出现问题。当然循环引用本身就说明设计上可能存在一些问题,如果特殊原因不得不使用循环引用,那可以让引用链上的一方持用普通指针(或弱智能指针weak_ptr)即可。
A对象: [A,share_ptr pb] B对象: [B,share_ptr pa]
在mian函数结束时,两个类的对象中智能指针的引用技术都是1,此时如果要释放A对象,需要B对象中的pa释放,而pa的释放友需要A对象的释放。
• 5:weak_ptr
为了解决share_ptr的循环引用的问题,weak_ptr采用了一种弱引用的方式。
解决循环引用的方式有三种:
当只剩下最后一个引用的时候,需要去手动打破循环引用释放对象
当b的生存期超出a时,a改为使用一个普通指针。
使用弱引用
weak_ptr使用的是非拥有性的引用,当weak_ptr指向一块内存时,对应的强引用指针引用计数并不加一weak_ptr 只能由shared_ptr或者其它的weak_ptr构造weak_ptr和shared_ptr共享一个引用计数对象,在引用计数对象上增加一个weak_count, 但不增加ref_count.引用计数对象当ref_count减至zero时会销毁其管理的资源,weak_ptr可以通过ref_count是否为0来判断指向的资源是否可用。当ref_count和weak_count都为0时引用计数对象会销毁其自身。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值