智能指针的循环引用与解决
智能指针的循环引用
class Node
{
public:
shared_ptr<Node> left;
shared_ptr<Node> right;
Node(int v)
{
this->value = v;
cout << "Constructor" << endl;
}
~Node()
{
cout << "Destructor" << endl;
}
private:
int value;
};
int main()
{
shared_ptr<Node> root = std::make_shared<Node>(4);
root->left = std::make_shared<Node>(2);
root->right = std::make_shared<Node>(3);
return 0;
}
上面的程序不会有问题,调用三次构造函数,三次析构函数。
下面增加一个指向父节点的指针。
class Node
{
public:
shared_ptr<Node> left;
shared_ptr<Node> right;
shared_ptr<Node> parent;
Node(int v)
{
this->value = v;
cout << "Constructor" << endl;
}
~Node()
{
cout << "Destructor" << endl;
}
private:
int value;
};
int main()
{
shared_ptr<Node> root = std::make_shared<Node>(4);
root->left = std::make_shared<Node>(2);
root->left->parent = root; //强引用计数加1
root->right = std::make_shared<Node>(3);
root->right->parent = root; //强引用计数加1
cout << "root reference count = " << root.use_count() << endl;
cout << "left reference count = " << root->left.use_count() << endl;
cout << "right reference count = " << root->right.use_count() << endl;
return 0;
}
调用了三次构造函数,但是没用调用析构函数,这就导致了内存泄漏。
shared_ptr
的循环引用定义:
当两个对象(主体是对象)使用shared_ptr
相互引用时,那么当超出范围时,都不会删除内存。发生这种情况的原因是shared_ptr
在其析构函数中递减关联内存的引用计数后,检查count
是否为0,如果不为0,析构函数就不会释放相应的内存。当出现了循环引用后,就会发现count
的值总是不为0。
这里我用goodnotes画了两张图,希望能把这个过程解释清楚:
那么如何解决这个问题?
使用weak_ptr
。
下面使用weak_ptr
改进二叉树。weak_ptr
不会导致强引用计数增加。
class Node
{
public:
shared_ptr<Node> left;
shared_ptr<Node> right;
//shared_ptr<Node> parent;
weak_ptr<Node> parent;
Node(int v)
{
this->value = v;
cout << "Constructor" << endl;
}
~Node()
{
cout << "Destructor" << endl;
}
private:
int value;
};
int main()
{
shared_ptr<Node> root = std::make_shared<Node>(4);
root->left = std::make_shared<Node>(2);
root->left->parent = root; //强引用计数加1
root->right = std::make_shared<Node>(3);
root->right->parent = root; //强引用计数加1
cout << "root reference count = " << root.use_count() << endl;
cout << "left reference count = " << root->left.use_count() << endl;
cout << "right reference count = " << root->right.use_count() << endl;
return 0;
}