1.智能指针通过RAII方法来管理指针:构造对象时,完成资源初始化;析构对象时,对资源进行清理及汕尾.
2.auto_ptr,通过“转移所有权”来防止析构一块内存多次.(如何转移?详情看第二篇文章)
3.scoped_ptr,不“转移所有权”而是禁止拷贝/赋值对象.(C++如何禁止拷贝对象?详情看第三篇文章)
4.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){} ~ListNode(){ std::cout<<"~ListNode()"<<std::endl; } }; 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; //构成死锁,出了函数作用域,也没有调用析构函数 std::cout<<sp1.use_count()<<std::endl; //sp1的引用计数 std::cout<<sp2.use_count()<<std::endl; //sp2的引用计数 }
sp1指向sp2、sp2又指向sp1,这种情况,就好像两个人打架:互相抓住对方耳朵,A说你先松手,你不松我就不松;B说你先松,你不松我也不松.就这样一直僵持着.....
那么,我们如何解决这样的问题呢?
用weak_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){} ~ListNode(){ std::cout<<"~ListNode()"<<std::endl; } }; 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; std::cout<<sp1.use_count()<<std::endl; std::cout<<sp2.use_count()<<std::endl; }
输出:
1
1
~ListNode()
~ListNode()
那么为什么用weak_ptr就可以解决循环引用的问题,简单点的来说:weak_ptr的构造和析构不会引起引用计数的增加或减少.
最后,作为补充:weak_ptr必须与shared_ptr配合使用,不能单独使用.