174

Why can't I do this?

class A
{
public:
    int a, b;
};

class B : public A
{
    B() : A(), a(0), b(0)
    {
    }

};
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
amrhassan
  • 2,285
  • 3
  • 16
  • 12
  • 10
    Are you asking *why* you can't do that, which is a language-design question, or are you asking *how* to work around that language limitation? – Rob Kennedy Sep 13 '11 at 17:14
  • I thought that there was some sort of a special way to do it that i'm not aware of, without having to use the base constructor. – amrhassan Sep 13 '11 at 20:48
  • 2
    The base class members are already initialized by the time your derived-class constructor gets to run. You can *assign* them, if you have access, or call setters for them, or you can supply values for them to the base class constructor, if there is one suitable. The one thing you *cannot* do in the devised class is initialize them. – user207421 Aug 23 '17 at 00:25

7 Answers7

194

You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:

class A 
{
protected:
    A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
    // Change "protected" to "public" to allow others to instantiate A.
private:
    int a, b; // Keep these variables private in A
};

class B : public A 
{
public:
    B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
    {
    } 
};
In silico
  • 51,091
  • 10
  • 150
  • 143
  • 46
    while your example is correct, your explanation is misleading. It's not that you can't *initialize* `a` and `b` in `B::B()` because they are private. You can't initialize them because they are not members of `class B`. If you made them public or protected you could *assign* them in the body of `B::B()`. – R Samuel Klatchko Sep 13 '11 at 17:22
  • 3
    additionally, you solution makes class A non-aggregate, which might be important, so it must be mentioned. – Gene Bushuyev Sep 13 '11 at 17:28
  • 1
    @R Samuel Klatchko: Good point. When I was writing the answer I initially typed "You can't access `a` and `b`..." and changed it to "You can't initialize..." without making sure the rest of the sentence made sense. Post edited. – In silico Sep 13 '11 at 17:30
  • 1
    @Gene Bushuyev: The class in the original code in the question is not an *aggregate* (there are non-static private members) – David Rodríguez - dribeas Sep 13 '11 at 18:04
  • @David -- correct, which is a user's error, and I'm trying to get to the user's intentions, skipping superficial. – Gene Bushuyev Sep 13 '11 at 18:20
  • Additionally: B(int a, int b) : A(a,b) { ; } – Jess May 03 '13 at 01:07
  • " You can make them public, then do assignment in B" this is confusing, can you elaborate what you mean? – zar Feb 18 '16 at 16:44
  • @zar Have a look at [Violet Giraffe's answer](https://stackoverflow.com/a/52557527/3982001) – Fabio says Reinstate Monica Sep 10 '21 at 14:59
31

Leaving aside the fact that they are private, since a and b are members of A, they are meant to be initialized by A's constructors, not by some other class's constructors (derived or not).

Try:

class A
{
    int a, b;

protected: // or public:
    A(int a, int b): a(a), b(b) {}
};

class B : public A
{
    B() : A(0, 0) {}
};
bruno
  • 32,421
  • 7
  • 25
  • 37
NPE
  • 486,780
  • 108
  • 951
  • 1,012
19

Somehow, no one listed the simplest way:

class A
{
public:
    int a, b;
};

class B : public A
{
    B()
    {
        a = 0;
        b = 0;
    }

};

You can't access base members in the initializer list, but the constructor itself, just as any other member method, may access public and protected members of the base class.

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • 1
    Nice. Is there any drawback in doing this way? – Wander3r Jan 09 '19 at 15:29
  • 2
    @SaileshD: there may be, if you're initializing an object with a costly constructor. It will first be default-initialized when the instance of `B` is allocated, then it will be assigned inside the `B`'s constructor. But I also think the compiler can still optimize this. – Violet Giraffe Jan 10 '19 at 11:36
  • 3
    Inside `class A` we can not rely on `a` and `b` being initialized. Any implementation of `class C : public A`, for example, might forget to call `a=0;` and leave `a` uninitialized. – Sparkofska Dec 02 '19 at 07:49
  • @Sparkofska, very true. It is best to default-initialize the fields either in-place when declaring them (`class A { int a = 0;};`), or in the constructor of the base class. The subclasses can still re-initialize them in their constructor as needed. – Violet Giraffe Dec 02 '19 at 11:15
  • 1
    @Wander3r Another drawback is that not all classes have assignment operators. Some can only be constructed, but not assigned to. Then you're done... – Martin Pecka Jun 03 '20 at 16:09
  • 1
    Of course **this is not initialization**. "The subclasses can still re-initialize them" is just nonsense, C++ has no such operation as re-initialization. – Ben Voigt Dec 08 '21 at 17:31
  • @BenVoigt, feel free to suggest your own wording. Whatever you want to call it, it works and does the job well. Inside the constructor it's assignment. From outside the constructor, however, it's seen as initialization, because it results in the class members being initialized to the desired values. – Violet Giraffe Dec 08 '21 at 18:36
  • 1
    One possible bad failure is if your a or b are references, not regular variables, and you want to initialize them to point at some variables. This can only be done in the initializer list. But in general, simple situation this indeed seems optimal. – SF. Mar 16 '23 at 17:06
3
# include<stdio.h>
# include<iostream>
# include<conio.h>

using namespace std;

class Base{
    public:
        Base(int i, float f, double d): i(i), f(f), d(d)
        {
        }
    virtual void Show()=0;
    protected:
        int i;
        float f;
        double d;
};


class Derived: public Base{
    public:
        Derived(int i, float f, double d): Base( i, f, d)
        {
        }
        void Show()
        {
            cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
        }
};

int main(){
    Base * b = new Derived(10, 1.2, 3.89);
    b->Show();
    return 0;
}

It's a working example in case you want to initialize the Base class data members present in the Derived class object, whereas you want to push these values interfacing via Derived class constructor call.

Pang
  • 9,564
  • 146
  • 81
  • 122
3

Why can't you do it? Because the language doesn't allow you to initializa a base class' members in the derived class' initializer list.

How can you get this done? Like this:

class A
{
public:
    A(int a, int b) : a_(a), b_(b) {};
    int a_, b_;
};

class B : public A
{
public:
    B() : A(0,0) 
    {
    }
};
John Dibling
  • 99,718
  • 31
  • 186
  • 324
1

While this is usefull in rare cases (if that was not the case, the language would've allowed it directly), take a look at the Base from Member idiom. It's not a code free solution, you'd have to add an extra layer of inheritance, but it gets the job done. To avoid boilerplate code you could use boost's implementation

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
-1

Aggregate classes, like A in your example(*), must have their members public, and have no user-defined constructors. They are intialized with initializer list, e.g. A a {0,0}; or in your case B() : A({0,0}){}. The members of base aggregate class cannot be individually initialized in the constructor of the derived class.

(*) To be precise, as it was correctly mentioned, original class A is not an aggregate due to private non-static members

Gene Bushuyev
  • 5,512
  • 20
  • 19