C++11特性:智能指针介绍

简介

在C++98标准里面就已经有了智能指针的概念,它是把指针封装成为了一个类。这层封装让指针像是一个对象,这个对象的行为像一个指针。它的作用就是为了实现对指针的生命周期的管理。例如下面代码

DataType *T_ptr = new DataType();
...
...
delete T_ptr;
T_ptr = NULL;

这段代码的后两句是用来结束掉指针T_ptr的声明周期,但是如果代码量大了之后很有可能程序员就忘记了这两步操作,这就会造成悬挂指针。只是很危险的事情。
在这个时候,智能指针的出现实际上就是为了可以方便的控制对象的生命期,在智能指针中,一个对象什么时候和在什么条件下要被析构或者是删除是受智能指针本身决定的,用户并不需要管理。

auto_ptr

auto_ptr是C++98里给出的一种智能指针的实现,他只是简单的对普通指针进行了封装,通过构造和析构实现对资源的管理。

他在设计的时候因为考虑到auto_ptr对象复制之后多个对象指向一个资源,此时对某个ptr的资源释放,就会造成类似原生指针中垂悬指针的情况,所以auto_ptr限制了某个资源每次只能有一个auto_ptr指向它。具体来说就是在auto_ptr对象复制的时候,被复制的ptr在复制完成的时候就会被置空,这样就完成了资源的所有权的转移。

这个做法现在看起来是很蠢的,因为在现在很多情况中,我们对对象的复制都是需要对其值进行拷贝,auto_ptr内部这种实现拷贝的方法显然很多时候是不可取的。这也就是为什么auto_ptr是不能放在vector中的原因。

新的智能指针

C++为了更好的实现RAII,在C++11中标准委员会又像C++中加入了另外几种新的智能指针:unique_ptr,shared_ptr,weak_ptr

unique_ptr

为了解决auto_ptr拷贝时对象所有权转移而不能实现值拷贝的情况,C++11里给出了一种新的智能指针unique_ptr。它给出了解决方法很简单,简单来说就是unique_ptr拒绝对其拷贝,也就是说同一时刻只能有一个unique_ptr指向给定对象。
不过unique_ptr也是支持对象内资源所有权转移的,因为它禁止拷贝操作所以他的所有权转移只能通过move语义来实现。
这样规定之后,unique_ptr就可以放在容器里面,但是这里有一个问题那就是,把某个对象用move语义放入容器之后,对象本身就会失效。

shared_ptr

这样看来前两种智能指针其实是大同小异,他们都只可以同时让一个对象管理资源。那么为了实现像原生指针一样,多个指针同时管理同一资源,C++11中又给出了shared_ptr这种智能指针。
shared_ptr是一个引用计数智能指针,用于共享对象的所有权也就是说它允许多个指针指向同一个对象。

《C++primer》中给出了详细的介绍:
每个shared_ptr都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。
例如,当用一个shared_ptr初始化另一个shared_ptr,或将它当做参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。当 我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的
shared_ptr离开其作用域)时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

weak_ptr

weak_ptr的出现是为了解决shared_ptr中出现的另一个问题,那就是当两个shared_ptr对象互相引用的时候,就会造成两个对象的引用计数无法到0,所以无法被释放。这就是循环引用问题。例如定义一个双向链表,它的两个指针域都使用shared_ptr。

template<typename T>
struct ListNode
{
    T _value;
    std::shared_ptr<ListNode> _prev;
    std::shared_ptr<ListNode> _next;

    ListNode(const T & value)
        :_value(value)
        ,_prev(NULL)
        ,_next(NULL)
    {}
};

void TestWeekPtr()
{
    std::shared_ptr<ListNode<int>> sp1(new ListNode<int>(10));
    std::shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
    sp1->_next = sp2;
    sp2->_prev = sp1;
}

这样的设计就会出现我们所说的循环引用,当出了函数作用域,释放sp1需要先释放sp2,而释放sp2又要先释放sp1,这样就导致了谁都没有办法释放。
weak_ptr的设计方案就是使他可以从一个shared_ptr或者weak_ptr中构造,但是这样并不会使资源的引用计数增加。weak_ptr里面没有重载“*”“->”,所以并没有真正的获得资源的所有权,只能说是在观测资源的引用计数(通过use_count()和expired()函数),不过weak_ptr里有一个非常重要的成员函数lock(),它可以从shared_ptr中获得一个可用的shared_ptr对象,从而操作资源。当引用计数为空的时候,则会返回一个空指针。
上面的代码就可以改为

template<typename T>
struct ListNode{
    T _value;
    weak_ptr<ListNode> _prev;
    weak_ptr<ListNode> _next;

    ListNode(const T & value)
        :_value(value)
        ,_prev(NULL)
        ,_next(NULL)
        {}

};

void TestWeekPtr()
{
    std::shared_ptr<ListNode<int>> sp1(new ListNode<int>(10));
    std::shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
    sp1->_next = sp2;
    sp2->_prev = sp1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值