2

I have a question about the way classes using inheritance are constructed. In the example below, the call to the Base constructor is dependent on a function implemented in the Derived class. This function is again dependent on the Derived member generator, which will not be initialized until after the Base constructor call.

If the Base class is constructed first, won't the variable Base::in_ contain garbage data?

class Derived
    : public Base
{

    Derived()
        : Base(get_data()),
          generator(5) {}

    Generator generator;

    int get_data() { return generator.get_some_data(); }
};

class Base
{
    Base(int in)
         : in_(in) {}

    int in_;

}
Q-bertsuit
  • 3,223
  • 6
  • 30
  • 55
  • Nice question. But the terms are awry. Can we replace polymorphism with inheritance? As for the answer, I think there's UB in here somewhere. – Bathsheba Jul 16 '14 at 14:05
  • 1
    If the return-value of function `get_some_data` depends on **non-static** member variables of class `Generator`, then the answer is yes. – barak manos Jul 16 '14 at 14:12
  • You'r right. I changed the wording from polymorphic to inheritance. Thanks – Q-bertsuit Jul 16 '14 at 14:32
  • @barakmanos: I would even says UB as calling a method from an uninitialized value. – Jarod42 Jul 16 '14 at 14:41
  • @Jarod42: `generator` is not a value. It's an object which contains values (AKA non-static member variables). – barak manos Jul 16 '14 at 14:42
  • @barakmanos: Which contains uninitialized values and/or objects. so depending of the method, it may be UB and not just garbage. – Jarod42 Jul 16 '14 at 14:49

3 Answers3

2

First, nothing in your code is polymorphic. Polymorphism is about virtual functions.

Next, your class Base depends on nothing. Look between class Base { and the matching }, there's nothing in there that depends on anything outside.

What happens is the construction of the Base subobject within Derived depends on another member of Derived, which is constructed after Base. Your analysis is basically correct, it's a real problem and it needs to be solved by refactoring your classes.

The easiest way is two-stage initialization of Base (the following is pseudocode and probably will not compile):

class Base {
    Base(int in = 0) : in_(in) {}
    void set(int in) { in_ = in; }
    int in_;
};

class Derived : public Base {    
    Derived() : Base(), generator(5) {
      Base::set(generator);
    }
    ...
};

Another method is moving the dependency to another base class:

class Base2 { ... generator implemented here ... };
class Derived : public Base2, public Base {
  Derived() : Base2(5), Base(Base2::generator) {}
};
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

I have no idea what the standard says, but this is clearly a matter of what is the value of generator when get_data() is called, as it would be if you call it from somewhere else then the constructor.

Code called from within a constructor is no way different to other code.
My guess is that get_data() is called before generator is constructed and thus you will get a NULL exception at runtime.

Another point to mention is that the base class is of course being constructed first. It is the base class. Derived IS A Base, it has a field in_.
As a rule of thumb it is always a good pattern to:

  1. Avoid method calls from the constructor. Use an additional init method for that.
  2. In any case beware of circular constructor calls.

You also could make get_data abstract in Base and then call it within the Init() of Base which would IMHO be the right way to implement a dependency to child classes as you call it.

class Derived
    : public Base
{

    Derived()
        : Base(),
          generator(5) {}

    Generator generator;

    virtual int get_data() { return generator.get_some_data(); }
};

class Base
{
    Base()
    {

    }

    Init() {
       in_ = get_data();}   
    int in_;

    virtual int get_data() = 0;
}
Martin Meeser
  • 2,784
  • 2
  • 28
  • 41
0

See Order of calling constructors/destructors in inheritance

The constructor for Base will be called before any members of Derived are initialized. So generator will not have been initialized when get_data() is called.

Basically, Base will be initialized with garbage.

Community
  • 1
  • 1
pzed
  • 817
  • 5
  • 8