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

发布于 2024-12-11 01:15:26 字数 726 浏览 0 评论 0原文

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

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

现在,我还有一个名为 StudentPerson 子类,带有额外的数据成员<强>专业和毕业年份。 Student类的构造函数是否需要调用初始化列表中基类Person的构造函数,如果需要,为什么?

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

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

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

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

话少情深 2024-12-18 01:15:26

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

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

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


我不能这样定义Student的构造函数吗 -

Student::Student(const string& nm, const string& id, const string& maj, intyear)
{
   人(nm,id);
   专业 = 专业;
   毕业年份=年份;
}

不,你不能。 在执行派生类构造函数的主体之前,从初始化列表中调用基类构造函数。这就是语言的定义方式。
您的语法创建了一个未命名的临时对象,该临时对象会立即被丢弃。


请注意,它也被认为是良好的风格(对于某些类型,也是技术上的必要性)初始化初始化列表中的数据成员

这样做的一个很好的理由是,在 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!
{
}

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!
{
}
素衣风尘叹 2024-12-18 01:15:26

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

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.

情绪操控生活 2024-12-18 01:15:26

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

是否仅当基类无法默认构造时才需要?构造函数初始化列表是唯一可以构造基类的地方。即使你可以不这样做,为什么呢? Student 是一个 Person,因此只有构建 Student 是一个包含构建 Person 的操作才有意义>。

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

学生::学生(const string& nm, const string& id, const string& maj, intyear)
{
人(nm,id);
专业 = 专业;
毕业年份=年份;
}

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

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

Is it only necessary if the base class cannot be default constructed. Constructor initialization lists are the only place where one can construct the base classes. Even if you would be able to not do it, why would you? A Student is a Person, so it only makes sense that constructing a Student is an operation that includes constructing a Person.

Can not I define the constructor of Student llike this -

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

Well have you tried it? No, you cannot call the base constructor like that. If anything, that would be constructing a temporary unused Person, not the base class.

老子叫无熙 2024-12-18 01:15:26

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

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

我不能像这样定义 Student 的构造函数吗......

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

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

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

In short, yes. The derived class constructor will ALWAYS try to call the base class constructor first. If you don't specifically tell it to call your overridden constructor, it will try to call a default constructor (with no params). In your case this will fail as you haven't defined it..

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

No, because you can't call a constructor in code like that. And besides, even if you could, your code would fail as the compiler will look for a default constructor for the reasons stated above. If you really wanted to (and I can't think why you would) you could define a default constructor Person::Person(), and then have this:

Student::Student(const string& nm, const string& id, const string& maj, int year) 
{    
    name = nm;     
    idNum = id;
    major =maj;    
    gradYear =year; 
} 
扛起拖把扫天下 2024-12-18 01:15:26

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

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

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

不,你不能。

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

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

The constructor of the base class needs to be called before the constructor of the derived class, so you need to call it in the initialization list of the derived or the compiler will call default constructor for base class before you enter derived class body.

Can not I define the constructor of Student like this -

No You cannot.

The second approach will create a new temporary local object of Person not call Base class constructor for the object being created.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文