在 C++ 中,对象本质上是eternal。语言中没有任何东西可以使物体消失。超出其生命周期的对象仍然是一个对象,它仍然占用存储空间,并且标准有具体的事情 https://timsong-cpp.github.io/cppwp/n4659/basic.life#6您可以使用指向超出其生命周期的对象的指针/引用来执行此操作。
只有当不可能有有效的指针/引用时,对象才会真正消失。当该对象占用的存储结束其存储持续时间时,就会发生这种情况。超过其持续时间的指向存储的指针是无效指针,even if地址本身稍后再次有效。
所以通过调用析构函数而不是使用delete f
(这也会释放存储空间),f
仍然指向类型的对象foo
,但该对象已超出其生命周期。
我上述陈述的理由基本上可以归结为以下标准:none支持非创建对象概念所需的规定。
对象非创造在哪里?
该标准提供了关于对象何时存在于存储中的清晰、明确的声明。[介绍对象]/1 https://timsong-cpp.github.io/cppwp/n4659/intro.object#1概述了激发对象创建的确切机制。
该标准提供了关于对象生命周期何时开始和结束的清晰、明确的声明。[基本生活] https://timsong-cpp.github.io/cppwp/n4659/basic.life其完整概述了这些内容,但 [basic.life]/1 特别解释了对象的生命周期何时开始和结束。
标准does not提供有关对象何时不再存在的任何声明(明确或其他)exists。该标准规定了对象何时创建、其生命周期何时开始以及何时结束。但never它是否说明它们何时停止存在于某个存储空间中。
还对以下形式的陈述进行了讨论:
表示对象所在存储位置地址的任何指针或位于可以使用,但只能以有限的方式使用。
添加了强调。
使用过去时表明该对象不再位于该存储中。但是这个物体什么时候不再位于那里了呢?没有clear关于到底是什么导致了这种情况发生的声明。如果没有这一点,这里使用过去时就无关紧要了。
如果您无法指出有关它何时停止存在的声明,那么您最多可以说的是标准中有几个地方的措辞可以清理。它并没有消除一个明显的事实,即该标准没有说明对象何时停止存在。
指针有效性
但它确实说明了对象何时不再可访问。
为了使对象停止存在,标准必须考虑当这些对象不再存在时指向这些对象的指针。毕竟,如果一个指针指向一个对象,那么该对象必须仍然存在, right?
[碱性化合物]/3 https://timsong-cpp.github.io/cppwp/n4659/basic.compound#3概述了指针可以具有的状态。指针可以处于四种状态之一:
- 指向对象或函数的指针(该指针被称为指向对象或函数),或者
- 超过对象末尾的指针 ([expr.add]),或
- 该类型的空指针值 ([conv.ptr]),或者
- 无效的指针值。
不允许指针指向任何对象。允许“无效指针值”,但指针仅在以下情况下才变得无效:它们指向的存储的存储持续时间 https://timsong-cpp.github.io/cppwp/n4659/basic.stc#4:
当到达存储区域的持续时间结束时,表示该存储区域的任何部分的地址的所有指针的值变成无效指针值。
请注意,此语句意味着所有指向此类对象的指针不再处于“指向对象的指针”状态,而是进入“无效指针”状态。因此,此类存储中的对象(无论是在其生命周期内还是在其生命周期外)都不再可访问。
这正是标准需要存在的那种声明来支持不再存在的对象的概念。
但不存在这样的说法。
[basic.life] 确实有几个语句,这些语句解决了可以使用指向生命周期之外的对象的指针的有限方式。但请注意它使用的具体措辞:
对于正在构造或销毁的对象,请参阅[class.cdtor]。否则,这样的指针引用分配的存储([basic.stc.dynamic.deallocation]),并且使用该指针就好像该指针是 void* 类型一样,是明确定义的。
It never表示指针“指向”分配的存储空间。它永远不会撤消 [basic.compound]/3 关于指针类型的声明。指针仍然是一个指向对象的指针;只是指针“指的是分配的存储空间”。并且该指针可以用作void*
.
也就是说,不存在“指向已分配存储空间的指针”这样的东西。有一个“指向生命周期之外的对象的指针,其指针值可用于引用分配的存储空间”。但仍然是“指向对象的指针”。
一生不等于存在
对象必须存在才能有生命周期。该标准明确了这一点。然而,该标准在任何时候都没有将对象的存在与其生命周期联系起来。
事实上,如果对象生命周期的结束意味着该对象不存在,则对象模型会简单得多。 [basic.life] 的大部分内容都是关于在该对象的生命周期之外使用对象名称或指向该对象的指针/引用的特定方式。如果对象本身不存在,我们就不需要这类东西。
关于此事的讨论是这样的:
我相信提到生命周期外的对象是为了解释正在构造的对象和正在被破坏的对象。
如果这是真的,那是什么[basic.life]/8 用这个语句来谈论 https://timsong-cpp.github.io/cppwp/n4659/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
While operator=
确实调用析构函数,该析构函数完成before the this
使用指针。因此,特别规定[类.cdtor] https://timsong-cpp.github.io/cppwp/n4659/class.cdtor不适用于this
此时新对象已创建。这样新的对象就被创建了outside析构函数调用旧的析构函数。
所以很明显,对象的“在其生命周期之外”规则的目的是always工作。它不仅仅是对构造函数/析构函数的规定(如果是的话,它会明确地指出这一点)。这意味着名称/指针/引用必须在其生命周期之外仍然命名/指向/引用对象,直到创建新对象。
为了实现这一点,他们命名/指向/引用的对象必须仍然存在.