我读过关于共享指针的相关内容并理解了如何使用。但是我从未理解过共享指针中的循环依赖问题,以及弱指针如何解决这些问题。请问有人能够清楚地解释这个问题吗?
我读过关于共享指针的相关内容并理解了如何使用。但是我从未理解过共享指针中的循环依赖问题,以及弱指针如何解决这些问题。请问有人能够清楚地解释这个问题吗?
问题并不复杂。让-->
表示共享指针:
The rest of the program --> object A --> object B
^ |
\ |
\ v
object C
所以我们在共享指针中遇到了循环依赖。每个对象的引用计数是多少?
A: 2
B: 1
C: 1
现在假设程序的其余部分(或者至少持有指向A的共享指针的部分)已经被销毁。那么A的引用计数会减1,因此循环中每个对象的引用计数都为1。那么什么被删除了?什么都没有被删除。但是我们想要被删除的是什么?一切,因为我们的任何对象都无法从程序的其余部分访问。
因此,在这种情况下的解决方法是将从C到A的链接更改为弱指针。弱指针不会影响其目标的引用计数,这意味着当程序的其余部分释放A时,它的引用计数降至0。所以它被删除了,因此B也被删除,C也被删除。
然而,在程序的其余部分释放A之前,C可以随时通过锁定弱指针来访问A。这将使其晋升为共享指针(并将A的引用计数增加到2),只要C正在积极地处理A。这意味着,如果在此期间以其他方式释放A,则其引用计数仅降至1。使用A的C中的代码不会崩溃,并且在锁定弱指针的代码块结束时销毁了短期共享指针,A也被删除了。
一般来说,决定在哪里放置弱指针可能是复杂的。为了选择中断循环的位置,需要在循环中的对象之间具有某种不对称性。在这种情况下,我们知道A是程序其余部分引用的对象,因此我们知道中断循环的位置是指向A的任何对象。
shard_ptr<A> <----| shared_ptr<B> <------
^ | ^ |
| | | |
| | | |
| | | |
| | | |
class A | class B |
| | | |
| ------------ |
| |
-------------------------------------
现在如果我们将类B和A的shared_ptr创建出来,那么两个指针的use_count都为2。
当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;
}
输出:
A()
B()
从输出结果可以看出,A和B指针从未被删除,因此存在内存泄漏问题。
为了避免这种情况,只需在A类中使用weak_ptr而非shared_ptr更为合理。
B
的对象被销毁会发生什么?在“for dummy”的解释中,它会在自身内部销毁对象。因此,它会调用shared_ptr
对象的销毁,从而导致类A
的对象被销毁。好的,A
销毁了指向B
对象的shared_ptr
。A
将看到B
处于哪种状态:已销毁/半销毁/仍然存在?接下来呢?为什么在运行时忽略这种行为?为什么不抛出异常? - DisplayName如果您了解循环依赖关系,那么可以继续使用shared_ptr
而不切换到weak_ptr
,但对象的删除需要一些手动工作。以下代码修改自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
作为一把剪刀,可以剪断圆圈,从而使系统能够完成自己的删除工作。
问题本身如上所示。解决方案如下: