1
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");}
}

So here's how I see it. Inside the try block, nothing is being printed while constructing B b;. The block ends. I think compiler is destructing the A a; member first. So A.f() would be printed. Does that mean the destruction of class B instance is finished? After that, would compiler simply call ~A() (destructing base class)?

I thought I should've got A.f(), then B.f() (destructing class B instance) and after that A.f() again (destructor of base class). Compiling this made me think a little. Exc is being printed at the end of course. I've gone through several topic and haven't found anything.

EDIT: Output from Dev-C++ (GCC 3.4.2) is

A.f A.f Exc

iammilind
  • 68,093
  • 33
  • 169
  • 336
macolour
  • 13
  • 3

5 Answers5

10

You really have two A objects here.

  1. B inherits from A, so a base class object of A is instantiated first before B is.
  2. Another A instance is created as you have a member field of type A as part of B.

When you create B b, you create the base class A, and also the instance A a.

However, you then throw the exception in B's constructor, so then all fully-constructed objects at that point are destructed, that is.

  • ~A() is called on the instance A a.
  • ~A() is called on the base class A.

That would explain why you get A.f A.f Exc.

B's destructor would not be called because B wasn't fully constructed as its constructor did not finish successfully.

wkl
  • 77,184
  • 16
  • 165
  • 176
  • +1 Nice answer, short and to the point. The whole virtual thing was a red herring. – Kerrek SB Sep 07 '11 at 12:33
  • @Tomalak - Sorry, I was typing this up prior to the edits. Yours is far more complete with the C++ std references though. – wkl Sep 07 '11 at 12:35
  • Thanks all of you, I didn't know that throw is terminating the construction. – macolour Sep 07 '11 at 12:36
  • 1
    @Tomalak: No dispute that you got the right answer, but sometimes the short, direct answer is more illuminating. Just saying "It's because you destroy the member object and the subobject" might be easier to grasp than the implications of a quote from the standard. – Kerrek SB Sep 07 '11 at 12:36
  • @Kerrek, you are right. Personally, I always admire simple English answer above the standard quote. Though standard is needed sometimes as Tomalak has pointed out. – iammilind Sep 07 '11 at 12:38
  • @Kerrek: There are fewer words in the core section of my answer than in this one. I'm not going to debate this question any more. – Lightness Races in Orbit Sep 07 '11 at 12:38
  • 1
    You have the destruction order the wrong way around. The member is destroyed first then the base class. – Martin York Sep 07 '11 at 12:51
4

You haven't shown us the output you get, just a wall of ranty text, so it's hard to know what you're asking.

However, for the record, the output of your code is:

A.f A.f Exc


Why?

  • Constructing b fails.
  • b's B destructor is not invoked, but destructors for its members are1.
  • It has a member of type A, whose destructor calls the function f().
  • There is also a fully-constructed A base of b; so, b's A destructor is also invoked, calling A::f() as before.
  • Exc is of course output by the surrounding exception handler.

Is this what you wanted to know?


1:

[n3290: 15.2/2]: An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. [..]

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 4
    You may want to delete this answer. B's destructor is not invoked. – Cheers and hth. - Alf Sep 07 '11 at 12:23
  • @Alf: At least remove your comment, then! – Lightness Races in Orbit Sep 07 '11 at 12:35
  • A petty nitpick: Your second point is sort of an ontological error: "`b`'s destructor" doesn't even exist, *because there is no object `b`*. Since the constructor didn't complete, there's no object, so there's no object to destroy in the first place. That's why the virtual function is also a red herring. – Kerrek SB Sep 07 '11 at 12:40
  • @Kerrek: Heh, arguably. Except `b` **does** exist, and it has a runtime type of `A`. But it does still have a static type of [unconstructed] `B`, so I think it still does "have" a destructor. This sort of stuff isn't really defined though. – Lightness Races in Orbit Sep 07 '11 at 12:42
4

The order should be: A.f, A.f, Exc

When B's constructor is invoked, before entering, first A's constructor is invoked due to inheritance. Next, before entering B's constructor (i.e. before {), a is default constructed.

B's construction would be complete only if it reaches matching }. But before that you have a throw statement. So the partially constructed B has to be destroyed, which has one object a and the the inherited subobject A. So both these are destroyed, hence A.f and A.f

Next, you reach the throw block where 'Exc' is printed

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
mukeshkumar
  • 2,698
  • 3
  • 19
  • 20
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");}
}

Destructor of A is called twice, that's it.

Output:

A.f 0
A.f 0
Exc
tauran
  • 7,986
  • 6
  • 41
  • 48
0

You can't call virtual functions from constructor or destructor. They won't work as virtual but will be called as non-virtual functions.
You can read about it in FAQ here and in related theme about constructors here.

Community
  • 1
  • 1
Roman Kruglov
  • 3,375
  • 2
  • 40
  • 46
  • 1
    This has nothing to do with the problem at hand. It's a noteworthy statement that could be a comment, though. – Kerrek SB Sep 07 '11 at 12:42