zoukankan      html  css  js  c++  java
  • 智能指针

    https://blog.csdn.net/gettogetto/article/details/66968307

    http://blog.csdn.net/zy19940906/article/details/50470087 

    本次讨论:c++11之前的auto_ptr; c++11新加的unique_ptr, shared_ptr以及weak_ptr。

         头文件:#include <memory>

    一、auto_ptr

    使用模板实现。

    int _tmain(int argc, _TCHAR* argv[])  
    {  
        auto_ptr <Base1> base1(new Base1);//可理解为先声明一个名为base1的Base1类型智能指针,然后再base1里面管理new Base1  
            if (base1.get())//get是智能指针的函数,返回当前当前智能指针对象,即用以判断是否为空  
            {  
               base1->func();  
            }  
            return 0;  
    }  

    1、base1.get():返回当前指针对象;

    2、base1.release():清空当前智能指针对象,并返回类型指针(但并没有释放内存)。

    所以假如我们要正常删除,那么需要这样:

    Base1*base2 = base1.release();  
    delete base2;  
    base2 = NULL;

    3、base1.reset():从图中可看出,是重置智能指针,即把内存删除,且智能指针指向空

    4、auto_ptr还重载了赋值运算符。由图可知意思是把赋值智能指针的内存交给被赋值智能指针,

    auto_ptr <Base1> base2;  
    base2 = base1;//将base1的控制权转交给base2,且base1清空了  
    base2->func();  

    缺点:因此这样就有些问题,控制权可以随便转换,但是只有一个在用,用起来会受到诸多限制

     二、unique_ptr

    C++11引入了许多便捷的功能,其中也包括这个,在用之前我们可以先看下底层:

    可以清楚的看到,unique_ptr中的拷贝构造和赋值操作符delete了,所以也就意味着,他和auto_ptr有区别,控制权唯一,不能随意转换。

    用法都差不多:

    unique_ptr<Base1> base1(new Base1);  
    unique_ptr<Base1> base2;//但是不能用拷贝构造和等号赋值把base1赋值给base2了  

    但是如果想切换控制权的话也不是没有办法,我们可以看到还有个这样的函数:

     

    unique_ptr<Base1> base1(new Base1);  
    unique_ptr<Base1> base2=move(base1);//base1变成empty  
    unique_ptr<Base1> base3;  
    base3 = move(base2);//base2变成empty  

    其它的成员函数就不一一赘述,和auto_ptr大致上是相同的。总结,某种程度来说比auto_ptr更为安全,适用部分特殊情况。

    三、shared_ptr

    如果完全理解了上面两个ptr的底层,那么shared_ptr的也就容易理解多了。但是和前两者有很大区别——

    前两者控制权唯一,切换的时候把前面的清除。而shared_ptr不会,照例看下底层:

    (特殊:赋值:先调用拷贝构造函数,再交换)

    很显然,可以直接赋值和调用拷贝构造函数,且不会清空原本的智能指针。用法就很简单了:

    shared_ptr<Base1> base1(new Base1);  
    shared_ptr<Base1> base2=base1;  
    shared_ptr<Base1> base3;  
    base3 = base2;//三个共享一个  

    有个地方需要注意,当删除一个智能指针时,并不影响其它两个智能指针的继续使用。

    因为该片内存添加了一个引用计数,每构造一次,引用计数+1;每次调用析构函数,引用计数减一。直到最后一个智能指针删除,才会释放内存。

    注意:

    1、在继续查看时,你会发现以下两个函数:

    其实就是和unique_ptr一样可以通过move来切换控制权,这个时候是切换,不是共享了。

    2、接下来继续翻看,还有两个函数:

    (其实auto_ptr也有,只是一样,没必要截图了)

    也就是说,auto_ptr和unique_ptr都可以通过move函数转换成shared_ptr类型,当然,一样是切换控制权的形式,即旧的置空。

    auto_ptr<Base1>   base1(new Base1);  
    shared_ptr<Base1> base2 = move(base1);  

    四、weak_ptr

    weak_ptr更像是shared_ptr的助手:

    1、他不像其余三种,可以通过构造函数直接分配对象内存;他必须通过shared_ptr来共享内存。

    2、没有重载opreator*和->操作符,也就意味着即使分配到对象,他也没法使用该对象

    3、不主动参与引用计数,即,share_ptr释放了,那么weak_ptr所存的对象也释放了。

    4、使用成员函数use_count()可以查看当前引用计数,expired()判断引用计数是否为空。

    5、lock()函数,返回一个shared_ptr智能指针:

    也就是让weak_ptr观测shared_ptr智能指针,并且在需要时候通过lock函数返回一个shared_ptr。

    (https://www.cnblogs.com/diysoul/p/5930372.html)

    weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段. 
      weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少. 
      定义在 memory 文件中(非memory.h), 命名空间为 std.

      weak_ptr 使用:

    std::shared_ptr<int> sp(new int(10));
    std::weak_ptr<int> wp(sp);
    wp = sp;
    printf("%d ", wp.use_count()); // 1
    wp.reset();
    printf("%d ", wp); // 0

    // 检查 weak_ptr 内部对象的合法性.
    if (std::shared_ptr<int> sp = wp.lock())
    {
    }

    成员函数

    weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象. 注意, weak_ptr 在使用前需要检查合法性.

    expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
    lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同.
    use_count 返回与 shared_ptr 共享的对象的引用计数.
    reset 将 weak_ptr 置空.
    weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.

    使用 weak_ptr 解决 shared_ptr 因循环引有不能释放资源的问题

    使用 shared_ptr 时, shared_ptr 为强引用, 如果存在循环引用, 将导致内存泄露. 而 weak_ptr 为弱引用, 可以避免此问题, 其原理:
      对于弱引用来说, 当引用的对象活着的时候弱引用不一定存在. 仅仅是当它存在的时候的一个引用, 弱引用并不修改该对象的引用计数, 这意味这弱引用它并不对对象的内存进行管理.
      weak_ptr 在功能上类似于普通指针, 然而一个比较大的区别是, 弱引用能检测到所管理的对象是否已经被释放, 从而避免访问非法内存。
    注意: 虽然通过弱引用指针可以有效的解除循环引用, 但这种方式必须在程序员能预见会出现循环引用的情况下才能使用, 也可以是说这个仅仅是一种编译期的解决方案, 如果程序在运行过程中出现了循环引用, 还是会造成内存泄漏.

    class CB;
            class CA;
         
            class CA
            {
            public:
                CA(){}
                ~CA(){PRINT_FUN();}
         
                void Register(const std::shared_ptr<CB>& sp)
                {
                    m_spb = sp;
                }
         
            private:
                std::weak_ptr<CB> m_spb;
            };
         
            class CB
            {
            public:
                CB(){};
                ~CB(){PRINT_FUN();};
         
                void Register(const std::shared_ptr<CA>& sp)
                {
                    m_spa = sp;
                }
         
            private:
                std::shared_ptr<CA> m_spa;
            };
         
            std::shared_ptr<CA> spa(new CA);
            std::shared_ptr<CB> spb(new CB);
         
            spb->Register(spa);
            spa->Register(spb);
            printf("%d
    ", spb.use_count()); // 1
            printf("%d
    ", spa.use_count()); // 2

    另一个循环依赖的例子,来自<C++标准库(第2版)>

    class Person : public enable_shared_from_this<Person>
    {
    public:
        Person(const string& name)
            : m_name {name}
        {
        }
    
        ~Person()
        {
            cout << "release " << m_name << endl;
        }
    
        string getName() const
        {
            return m_name;
        }
    
        void setFather(shared_ptr<Person> f)
        {
            m_father = f;
            if (f)
            {
                f->m_kids.push_back(shared_from_this());
            }
        }
    
        void setMother(shared_ptr<Person> m)
        {
            m_mother = m;
            if (m)
            {
                m->m_kids.push_back(shared_from_this());
            }
        }
    
        shared_ptr<Person> getKid(size_t idx)
        {
            if (idx < m_kids.size())
            {
                weak_ptr<Person> p = m_kids.at(idx);
                if (!p.expired())
                {
                    return p.lock();
                }
            }
            return nullptr;
        }
    
    private:
        string                        m_name;
        shared_ptr<Person>            m_father;
        shared_ptr<Person>            m_mother;
        //vector<shared_ptr<Person>>    m_kids; // 循环依赖
        vector<weak_ptr<Person>>      m_kids;
    };
    
    
    // 测试代码
        shared_ptr<Person> jack {make_shared<Person>("Jack")};
        shared_ptr<Person> lucy {make_shared<Person>("Lucy")};
        shared_ptr<Person> john {make_shared<Person>("John")};
        john->setFather(jack);
        john->setMother(lucy);
    
        auto p = jack->getKid(0);
        if (p)
        {
            cout << p->getName() << endl;
        }

    VC中的源码实现

    template<class _Ty>
    class weak_ptr
        : public _Ptr_base<_Ty>
    {    // class for pointer to reference counted resource
        typedef typename _Ptr_base<_Ty>::_Elem _Elem;
    
    public:
        weak_ptr()
        {    // construct empty weak_ptr object
        }
    
        template<class _Ty2>
        weak_ptr(const shared_ptr<_Ty2>& _Other,
            typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
            void *>::type * = 0)
        {    // construct weak_ptr object for resource owned by _Other
            this->_Resetw(_Other);
        }
    
        weak_ptr(const weak_ptr& _Other)
        {    // construct weak_ptr object for resource pointed to by _Other
            this->_Resetw(_Other);
        }
    
        template<class _Ty2>
        weak_ptr(const weak_ptr<_Ty2>& _Other,
            typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
            void *>::type * = 0)
        {    // construct weak_ptr object for resource pointed to by _Other
            this->_Resetw(_Other);
        }
    
        ~weak_ptr()
        {    // release resource
            this->_Decwref();
        }
    
        weak_ptr& operator=(const weak_ptr& _Right)
        {    // assign from _Right
            this->_Resetw(_Right);
            return (*this);
        }
    
        template<class _Ty2>
        weak_ptr& operator=(const weak_ptr<_Ty2>& _Right)
        {    // assign from _Right
            this->_Resetw(_Right);
            return (*this);
        }
    
        template<class _Ty2>
        weak_ptr& operator=(shared_ptr<_Ty2>& _Right)
        {    // assign from _Right
            this->_Resetw(_Right);
            return (*this);
        }
    
        void reset()
        {    // release resource, convert to null weak_ptr object
            this->_Resetw();
        }
    
        void swap(weak_ptr& _Other)
        {    // swap pointers
            this->_Swap(_Other);
        }
    
        bool expired() const
        {    // return true if resource no longer exists
            return (this->_Expired());
        }
    
        shared_ptr<_Ty> lock() const
        {    // convert to shared_ptr
            return (shared_ptr<_Elem>(*this, false));
        }
    };
  • 相关阅读:
    leetcode 309. Best Time to Buy and Sell Stock with Cooldown
    leetcode 714. Best Time to Buy and Sell Stock with Transaction Fee
    leetcode 32. Longest Valid Parentheses
    leetcode 224. Basic Calculator
    leetcode 540. Single Element in a Sorted Array
    leetcode 109. Convert Sorted List to Binary Search Tree
    leetcode 3. Longest Substring Without Repeating Characters
    leetcode 84. Largest Rectangle in Histogram
    leetcode 338. Counting Bits
    git教程之回到过去,版本对比
  • 原文地址:https://www.cnblogs.com/pjl1119/p/9680160.html
Copyright © 2011-2022 走看看