#cpp #классы #конструктор #инициализация #объявление
Подскажите пожалуйста, а есть ли разница между использованием круглых и фигурных скобок при инициализации конструктора внутри класса. Оба варианта работают корректно. class A { public: char c; int d; A(char ch) :c(ch) {} A(char ch, int i) :c{ch}, d{i} {} }; int main() { A first = A('a'); A second = A('b', 1); cout << "First: " << first.c << endl; cout << "Second: " << second.c << ' ' << second.d << endl; return 0; }
Ответы
Ответ 1
Начнем с того, что имеются случаи, когда нельзя применять инициализацию одного из видов к членам данных класса. Например, если у вас имеется член класса, который является агрегатом (структурой или массивом), то возможные виды инициализации ограничены. Рассмотрим несколько примеров. В данном примере внутри структуры A объявляется агрегатный член данных B, имеющий тип структуры. Тогда данное объявление конструктора будет некорректныым struct A { A(int x) : b( x ) {} struct B { int x; } b; }; int main() { A a( 10 ); } Компилятор выдаст сообщение об ошибке, говорящее о том, что он не может преобразовать объект типа int в объект типа struct B. Однако если вы замените круглые скобки на фигурные, struct A { A(int x) : b{ x } {} struct B { int x; } b; }; int main() { A a( 10 ); } то код будет успешно компилироваться, так как объект b будет инициализирован как агрегат. Теперь если в конструкторе заменить тип у параметра с int на A::B, то ситуация изменится. Данная программа будет успешно компилироваться struct A { struct B; A(B x) : b( x ) {} struct B { int x; } b; }; int main() { A::B b = { 10 }; A a( b ); } так как структуры, которые являются агрегатами, имеют конструктор копирования, создаваемый компилятором неявно. Иная ситуация складывается, когда членом данных, который представляет собой агрегат, является массив. Как и в случае со структурой, данная программа не будет компилироваться struct A { struct B; A(int x) : b( x ) {} int b[1]; }; int main() { int b = 10; A a( b ); } так как нет преобразования из целочисленного типа в массив. Однако если заменить параметр на массив, не важно, является он ссылкой или нет, как показано ниже struct A { struct B; A(int x[1]) : b( x ) {} int b[1]; }; int main() { int b[1] = { 10 }; A a( b ); } или struct A { struct B; A(int ( &x )[1]) : b( x ) {} int b[1]; }; int main() { int b[1] = { 10 }; A a( b ); } то программа не будет компилироваться, так как в первом случае нет преобразования из указателя в массив, а во втором случае, когда параметр объявлен как ссылка, массивы не имеют конструктора копирования. Для инициализации члена данных, который является массивом, можно использовать следующую запись struct A { struct B; A(int x) : b{ x } {} int b[1]; }; int main() { int b = 10; A a( b ); } Данная программа успешно скомпилируется. Однако вы не можете запись с фигурными скобками заключить еще в круглые собкки, как показано ниже struct A { struct B; A(int x) : b({ x }) {} int b[1]; }; int main() { int b = 10; A a( b ); } Компилятор выдаст сообщение об ошибке, так как, опять-таки, для массивов нет конструктора копирования. Однако для структур такая запись инициализации будет успешно воспринята компилятором, так как структуры, как агрегаты, имеют неявно объявленный компилятором конструктор копирования. struct A { A(int x) : b({ x }) {} struct B { int x; } b; }; int main() { int b = 10; A a( b ); } Данная программа успешно скомпилируется. Для арифметических типов инициализация с фигурными скобками не разрешает "сужение" значения, то есть использовать в качестве инициализатора значение, которое потенциально не может разместиться в инициализируемом объекте. Поэтому следующая программа не будет компилироваться struct A { A(int x) : b{ x } {} short b; }; int main() { int b = 10; A a( b ); } Причиной ошибки будет то, что объект типа short не в состоянии разместить все значения объекта типа int, то есть будет иметь место "сужение" инициализирующего значения. Однако если заменить фигурные скобки на круглые, то программа успешно скомпилируется struct A { A(int x) : b( x ) {} short b; }; int main() { int b = 10; A a( b ); } Когда инициализируемый член класса является определенный пользователем тип, то в дело вступают конструкторы, которые имеют параметр типа std::initializer_list Например, в приведенной ниже программе, когда нет такого конструктора, можно инициализировать член класса как b( x ), или как b{ x }, или даже как b( { x } ) struct A { A(int x) : b({ x }) {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } } b; }; int main() { int b = 10; A a(b); } Однако если в классе присутствует конструктор с параметром типа std::initializer list, то для инициализаций вида b{ x } и b( { x } ) будет вызван именно он. А для инициализации вида b( x ) будет вызван другой конструктор. Например, для этой программы struct A { A(int x) : b( x ) {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } B(std::initializer_list) { std::cout << "B( std::initializer_list )" << std::endl; } } b; }; int main() { int b = 10; A a(b); } будет выведено сообзение B( int ) А для этой программы struct A { A(int x) : b{ x } {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } B(std::initializer_list ) { std::cout << "B( std::initializer_list )" << std::endl; } } b; }; int main() { int b = 10; A a(b); } будет выведено сообщение B( std::initializer_list ) Вообще, эта тема достаточно обширная. О некоторых причудах инициализации я написал на своем сайте в конце темы Шутка - ложь, но в ней намек, добрым молодцам урок. Ответ 2
Ну, например, напишите int main(int argc, const char * argv[]) { A a(300); A b{300}; } Вам сразу сообщат, что второе объявление не годится - поскольку 300 в char не поместится. Инициализация с фигурными скобками не разрешает сужающее преобразование. Есть и другие отличия - например, при фигурных скобках компилятор будет искать конструктор от initializer_list. Например, если в вашем классе будет такое: class A { vectorv; ... A(int x):v{x} {} A(int x):v(x) {} то последние две строчки означают совершенно разные вещи - первая - вектор с элементом x, а вторая - вектор с x элементами...
Комментариев нет:
Отправить комментарий