Hemant Bhargava
Hemant Bhargava

Reputation: 3575

Base constructors inherited to derived class?

I have always thought that base class constructors/destructors/friends are not inherited by derived class. This link confirms this : http://www.geeksforgeeks.org/g-fact-4/.

I was also aware that we can write base class constructors in the initialization list of derived class initializer list.

Having said that: I tried to check my skills today about it. But I failed to guess the output of this program.

#include<iostream>

class A {
  int x, y;
  public:
    A(int a = 0, int b = 0) : x(a), y(b) {
      std::cout << "A ctor called" << std::endl;
    }
    void print_A() {
      std::cout << "x = " << x << std::endl;
      std::cout << "y = " << y << std::endl;
    }
};

class B : public A {
  int z;
  public:
    // I knew that A member can be initilized like this.
    B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {
      std::cout << "C ctor called" << std::endl;
      // I was not aware about that. 
      A(b, c);
    }
    void print_B() {
      std::cout << "z = " << z << std::endl;
    }
};

int main() {
  B b(1, 2, 3);
  b.print_A();
  b.print_B();
}

Output :

A ctor called
C ctor called
A ctor called
x = 2
y = 3
z = 1

Couple of questions:

Upvotes: 0

Views: 423

Answers (4)

Useless
Useless

Reputation: 67713

If constructors/desctructors/friends are not inherited from base, how can class 'B' is able to access constructor of class 'A' here.

You mean on these lines?

  // I was not aware about that. 
  A(b, c);

It isn't "accessing the constructor" in the sense you think. It just creates (and immediately discards) an anonymous temporary A in the body of the constructor function.

How come you get this output? How come two constructors of 'A' have been called.

Because you created two instances of A: the base-class subobject of B b, and the anonymous temporary.

Here's an easy experiment to verify this, with the following logging:

// in A
A(int a = 0, int b = 0) : x(a), y(b) {
  std::cout << "A::A @" << static_cast<void*>(this) << std::endl;
}
~A() {
  std::cout << "A::~A @" << static_cast<void*>(this) << std::endl;
}

// in B
B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {
  std::cout << "B::B @" << static_cast<void*>(this) << std::endl;
  A(b, c);
}
~B() {
    std::cout << "B::~B @" << static_cast<void*>(this) << std::endl;
}

I get output something like

A::A @0xffec7054
B::B @0xffec7054
A::A @0xffec704c
A::~A @0xffec704c
x = 2
y = 3
z = 1
B::~B @0xffec7054
A::~A @0xffec7054

See that the instance address of the second A::A is different from the first (so it's a different object), and it's followed by an A::~A (because the anonymous temporary goes immediately out of scope).


Notes:

  1. If you could "call the constructor" as you suggested originally, it would look something like

    auto subobject = static_cast<A*>(this);
    new (subobject) A(b, c);
    

    and would be very wrong. The A subobject of b was completely constructed when it's own constructor completed, before the body of B's constructor starts. You can't just re-create a new object in the same space, what would happen to the old one?

    This may seem trivial, but for objects with dynamically-allocated resources, it would be a serious bug. It's not allowed.

  2. You wrote your initializer list : z(a), A(b, c), but should be aware the base-class subobject will be constructed before the derived-class members are initialized. That is, those two things will happen in the opposite order to what you wrote. It's not (necessarily) an error, but it's worth knowing.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

If constructors/desctructors/friends are not inherited from base, how can class 'B' is able to access constructor of class 'A' here?

"Not inherited" does not mean "inaccessible to the derived class". A derived class can certainly reference a base constructor. B's constructor does it twice:

  • The first access is in the initialization list
  • The second access is in the body of B's constructor; it creates a temporary object

Inheriting a constructor would mean that users of B would be able to access B(int, int), which they cannot do*.

It seems like a constructor call. Why does it create a temporary object?

Consider this method:

void foo(const A& a);

A common way is to call it like this:

A a(1, 2);
foo(a);

but C++ also lets you call it without creating A on a separate line:

foo(A(1, 2));

In this case C++ creates a temporary object, and passes foo a reference to it. When you write

A(1, 2)

C++ also creates a temporary object for you by calling its constructor.

How come two constructors of 'A' have been called.

The constructor is invoked twice; that is why you get the output.

* C++11's using mechanism allows you to achieve an effect very similar to constructor inheritance, provided that you follow specific rules.

Upvotes: 1

Adrian Bratu
Adrian Bratu

Reputation: 508

the constructor is called by the every time A(b, c) is called;

you are calling it on line

B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {

and you are calling it inside the b constructor

// I was not aware about that. 
      A(b, c);

the code works as intended

Upvotes: 0

user2100815
user2100815

Reputation:

Your understanding is faulty. This:

// I was not aware about that. 
  A(b, c);

doesnt initialise the A member of B, it (notionally at least) creates a temporary, nameless local variable in the body of the constructor, somewhat analogous to if you had said:

  A a(b, c);

The constructor for A is a public member, so anything can call it.

Upvotes: 3

Related Questions