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

时间:2015-09-13 13:57:49

标签: c++ destructor

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

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

B的实例被破坏时,也会调用A的析构函数。当我通过类型B的指针破坏A*的实例时,将不会调用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;

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

2 个答案:

答案 0 :(得分:0)

实际上,有些情况(即:当使用池来避免常量内存分配时),通常将析构函数称为普通函数,否则当您尝试再次使用new 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

答案 1 :(得分:0)

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

  

在显式析构函数调用中,析构函数名称显示为〜   后跟一个表示名称的类型名称或decltypespecifier   析构函数的类类型。析构函数的调用受制于   成员职能的通常规则(9.3);也就是说,如果对象是   不是析构函数的类类型,也不是派生自类的类   析构函数的类类型(包括通过时调用析构函数的类型)   一个空指针值),程序有未定义的行为。

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

  

一旦为对象调用析构函数,该对象就不再存在   存在;如果为a调用析构函数,则行为未定义   生命周期结束的对象(3.8)。 [例如:如果是析构函数   显式调用自动对象,块为   随后以通常会隐含的方式离开   破坏对象,行为未定义

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