Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this correct: virtual method of Derived called before constructing Base object?

I know that within constructor of Base class - when calling virtual method - the Base method is called, not derived - see Calling virtual functions inside constructors.

My question is related to this topic. I've just wondered what happens if I call virtual method in Derived class constructor - but before constructing Base part. I mean calling virtual method to evaluate Base class constructor argument, See code:

class Base {
public:
  Base(const char* name) : name(name) {
    cout << "Base():" << name << endl;
  }
  virtual const char* getName() { 
    cout << "Base::getName()" << endl;
    return "Base";
  }
protected:
  const char* name;
};

class Derived : public Base {
public:
  Derived() : Base(getName()) {
    cout << "Derived():" << name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return "Derived";
  }
};

int main() {
  Derived d;
}

Compiler g++ (4.3.x-4.5x versions) output is:

Derived::getName()
Base():Derived
Derived():Derived 

However I'd expect:

Base::getName()
Base():Base
Derived():Base

This does not look wrong - but consider this example, which produces segmentation fault:

class Derived : public Base {
public:
  Derived() : Base(getName()), name(new string("Derived")) {
    cout << "Derived():" << Base::name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return name->c_str();
  }
private:
  string* name;
};

Please answer: Is this correct g++ behavior? What C++ standard says about that? Maybe it is undefined behavior?

[UPDATE1] I take into consideration Robert and Oli answers - and I changed my first example. Then it getName() is called "virtual" - and it produces Segmentation Fault. Please answer my question to this part too.

const char* virtualGetName(Base* basePtr)
{
  return basePtr->getName();
}

class Derived : public Base {
public:
  Derived() : Base(virtualGetName(this)) {
    cout << "Derived():" << Base::name << endl;
  }
  virtual const char* getName() { 
    cout << "Derived::getName()" << endl;
    return "Derived";
  }
};
like image 396
PiotrNycz Avatar asked Dec 16 '22 22:12

PiotrNycz


1 Answers

All of your examples exhibit undefined behavior. The C++ language standard states (C++11 §12.6.2/13):

Member functions (including virtual member functions) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator or of a dynamic_cast.

However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined.

You are calling the member function getName() from the initialization list (the ctor-initializer) of the Derived class constructor. This member function call must take place before the initializer for Base completes (the mem-initializer for Base) because it is an argument to the initializer itself.

Therefore, the behavior is undefined.

As a rule, Never Call Virtual Functions during Construction or Destruction.

like image 55
James McNellis Avatar answered Mar 09 '23 01:03

James McNellis