Каковы правила для вызова конструктора суперкласса?

Каковы правила С++ для вызова конструктора суперкласса из подкласса?

Например, я знаю в Java, вы должны сделать это как первую строку конструктора подкласса (и если вы этого не сделаете, предполагается неявный вызов суперструктора no-arg), что даст вам ошибку компиляции, если что отсутствует).

Ответ 1

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

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

Подробнее о списке инициализации конструктора здесь и здесь.

Ответ 2

В С++ конструкторы без аргументов для всех суперклассов и переменных-членов вызываются для вас, прежде чем вводить ваш конструктор. Если вы хотите передать их аргументы, существует отдельный синтаксис для этого, называемый "цепочка конструктора", который выглядит следующим образом:

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

Если что-то запустится в этот момент, базы/члены, которые ранее завершили строительство, вызвали их деструкторы, и исключение было обращено к вызывающему. Если вы хотите поймать исключения во время цепочки, вы должны использовать блок try функции:

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

В этой форме обратите внимание, что блок try является телом функции, а не находится внутри тела функции; это позволяет ему перехватывать исключения, вызванные неявными или явными инициализациями членов и базового класса, а также во время тела функции. Однако, если блок catch функции не генерирует другое исключение, среда выполнения перезапустит исходную ошибку; исключения при инициализации не могут игнорироваться.

Ответ 3

В С++ существует концепция списка инициализации конструктора, где вы можете и должны вызывать конструктор базового класса и где вы также должны инициализировать элементы данных. Список инициализации появляется после подписи конструктора после двоеточия и перед телом конструктора. Пусть говорят, что мы имеем класс A:


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

Тогда, если B имеет конструктор, который принимает int, конструктор может выглядеть следующим образом:


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

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

Имейте в виду, что члены данных всегда инициализируются в том порядке, в котором они объявлены в определении класса, независимо от их порядка в списке инициализации. Чтобы избежать странных ошибок, которые могут возникнуть, если ваши члены данных зависят друг от друга, вы всегда должны следить за тем, чтобы порядок членов был одинаковым в списке инициализации и определении класса. По той же причине конструктор базового класса должен быть первым элементом в списке инициализации. Если вы вообще опустите его, то конструктор по умолчанию для базового класса будет вызываться автоматически. В этом случае, если базовый класс не имеет конструктора по умолчанию, вы получите ошибку компилятора.

Ответ 4

Единственный способ передать значения родительскому конструктору - это список инициализации. Список инициализации реализуется с помощью: и затем списка классов и значений, которые должны быть переданы этому конструктору классов.

Class2::Class2(string id) : Class1(id) {
....
}

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

Ответ 5

Если у вас есть конструктор без аргументов, он будет вызываться до выполнения конструктора производного класса.

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

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

Вы не можете построить производный класс без вызова родительского конструктора в С++. Это происходит автоматически, если это не-arg C'tor, это происходит, если вы вызываете производный конструктор напрямую, как показано выше, или ваш код не будет компилироваться.

Ответ 6

Все упомянули вызов конструктора через список инициализации, но никто не сказал, что конструктор родительского класса можно вызывать явно из тела производного члена. См. Вопрос Например, вызов конструктора базового класса из тела конструктора подкласса. Дело в том, что если вы используете явный вызов родительского класса или конструктора суперклассов в теле производного класса, это на самом деле просто создает экземпляр родительского класса и не вызывает конструктор родительского класса на производном объекте, Единственный способ вызвать родительский класс или конструктор суперклассов в объекте производного класса - через список инициализации, а не в тело конструктора производного класса. Поэтому, возможно, его нельзя назвать "вызовом конструктора суперкласса". Я поставил здесь этот ответ, потому что кто-то может запутаться (как и я).

Ответ 7

Если у вас есть параметры по умолчанию в вашем базовом конструкторе, базовый класс будет вызываться автоматически.

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

Выход: 1

Ответ 8

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }

Ответ 9

Никто не упоминал последовательность вызовов конструктора, когда класс выводится из нескольких классов. Последовательность, как упоминалось при выводе классов.