Бьерн Страуструп - Язык программирования С++. Главы 2-4 - Преобразование типа

ОГЛАВЛЕНИЕ

 

3.2.5  Преобразование типа

Иногда бывает необходимо явно преобразовать значение одного типа в значение другого. Результатом явного преобразования будет значение указанного типа, полученное из значения другого типа. Например:

        float r = float(1);

Здесь перед присваиванием целое значение 1 преобразуется в значение с плавающей точкой 1.0f. Результат преобразования типа не является адресом, поэтому ему присваивать нельзя (если только тип не является ссылкой).

Существуют два вида записи явного преобразования типа: традиционная запись, как операция приведения в С, например, (double)a и функциональная запись, например, double(a). Функциональную запись нельзя использовать для типов, которые не имеют простого имени. Например, чтобы преобразовать значение в тип указателя, надо или использовать приведение

       char* p = (char*)0777;

или определить новое имя типа:

      typedef char* Pchar;
      char* p = Pchar(0777);

По мнению автора, функциональная запись в нетривиальных случаях предпочтительнее. Рассмотрим два эквивалентных примера:

       Pname n2 = Pbase(n1->tp)->b_name; // функциональная запись
       Pname n3 = ((Pbase)n2->tp)->b_name;   // запись с приведением

Поскольку операция -> имеет больший приоритет, чем операция приведения, последнее выражение выполняется так:

      ((Pbase)(n2->tp))->b_name

Используя явное преобразование в тип указателя можно выдать данный объект за объект произвольного типа. Например, присваивание

      any_type* p = (any_type*)&some_object;

позволит обращаться к некоторому объекту (some_object) через указатель p как к объекту произвольного типа (any_type). Тем не менее, если some_object в действительности имеет тип не any_type, могут получиться странные и нежелательные результаты.

Если преобразование типа не является необходимым, его вообще следует избегать. Программы, в которых есть такие преобразования, обычно труднее понимать, чем программы, их не имеющие. В то же время программы с явно заданными преобразованиями типа понятнее, чем программы, которые обходятся без таких преобразований, потому что не вводят типов для представления понятий более высокого уровня. Так, например, поступают программы, управляющие регистром устройства с помощью сдвига и маскирования целых, вместо того, чтобы определить подходящую структуру (struct) и работать непосредственно с ней
(см. $$2.6.1). Корректность явного преобразования типа часто существенно зависит от того, насколько программист понимает, как язык работает с объектами различных типов, и какова специфика данной реализации языка. Приведем пример:

          int i = 1;
          char* pc = "asdf";
          int* pi = &i;
          i = (int)pc;
          pc = (char*)i; // осторожно: значение pc может измениться.
                         // На некоторых машинах sizeof(int)
                         // меньше, чем sizeof(char*)
         pi = (int*)pc;
         pc = (char*)pi; // осторожно: pc может измениться
                         // На некоторых машинах char* имеет не такое
                         // представление, как int*

Для многих машин эти присваивания ничем не грозят, но для некоторых результат может быть плачевным. В лучшем случае подобная программа будет переносимой. Обычно без особого риска можно предположить, что указатели на различные структуры имеют одинаковое представление. Далее, произвольный указатель можно присвоить (без явного преобразования типа) указателю типа void*, а void* может быть явно преобразован обратно в указатель произвольного типа.

В языке С++ явные преобразования типа оказывается излишними во многих случаях, когда в С (и других языках) они требуются. Во многих программах можно вообще обойтись без явных преобразований типа, а во многих других они могут быть локализованы в нескольких подпрограммах.