为什么禁止使用析构函数的地址?

时间:2011-10-26 15:48:51

标签: c++ pointers destructor standards

12.4.2的C ++标准指出

  

[...]不得采用析构函数的地址。 [...]

然而,编译器可以毫无怨言地采用类析构函数周围的包装器的地址,如下所示:

struct Test {
    ~Test(){};

    void destructor(){
        this->~Test();
    }
};

void (Test::*d)() = &Test::destructor;

那么禁止直接获取析构函数地址的理由是什么?

3 个答案:

答案 0 :(得分:17)

构造函数和析构函数有点特殊。编译器经常 在调用它们时使用不同的约定(例如,传递额外的隐藏 参数)。如果你拿走了地址并将其保存在某个地方,那么 编译器会丢失该函数是构造函数的信息 或析构函数,并且不知道使用特殊约定。

答案 1 :(得分:0)

关于调用约定的其他答案可能有所不同,这是一个原因。

但是我也想:尚不清楚有用对析构函数的地址到底有多大作用!你会用它做什么?调用它可能非常不安全-您是否会将它用于相等性比较?要确定对象的确切类型,也许?还有其他语言工具。

最终,如果析构函数作为函数发出,它确实具有一个地址-但是该语言会考虑将该地址用于任何内容,即使相等比较也是如此,因此非常无用,只是在语言级别被禁止

从技术上讲,编译器可以允许您执行此操作-可能将其强制为void *指针或其他内容-但随后必须指定确切的地址。编译器甚至不会将琐碎类的析构函数作为函数发出析构函数,也并非总是如此-因此,获取地址将要求它这样做。

这可能仅对平等比较有用,并且-定义平等保证的内容可能不值得,因为其他语言工具也可能实现相同的目的。

Tl; Dr:只是被认为是不安全的而且没有用,所以该语言禁止使用它。

答案 2 :(得分:0)

显然不能保证析构函数存在。如果您尝试以下操作并检查反汇编,您会注意到没有调用析构函数,也没有可引用的析构函数。

https://godbolt.org/z/6nYWf48Gz

struct Foo {
    Foo() {}
    int a = 3;
};

void fizz() {
    Foo foo;
}

int main() {
    Foo foo;
    foo.~Foo(); // call destructor if exists, otherwise noop
    fizz();
    return 0;
}