2008-09-23 15 views
517

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

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

ответ

717

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

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

+36

я удалил 'явный' из конструктора суперкласса. Несмотря на то, что они были лучшей практикой для конструкторов с одним аргументом, это не было связано с обсуждением. Дополнительные сведения о явном ключевом слове см. По адресу: http://weblogs.asp.net/kennykerr/archive/2004/08/31/Explicit-Constructors.aspx – luke 2008-09-24 12:38:48

+1

Оператор двоеточия: вы использовали для вызова конструктора суперкласса перед созданием экземпляра дочернего элемента конструктор класса, я полагаю, это также верно для методов? – ha9u63ar 2014-10-31 09:33:57

+2

@hagubear, только для конструкторов, AFAIK – luke 2014-10-31 12:24:26

14

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

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

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

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

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

16

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

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

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

174

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

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

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

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

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


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_ внутри тела конструктора, поскольку вы сохраняете дополнительную стоимость назначения.

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

11

Все упомянули вызов конструктора через список инициализации, но никто не сказал, что конструктор родительского класса можно явно вызывать из тела конструктора производного члена. Например, см. Вопрос Calling a constructor of the base class from a subclass' constructor body. Дело в том, что если вы используете явный вызов родительского класса или конструктора суперклассов в теле производного класса, это фактически просто создает экземпляр родительского класса и не вызывает конструктор родительского класса в производном объект. Единственный способ вызвать родительский класс или конструктор суперклассов в объекте производного класса - через список инициализации, а не в тело конструктора производного класса. Поэтому, возможно, его нельзя назвать «вызовом конструктора суперкласса». Я поставил здесь этот ответ, потому что кто-то может запутаться (как и я).

1

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^