Почему конструктор для базового класса должен вызываться в списке инициализатора производного класса? - PullRequest
3 голосов
/ 18 октября 2011

В C ++, предположим, у меня есть класс Person со следующим конструктором -

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

Теперь у меня также есть подкласс Person с именем Student с дополнительными членами данных major и 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::Student(const string& nm, const string& id, const string& maj, int year)
{
   Person(nm, id);
   major =maj;
   gradYear =year;
}

Ответы [ 5 ]

9 голосов
/ 18 октября 2011

Необходимо ли конструктору класса Student вызывать конструктор для базового класса Person в списке инициализатора, если да, почему?

Базовый классКонструкторы всегда вызываются перед конструкторами производного класса. Если вы не вызываете их явно в списке инициализации, конструкторы по умолчанию называются неявно .Если базовый класс не имеет конструктора по умолчанию, это не сработает, поэтому вы должны явно указать конструктор базового класса для вызова.

Необходимо вызывать конструкторы базовых классов перед конструкторами производных классов , поскольку вы можете получить доступ к подобъектам базового класса из конструкторов производных классов.Вы можете вызывать их публичные и защищенные функции-члены, и для этого их члены-данные, безусловно, должны быть инициализированы - это то, что делают конструкторы базового класса.


Не могу ли я определить конструктор Student следующим образом -

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

Нет, вы не можете. Конструкторы базового класса вызываются из списка инициализации перед выполнением тела конструктора производного класса.Именно так определяется язык.
Ваш синтаксис создает неназванный временный объект, который немедленно отбрасывается.


Обратите внимание, что считается хорошим стилем (а для некоторых типов также технической необходимостью) также инициализировать элементы данных в списке инициализации .

Очень веская причина для этого заключается в том, что в 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!
{
}
2 голосов
/ 18 октября 2011

Потому что вы не можете вызывать конструкторы. Списки инициализаторов Construct являются особенными в нескольких отношениях, и одним из них является предоставление вам доступа к базовому ctor. Кроме того, вы всегда должны инициализировать поля в списке инициализации.

1 голос
/ 18 октября 2011

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

Короче говоря, да.Конструктор производного класса ВСЕГДА попытается сначала вызвать конструктор базового класса.Если вы специально не скажете ему вызвать ваш переопределенный конструктор, он попытается вызвать конструктор по умолчанию (без параметров).В вашем случае это не удастся, поскольку вы не определили это ..

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; 
} 
1 голос
/ 18 октября 2011

Необходимо ли конструктору класса Student вызывать конструктор для базового класса Person в списке инициализатора, если да, то почему?

Конструктор базового класса необходимо вызывать перед конструктором производного класса, поэтому вам нужно вызвать его в списке инициализации производного, иначе компилятор вызовет конструктор по умолчанию для базового класса, прежде чем вводить тело производного класса. .

Не могу я определить конструктор Student следующим образом -

Нет, вы не можете.

Второй подход создаст новый временный локальный объект Person, не вызывающий конструктор базового класса для создаваемого объекта.

1 голос
/ 18 октября 2011

Необходимо ли конструктору класса Student вызывать конструктор для базового класса Person в списке инициализатора, если да, то почему?

Это необходимо, только если базовый класс не может быть создан по умолчанию. Списки инициализации конструктора - единственное место, где можно создавать базовые классы. Даже если бы вы могли этого не делать, почему бы вам? Student - это Person, поэтому имеет смысл только то, что построение Student - это операция, которая включает в себя построение Person.

Не могу я определить конструктор Студента, как это -

Student :: Student (const строка & nm, const string & id, const string & maj, int year) { Человек (nm, id); мажор = майор; gradYear = год; }

Ну, вы пробовали это? Нет, вы не можете вызвать базовый конструктор таким образом. Во всяком случае, это будет создание временного неиспользованного Person, а не базового класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...