为什么需要在派生类的初始化列表中调用基类的构造函数?

时间:2011-10-18 16:02:47

标签: c++ constructor

在C ++中,假设我有一个带有以下构造函数的 Person 类 -

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

现在,我还有一个的子类,名为学生,其中包含额外的数据成员主要 gradYear 。是否有必要让Student类的构造函数在初始化列表中调用基类Person的构造函数,如果是,为什么?

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

我不能像这样定义学生的构造函数 -

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

5 个答案:

答案 0 :(得分:9)

  

Student类的构造函数是否需要在初始化列表中调用基类Person的构造函数,如果是,为什么?

基类构造函数总是在派生类构造函数之前调用。 如果未在初始化列表中显式调用它们,则默认构造函数称为 隐式 即可。如果基类没有默认构造函数,则这不起作用,因此您必须显式指定要调用的基类构造函数。

必须在派生类 的构造函数之前调用基类构造函数,因为您可以从派生类的构造函数访问基类子对象。您可以调用它们的公共和受保护的成员函数,为了成功,它们的数据成员当然必须被初始化 - 这正是基类构造函数所做的。


  

我不能像这样定义学生的构造函数 -

Student::Student(const string& nm, const string& id, const string& maj, int year)
{
   Person(nm, id);
   major =maj;
   gradYear =year;
}
不,你不能。 在执行派生类的构造函数体之前,从初始化列表 调用基类构造函数。这就是语言定义的方式 您的语法会创建一个立即丢弃的未命名临时文件。


请注意,对于 initialize data members in the initialization list ,它被认为是好的风格(对于某些类型,也是技术上的必需品)。

一个很好的理由是,在C ++中,对象具有 值语义 ,它们不是引用。也就是说,变量是指实际对象,而不是对对象的引用,并且变量之间的分配会更改实际对象的内容,而不是使它们引用其他对象。

考虑这种类型:

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

该赋值name = n不分配对字符串的引用,它实际上分配了字符串,也就是说,它调用赋值运算符然后复制字符!为了调用names的赋值运算符,name当然必须正确构造。因此,编译器会静默插入对构造函数的调用,因此构造函数如下所示:

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

现在,这将首先创建一个空字符串,只是为了在下一个语句中立即覆盖它。那太愚蠢了。因此,您最好在初始化列表中初始化所有数据成员(以及基类):

Student(const std::string& n)
  : name(n) // good!
{
}

答案 1 :(得分:2)

因为你不能调用构造函数。构造初始化列表在几个方面是特殊的,并且为您提供访问基本ctor的方法就是其中之一。此外,您应该始终初始化init列表中的字段。

答案 2 :(得分:1)

  

Student类的构造函数是否需要在初始化列表中调用基类Person的构造函数,如果是,为什么?

仅在基类不能默认构造时才需要。构造函数初始化列表是可以构造基类的唯一位置。即使你不能这样做,你为什么要这样做? StudentPerson,因此构建Student是一项包含构建Person的操作才有意义。

  

我无法定义学生的构造函数llike this -

     

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

你尝试过吗?不,你不能像这样调用基础构造函数。如果有的话,那就是构建一个临时的未使用的Person,而不是基类。

答案 3 :(得分:1)

Student类的构造函数是否需要在初始化列表中调用基类Person的构造函数,如果是,为什么?

需要在派生类的构造函数之前调用基类的构造函数,因此需要在派生的初始化列表中调用它,否则编译器将在进入派生类体之前调用基类的默认构造函数

我不能像这样定义学生的构造函数 -

不,你不能。

第二种方法将创建一个Person的新临时本地对象,而不是为正在创建的对象调用Base类构造函数。

答案 4 :(得分:1)

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

简而言之,是的。派生类构造函数将始终首先尝试调用基类构造函数。如果你没有特别告诉它调用你的重写构造函数,它将尝试调用默认构造函数(没有params)。在你的情况下,这将失败,因为你没有定义它..

Can not I define the constructor of Student like.....

不,因为您无法在类似的代码中调用构造函数。此外,即使你可以,你的代码也会失败,因为编译器会因上述原因寻找默认构造函数。如果你真的想(并且我想不出你为什么会这么做),你可以定义一个默认的构造函数Person::Person(),然后这个:

Student::Student(const string& nm, const string& id, const string& maj, int year) 
{    
    name = nm;     
    idNum = id;
    major =maj;    
    gradYear =year; 
} 
相关问题