Search code examples
c++inheritanceconstructorinteger

Not sure whether the built-in type(like int) data member in base class would be default intialized?


Thanks for reading my questions.

I am doing a test regarding to the behavior of the default constructor of a derived class.

For example, here is my structure:

class Base1 {
public:
    
    int a1;
    int b1;
    virtual void display() const {
        cout << "Class Base1" << endl;
    }
};


class Derived : public Base1{
public:     
    int c;
    int d;
    Derived() :Base1(){
         
    }
};

As you can see, I did not explicit define constructor for the base1 class, so I was expecting that the a1 and a2 would not be initialized.

Base1 b1; 
Derived d; 
Base1& rb1=d; 
cout<<b1.a1<<endl; 
cout<<rb1.a1<<endl; 

output:

 b1.a1  ->   -858993460
 rb1.a1  ->  0

I am not sure why the int member a1 is intialized when I call the derived class's default constructor.

What's more, if I change the delegate method of the derived class's contructor:

class Derived : public Base1{
public:
    using Base2::Base2; //use 'using' to generate the derived class constructor
    using Base1::Base1; 
    
    int c;
    int d;
    
};

I run the code again:

Base1 b1; 
Derived d; 
Base1& rb1=d; 
cout<<b1.a1<<endl; 
cout<<rb1.a1<<endl;

The output changed to:

   b1.a1 ->  -858993460
   rb1.a1 -> -858993460

Anything special about this format Derived() :Base1() ?

Thanks for your comments in advance!


Solution

  • There are certain types that different behavior when value-initialized (i.e. when () or {} is involved) and default-initialized (no initializer is provided, and neither () nor {} were used).

    Scalar types are like that (integral, floating-point, enums, pointers, pointers-to-members). Value-initializing zeroes them, while default-initializing leaves them uninitialized.

    Some classes are like that. When the default constructor is either implicitly generated or explicitly =defaulted on the first declaration, the choice between those two initialization methods propagates to all members that would otherwise be uninitialized. (Apparently inherited default constructors are allowed here too, I'm not sure if inheriting one has any effect in the first place.)


    In your first snippet, Base1 matches this condition, therefore:

    • Base1 x; leaves a1,b1 uninitialized.
    • Base1 x{}; zeroes a1,b1.

    Derived doesn't match the condition, and it's base Base1 is value-initialized by the Derived constructor (: Base1()), so:

    • Derived x; and Derived x{} are the same, they zero a1,b1 and leave c,d uninitialized.

    In the second snippet, Derived does match this condition, so:

    • Derived x; leaves all members uninitialized.
    • Derived x{} zeroes all members.