析构函数是正常的函数调用吗?

Is destructor a normal function call?

假设我有两个简单的 类 这样的非虚拟析构函数:

struct A
{
    ~A() { std::cout << "A destructor" << std::endl; }
}
struct B : A
{
    ~B() { std::cout << "B destructor" << std::endl; }
}

B的实例被析构时,A的析构函数也会被调用。当我通过 A* 类型的指针销毁 B 的实例时,将不会调用 B 的析构函数。这是否也算作以您也可以调用普通成员函数的方式显式调用析构函数?

struct A
{
    ~A() { std::cout << "Hello" << std::endl; }
    void f() { std::cout << "Hello" << std::endl; }
}

A a;
a.~A(); // case 1
a.f(); // case 2

在这个例子中,这两种情况除了调用函数的名称不同外,还有什么区别吗?

编辑:考虑与 CRTP 相同的示例:

template <typename C>
struct A
{
    ~A() { static_cast<C*>(this)->~C(); }
}
struct B : public A<B>
{
    ~B() { std::cout << "B destructor" << std::endl; }
}

A<B>* a = new B();
delete a;

这会导致未定义的行为或内存泄漏吗?

事实上,在某些情况下(即:当使用池来避免常量内存分配时),您通常会将析构函数作为普通函数调用,否则当您尝试使用 placement new 时会导致未定义的行为再次在该内存地址上。

T* Pool::alloc() {
    ...
    return new (memory) T();
}

void Pool::dealloc( T* memory ) {
    ...
    memory->~T();
}

关于非虚拟析构函数问题,如果析构函数不是虚拟的,删除指向 A 的指针内的 B 对象将导致未定义的行为。

struct A
{
    ~A() { std::cout << "A destructor" << std::endl; }
};
struct B : A
{
    ~B() { std::cout << "B destructor" << std::endl; }
};

A* a = new B();
delete a; // undefined behaviour, just ~A() will be called

B* b = new B();
delete b; // it's OK because b is B*

更多信息in this FAQ

如果要显式调用析构函数,必须从与被调用的析构函数类型相同的对象中调用它。否则,这将产生未定义的行为:

In an explicit destructor call, the destructor name appears as a ~ followed by a type-name or decltypespecifier that denotes the destructor’s class type. The invocation of a destructor is subject to the usual rules for member functions (9.3); that is, if the object is not of the destructor’s class type and not of a class derived from the destructor’s class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior.

另请注意,如果显式调用析构函数,则在实例所在作用域的末尾,析构函数将隐式调用第二次,前提是该对象已在堆栈上实例化。这也会产生未定义的行为:

Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined

我建议您阅读规范的第 12.4 节。 working draft 是免费提供的。