在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;
}
答案 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的构造函数,如果是,为什么?
仅在基类不能默认构造时才需要。构造函数初始化列表是可以构造基类的唯一位置。即使你不能这样做,你为什么要这样做? Student
是Person
,因此构建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;
}