Подскажите пожалуйста, а есть ли разница между использованием круглых и фигурных скобок при инициализации конструктора внутри класса. Оба варианта работают корректно.
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;
}
Ответ
Начнем с того, что имеются случаи, когда нельзя применять инициализацию одного из видов к членам данных класса.
Например, если у вас имеется член класса, который является агрегатом (структурой или массивом), то возможные виды инициализации ограничены.
Рассмотрим несколько примеров.
В данном примере внутри структуры 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
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
int main()
{
int b = 10;
A a(b);
}
будет выведено сообщение
B( std::initializer_list
Вообще, эта тема достаточно обширная.
О некоторых причудах инициализации я написал на своем сайте в конце темы Шутка - ложь, но в ней намек, добрым молодцам урок.
Комментариев нет:
Отправить комментарий