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

Fra*_*eux 12 c++ lifetime object-lifetime language-lawyer

在下面的简短示例中,关于指针f指向或用于在从 返回之前指向的对象可以说些什么main

#include <vector>

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

int main()
{
    auto f = new foo;
    f->~foo();
}
Run Code Online (Sandbox Code Playgroud)

我相信,不再是一个对象foo,其中f用于点。我收到了很多评论,说这可能不正确,而且可能有一个对象foo处于销毁、死亡或其他无效状态。

对于显式销毁但其存储仍然有效的对象的存在,语言标准有什么说法?

换句话说,是否可以合理地说仍然存在一个f超出其生命周期的对象?有没有一个对象不在它的生命周期中,没有开始构造并且没有被破坏?


编辑 :

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

[...] 一个对象在其构建期间 ([class.cdtor])、整个生命周期和销毁期间 ([class.cdtor]) 占用一个存储区域。[...]

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


编辑2:

如果不再有对象,那么还有什么样的价值foo?似乎它可以拥有的唯一合理的可能值是指向对象的指针,这与该语句相矛盾。看到这个问题

Nic*_*las 9

在 C++ 中,对象本质上是永恒的。语言中没有任何东西可以使对象消失。超出其生命周期的对象仍然是一个对象,它仍然占用存储空间,并且标准具有您可以使用指向其生命周期之外的对象的指针/引用执行的特定操作

一个对象只有在不可能有​​一个有效的指针/引用时才真正消失。当该对象占用的存储结束其存储持续时间时,就会发生这种情况。超过其持续时间的指向存储的指针是无效指针,即使地址本身稍后再次有效。

因此,通过调用析构函数而不是使用delete f(这也会释放存储),f仍然指向一个类型为 的对象foo,但该对象已超出其生命周期。


我上述陈述的理由基本上归结为标准没有任何规定来支持非创造对象的概念。

对象创建在哪里?

该标准提供了关于对象何时出现在存储区中的清晰、明确的说明。[intro.object]/1概述了激发对象创建的确切机制。

该标准提供了关于对象生命周期何时开始和结束的清晰、明确的陈述。[basic.life]在其完全概述了这些事情,但 [basic.life]/1 特别解释了对象的生命周期何时开始和结束。

该标准没有提供任何关于对象何时不再存在的声明(明确或其他)。该标准规定了对象的创建时间、生命周期的开始时间以及结束时间。但它从来没有说它们何时停止存在于一块存储中。

也有人讨论过这种形式的陈述:

任何表示对象将位于曾经位于的存储位置地址的指针都可以使用,但只能以有限的方式使用。

加了重点。

过去时的使用表明该对象不再位于该存储中。但是物体什么时候停止定位在那里呢?没有明确说明究竟是什么导致了这种情况的发生。没有那个,过去时态在这里的使用就无所谓了。

如果您不能指出它何时停止存在的声明,那么您最多只能说标准中有几个地方的措辞可以清理。它并没有消除标准没有说明对象何时停止存在的明确事实。

指针有效性

但它确实说明了何时不再可以访问对象。

为了使对象不再存在,标准必须考虑在这些对象不再存在时指向这些对象的指针。毕竟,如果一个指针指向一个对象,那么该对象一定仍然存在,对吗?

[basic.compound]/3概述了指针可以具有的状态。指针可以处于以下四种状态之一:

  • 指向对象或函数的指针(该指针被称为指向该对象或函数),或
  • 超过对象末尾的指针 ([expr.add]),或
  • 该类型的空指针值 ([conv.ptr]),或
  • 无效的指针值。

不允许指向任何对象的指针。允许“无效指针值”,但指针仅在它们指向的存储的存储持续时间结束时才变为无效:

当到达存储区域的持续时间结束时,表示该存储区域的任何部分的地址的所有指针的值变为无效指针值。

请注意,此语句意味着指向此类对象的所有指针不再处于“指向对象的指针”状态并进入“无效指针”状态。因此,此类存储中的对象(在其生命周期内和生命周期外)不再可访问。

这正是标准需要存在的声明以支持不再存在的对象的概念。

但不存在这样的声明。

[basic.life] 确实有几个语句解决了可以使用指向生命周期之外的对象的指针的有限方式。但请注意它使用的具体措辞:

对于正在构建或销毁的对象,请参阅 [class.cdtor]。否则,这样的指针指向已分配的存储([basic.stc.dynamic.deallocation]),并且使用指针就像指针是 void* 类型一样,是明确定义的。

从不说指针“指向”分配的存储。它永远不会撤销 [basic.compound]/3 关于指针种类的声明。指针仍然是指向对象的指针;只是指针“指向分配的存储”。并且该指针可以用作void*.

也就是说,没有“指向已分配存储的指针”这样的东西。有一个“指向生命周期之外的对象的指针,其指针值可用于指代已分配的存储空间”。但仍然是“指向对象的指针”。

一生不是存在

对象必须存在才能有生命周期。标准清楚地表明了这一点。但是,该标准在任何时候都没有将对象的存在与其生命周期联系起来。

事实上,如果结束一个对象的生命周期意味着该对象不存在,那么对象模型就会简单得多。[basic.life] 的大部分内容都是关于制定特定方式,您可以在该对象的生命周期之外使用对象的名称或指向它的指针/引用。如果对象本身不存在,我们就不需要那种东西。

在关于此事的讨论中提到的是:

我相信提到生命周期外的对象是为了解释正在构造的对象和正在销毁的对象。

如果这是真的,那么[basic.life]/8 用这个语句谈论的是什么:

如果在一个对象的生命周期结束后,在该对象所占用的存储空间被重用或释放之前,在原对象所占用的存储位置创建一个新对象,一个指向原对象的指针,一个指向该对象的引用引用原始对象,或原始对象的名称

如果在对象的生命周期结束时指向原始对象的指针变成指向已分配内存的指针,那么为什么此语句谈论指向原始对象的指针?指针不能指向不存在的对象,因为它们不存在

只有当这些对象在其生命周期之外继续存在时,这段话才有意义。不,这不仅仅是在构造函数/析构函数中;本节中的示例非常清楚地说明了这一点:

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

Run Code Online (Sandbox Code Playgroud)

虽然operator=确实调用了析构函数,但该析构函数使用this指针之前完成。因此,[class.cdtor]的特殊规定不适用于this创建新对象的那一刻。因此,新对象是在对旧对象的析构函数调用之外创建的。

所以很明显,对象的“在其生命周期之外”规则意味着始终有效。它不仅仅是对构造函数/析构函数的规定(如果是,它会明确指出)。这意味着名称/指针/引用必须在其生命周期之外仍然命名/指向/引用对象,直到创建新对象。

为了实现这一点,他们命名/指向/引用的对象必须仍然存在

  • _超出其生命周期的对象仍然是一个对象,它仍然占用存储空间_ [此](https://timsong-cpp.github.io/cppwp/n4861/intro.object#1.sentence-3) 不同意 (2认同)