为什么共享指针在main末尾没有超出范围



我特意在下面的代码中引入了一个循环依赖项。

我的怀疑是,因为当共享指针w超出范围时,ref计数不为零,因此Widget对象不会被破坏。

但是在main的末尾,共享指针'w->mGadget->mWidget也超出了范围,因为所有的东西都知道在main的末尾不存在了?我对这里的范围有点困惑。我原以为当main退出时,所有实体范围都应该结束。我理解中缺失的环节在哪里?

#include <memory>
#include <iostream>
struct Gadget;
struct Widget
{
std::shared_ptr<Gadget> mGadget;
};
struct Gadget
{
std::shared_ptr<Widget> mWidget;
};
int main()
{
auto w = std::make_shared<Widget>();
w->mGadget = std::make_shared<Gadget>();
w->mGadget->mWidget = w;
return 0;
}

您混淆了shared_ptr的生存期和它们引用的对象的生存期。

std::shared_ptr维护一个指向对象的指针,并将该指针与引用计数相关联。shared_ptr在引用计数下降到0之前不破坏所指向的对象。

  • auto w = std::make_shared<Widget>();
    这将创建一个新的Widget对象,然后创建一个指向它的新shared_ptr,并将shared_ptr的引用计数设置为1。

  • w->mGadget = std::make_shared<Gadget>();
    这将创建一个新的Gadget对象,然后创建一个指向它的新shared_ptr,并将shared_ptr的引用计数设置为1。

  • w->mGadget->mWidget = w;
    这会将w分配给Gadget对象的mWidget成员,从而将Widget指针的引用计数增加到2。

现在,当main()退出时,唯一超出作用域的变量是w,因此这是唯一被销毁的变量,将Widget指针的引用计数减为1而不是0。由于仍然存在对该指针的活动引用,因此Widget对象不会被破坏。由于其mGadget成员具有对Gadget指针的活动引用,因此Gadget对象也不会被破坏。

要解决此问题,您需要将Gadget::mWidget成员改为std::weak_ptr

#include <memory>
struct Gadget;
struct Widget
{
std::shared_ptr<Gadget> mGadget;
};
struct Gadget
{
std::weak_ptr<Widget> mWidget; // <-- here
};
int main()
{
auto w = std::make_shared<Widget>();
w->mGadget = std::make_shared<Gadget>();
w->mGadget->mWidget = w;
return 0;
}

std::weak_ptr不会增加std::shared_ptr的引用计数(至少在调用std::weak_ptr::lock()之前),因此Widget指针的引用计数将是1而不是2。因此,当w超出范围并被销毁时,Widget指针的引用计数降至0,销毁Widget对象,这将销毁其mGadget成员,这将使Gadget指针的引用数降至0,从而销毁Gadget对象。

这里有三个std::shared_ptr

  1. main中的w
  2. mGadget
  3. mWidget

后两者是在动态范围中创建的对象的成员。

wmain()中超出了作用域,就像任何其他自动作用域的对象一样。但留下了mGadgetmWidget,它们都引用了对方的对象。这两个对象是在动态作用域中创建的。

这个循环引用防止任何一个对象的最后一个引用超出范围并被销毁。相互保证的僵局。

相关内容

最新更新