Effective C++学习笔记:shared_ptr 析构剖析

例子是这样的,使用 shared_ptr 创建一个链表:

class Node {
public:
	int x;
	Node(int xx):x(xx){}
	~Node() { cout << "Node destructor " << x << endl; }
	shared_ptr<Node> next;
};

void main() {
	shared_ptr<Node> sp1(new Node(1));
	shared_ptr<Node> sp2(new Node(2));
	sp1->next = sp2;
	cout << sp1.use_count() << endl;
	cout << (sp1->next).use_count() << endl;
	cout << sp2.use_count() << endl;
}

即链表有两个结点,1 -> 2,Node(2) 被引用两次,所以 sp2 和 sp1->next 的 use_count 是2,sp1则是1

这时候没有环形引用,shared_ptr 能够正常回收资源,因为 Node(1) 是 Node(2) 的前驱,所以 Node(1) 会先被析构,然后是 Node(2)。程序的输出也能证明这一点。

这样看起来就好像,sp1 先于 sp2 被析构。c++自动对象的析构顺序是后定义的先析构,这不就产生矛盾了吗?
还是说智能指针有特殊的析构顺序?这应该是不可能的,因为智能指针就是利用自动变量被自动回收时,调用指向对象的析构函数来辅助进行资源管理的。它不可能实现一个拓扑排序机制,去判断先析构谁,后析构谁。

那么,它究竟是怎么做的呢?

  1. 首先,是 sp2 先被析构,它将 Node(2) 资源的引用数从 2 减至 1 ,然后 sp2 的析构过程就结束了。(它知道还有智能指针指向Node(2),所以它不去释放 Node(2) ,将引用数从1 减至 0 的那个智能指针会负责释放资源)。
    注:这里是shared_ptr析构过程的简化描述,shared_ptr 继承自 _Ptr_base, 而 _Ptr_base 包含 _Ref_count_base 等成员对象,这些都需要析构。但这些不是本篇文章想要介绍的重点,就省略了。

  2. 然后是 sp1 被析构,它将 Node(1) 资源的引用数从 1 减至 0 ,达到释放对象的条件,delete 指针,这会调用 Node(1) 的析构函数。程序输出“Node destructor 1”。Node(1) 的析构函数执行完函数体内的代码后,开始析构自身的成员变量。

  3. 析构 next 成员变量时,它将 Node(1) 资源的引用数从 1 减至 0, 释放资源, Node(2) 的析构函数被调用。程序输出“Node destructor 2”。

之所以会产生 sp1 先被析构的错觉,是因为思维定势,以为 sp2 会释放 Node(2), 而实际上 Node(2) 是被 sp1->next 这个 shared_ptr 释放掉的。

总结

shared_ptr 析构的顺序也符合一般规律(后定义先析构)
例子中的 Node(2) 并没有被 sp2 释放

你可能感兴趣的:(c++,学习笔记,c++,指针)