调用基类的析构函数而不破坏基类!

时间:2010-10-26 08:27:40

标签: c++ inheritance destructor ctor-initializer

#include<iostream>
using namespace std;

class A
{
public:
        int i;
        A() {cout<<"A()"<<endl;}
        ~A() {cout<<"~A()"<<endl;}
};
class B:public A
{
public:
        int j;
        B(): j(10)
        {
                this->i=20;
                this->~A();
        }
};

int main()
{
        B abc;
        cout<<"i="<<abc.i<<" j="<<abc.j<<endl;
}//main

两个问题:

  1. 为什么A的析构函数被调用为普通函数而不是破坏对象? (或者只是在子类的析构函数调用基类的析构函数时才会销毁基类的某种规则?)我试用这个示例代码来找出析构函数是如何工作的。因此,如果简单地调用析构函数不会破坏对象,那么显然有一些其他类型的调用会调用析构函数,然后才会破坏对象。那种电话有什么特别之处,有什么电话呢?
  2. 有没有办法在B的构造函数中为A创建一个初始化列表?像这样:

    class B:public A
    { 
        B(): j(10), A():i(20) {}
    };
    

7 个答案:

答案 0 :(得分:5)

  1. 基类的析构函数应该是虚拟的。在这里,因为它是在堆栈上创建的,它不是问题,但无论如何..
  2. 不,但您可以在B的构造函数的初始化列表中调用class A()构造函数,如下所示:
    B(): A( .. ), ...

  3. A* a = new B();
    //..
    delete a;
    
    除非class A析构函数是虚拟的,否则

    调用B的析构函数。这就是为什么不应该派生STL容器 - 他们的析构函数不是虚拟的。

答案 1 :(得分:5)

  1. 析构函数与您可以调用的任何其他常规函数一样(但除非您使用新的展示位置,否则不应该这样做)。当您在对象上调用delete时,会发生两件事情:调用析构函数进行清理,然后调用operator delete来释放为该对象分配的内存。这里第二步没有发生。

  2. 不,你不能这样称呼它。你可以做的是这样的事情:

    A班 {   上市:   A(int n):i(n){} };

    B类:公众A {   上市:    B():A(20),j(10){} };

答案 2 :(得分:3)

要点:

  1. 这是一个未定义的行为,但只有〜A()通过类B的实例调用,因为~A()未声明为虚拟。有关Wikipedia
  2. 的更多信息,请参阅
  3. 否。对于派生类,首先调用父类,然后分配参数。
  4. 维基百科上的第1点):

      

    没有虚拟析构函数,而   删除B类的实例   正确地为两个B调用析构函数   如果对象被删除,则为A.   B的实例,B的实例   通过指向其基础的指针删除   A类将产生未定义的   行为。

    示例(针对第2点):

    B(): A(), j(10) {}
    

    B(): A() {j = 10;}
    

答案 3 :(得分:3)

@Nav:不,你对“被摧毁”的理解是错误的。调用对象的析构函数时,对象将被销毁。你似乎相信它所存在的记忆完全蒸发,但这种情况从未发生过。该对象不再存在,但是对象通常会遗留一些垃圾数据,如果您愿意违反C ++的规则并调用未定义的行为,那么您可以读取剩余的字节,并且它们将看起来像对象,并且由于没有运行时检查您是否正在访问有效对象,您通常可以将它们视为对象。你做了什么。

这是非法的,它是未定义的行为,但在实践中它通常有效。

再一次,析构函数不会物理地蒸发内存。析构函数执行后,您的RAM仍具有相同的容量。从概念上讲,一旦析构函数运行,该对象就不再存在。但它所包含的数据仍在记忆中。

答案 4 :(得分:2)

1)C ++中的析构函数调用顺序与构造函数调用顺序的顺序相反。所以首先派生类对象得到破坏,然后是基类对象。

2)否。

答案 5 :(得分:2)

在您提供的代码中,您确实正在销毁基类,因此i。调用析构函数然后使用死对象是未定义的行为 - 它可能有效或可能崩溃。

i应该是int(例如vector)更复杂的东西,尝试对此做任何事情都可能导致崩溃。

答案 6 :(得分:2)

如果你自己调用〜SomeClass(),显式地调用析构函数。这使得对象(在这种情况下,对象的基类部分)处于被破坏状态。

由于析构函数不是虚拟的,因此不会调用派生类的析构函数,但也会销毁SomeClass的基类。

试图通过仅使用i成员来确定A是否真的被破坏,这不是一个好的测试。实际上,您无法对此进行测试,因为使用该对象会导致未定义的行为。它可能有用,或者它可能没有(在你的情况下,它可能会打印“i = 20 j = 10”,但我已经被销毁了。)