Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the constructor for a base class needs to be called in the initializer list of the derived class?

In C++, suppose I have a class Person with following constructor -

Person::Person(const string& nm, const string& id)
{
   name = nm;
   idNum = id; 
}

Now, I also have a subclass of Person called Student with extra data members major and gradYear. Is it necessary for constructor of the Student class to call the constructor for base class Person in the initializer list, if yes, why?

Student::Student(const string& nm, const string& id, const string& maj, int year)
: Person(nm, id){
   major =maj;
   gradYear =year;
}

Can not I define the constructor of Student like this -

Student::Student(const string& nm, const string& id, const string& maj, int year)
{
   Person(nm, id);
   major =maj;
   gradYear =year;
}
like image 979
SegFault Avatar asked Dec 07 '22 18:12

SegFault


2 Answers

Is it necessary for constructor of the Student class to call the constructor for base class Person in the initializer list, if yes, why?

Base class constructors are always called before derived class constructors. If you do not call them explicitly in the initialization list, default constructors are called implicitly. If a base class has no default constructor, this doesn't work, so you have to explicitly specify a base class constructor to be called.

It is necessary to invoke base class constructors before the constructors of derived classes because you can access the base class sub-objects from the constructors of derived classes. You can call their public and protected member functions, and for that to succeed, their data members certainly have to be initialized - which is just what base class constructors do.


Can not I define the constructor of Student like this -

Student::Student(const string& nm, const string& id, const string& maj, int year)
{
   Person(nm, id);
   major =maj;
   gradYear =year;
}

No, you can't. Base class constructors are called from the initialization list, before the body of the derived class' constructor is executed. That's just the way the language is defined.
Your syntax creates an unnamed temporary that is immediately discarded.


Note that it is considered good style (and, for some types, also a technical necessity) to also initialize data members in the initialization list.

A very good reason for this is that, in C++, objects have value semantics, they are not references. That is, a variable refers to an actual object, not a reference to an object, and assigning between variables changes actual objects' contents, rather than making them refer to other objects.

Consider this type:

class Student {
  public:
    Student(const std::string& n)
    {
      name = n; // this is bad!
    }
    // ...
  private:
    std::string name;
    // ...
};

That assignment name = n does not assign references to strings, it actually assigns the strings, that is, it invokes the assignment operator which then copies the characters! In order to invoke names's assignment operator, name certainly has to be properly constructed. Therefore, a call to the constructor is silently inserted by the compiler, so that the constructor looks like this:

Student(const std::string& n)
  : name() // therefore 
{
  name = n; // this is bad!
}

Now, this will first create an empty string, just to immediately override it in the very next statement. That's stupid. Therefore, you better initialize all your data members (along with your base classes) in the initialization list:

Student(const std::string& n)
  : name(n) // good!
{
}
like image 159
sbi Avatar answered Jan 04 '23 05:01

sbi


Because you cannot call constructors. Construct initialiser lists are special in several ways, and giving you a means to access the base ctor is one of them. Also, you should always initialise fields in the init list.

like image 36
Cat Plus Plus Avatar answered Jan 04 '23 04:01

Cat Plus Plus