#опытным
Вот здесь мы поговорили о том, почему важно соблюдать порядок следования полей класса в списке инициализации конструктора. Дело в том, что вне зависимости от того, как написан этот список, поля будут инициализироваться в порядке появления их объявления.
Также в С++11 у нас появилась фича под названием default member initializer. Это та самая штуковина, которая позволяет вам инициализировать нестатические поля класса не в конструкторе, а прям inplace. Типа того:
struct Class {
int field = 5;
};
Фича полезная, многие ей часто пользуются. Но вот возникает вопрос: как список инициализации конструктора взаимодействует с default member initializer? Если я инициализирую поля вне конструктора и компилятор видит эти значения явным образом, то возможно эти поля и получают значение первыми? Сейчас все узнаем.
Посмотрим на такой пример:
struct Char {
Char(char c) : field{c} {std::cout << "Char " << field << std::endl;}
Char() = default;
char field;
};
struct TestClass {
TestClass() : a{'1'},
c{'3'},
e{'5'} {}
Char a;
Char b = '2';
Char c;
Char d = '4';
Char e;
};
Есть простенький класс Char, который выводит на консоль момент создания объекта. И тестовый класс, на котором мы и проводим эксперимент. И в этом эксперименте мы и проверим, в каком порядке свои значения получают поля
b и d, относительно a, c, e. На самом деле здесь правило ровно такое же. Нестатические поля класса инициализируются в порядке их появления в описании класса. Поэтому вывод будет таким:
Char 1
Char 2
Char 3
Char 4
Char 5
С этим разобрались.
И тут назревает вопрос: а что будет, если я в начале проициализирую поле inplace, а потом еще раз в constructor initializer list? Какая из инициализаций победит другую? Или быть может они произойдут обе в какой-то очередности?
Выглядеть это может так:
struct Char {
Char(char c) : field{c} {std::cout << "Char " << field << std::endl;}
Char() = default;
char field;
};
struct TestClass {
TestClass() : a{'1'},
b{'2'},
c{'3'},
d{'4'},
e{'5'} {}
Char a;
Char b = 'b';
Char c;
Char d = 'd';
Char e;
};
Опять в подопытные мы взяли поля
b и d и задали им значения с помощью default member initializer. А вдогонку еще и в списке инициализации присвоили им значение. В такой ситуации default member initializer не играет никакой роли, блаженно складывает лапки и отдает бразды правления списку инициализации. Вывод будет тем же, что и в прошлом примере:
Char 1
Char 2
Char 3
Char 4
Char 5
Но это только список инициализации так работает. Если для инициализации поля вы используете обычный конструктор, то оно первый раз проинициализируется с помощью default member initializer(которая обязательно происходит до входа в тело конструктора), а второй раз - в теле конструктора.
struct TestClass {
TestClass() : a{'1'},
c{'3'},
d{'4'},
e{'5'} {b = '2';}
Char a;
Char b = 'b';
Char c;
Char d = 'd';
Char e;
};
// Output
Char 1
Char b
Char 3
Char 4
Char 5
Char 2
Пишите в комменты, если есть еще какие-то интересные кейсы взаимодействия этих сущностей. В будущем, разберем их на канале.
Mix things properly. Stay cool.
#cpp11 #cppcore