为啥共享指针在 main 末尾没有超出范围?
Posted
技术标签:
【中文标题】为啥共享指针在 main 末尾没有超出范围?【英文标题】:Why is the shared pointer not going out of scope at end of main?为什么共享指针在 main 末尾没有超出范围? 【发布时间】:2021-10-17 21:14:10 【问题描述】:我特意在下面的代码中引入了循环依赖。
我的疑问是,当共享指针 w 超出范围时,引用计数不为零,因此 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;
【问题讨论】:
它们彼此处于参考循环中。 您可能混淆了范围和生命周期。两个共享指针都超出范围,但对循环引用执行它们的生命周期并没有结束。 @TanveerBadar - 如果两者都超出范围,则引用计数应为零。因此不会发生内存泄漏。这违反了共享指针引用计数逻辑。你能说得详细一点吗,因为我不明白你的意思。我知道我故意添加了循环依赖。 C++ 标准规定,如果引用计数达到零,则对象被销毁。 因为它不是具有自动存储期限的对象。它是一个非静态数据成员,它与它所属的类的对象一起创建和销毁。考虑一下:Widget* p; p = new Widget;
你不会期望p->mGadget
在右大括号处被销毁,而*p
还活着,对吗?
好吧,当然,如果这对你有帮助的话。它一开始并没有与任何特定的范围相关联,所以真的没有什么可以摆脱的。
【参考方案1】:
这里有三个std::shared_ptr
s:
w
在main
mGadget
mWidget
后两者是在动态范围内创建的对象的成员。
w
超出了main()
的范围,就像任何其他自动范围的对象一样。但是mGadget
和mWidget
被留下了,每个都引用了对方的对象。这两个对象是在动态范围内创建的。
此循环引用可防止对象的最后一个引用超出范围并被破坏。相互保证的僵局。
【讨论】:
假设 'w' 被一些完全独立的堆构造的 Gadget 指向,它没有被释放,那么我们会看到同样的问题吗?w
是自动范围内的对象,它正在引用某个对象。还有谁在引用同一个对象并不重要。这里的两个对象相互引用。结束。
我为什么要对我认为值得回答的问题投反对票?
好的。不知道为什么有人刚刚投反对票。可能有人认为这个问题不值得问。
不幸的是,在 *** 上提出的绝大多数问题质量都很差。这里大约 90% 的问题来自寻找 pleasedomyhomeworkforme.com 或 pleasewritemycodeforme.com 的人。有时人们出于习惯而投反对票。我认为这个问题不应该被否决,所以我采取了相应的行动。【参考方案2】:
您将shared_ptr
s 的生命周期与它们引用的对象的生命周期混淆了。
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
对象。
【讨论】:
以上是关于为啥共享指针在 main 末尾没有超出范围?的主要内容,如果未能解决你的问题,请参考以下文章