一、shared_ptr的弊端
struct Listnode
{int _val;std::shared_ptr<Listnode> _prev;std::shared_ptr<Listnode> _next;Listnode(int val ):_val(val),_prev(nullptr),_next(nullptr){}~Listnode(){cout << "~Listnode()" << endl;}
};
int main()
{std::shared_ptr<Listnode> n1(new Listnode(20));std::shared_ptr<Listnode> n2(new Listnode(10));n1->_next = n2; n2->_prev = n1;return 0;
}
如上的情况就出现了循环引用,两个对象相互掣肘。
1. node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
2. node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
4. 也就是说_next析构了,node2就释放了。
5. 也就是说_prev析构了,node1就释放了。
6. 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放。
展开来讲:
两个对象的引用计数都变为2,当要进行析构时就陷入了循环引用。
1、左边节点要想析构就得等右边的_prev去调用析构。
2、右边节点被delete时去调用_prev.
3、而右边节点被析构需要左边节点的_next去调用析构
4、左边节点被delete时去调用_prev
5、左边节点要想析构就得等右边的_prev去调用析构。
到这里就以及死循环了。
二、weak
它的特点就是不增加引用计数,专门用于shared_ptr出现循环引用的情况。
struct Listnode
{int _val;std::weak_ptr<Listnode> _prev;std::weak_ptr<Listnode> _next;Listnode(int val ):_val(val){}~Listnode(){cout << "~Listnode()" << endl;}
};
int main()
{std::shared_ptr<Listnode> n1(new Listnode(20));std::shared_ptr<Listnode> n2(new Listnode(10));n1->_next = n2; n2->_prev = n1;return 0;
}
2.1模拟实现
struct Listnode
{int _val;gaz::weak_ptr<Listnode> _prev;gaz::weak_ptr<Listnode> _next;Listnode(int val ):_val(val){}~Listnode(){cout << "~Listnode()" << endl;}
};
int main()
{gaz::shared_ptr<Listnode> n1(new Listnode(20));gaz::shared_ptr<Listnode> n2(new Listnode(10));n1->_next = n2; n2->_prev = n1;return 0;
}
template<class T>class weak_ptr{public://RALLweak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp){_ptr = sp.get();}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};