继承与析构 - 理论问题 - C ++

时间:2011-09-07 12:15:14

标签: c++ exception inheritance destructor

class A
{
    public:
         virtual void f(){ printf("A.f "); }
         ~A(){ f(); }
};

class B : public A
{
    A a;

    public:
         void f(){ printf("B.f "); }
         B(){ throw -1; }
        ~B(){ f(); }
};

int main()
{
    try{ B b; }
    catch(...){ printf("Exc");}
}

所以这就是我看到它的方式。在try块中,构造B b;时没有任何内容被打印出来。块结束。我认为编译器首先破坏A a;成员。因此将打印A.f()。这是否意味着class B实例的破坏已经完成?之后,编译器会简单地调用~A()(破坏基类)吗?

我认为我应该得到A.f(),然后B.f()(破坏B类实例),然后再次A.f()(基类的析构函数)。编译这个让我想一点。 当然,正在打印Exc。 我已经完成了几个主题,但没有找到任何内容。

编辑:Dev-C ++(GCC 3.4.2)的输出

  

A.f A.f Exc

5 个答案:

答案 0 :(得分:10)

这里确实有两个A个对象。

  1. B继承自A,因此{B}之前首先实例化A的基类对象。
  2. 创建另一个A实例,因为A的成员字段属于B
  3. 创建B b时,您可以创建基类A,还可以创建实例A a

    但是,然后在B的构造函数中抛出异常,那么那个点上的所有完全构造的对象都被破坏了,即。

    • ~A()在实例A a上调用。
    • 在基类~A()上调用
    • A

    这可以解释为什么你得到A.f A.f Exc

    B的析构函数不会被调用,因为B没有完全构造,因为它的构造函数没有成功完成。

答案 1 :(得分:4)

你没有向我们展示你得到的输出,只是一大堆文字,所以你很难知道你在问什么。

然而,为了记录,the output of your code is

  

A.f A.f Exc


为什么?

  • 构建b失败。
  • b的{​​{1}}析构函数未被调用,但其成员的析构函数是 1
  • 它有B类型的成员,其析构函数调用函数A
  • f()还有一个完整构建的A基础;所以,b的{​​{1}}析构函数也被调用,像以前一样调用b
  • A当然是由周围的异常处理程序输出的。

这是你想知道的吗?


1

  

A::f()任何存储持续时间的对象   初始化或销毁由异常终止   为所有完全构造的子对象执行的析构函数   (不包括类似联合类的变体成员),即for   主要构造函数(12.6.2)已完成的子对象   执行和析构函数尚未开始执行。 [..]

答案 2 :(得分:4)

订单应该是: A.f,A.f,Exc

当调用B的构造函数时,在进入之前,由于继承而调用第一个A的构造函数。接下来,在进入B的构造函数之前(即在{之前),a是默认构造的。

B的构造只有在达到匹配}时才会完成。但在此之前你有一个抛出声明。所以部分构造的B必须被销毁,它有一个对象a和继承的子对象A.所以这两个都被破坏了,因此A.f和A.f

接下来,您到达打印'Exc'的投掷区

答案 3 :(得分:0)

#include <stdio.h>

class A
{
    public:
         virtual void f(int i){ printf("A.f %i\n", i); }
         ~A(){ f(0); }
};

class B : public A
{
    A a;

    public:
         void f(int i){ printf("B.f %i\n", i); }
         B(){ throw -1; }
         ~B(){ f(1); }
};

int main()
{
    try{ B b; }
    catch(...){ printf("Exc\n");}
}

A的析构函数被调用两次,就是这样。

输出:

A.f 0
A.f 0
Exc

答案 4 :(得分:0)

您无法从构造函数或析构函数中调用虚函数。它们不会作为虚拟工作,但会被称为非虚函数。
您可以在常见问题解答here和有关构造函数here的相关主题中阅读相关内容。