Краткий ответ
Это конструктор с пустым телом и списком инициализации элемента , который инициализирует два элемента данных значением 0
.
т.е.
class-name([ ctor-args ]) [ : member-init-list ] { [ ctor-body ] }
, где member-init-list
равно member-name(args) [, member-name(args) [, ... ] ]
.
(Примечание: не фактические лексические грамматические конструкции C ++)
Длинный ответ
Фон
Примите следующие определения классов:
struct Base {};
struct Derived : Base {};
Возможно, вы уже знаете, что Derived
происходит от Base
.
Вы, возможно, уже знаете, что оба Derived
и Base
имеют синтезированный (неявно объявленный) конструктор по умолчанию (без аргументов). Конструктор Derived
неявно / автоматически вызывает Base
.
Теперь давайте добавим базовую функцию-член:
struct Derived : Base {
void foo() {}
};
Я объявил и , которые определили этот член-функцию foo
; его тело пусто, поэтому, когда вы вызываете его, ничего не происходит. Это довольно бессмысленно, но совершенно справедливо.
Теперь вместо этого давайте создадим наш собственный конструктор:
struct Derived : Base {
Derived() {}
};
Это выглядит более знакомым. Это все еще [специальная] функция с пустым телом, и конструктор Base
все еще вызывается неявно. Мы не изменили это.
Обработка данных членов
Давайте добавим некоторые элементы данных и установим их значение в нашем конструкторе:
struct Derived : Base {
Derived() {
x = 0;
y = 0;
}
int x, y;
};
И x
, и y
будут иметь значение 0
после построения объекта. Это все еще фундаментальный C ++.
Но вы можете не знать, что вы не инициализируете эти элементы данных. Вы просто назначаете им после возможности их инициализации.
Фактически, члены данных встроенных типов не инициализируются неявно, поэтому давайте выберем лучший пример:
struct Derived : Base {
Derived() /* HERE */ {
x = "";
y = "";
}
std::string x, y;
};
x
и y
неявно инициализируются до запуска кода в теле конструктора. Когда тело конструктора начинает работать, все члены, которые будут инициализированы, уже инициализированы , и базовый конструктор был вызван неявно.
Мы можем перехватить это поведение и предоставить наши собственные значения инициализации, написав список инициализаторов в том месте, где я написал /* HERE */
в предыдущем фрагменте. С помощью списка инициализатора элемента фрагмент выглядит следующим образом:
struct Derived : Base {
Derived() : x(""), y("") {
x = "";
y = "";
}
std::string x, y;
};
Ничего себе! ХОРОШО; теперь мы инициализируем строки в ""
, и , а затем присваиваем им то же пустое значение в теле конструктора. Мы можем избавиться от этих заданий, тогда:
struct Derived : Base {
Derived() : x(""), y("") {}
std::string x, y;
};
Теперь тело конструктора пустое, но конструктор все еще делает вещи. Он неявно вызывает базовый конструктор и явно инициализирует элементы данных x
и y
пустой строкой.
И, поскольку std::string
имеет конструктор по умолчанию, который делает то же самое, мы можем написать для краткости:
struct Derived : Base {
Derived() : x(), y() {}
std::string x, y;
};
Возвращаясь к исходному примеру, это относится и к объектам встроенных типов. Давайте рассмотрим два указателя:
struct Derived : Base {
Derived() : next(0), prev(0) {}
Derived* next;
Derived* prev;
};
Обработка базовых классов
И, в качестве дополнительного бонуса, мы можем использовать список инициализации членов для явного вызова базового конструктора :
struct Derived : Base {
Derived() : Base(), next(0), prev(0) {}
Derived* next;
Derived* prev;
};
Это довольно бессмысленно, если базовый конструктор не хочет аргументов:
struct Base {
Base(int x) {}
};
struct Derived : Base {
Derived() : Base(0), next(0), prev(0) {}
Derived* next;
Derived* prev;
};
Я надеюсь, что это было полезно.