是否可以在其析构函数中使用指向已销毁对象的指针?

Can I use a pointer to a destructed object inside its destructor?

本文关键字:对象 指针 析构函数 是否      更新时间:2023-10-16

我是否可以从其析构函数内的指针访问已销毁的对象?是否保证对象仍位于该指针位置并可访问? 例如,在下面的代码中,我销毁了 a1,并在其析构函数中从指向 a1 的 a2 访问 a1。

#include <iostream>
class A
{
public:
A(int* i) : m_i(i) {}
~A();
int* m_i;
};
A* a1;
A* a2;
int x = 0;
A::~A()
{
*a2->m_i = 1;
}
int main()
{
a1 = new A(&x);
a2 = a1;
delete a1;
std::cout << x << std::endl;
return 0;
}

这是一个很好的问题。基本答案是,当A对象的析构函数启动时,对象的生存期结束,但在A的析构函数完成之前,访问A对象的成员(有一些限制(仍然是合法的。标准参考是:

[basic.life]/1:

。类型为To的对象生存期在以下时间结束:如果T是具有非平凡析构函数 (15.4( 的类类型,则析构函数调用开始,...

[basic.life]/6:

在对象的生存期开始之前,但在对象将占用的存储之后 已分配41,或者在对象的生存期结束之后,在该对象占用的存储之前 重用或释放,表示对象将所在的存储位置地址的任何指针,或 可以以有限的方式使用。关于正在建造或销毁的物体,见15.7。...

[class.cdtor]/2 (15.7/2(:

。为了形成指向对象obj的直接非静态成员的指针(或访问其值(,obj的构造应该已经开始,并且它的销毁不应该完成,否则指针值的计算(或访问 成员值(会导致未定义的行为。

因此,由于对a2->m_i的访问发生在对象*a2析构函数的执行时,因此是允许的。(注意:A::m_iA的直接成员,因为A没有基类。

请注意,(正如其他人已经指出的那样(,在析构函数完成之前,不会释放存储A对象的内存。这就是delete表达式的工作方式。

a1指向的对象在析构函数退出之前不会被销毁。 这意味着可以在析构函数中调用任何成员函数以及访问和成员变量。

你的代码,按原样,是有效的。

delete a1;一起使用的delete运算符执行以下操作:

  1. 它使用其静态类型破坏a1指向的对象,即执行a1.~A()
  2. 它使用::operator delete(a1)来释放a1指向的对象曾经所在的内存(假设您没有重载它......

第一步中调用的析构函数依次执行以下两个操作:

  1. 执行用户定义的代码。
  2. 销毁所有成员对象(通过使用其析构函数(。

总之,首先执行您的代码(此时 a1 和 a2 都有效,并且对象仍然存在(,然后销毁成员(现在该操作在技术上是无效的,但仍然像通常具有相当无聊析构函数的int一样工作(,最后释放内存(之后执行该操作将非常糟糕(。

如果表达式不是空指针,并且释放函数不是销毁删除(自 C++20 起(,则删除表达式将为要销毁的对象或要销毁的数组的每个元素(从数组的最后一个元素到第一个元素(调用析构函数(如果有(。

之后,无论任何析构函数是否抛出异常,delete 表达式都会调用释放函数:运算符 delete(对于表达式的第一个版本(或运算符 delete[](对于表达式的第二个版本(

https://en.cppreference.com/w/cpp/language/delete

因此,析构函数将在释放之前被调用。