为什么在delete this之后调用printf("%p", this)是违法的?

3
这篇答案涉及到这个问题。该问题引用了此页面,该页面指出,为了使delete this;正确工作,您必须绝对确定this指针在删除之后没有被任何人使用。换句话说, 您不能检查它、与其他指针比较、与 NULL 比较、打印它、强制转换它等等。
我不明白为什么触碰指针本身会导致问题。请问有人可以解释一下吗?

some_pointer 的类型不是 void*,且没有进行强制转换时,printf("%p", some_pointer); 是否被定义? - Ben Voigt
@BenVoigt:嗯,如果这让你不舒服,就假装我写了this == this;。反正这也不是问题的重点。 - user541686
1
@user93353:感谢您找到了那个!Steve的回答解答了我的问题! - user541686
@Mehrdad - 那么结论是这不是特定于this的,对吧? - DaoWen
2个回答

4

这可能在大多数现代硬件上工作,但这是未定义的行为。我相信(我在网上读到过)的理由是,某些架构在其指针寄存器中具有陷阱功能,因此如果您将指针加载到寄存器中并且该指针指向未分配的内存,则该架构可能会发出信号。


@Mehrdad,是的,问题出在已删除的指针上,而不是该指针是否为“this”。 - Adam H. Peterson
@BenVoigt,这取决于实现。通过使此用法非法,架构可以自由选择是直接使用指针寄存器还是整数寄存器。如果实现可以假定任何指针读取都需要指针有效,则可以选择始终将指针读入指针寄存器,而不是可能被迫将指针加载到相同大小的整数寄存器中,直到它能够保证指针将被解引用并将其移动到指针寄存器。 - Adam H. Peterson
@JamesKanze,根据我的汇编经验(尽管已经过去15年),我不知道X86指针的这种行为。你能详细解释一下吗?当然,我知道如果预先跟踪可能被释放的指针可能会导致页面错误。(但是这种行为并不需要为每个分配单独设置一个段;您可能会(不)幸运地命中唯一位于其段中的一个分配。) - Adam H. Peterson
@AdamH.Peterson:实际上我不知道那就是情况 - 我的理解是,delete 本身可能会修改指针使其无效,但如果没有触及到指针,它们是可以随意复制的。现在看来有点可怕...谢谢。 - user541686
1
@AdamH.Peterson 在80x86上的地址有两个部分,一个段和一个偏移量。将非法段加载到段寄存器中将导致系统陷入陷阱。在复制指针时,实现可以自由使用段寄存器。(事实是,大多数当前系统完全忽略段寄存器,只加载一次,然后永远不再触及它们,以给出线性寻址的印象。但这并不总是这样,特别是在80286中,偏移量仅为16位。) - James Kanze
显示剩余15条评论

2

this指针有点特殊,因为在虚继承中计算它需要通过对象本身读取虚拟子对象的偏移量。(我希望调用者在进入成员函数之前就完成此操作,但我不知道100%的实际编译器是否这样做)


如果是这样的话,那么在这些情况下delete this甚至不应该合法,因为它指向实际分配块内的“子对象偏移量”,对吧? - DaoWen
1
这个 this 指针不是在函数进入之前就必须计算出来吗?函数并不会“知道”指针指向一个子类,它期望的是指向当前类的指针... - user541686
@Mehrdad - 是的!我一直在尝试想出一个好的措辞方式。你说得很好。如果你传递一个指向“子对象”的指针,那么你调用的函数就无法知道它不是一个可以被删除的普通指针,因此它无法返回并“找到要删除的完整对象”。 - DaoWen
@DaoWen:当然,该函数有一种方法可以找到完整的对象。如果你编写像dynamic_cast<void*>(this)这样的代码,那么就必须这样做。 - Ben Voigt
@Mehrdad:这也适用于this->whatever_member,包括源代码没有明确写出this的情况。 - Ben Voigt
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接