关闭

C++ shared_ptr

标签: C++ STLC++
186人阅读 评论(0) 收藏 举报
分类:
        std::shared_ptr属于STL库,它的作用和auto_ptr相似,也是管理一个对象指针,提供一个释放内存的封装操作,但是它可以和其他shared_ptr对象共享对指针的管理。
它的实现原理是再内部维护一个引用计数,当引用计数为0的时候进行释放销毁操作。
        std::shared_ptr的构造函数有多重,我们常用的是默认构造函数和带有指针参数的构造函数:
        
default (1)
constexpr shared_ptr() noexcept;
from pointer (3)
template <class U> explicit shared_ptr (U* p);
默认的构造函数初始化引用为0,带有指针参数的构造函数初始化引用为1。另外拷贝构造函数:
copy (6)
shared_ptr (const shared_ptr& x) noexcept;
template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
如果x不为空,那么引用计数在x的基础上加1。如果x为空,那么本次构造为默认构造,引用计数为0:
    std::shared_ptr<int> pTest; //默认构造.
    std::shared_ptr<int> pTest2(nullptr); //默认构造
    std::shared_ptr<int> pTest3(pTest);   //默认构造

    std::shared_ptr<int> pTest4(new int(10)); //引用为1
    std::shared_ptr<int> pTest5(pTest4);      //引用为2

        std::shared_ptr在操作符=的实现中同样会对引用计数进行操作,原理和构造函数差不多。
        到这里我们已经可以清楚地知道std::shared_ptr可以共享所有权,并且用引用计数来进行管理,当引用计数为0的时候才会进行释放操作,那么就可能造成一种引用环路问题,我们用一个网上的例子(http://blog.csdn.net/shanno/article/details/7363480):

#include <iostream>
#include <memory>

class Woman;
class Man 
{
private:
    std::weak_ptr<Woman> _wife;
    //std::shared_ptr<Woman> _wife;
public:
    void setWife(std::shared_ptr<Woman> woman)
    {
        _wife = woman;
    }

    void doSomthing()
    {
        if (_wife.lock())
        {
        }
    }

    ~Man() 
    {
        std::cout << "kill man\n";
    }
};

class Woman
{
private:
    //std::weak_ptr<Man> _husband;
    std::shared_ptr<Man> _husband;
public:
    void setHusband(std::shared_ptr<Man> man) 
    {
        _husband = man;
    }
    ~Woman() {
        std::cout << "kill woman\n";
    }
};


int main(int argc, char** argv) 
{
    std::shared_ptr<Man> m(new Man());
    std::shared_ptr<Woman> w(new Woman());
    if (m && w) 
    {
        m->setWife(w);
        w->setHusband(m);
    }
    return 0;
}

        在Man类内部会引用一个Woman,Woman类内部也引用一个Man。当一个man和一个woman是夫妻的时候,他们直接就存在了相互引用问题。man内部有个用于管理wife生命期的shared_ptr变量,也就是说wife必定是在husband去世之后才能去世。同样的,woman内部也有一个管理husband生命期的shared_ptr变量,也就是说husband必须在wife去世之后才能去世。这就是循环引用存在的问题:husband的生命期由wife的生命期决定,wife的生命期由husband的生命期决定,最后两人都死不掉,违反了自然规律,导致了内存泄漏。
        要解决这个问题我们就需要认识std::weak_ptr。std::weak_ptr同样是一种智能指针,但是是为了配合shared_ptr工作而存在的。它可以从一个shared_ptr或者weak_ptr中来构造,但是并不会改变引用计数
  
default (1)
constexpr weak_ptr() noexcept;
copy (2)
weak_ptr (const weak_ptr& x) noexcept;
template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;
from shared_ptr (3)
template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;
它比较重要的成员函数是expired和lock。expired是用来检查从属的shared_ptr是否已经析构(删除empty),lock是在expired返回值为true(从属的shared_ptr没有被删除)的情况下,返回一个从事的shared_ptr临时对象,这个临时对象的产生同样会使原来的shared_ptr引用计数增加,所以我们在实际操作中一定要控制这个临时产生的shared_ptr的生命周期。
        我们如果用weak_ptr来解决上述问题就比较简单了,在环路引用中改变一个引用为weak_ptr,不增加引用计数,在使用的时候仅仅临时产生一个shared_ptr对象来进行操作,当然如果在这个临时对象的声明周期没有销毁前原来的shared_ptr销毁了,那么管理的指针会延迟到这个临时shared_ptr销毁的时候销毁。
        和std::shared_ptr相关的比较常用的方法有:
        std::shared_ptr<int> pTest6 = std::make_shared<int>(new int());
        std::shared_ptr<A> pFather = std::dynamic_pointer_cast<B>(pChild);//static_pointer_cast const_point_cast形式差不多。

        std::shared_ptr更方便地管理内存,我们不需要再考虑析构时的顺序问题了,完全交给智能指针来管理,大大减少了程序猿的负担,减少bug的产生。
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:10604次
    • 积分:255
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:2篇
    • 译文:0篇
    • 评论:4条
    文章分类
    最新评论