在本段 C++ FAQdelete this
中讨论了构造的用法。列出了 4 个限制。
限制 1 到 3 看起来很合理。但是为什么限制 4 我“不能检查它,将它与另一个指针进行比较,将它与 NULL 进行比较,打印它,强制转换它,用它做任何事情”?
我的意思this
是另一个指针。为什么我不能调用reinterpret_cast
它来输出它的值?int
printf()
在本段 C++ FAQdelete this
中讨论了构造的用法。列出了 4 个限制。
限制 1 到 3 看起来很合理。但是为什么限制 4 我“不能检查它,将它与另一个指针进行比较,将它与 NULL 进行比较,打印它,强制转换它,用它做任何事情”?
我的意思this
是另一个指针。为什么我不能调用reinterpret_cast
它来输出它的值?int
printf()
调用 delete 后 'this' 的值是未定义的,你对它所做的任何事情的行为也是未定义的。虽然我希望大多数编译器做一些明智的事情,但没有什么(在规范中)阻止编译器决定它在这种特殊情况下的行为将发出代码来格式化你的硬盘。调用未定义的行为(几乎)总是一个错误,即使您的特定编译器以您希望的方式运行。
您可以通过在调用 delete 之前获取指针的副本(作为整数)来解决此问题。
删除指针(this 或任何其他指针)后无法对指针执行任何操作的原因是,硬件可能(以及一些较旧的机器确实)陷入尝试将无效内存地址加载到寄存器中的陷阱。尽管它在所有现代硬件上都可以,但该标准表示,您唯一可以对无效指针(未初始化或已删除)执行的操作就是分配给它(NULL,或从另一个有效指针)。
啊哈!
3.7.3.2/4:“......释放函数应释放指针引用的存储空间,使所有指向释放存储空间的任何部分的指针无效。使用无效指针值的效果(包括将其传递给释放空间)函数)未定义”。
请注意,这表示“使用值”,而不是“取消引用指针”。
该段并非特定于this
,它适用于已删除的任何内容。
因为您可以对该指针执行的任何操作都可能触发对该对象的类方法进行解释的逻辑,这可能导致崩溃。
现在,您指出的某些操作可能显然是“安全的”,但是很难说在您可以调用的任何方法中会发生什么。
来自帖子:“不得检查它,将其与另一个指针进行比较,将其与 NULL 进行比较,打印它,转换它,用它做任何事情”?
所有这些动作都可以触发与运算符相关的函数,这些函数使用未定义的指针进行评估。铸造同上。
现在,如果您执行 reintepret_cast,这可能是一个不同的故事,您可能会相处得很好,因为 reinterpret 只是一点一点的重新解释,而不涉及(据我所知)任何方法调用。
出于同样的原因,您不会删除任何其他指针,然后尝试对其执行任何操作。
b/c this 现在指的地址,它未定义,你不知道那里可能有什么......
在多线程程序中,只要你delete
一个指针,空闲空间就可以被另一个线程分配,覆盖this
. 即使在单线程程序中,除非您非常小心在return
ing 之前调用的内容,否则您之后执行的任何操作都delete this
可能分配内存并覆盖this
.
在以调试模式编译的 Microsoft Visual C++ 可执行文件中,delete
输入指针会导致其内存立即被 0xCC 测试模式覆盖(未初始化的变量也使用此模式初始化),以帮助识别悬空指针错误,例如这个。
这让我想起了我在一个在线可玩游戏中修复了一个错误,在该游戏中,如果火的总数达到一定数量,火对象的构造函数会删除最旧的火。被删除的 Fire 有时是父 Fire 创建了一个新的 Fire——bam,悬空指针错误!只是运气好,这个 bug 以完全可预测的方式与内存分配算法交互(删除的 Fire 总是以同样的方式被新的 Fire 覆盖)——否则它会导致在线玩家之间的不同步。我在重写游戏进行内存分配的方式时发现了这个错误。由于它的可预测性,当我修复它时,我还能够模拟它的行为以与旧游戏客户端兼容。