c++ - 对象生命周期结束和它何时不复存在之间有什么关系?

标签 c++ language-lawyer lifetime object-lifetime

在下面的简短示例中,可以说明对象指针 f 的内容。指向或用于在从 main 返回之前指向?

#include <vector>

struct foo {
    std::vector<int> m;
};

int main()
{
    auto f = new foo;
    f->~foo();
}
我相信不再有对象foo哪里f用来指。我收到了很多评论说这可能不正确,而是可能存在一个对象 foo处于毁坏、死亡或其他无效状态。
对于显式销毁但其存储仍然有效的对象的存在,语言标准有什么说法?
换句话说,是否可以合理地说f处还有一个对象?那是在它的生命周期之外吗?有没有一个对象不在它的生命周期中,没有开始构造并且没有被破坏?

编辑 :
很明显,一个对象可以在它不在其生命周期内时存在。在构造和销毁过程中,有一个对象,它的生命周期尚未开始或已经结束。来自 https://timsong-cpp.github.io/cppwp/intro.object#1 :

[...] An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]). [...]


但是在f->~foo();之后f指向的对象(我们称之为 o )没有被构建,它不在它的生命周期中,也没有被破坏。我对本节的阅读是o不能再占用存储空间,因为它不在任何列举的情况下。这似乎意味着没有 o并且不能有指向 o 的指针了。矛盾的是,如果你有一个指向 o 的指针那么该指针将指向存储 o不能占。

编辑2:
如果不再有对象,那么 foo 是什么值?有?似乎它可以拥有的唯一合理的可能值是指向对象的指针,这与该语句相矛盾。见 this question .

最佳答案

在 C++ 中,对象本质上是永恒的。语言中没有任何东西可以使对象消失。超出生命周期的对象仍然是对象,它仍然占用存储空间,标准有specific things您可以使用指向超出其生命周期的对象的指针/引用来执行此操作。
一个对象只有在不可能有​​一个有效的指针/引用时才真正消失。当该对象占用的存储结束其存储持续时间时,就会发生这种情况。超过其持续时间的指向存储的指针是无效指针,即使地址本身稍后再次有效。
所以通过调用析构函数而不是使用 delete f (这也会释放存储),f仍然指向 foo 类型的对象,但该对象已超出其生命周期。

我上述陈述的理由基本上归结为标准没有任何规定来支持非创造对象的概念。
对象创建在哪里?
该标准提供了关于对象何时出现在存储区中的清晰、明确的说明。 [intro.object]/1概述了激发对象创建的确切机制。
该标准提供了关于对象生命周期何时开始和结束的清晰、明确的陈述。 [basic.life]在它完全概述了这些事情,但 [basic.life]/1 特别解释了对象的生命周期何时开始和结束。
标准提供有关对象何时不再存在的任何声明(明确或其他方式)。该标准规定了对象的创建时间、生命周期的开始时间以及结束时间。但它从来没有说它们何时停止存在于一块存储中。
也有人讨论过这种形式的陈述:

any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.


加了重点。
过去时的使用表明该对象不再位于该存储中。但是物体什么时候停止定位在那里呢?没有明确说明究竟是什么导致了这种情况的发生。没有那个,过去时态在这里的使用就无所谓了。
如果您不能指出它何时停止存在的声明,那么您最多只能说标准中有几个地方的措辞可以清理。它并没有消除标准没有说明对象何时停止存在的明确事实。
指针有效性
但它确实说明了何时不再可以访问对象。
为了使对象不再存在,标准必须考虑在这些对象不再存在时指向这些对象的指针。毕竟,如果一个指针指向一个对象,那么那个对象一定仍然存在,对吧?
[basic.compound]/3概述了指针可以具有的状态。指针可以处于以下四种状态之一:
  • a pointer to an object or function (the pointer is said to point to the object or function), or
  • a pointer past the end of an object ([expr.add]), or
  • the null pointer value ([conv.ptr]) for that type, or
  • an invalid pointer value.

不允许指向任何对象的指针。允许“无效指针值”,但指针仅在 the storage duration for the storage they point into ends 时才变为无效。 :

When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of that region of storage become invalid pointer values.


请注意,此语句意味着指向此类对象的所有指针不再处于“指向对象的指针”状态并进入“无效指针”状态。因此,此类存储中的对象(在其生命周期内和生命周期外)不再可访问。
这正是标准需要存在的声明以支持不再存在的对象的概念。
但不存在这样的声明。
[basic.life] 确实有几个语句解决了可以使用指向生命周期之外的对象的指针的有限方式。但请注意它使用的具体措辞:

For an object under construction or destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the pointer as if the pointer were of type void*, is well-defined.


它从不说指针“指向”分配的存储。它永远不会撤销 [basic.compound]/3 关于指针种类的声明。指针仍然是指向对象的指针;只是指针“指向分配的存储”。并且该指针可以用作 void* .
也就是说,没有“指向已分配存储的指针”这样的东西。有一个“指向生命周期之外的对象的指针,其指针值可用于指代已分配的存储空间”。但仍然是“指向对象的指针”。
一生不是存在
对象必须存在才能有生命周期。标准清楚地表明了这一点。但是,该标准在任何时候都没有将对象的存在与其生命周期联系起来。
事实上,如果结束一个对象的生命周期意味着该对象不存在,那么对象模型就会简单得多。 [basic.life] 的大部分内容都是关于制定在该对象的生命周期之外使用对象名称或指向它的指针/引用的特定方式。如果对象本身不存在,我们就不需要那种东西。
在关于此事的讨论中提到的是:

I believe mentions of out-of-lifetime objects are there to account for objects that are being constructed and objects that are being destructed.


如果这是真的,什么是 [basic.life]/8 talking about with this statement :

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object


如果在对象的生命周期结束时指向原始对象的指针变成指向已分配内存的指针,那么为什么此语句谈论指向原始对象的指针?指针不能指向不存在的对象,因为它们不存在。
只有当这些对象在其生命周期之外继续存在时,这段话才有意义。不,这不仅仅是在构造函数/析构函数中;本节中的示例非常清楚地说明了这一点:
struct C {
  int i;
  void f();
  const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();                 // lifetime of *this ends
    new (this) C(other);        // new object of type C created
    f();                        // well-defined
  }
  return *this;
}

C c1;
C c2;
c1 = c2;                        // well-defined
c1.f();                         // well-defined; c1 refers to a new object of type C

虽然 operator=确实调用析构函数,该析构函数在 this 之前完成使用指针。因此,[class.cdtor]的特殊规定不适用于this在创建新对象的那一刻。因此,新对象是在对旧对象的析构函数调用之外创建的。
所以很明显,对象的“在其生命周期之外”规则意味着始终有效。它不仅仅是对构造函数/析构函数的规定(如果是,它会明确指出)。这意味着名称/指针/引用必须在其生命周期之外仍然命名/指向/引用对象,直到创建新对象。
为了实现这一点,他们命名/指向/引用的对象必须仍然存在。

关于c++ - 对象生命周期结束和它何时不复存在之间有什么关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63770418/

相关文章:

c++ - noexcept(false) 析构函数覆盖所有特殊成员函数的异常规范?

c++ - 成员子对象继承存储持续时间是否有任何不直观的副作用?

rust - 没有引用,我怎么能有终生依赖?

c++ - 如何将整数存储在字符数组中?

c++ - 使用 ffmpeg 将 PCM-ALAW 数据转换为音频文件

c - 在C中,是否保证数组起始地址小于其他元素的地址?

rust - Rust:Fn成员签名中使用的Struct泛型类型参数需要命名生命周期

c++ - 如果我写一个字符串,fprintf 会将 '\0' 写入文件吗?

c++ - <ctime> 中的 clock() 函数如何访问系统时钟?

rust - 如何根据编译功能标志为枚举添加生命周期