[英]What is the cyclic dependency issue with shared_ptr?
I read about shared pointers and understood how to use.我阅读了共享指针并了解了如何使用。 But I never understood the cyclic dependency problem with shared pointers and how weak pointers are going to fix those issues.
但我一直不明白共享指针的循环依赖问题以及弱指针将如何解决这些问题。 Can any one please explain this problem clearly?
任何人都可以清楚地解释这个问题吗?
The problem isn't that complex.问题没那么复杂。 Let
-->
represent a shared pointer:让
-->
代表一个共享指针:
The rest of the program --> object A --> object B
^ |
\ |
\ v
object C
So we've got ourselves a circular dependency with shared pointers.所以我们已经有了一个共享指针的循环依赖。 What's the reference count of each object?
每个对象的引用计数是多少?
A: 2
B: 1
C: 1
Now suppose the rest of the program (or at any rate the part of it that holds a shared pointer to A) is destroyed.现在假设程序的其余部分(或者至少是其中包含指向 A 的共享指针的部分)被销毁。 Then the refcount of A is reduced by 1, so the reference count of each object in the cycle is 1. So what gets deleted?
那么 A 的 refcount 减 1,所以循环中每个对象的引用计数都是 1。那么删除的是什么? Nothing.
没有什么。 But what do we want to be deleted?
但是我们要删除什么? Everything, because none of our objects can be reached from the rest of the program any more.
一切,因为我们的任何对象都无法从程序的其余部分访问。
So the fix in this case is to change the link from C to A into a weak pointer.因此,在这种情况下,解决方法是将 C 到 A 的链接更改为弱指针。 A weak pointer doesn't affect the reference count of its target, which means that when the rest of the program releases A, its refcount hits 0. So it's deleted, hence so is B, hence so is C.
弱指针不会影响其目标的引用计数,这意味着当程序的其余部分释放 A 时,它的引用计数为 0。因此它被删除,因此 B 也是如此,因此 C 也是如此。
Before the rest of the program releases A, though, C can access A whenever it likes by locking the weak pointer.但是,在程序的其余部分释放 A 之前,C 可以通过锁定弱指针随时访问 A。 This promotes it to a shared pointer (and increases the refcount of A to 2) for as long as C is actively doing stuff with A. That means if A is otherwise released while this is going on then its refcount only falls to 1. The code in C that uses A doesn't crash, and A is deleted whenever that short-term shared pointer is destroyed.
只要 C 正在积极地对 A 做任何事情,这就会将它提升为共享指针(并将 A 的引用计数增加到 2)。这意味着如果 A 在此过程中以其他方式被释放,那么它的引用计数只会下降到 1。 C 中使用 A 的代码不会崩溃,只要该短期共享指针被销毁,A 就会被删除。 Which is at the end of the block of code that locked the weak pointer.
这是锁定弱指针的代码块的末尾。
In general, deciding where to put the weak pointers might be complex.通常,决定将弱指针放在哪里可能很复杂。 You need some kind of asymmetry among the objects in the cycle in order to choose the place to break it.
您需要循环中的对象之间存在某种不对称性,以便选择打破它的地方。 In this case we know that A is the object referred to by the rest of the program, so we know that the place to break the cycle is whatever points to A.
在这种情况下,我们知道 A 是程序其余部分引用的对象,因此我们知道打破循环的地方是指向 A 的任何地方。
shard_ptr<A> <----| shared_ptr<B> <------
^ | ^ |
| | | |
| | | |
| | | |
| | | |
class A | class B |
| | | |
| ------------ |
| |
-------------------------------------
Now if we make the shared_ptr of the class B and A, the use_count of the both pointer is two.现在如果我们制作B类和A类的shared_ptr,两个指针的use_count都是2。
When the shared_ptr goes out od scope the count still remains 1 and hence the A and B object does not gets deleted.当 shared_ptr 超出范围时,计数仍然保持为 1,因此 A 和 B 对象不会被删除。
class B;
class A
{
shared_ptr<B> sP1; // use weak_ptr instead to avoid CD
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void setShared(shared_ptr<B>& p)
{
sP1 = p;
}
};
class B
{
shared_ptr<A> sP1;
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void setShared(shared_ptr<A>& p)
{
sP1 = p;
}
};
int main()
{
shared_ptr<A> aPtr(new A);
shared_ptr<B> bPtr(new B);
aPtr->setShared(bPtr);
bPtr->setShared(aPtr);
return 0;
}
output:输出:
A()
B()
As we can see from the output that A and B pointer are never deleted and hence memory leak.正如我们从输出中看到的,A 和 B 指针永远不会被删除,因此内存泄漏。
To avoid such issue just use weak_ptr in class A instead of shared_ptr which makes more sense.为避免此类问题,只需在 A 类中使用 weak_ptr 而不是更有意义的 shared_ptr 。
If you are aware of the cyclic dependency then you can stick with shared_ptr
without switching to weak_ptr
but the deletion of objects require some manual work.如果您知道循环依赖性,那么您可以坚持使用
shared_ptr
而无需切换到weak_ptr
,但是删除对象需要一些手动工作。 The following code is modified from Swapnil's answer.以下代码是根据 Swapnil 的回答修改的。
#include <iostream>
#include <memory>
using namespace std ;
class B;
class A
{
shared_ptr<B> sP1; // use weak_ptr instead to avoid CD
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void setShared(shared_ptr<B>& p)
{
sP1 = p;
}
// nullifySharedPtr cuts the circle of reference
// once this is triggered, then the ice can be broken
void nullifySharedPtr() {
sP1 = nullptr;
}
};
class B
{
shared_ptr<A> sP1;
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void setShared(shared_ptr<A>& p)
{
sP1 = p;
}
};
int main()
{
shared_ptr<A> aPtr(new A);
shared_ptr<B> bPtr(new B);
aPtr->setShared(bPtr);
bPtr->setShared(aPtr);
cout << aPtr.use_count() << endl;
cout << bPtr.use_count() << endl;
// to be break the ice:
aPtr->nullifySharedPtr() ;
return 0;
}
nullifySharedPtr
acts as a scissor to cut the circle and hence enables the system to do its own job of deletion. nullifySharedPtr
充当剪刀剪圆,因此使系统能够完成自己的删除工作。
The Issue itself is displayed above.问题本身显示在上面。 Solutions are
解决方案是
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.