C++: Initialization of inherited field
Asked Answered
P

5

23

I've a question about initialization of inherited members in constructor of derived class. Example code:

class A
    {
public:
    int m_int;
    };

class B: public A
    {
public:
    B():m_int(0){}
    };

This code gives me the following output:

In constructor 'B::B()': Line 10: error: class 'B' does not have any field named 'm_int'

(see http://codepad.org/tn1weFFP)

I'm guessing why this happens? m_int should be member of B, and parent class A should already be initialized when initialization of m_int in B happens (because parent constructors run before member initialization of inherited class). Where is a mistake in my reasoning? What is really happens in this code?

EDIT: I'm aware of other possibilities to initialize this member (base constructor or assignment in derived constructor), but I want to understand why is it illegal in the way I try it? Some specific C++ language feature or such? Please point me to a paragraph in C++ standard if possible.

Piazza answered 21/10, 2010 at 4:11 Comment(0)
R
22

You need to make a constructor for A (it can be protected so only B can call it) which initializes m_int just as you have, then you invoke :A(0) where you have :m_int(0)

You could also just set m_int = 0 in the body of B's constructor. It is accessible (as you describe) it's just not available in the special constructor syntax.

Recuperator answered 21/10, 2010 at 4:14 Comment(3)
This is a good answer, but I just would like to add some explanation. When you use the : operator, you are telling the compiler that before it does anything else it should run these instructions. Basically you are setting a variable before the parent constructor (or anything else) has run. Therefore the variable does not exist. :A(), m_int(0) should work as well. By default, if you do not use :, the compiler will run the base classes constructor. In other words, if you do nothing C++ will do default things for you, if you start specifying things, it assumes you know what you are doing.Krispin
Hmmm... As far as I know constructor of parent class is always run before any other initializations, and so the variable m_int already exists when I'm trying to initialize it. So this shouldn't be the issue...Piazza
@Piazza Yes, it already exists but that's why you're getting the error. It's already been default initialized by the constructor of A. You can't re-initialize a variable in B's constructor. You can reassign as Ben Jackson states above (m_int = 0) and that's about it at that point.Keto
C
5

In order to construct an instance of class B you first instantiate an instance of class A. During that instantiation m_int gets initialized. It's after that intialization that b's constructor is called, so you can't reinitialize m_int. If that's your goal then you can implement a constructor for A that takes an int and then call that in B's initialization list:

class A
{
public:
  A(int x): m_int(x) {}
  int m_int;
};

class B: public A
{
public:
  B(): A(2) {}
};
Cataphyll answered 21/10, 2010 at 4:17 Comment(2)
Is there any reason why it is not available in the initialization list? I'm aware of other possibilities to initialize this member (base constructor or assignment in derived constructor), but I want to understand why is it illegal in the way I try? Some specific C++ language feature or such? Please point me to a paragraph in C++ standard if possible.Piazza
It's two things - derived classes instantiate their base classes first, and members are initialized in the order they are declared, not the order they appear in the initialization list. Therefore the members of the base are initialized first and you can't reinitialize them.Cataphyll
K
4

What you want is this:

class A{
public:
    A() : m_int(0);
    int m_int;
};

so that m_int is initialized in the correct place.

Edit:

From a comment above, the reason the compiler complains when you try to initialize the m_int variable in B is that it's already been initialized by the constructor of A. That is, you can't re-initialize something, only reassign. So, you can reassign like Ben Jackson stated above or you can initialize in the proper place.

Keto answered 21/10, 2010 at 4:15 Comment(0)
B
0

make a constructor in A and use B(): A(2) {} insteed of B():m_int(0){} its working.

Balcom answered 5/5, 2017 at 9:53 Comment(0)
B
-2

Go with the first suggestion by Ben Jackson. The second one (i.e. set m_int = 0 in the body of the constructor) is OK as long as m_int is not a const. I believe this would not work if m_int were const (but the first suggestion would), which, unless you intend to make mutable, I would recommend.

So, do:

class A {
protected:
    A():m_int{0}{} // Move constructor to parent class
    
public:
    const int m_int;
    auto print() {std::cout << m_int << std::endl;} // For example
};

class B: virtual public A {
public:
    B():A(){}
};

Example:

auto x {B()};
x.print() // Prints 0
Beitz answered 29/11, 2022 at 14:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.