为什么我不应该尝试使用“this”? “删除此”后的值?
在C++ FAQ 的这段中使用delete this<讨论了 /code> 构造。列出了 4 项限制。
限制 1 至 3 看起来相当合理。但为什么限制 4 是我“不能检查它、将它与另一个指针比较、将它与 NULL 比较、打印它、强制转换它、对它做任何事情”?
我的意思是 this
是另一个指针。为什么我不能将其 reinterpret_cast
转换为 int
或调用 printf()
来输出其值?
In this paragraph of C++ FAQ usage of delete this
construct is discussed. 4 restrictions are listed.
Restrictions 1 to 3 look quite reasonable. But why is restriction 4 there that I "must not examine it, compare it with another pointer, compare it with NULL, print it, cast it, do anything with it"?
I mean this
is yet another pointer. Why can't I reinterpret_cast
it to an int
or call printf()
to output its value?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
调用删除后“this”的值是未定义的,并且您用它执行的任何操作的行为也是未定义的。虽然我希望大多数编译器会做一些明智的事情,但没有什么(在规范中)阻止编译器决定在这种特殊情况下其行为将发出代码来格式化您的硬盘。调用未定义的行为(几乎)总是一个错误,即使您的特定编译器按照您希望的方式运行。
您可以通过在调用删除之前获取指针的副本(作为整数)来解决此问题。
The value of 'this' after calling delete is undefined, and the behaviour of anything you do with it is also undefined. While I would expect most compilers to do something sensible, there's nothing (in the spec) stopping the compiler from deciding that its behaviour in this particular case will be emit code to format your hard-disk. Invoking undefined behaviour is (almost) always a mistake, even when your particular compiler behaves in the way you'd like it to.
You could work around this by taking a copy of the pointer (as an integer) before calling delete.
删除指针(此指针或任何其他指针)后无法对其执行任何操作的原因是硬件可能(某些较旧的机器确实如此)捕获试图将无效内存地址加载到寄存器中的行为。尽管它在所有现代硬件上可能都很好,但标准规定,您可以对无效指针(未初始化或已删除)做的唯一事情就是分配给它(NULL,或从另一个有效指针)。
The reason that you cannot do anything with a pointer after you delete it (this, or any other pointer), is that the hardware could (and some older machines did) trap trying to load an invalid memory address into a register. Even though it may be fine on all modern hardware, the standard says that the only thing that you can do to a invalid pointer (uninitialized or deleted), is to assign to it (either NULL, or from another valid pointer).
啊哈!
3.7.3.2/4:“...释放函数应释放指针引用的存储,使引用已释放存储的任何部分的所有指针无效。使用无效指针值的影响(包括将其传递给释放函数)未定义”。
请注意,这表示“使用值”,而不是“取消引用指针”。
该段落并非特定于
this
,它适用于已删除的任何内容。Aha!
3.7.3.2/4: "... the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined".
Note that this says "using the value", not "dereferencing the pointer".
That paragraph is not specific to
this
, it applies to anything that has been deleted.因为您可以对该指针执行的任何操作都可能触发在该对象的类方法上解释的逻辑,这可能会导致崩溃。
现在,您指出的某些操作可能显然是“安全的”,但很难说出您可以调用的任何方法中会发生什么。
从帖子中:“一定不能检查它,将它与另一个指针进行比较,将它与 NULL 进行比较,打印它,转换它,用它做任何事情”?
所有这些操作都可以触发与运算符相关的函数,这些函数使用未定义的指针进行评估。同上铸造。
现在,如果您执行reintepret_cast,那可能是一个不同的故事,您可能会接受它,因为reinterpret 只是一点一点地重新解释,而不涉及(据我所知)任何方法调用。
because any action you can take with that pointer could trigger logic which is interpreted on the class methods of that object, which could lead to a crash.
Now, some of the actions you point at could be apparently "safe", but it's difficult to say what happens within any method you can call.
From the post: "must not examine it, compare it with another pointer, compare it with NULL, print it, cast it, do anything with it"?
All these actions can trigger operator related functions, which are evaluated with an undefined pointer. Idem for casting.
Now if you perform a reintepret_cast, that's probably a different story, and you could probably get along with it, as reinterpret is just a bit by bit reinterpretation, without involving (as far as I know) any method call.
出于同样的原因,您不会删除任何其他指针,然后尝试对其执行任何操作。
For the same reason you would not delete any other pointer and then try and perform any operations on it.
b/c this 现在引用的地址,它未定义,你不知道那里可能有什么......
b/c the address that this refers to now, it undefined, and you don't know what might be there...
在多线程程序中,当您
删除
指针时,可用空间可以由另一个线程分配,覆盖this
使用的空间。即使在单线程程序中,除非您非常小心返回
之前调用的内容,否则在删除此
之后执行的任何操作都可能会分配内存并覆盖以前的内存由this
指向。在调试模式下编译的 Microsoft Visual C++ 可执行文件中,删除指针会导致其内存立即被 0xCC 测试模式覆盖(未初始化的变量也会使用此模式进行初始化),以帮助识别悬空像这样的指针错误。
这让我想起当我修复了一个在线游戏中的一个错误时,如果 Fire 总数达到一定数量,Fire 对象的构造函数会删除最旧的 Fire。删除的 Fire 有时是创建新 Fire 的父 Fire — 砰,悬空指针错误!幸运的是,这个错误以完全可预测的方式与内存分配算法交互(删除的 Fire 总是以相同的方式被新的 Fire 覆盖)——否则就会导致在线玩家之间的不同步。我在重写游戏内存分配方式时发现了这个错误。由于其可预测性,当我修复它时,我还能够实现其行为的模拟,以与旧游戏客户端兼容。
In a multi-threaded program, the moment you
delete
a pointer, the free space can be allocated by another thread, overwriting the space used bythis
. Even in a single-thread program, unless you're very careful about what you call beforereturn
ing, anything you do afterdelete this
could allocate memory and overwrite what used to be pointed to bythis
.In a Microsoft Visual C++ executable compiled in Debug mode,
delete
ing a pointer causes its memory to be immediately overwritten with a 0xCC test pattern (uninitialized variables are also initialized with this pattern), to help in identifying dangling pointer bugs such as this one.This reminds me of when I fixed a bug in a online-playable game in which a Fire object's constructor deleted the oldest Fire if the total number of Fires had reached a certain number. The deleted Fire was sometimes the parent Fire creating a new Fire — bam, dangling pointer bug! It was only due to luck that this bug interacted with the memory allocation algorithm in a completely predictable way (the deleted Fire was always overwritten with a new Fire in the same way) — otherwise it would have caused a desynchronization between online players. I found this bug when rewriting the way the game did memory allocation. Due to its predictability, when I fixed it, I was also able to implement emulation of its behavior for compatibility with older game clients.