简体   繁体   中英

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:

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

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

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.

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.

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM