Eutherpy
Eutherpy

Reputation: 4581

Explain order of constructor/destructor calls in this C++ code

I have this C++ code:

class BaseClass {
    int id;
public:
    BaseClass() { printf("BaseClass()\n"); }
    virtual ~BaseClass() { printf("~BaseClass()\n"); }
};

class Class1 : public BaseClass
{
    int id;
public:
    Class1() { printf("Class1()\n"); }
    ~Class1() { printf("~Class1()\n"); }
};

class Class2 : public Class1
{
    BaseClass id;
public:
    Class2() { printf("Class2()\n"); }
    ~Class2() { printf("~Class2()\n"); }
};

class Class3 : virtual public BaseClass
{
    int id;
public:
    Class3() { printf("Class3()\n"); }
    ~Class3() { printf("~Class3()\n"); }
};

class Class4 : public Class3, virtual public Class1
{
    Class3 id;
public:
    Class4() { printf("Class4()\n"); }
    ~Class4() { printf("~Class4()\n"); }
};

int main(int argc, char* argv[])
{
    BaseClass *p = new Class2;
    Class2 *p1 = new Class2;
    Class3 *p2 = new Class3;
    delete p;
    delete p1;
    delete p2;
    return 0;
}

This is the output:

BaseClass()
Class1()
BaseClass()
Class2()
BaseClass()
Class1()
BaseClass()
Class2()
BaseClass()
Class3()
~Class2()
~BaseClass()
~Class1()
~BaseClass()
~Class2()
~BaseClass()
~Class1()
~BaseClass()
~Class3()
~BaseClass()

and I don't understand why. I would expect that the output is the following:

BaseClass()
Class1()
Class2()
BaseClass()
Class1()
Class2()
...

etc.

Why isn't Class2() printed after Class1() when creating, for example, p1? Does this have something to do with virtual inheritance?

Upvotes: 2

Views: 81

Answers (2)

songyuanyao
songyuanyao

Reputation: 173044

Why isn't Class2() printed after Class1() when creating, for example, p1?

Because Class2 has a non-static member object with type BaseClass, its ctor will be called before the ctor of Class2.

According to the initialization order:

Initialization order

1) If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)

2) Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list

3) Then, non-static data members are initialized in order of declaration in the class definition.

4) Finally, the body of the constructor is executed

For new Class2;, the direct base class Class1 and its base class BaseClass will be invoked firstly. And then the non-static data member id with type BaseClass will be invoked. And the body of ctor of Class2 will be invoked at last. So you'll get

BaseClass()
Class1()
BaseClass()
Class2()

Upvotes: 3

Sam Varshavchik
Sam Varshavchik

Reputation: 118445

Let's go step by step with the first object being constructed:

new Class2;

This is the first object you're contructing, let's called it p.

BaseClass()

p's BaseClass.

Class1()

p's Class1, the subclass of BaseClass gets constructed.

BaseClass()

This is the id member of Class2 being constructed.

Class2()

And now, finally, Class2, p itself.

So, despite your belief otherwise, Class2() is printed after Class1(). Except that you forgot that Class2 also has an id member, that's a BaseClass, and which must also be constructed before the Class2::Class2() constructor gets invoked. You believed that you were seeing the second newed object's BaseClass being constructed, but what was really getting constructed was the BaseClass member object.

P.S. This is C++. In C++ we use std::cout instead of printf(). printf() is so ...last century.

Upvotes: 2

Related Questions