значений с плавающей точкой. Значение с плавающей точкой можно преобразовать в значение с плавающей точкой другого типа. Если исходное значение можно точно представить с помощью целевого типа, то результатом будет исходное числовое значение. Если же исходное значение лежит между двумя целевыми значениями, то результатом будет одно из этих значений. Иначе говоря, результат непредсказуем. Обратите внимание на то, что преобразование значения типа float в значение типа double считается продвижением.
• Преобразование указателей и ссылок. Любой указатель на тип объекта можно преобразовать в указатель типа void* (см. разделы 17.8 и 27.3.5). Указатель (ссылка) на производный класс можно неявно преобразовать в указатель (ссылку) на доступный и однозначно определенный базовый класс (см. раздел 14.3). Константное выражение (см. разделы A.5 и 4.3.1), равное нулю, можно неявно преобразовать в любой другой тип указателя. Указатель типа T* можно неявно преобразовать в указатель const T*. Аналогично ссылку T& можно неявно преобразовать в ссылку типа const T&.
• Булевы преобразования. Указатели, целые числа и числа с плавающей точкой можно неявно преобразовать в значение типа bool. Ненулевое значение преобразовывается в значение true, а нуль — в значение false.
• Преобразования чисел с плавающей точкой в целые числа. Если число с плавающей точкой преобразуется в целое число, то его дробная часть отбрасывается. Иначе говоря, преобразование из типа с плавающей точкой в целый тип является усечением. Если усеченное значение невозможно представить с помощью целевого типа, то результат становится непредсказуемым. Преобразования целых чисел в числа с плавающей точкой являются математически корректными только в той степени, в которой это допускается аппаратным обеспечением. Если целое число невозможно точно представить как число с плавающей точкой, происходит потеря точности.
• Обычные арифметические преобразования. Эти преобразования выполняются над операндами бинарных операторов, чтобы привести их к общему типу, а затем использовать этот тип для представления результата.
1. Если один из операндов имеет тип long double, то другой преобразовывается в тип long double. В противном случае, если один из операндов имеет тип double, другой преобразовывается в тип double. В противном случае, если один из операндов имеет тип float, другой преобразовывается в тип float. В противном случае над обоими операндами целочисленного типа выполняется продвижение.
2. Если один из операндов имеет тип unsigned long, то другой преобразовывается в тип unsigned long. В противном случае, если один из операндов имеет тип long int, а другой — unsigned int, значение типа unsigned int преобразуется в значение типа long int, при условии, что тип long int может представить все значения типа unsigned int. В противном случае оба операнда преобразовываются в тип unsigned long int. В противном случае, если один из операндов имеет тип long, другой преобразовывается в тип long. В противном случае, если другой операнд имеет тип unsigned, другой преобразовывается в тип unsigned. В противном случае оба операнда имеют тип int.
Очевидно, что лучше не полагаться на слишком запутанные сочетания типов и минимизировать необходимость неявных преобразований.
A.5.2.3. Преобразования, определенные пользователем
Кроме стандартных преобразований и продвижений, программист может определить преобразования типов, определенных пользователем. Конструктор, принимающий один аргумент, определяет преобразование этого аргумента в значение своего типа. Если конструктор имеет спецификатор explicit (см. раздел 18.3.1), то преобразование происходит, только если программист явно потребует его выполнить. В противном случае преобразование может быть неявным.
A.5.3. Константные выражения
Константное выражение (constant expression) — это выражение, которое может быть вычислено на этапе компиляции и содержит только операнды типа int. (Это немного упрощенное определение, но для большинства целей оно вполне подходит.) Рассмотрим пример.
const int a = 2*3;
const int b = a+3;
Константные выражения требуются в немногих случаях, например, при вычислении границ массивов, меток разделов case, инициализаторов перечислений и шаблонных аргументов типа int. Рассмотрим пример.
int var = 7;
switch (x) {
case 77: // OK
case a+2: // OK
case var: // ошибка (var — не константное выражение)
// ...
};
A.5.4. Оператор sizeof
В выражении sizeof(x) аргумент x может быть типом или выражением. Если x — выражение, то значением sizeof(x) является размер результирующего объекта. Если x — тип, то значением sizeof(x) является размер объекта типа x. Размеры измеряются в байтах. По определению sizeof(char)==1.
A.5.5. Логические выражения
В языке C++ предусмотрены логические операторы для целочисленных типов.
Эти операторы применяются к каждому биту своих операндов, в то время как логические операторы (&& и ||) трактуют число 0 как значение false, а все — как true. Определения этих операторов приведены ниже.
A.5.6. Операторы new и delete
Свободная память (динамическая память, или куча) выделяется с помощью оператора new, а освобождается — с помощью оператора delete (для индивидуальных объектов) или delete[] (для массива).
Если память исчерпана, то оператор new генерирует исключение bad_alloc. В случае успеха операция new выделяет как минимум один байт и возвращает указатель на объект, размещенный в памяти. Тип этого объекта определяется после выполнения оператора new. Рассмотрим пример.
int* p1 = new int; // размещает (неинициализированное) число
// типа int
int* p2 = new int(7); // размещает число типа int,
// инициализированное
// числом 7
int* p3 = new int[100]; // размещает 100 (неинициализированных)
// чисел int
// ...
delete p1; // удаляет индивидуальный объект
delete p2;
delete[] p3; // удаляет массив
Если с помощью оператора new вы размещаете в памяти объекты встроенного типа, они не будут инициализированы, если не указан инициализатор. Если с помощью оператора new вы размещаете в памяти объекты класса, имеющего конструктор, то, если не указан инициализатор, будет вызван этот конструктор (см. раздел 17.4.4).