为什么C++优化器在删除同一个指针时使用不同的delete

Why does the C++ optimizer use different delete when deleting the same pointer

当 gcc 7.3 优化我从 google 测试库中使用的一些代码时,我遇到了一个奇怪的问题。该问题已在 google 测试中得到修复,但我仍然对以下问题感到困惑:出于某种原因是否有更多具有相同签名的全局删除运算符?

我的代码在这里:https://godbolt.org/z/86Eevc 更有趣的是,对于 -O0,所有 call 指令都使用相同的删除地址。有了 -O3,一切都开始变得有趣起来。 (godbolt 上的 link 在选中“编译为二进制”时很有用,这样您就可以看到为调用参数生成的地址)。

我的 class (DeleteInside) 派生自具有虚拟 table 的 class,并具有执行 delete this; 的 deleteMe() 方法。如果我在全局范围内声明一个 DeleteInside *ptr = nullptr;,然后在主函数中调用 delete ptr;,删除运算符的地址与我使用 ptr->deleteMe(); 时调用的删除不同。 谁能解释一下?

static int timesDeleted = 1;
class DeleteBase
{
public:
    virtual ~DeleteBase() {
        timesDeleted += 1;
    }
};
class DeleteInside
{
    int _val;
public:
    ~DeleteInside() {
        timesDeleted = (timesDeleted * _val);
    }
    bool doSomething() {
        return false;
    }

    __attribute__((noinline)) void deleteMe() const {
        timesDeleted += 1;
        delete this;
    }
};
DeleteInside *delInside = nullptr;
int main(int argc, char *argv[]) {
    
    //delete delInside;
    delInside->deleteMe();
    delete delInside;
    return timesDeleted;
}

解释很简单:您调用未定义的行为(取消引用空指针),因此任何事情都可能发生。这种事情的一个 tell-tale 标志是优化级别会影响您注意到的结果。

您看到的地址在这两种情况下都是垃圾。至于它们到底为什么不同,您需要了解 GCC 7.3 如何处理这种未定义行为的特定情况的人,但对于所有意图和目的,所有有用的 C++ 解释都停留在“它是未定义的行为”。

如果非要我猜的话,vtable 及其在这种情况下的优化方式可能与此有关。