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

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

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

9 ответов


конструкторы базовых классов вызываются автоматически для вас, если у них нет аргументов. Если вы хотите вызвать конструктор суперкласса с аргументом, вы должны использовать список инициализации конструктора подкласса. В отличие от Java, C++ поддерживает множественное наследование (к лучшему или худшему), поэтому базовый класс должен называться по имени, а не "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
        }
};

дополнительная информация о списке инициализации конструктора здесь и здесь.


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

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

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

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 is тело функции, а не внутри тела функции; это позволяет ему перехватывать исключения, создаваемые неявными или явными инициализациями членов и базового класса, а также во время тела функции. Однако, если блок catch функции не создает другое исключение, среда выполнения будет перестроить исходную ошибку; исключения во время инициализации не может игнорируется.


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


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

тогда, предполагая, что B имеет конструктор, который принимает int, конструктор A может выглядеть так:


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

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

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


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

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

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


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

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

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

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

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


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


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

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


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

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