2

I have the following C++ code:

#include <iostream>
#include <string>
using namespace std;

class Surface
{
public:
    virtual void draw();
protected:
    int x,y;
};

class Control: public Surface
{
public:
    Control(): x(10), y(10), name("control") { cout << "Control constructor" << endl; }
    void draw() {}
protected:
    string name;
};

class Label: public Control
{
public:
    Label(): x(10), y(10), name("label"), text("label") { cout << "Label constructor" << endl; }
    void draw() { cout << "drawing a label" << endl; }

protected:
    string text;
};

int main(int argc, const char *argv[])
{
    Label l;
    return 0;
}

When trying to compile, i get the following errors:

$ g++ main.cpp
main.cpp: In constructor 'Control::Control()':
main.cpp:16:16: error: class 'Control' does not have any field named 'x'
main.cpp:16:23: error: class 'Control' does not have any field named 'y'
main.cpp: In constructor 'Label::Label()':
main.cpp:25:14: error: class 'Label' does not have any field named 'x'
main.cpp:25:21: error: class 'Label' does not have any field named 'y'
main.cpp:25:28: error: class 'Label' does not have any field named 'name'

I don't understand why aren't Control and Label inheriting Surfaces properties ?

Mihai Rotaru
  • 1,953
  • 3
  • 26
  • 28

3 Answers3

12

The inherited members cannot appear in the member-initialization-list. Just think, how can they appear in the derived class's member-initialization-list, because by the time it will be executed, they (i.e base class members) are already created (recall that base subobject gets created before the derived class constructor and member-initialization-list).

If it was allowed, then that would mean the base class members would be allowed to be initialized more than once, which doesn't make sense. In C++, objects don't get initialized1 more than once. Initialization happens only once; assignment can happen many times.

The proper way to write that code is to parameterize the base class constructor, and pass values of x and y as argument to the base class constructor.

1. I mean dynamic initialization happens only once. However, an object can be initialized twice : once at compile time called static initialization, then other at runtime called dynamic initialization. For more, see this : What is dynamic initialization of object in c++?

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
1

Your constructor could be rewritten as (just a pseudocode to illustrate the point, this wouldn't compile):

Control(): Surface::x(10), Surface::y(10), name("control") { 
cout << "Control constructor" << endl; 
}

So you are going to initialize variables, which are (or may be) already initialized in you parent class. You can't initialize a variable twice. However, you can assing new values to x and y:

Control(): name("control") { 
cout << "Control constructor" << endl; 
/*Surface::*/x = 10;
/*Surface::*/y = 10;
}

Or you can make a constructor Surface(int x, int y) and pass your x and y there:

Control(): Surface(/*x=*/10, /*y=*/10), name("control") { 
cout << "Control constructor" << endl; 
}
Steed
  • 1,292
  • 1
  • 14
  • 33
  • You make your "rewrite" of the constructor look like valid code - it's not (and that's probably why you got downvoted). It's more of an interpretation. – Anthales Apr 22 '12 at 13:29
  • 1
    I've got a '-1' with the reason 'subclass does not inherit grandparent properties'. Are you kidding?;) – Steed Apr 22 '12 at 13:31
  • @Anthales, you are right that the last constructor didn't compile. I'd put name(...) before Surface(...). I've corrected this in the post, thanks. However, everything else is (and was) fine. Ah, now I get your point. Yes, the first rewrite is pseudocode. Sorry for being not clear enough. – Steed Apr 22 '12 at 13:38
1

Your problem would cease to exist if you defined a proper parametrized constructor for your base classes to take care of initializing their own fields. Leaving it to subclasses is bad design. So your code could be rewritten as:

class Surface
{
public:
    Surface(int x_, int y_): x(x_), y(y_) { cout << "Surface constructor" << endl; }
    virtual void draw();
protected:
    int x,y;
};

class Control: public Surface
{
public:
    Control(int x_ = 10, int y_ = 10, string name_ = "control")
        : Surface(10, 10), name(name_) { cout << "Control constructor" << endl; }
    void draw() {}
protected:
    string name;
};

class Label: public Control
{
public:
    Label(): Control(10, 10, "label"), text("label") { cout << "Label constructor" << endl; }
    void draw() { cout << "drawing a label" << endl; }

protected:
    string text;
};
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • Thanks for taking the time to write the code; however, I get an error on `Control(): Control(10, 10, "control") {}`: "type 'Control' is not a direct base of 'Control'". That error goes away with `Control(): Surface( 10, 10 ), name("control" ) {}` – Mihai Rotaru Apr 22 '12 at 14:09
  • however, I get anther one: "undefined reference to 'vtable for Surface'" ? – Mihai Rotaru Apr 22 '12 at 14:13
  • @MihaiRotaru, that was a naive attempt to chain constructors (my C++ is rusty)... As it turned out, [it is supported only in C++11](http://en.wikipedia.org/wiki/C%2B%2B0x#Object_construction_improvement). As a workaround, I replaced the default constructors with default parameter values in the parametrized constructor. – Péter Török Apr 22 '12 at 14:19
  • @MihaiRotaru, I can only guess about that one: have you provided a definition for `Surface::draw()`? Or did you intend to make it pure virtual, just forgot the `= 0`? (Btw you should add a virtual destructor too.) – Péter Török Apr 22 '12 at 14:25