【问题标题】:`weak_ptr::expired` behavior in the dtor of the object对象的 dtor 中的 `weak_ptr::expired` 行为
【发布时间】:2017-06-10 15:06:00
【问题描述】:

考虑以下代码:

#include <iostream>
#include <memory>
using namespace std;

class T;

std::weak_ptr<T> wptr;

class T
{
public:
    T() {  }
    ~T() {
        std::cout << "in dtor" << std::endl;
        std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
    }
};

int main() {
    {
        auto ptr = std::make_shared<T>();
        wptr = ptr;
        std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
    }
    return 0;
}

在这段代码中,我试图找出weak_ptrs 是否在对象销毁阶段过期。似乎是这样。输出是:

not expired
in dtor
expired

我使用 gcc-5.1 和 ideone

现在,我有另一个问题。我找不到任何说明这是标准行为的文档。是否保证以这种方式工作,总是

【问题讨论】:

  • 这是timsong-cpp.github.io/lwg-issues/2751的另一个版本
  • 独立于规范,我认为它必须在析构函数中过期。如果您能够将其转换为 shared_ptr,那么您将拥有无法阻止对象死亡的 shared_ptr
  • @T.C.我不认为程序说wptr.lock 是不合理的,而不同的线程可能会或可能不会破坏被监视的对象(它运行对象的析构函数)。这是一个主要的用例,他们似乎在说这是“不合理的”。看来我误会了?
  • @T.C.,这是答案之一中没有的有价值的信息。您想将此扩展为答案吗?
  • 我没有编辑问题文本。我重新标记了它,因为它显然是在要求提供保证的答案。我知道一些对此有更多了解的人会发现问题是否被标记为语言律师,而他们不会费心去查看 C++。希望OP本人/她自己澄清一下。

标签: c++ c++11 c++14 shared-ptr weak-ptr


【解决方案1】:

现在,我有另一个问题。我找不到任何说明这是标准行为的文档。是否保证以这种方式工作,总是

没有。事实上,正如LWG issue 2751 提出的那样,它在标准中未得到充分说明。

C++14 标准不包含保证由shared_ptr 运行的删除程序将看到所有关联的weak_ptr 实例都已过期的语言。例如,该标准似乎不能保证以下 sn-p 中的断言不会触发:

std::weak_ptr<Foo> weak;
std::shared_ptr<Foo> strong{
  new Foo,
  [&weak] (Foo* f) {
    assert(weak.expired());
    delete f;
  },
};

weak = strong;
strong.reset();

似乎很明显,其意图是关联的weak_ptrs 已过期,因为否则shared_ptr 删除器可能会恢复对正在删除的对象的引用。

建议的修复:23.11.3.2 [util.smartptr.shared.dest] 应指定由析构函数引起的 use_count() 减少在调用删除程序或调用 delete p 之前排序。

上面链接的~shared_ptr() 的当前措辞只是说明调用了删除器,并带有非规范性说明,即共享所有权的实例数量减少了。

虽然调用删除器时的意图可能是weak.expired(),但依赖它是有问题的。只有在shared_ptr 被销毁后 不再共享所有权的自信声明才是合理的 - 在 销毁期间提出这个问题有点奇怪。

【讨论】:

  • 但是如果 shared_ptr 在不同的线程上被销毁,你不知道你在问during销毁的问题。
  • @Nevin 是的,但在这种情况下,我们处于删除程序调用中。
  • @Nevin 那你在问什么问题?你能定义它吗?
  • @curiousguy 问题expired() 提出:“lock() 会返回一个空的shared_ptr 吗?”一旦它返回true,答案仍然是true;否则,答案是关于过去会发生什么(尽管在同一个线程上会出现退化情况)。这有点令人困惑,因为在 [util.smartptr.weak.obs] 中,lock() 是根据 expired() 定义的。一旦我们在删除器中lock() 就不能再返回一个非空的shared_ptr(因为当前对象将被销毁),而expired() 返回true。一般来说,只有true 可以真正被执行。
  • 我不同意最后一段。不仅因为我编写了依赖于 this 的代码;)还因为很难编写一个不保证问题中的行为但仍保证另一个线程在我们处于析构函数时无法成功 lock() 的实现.尽管如此,这个答案仍然包含最有用的信息。谢谢!
【解决方案2】:

像这样使用 make_shared 将使用您提供的默认构造函数创建一个对象。

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

构造一个 T 类型的对象并将其包装在 std::shared_ptr 中 使用 args 作为 T 的构造函数的参数列表。对象 好像是由表达式 (std::make_shared) 构造的

在 main 中的匿名作用域之后。共享的 ptr 将被删除。

当任何一个对象被销毁并释放其内存时 发生以下情况:

最后剩下的拥有该对象的 shared_ptr 被销毁; (std::shared_ptr)

.

shared_ptr 的析构函数减少共享所有者的数量 控制块。如果该计数器达到零,则控制块 调用托管对象的析构函数。控制块不 释放自身直到 std::weak_ptr 计数器达到零 好吧。 std::shared_ptr Implementation notes

这意味着您的对象将在最后一个共享 ptr 的销毁开始后调用其析构函数。 输出:

not expired
in dtor
expired

是预期的行为。

【讨论】:

  • 而不是“共享ptr销毁后”。我想你的意思是写“在最后一个共享 ptr 的销毁开始之后”。
【解决方案3】:

当没有更多 shared_ptrs 引用该对象时,weak_ptr 将过期。

当最后一个 shared_ptr 停止引用它时(紧接着)它被销毁。

此时没有shared_ptrs 引用它,因此任何weak_ptr 都已过期。

现在调用对象的析构函数,如果它有单独的存储(即不是用make_shared 创建的),它的存储将被释放。

如果有任何weak_ptrs 引用它,则存储引用计数、原始指针和删除函数的控制块将持续存在。当最后一个weak_ptr 停止引用它时,控制块也被销毁并释放。即,shared_ptr 实例使对象本身及其控制块保持活动状态,而weak_ptr 实例使控制块保持活动状态。

【讨论】:

  • "weak_ptr 实例使控制块保持活动状态" IOW,weak ptr 是对元数据的强引用
【解决方案4】:

不是标准本身,而是:

http://en.cppreference.com/w/cpp/memory/weak_ptr/expired

检查托管对象是否已被删除。相等的 使用_count() == 0。

所以它变成了天气use_count在删除之前或之后设置为0的问题。现在在标准草案中没有关于这一点: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf [第 566 页 20.9.2.2.2]

~shared_ptr();

效果:

  • 如果*this 为空或与另一个shared_ptr 实例共享所有权(use_count() > 1),则没有副作用。
  • 否则,如果*this 拥有一个对象p 和一个删除器d,则调用d(p)
  • 否则,*this 拥有指针 p,并调用 delete p

[注:由于*this的销毁会减少 在 *this 之后与 *this 共享所有权的实例 已销毁与其共享所有权的所有 shared_ptr 实例 *this 将报告一个use_count(),它比之前的少一个 价值。 ——尾注]

【讨论】:

  • 注释还是没有明确指定顺序,不是吗?
  • 可以说,析构函数执行的引用计数更新的副作用。因为如果不是,那么它就是魔法,而 C++ 不涉及魔法。恕我直言这里的标准完全是错误的。这不是一个微妙的点。它尽可能地直面你。
  • @songyuanyao:引用的规范文本指定了顺序:删除器或delete 表达式仅在*this 非空且use_count() == 0 时调用。或者,如果指定了引用计数减少,这将是一个顺序规范,但实际上是标准的引用部分,有效地保持 什么都没有发生,因为引用计数永远不会减少, 是错误的。注释不规范,注释未指定顺序。
猜你喜欢
  • 2015-03-22
  • 2023-03-24
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 2020-04-21
  • 2017-08-23
  • 1970-01-01
相关资源
最近更新 更多