Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 39
Текст из файла (страница 39)
Новичок, конечно, с такой задачей вряд ли справится, но можно приобрести готовый и оттестированный обработчик от сторонних производителей ($С.9.1). Предоставляя новый обработчик, мы контролируем ситуацию с исчерпанием памяти при обращении к операции пею. Существуют два способа управления процессом выделения памяти.
Можно либо предоставить нестандартные реализации функций орегагог нем() и орегагог де!его О, которые вызываются стандартной формой операции пеп, либо положиться на информацию о блоке памяти, предоставленную пользователем (510.4.11, 519.4.5). 6.2.7. Явное приведение типов Иногда приходится иметь дело с «сырой» памятью (га»г тетогу), то есть памятью, которая предназначена для хранения объектов неизвестного компилятору типа.
К примеру, распределитель памяти возвращает значение типа гоЫ*, указывающее на выделенный блок памяти, или мы трактуем целое.число как адрес устройства ввода/вывода; го[0* тайас(в[ее г) з гоЫ 1'() ( гпг* ртиайс саик[о[*> [тайас(100) ); УУ память выделена под целые 1О де»[се* 01= ге[п<егргег савг<1О демсе*> [ОХЯОО); У.. ) У устройство по адресу ОООО Компилятор не знает тип объекта, адресуемого указателем типа го[з[*. Не может он и знать, корректен ли адрес ОХ01)0.
Следовательно, именно программист несет всю ответственность за корректность преобразований типов. Явное преобразование типов (приведение типов' — сов[!пя) на практике важно и применимо. Но по установившейся традиции его применение избыточно и часто служит источником ошибок. Операция аванс сов[ осуществляет преобразования между родственными типами данных, например от указателя на некоторый класс к указателю на другой класс из той же иерархии наследования, от интегрального типа к перечислению, от типа 1В2 Глава б. Выражения и операторы 6.2.8.
Конструкторы Для конструирования (создония) значения типа Т из значения е используется функциональная форма записи Т(е) . Например: гоЫ Т(доиые д) ( 1п( 1=1п( (Ф); сотр1сх х=сотр1сх (д); ,Чусекаеи д (отбрасываем дробную часть) У создаем сотр1сх из д с плавающей запятой к интегральному типу. Операция ге(пгегргег сазг осуществляет преобразования между несвязанными типами, например от целого к указателю или от одного указателя к иному произвольному указательному типу.
Различие между этими операциями позволяет компилятору немного контролировать процесс приведения типов операцией агапе сазг и помогает программисту следить за потенциально опасными приведениями, выполняемыми в программе с помощью геупгегргег сава Преобразования типов с помощью мопс саят более-менее переносимы, а преобразования через ге!пгегргег савг — практически никогда. Нельзя утверждать со 100%-ой уверенностью, но все же чаще всего ге)пгегргег спадает новую трактовку типа, оставляя последовательности битов аргумента неизменными. Если конечный тип преобразования не уже, чем исходный (то есть содержит не меньшее число бит), то можно еше раз применить ге1пгегргег сам в обратном порядке и вернуться к исходному аргументу.
В общем случае, результат операции ге)пгегргег савг гарантированно приемлем для использования лишь тогда, когда преобразуемое значение соответствует целевому типу. Если вы решили выполнить явное приведение типа, то сначала задумайтесь, насколько оно действительно необходимо. В С++ необходимость в явных преобразованиях типов встречается существенно реже, чем в С (В!.6) или в ранних версиях С++ (В1.6.2, ВВ.2.3). Во многих программах можно полностью избавиться от явного преобразования типов. В других — тщательно локализовать их внутри немногочисленных процедур.
В настоящей книге реальные случаи применения явных преобразований типов встречаются лишь в В6.2.7, В7.7, З!3.5, З13.6, З17.6.2.3, В!5.4, З25.4.! и $Е.3.1. Имеются также операция преобразования Фупат1с сам, действие которой контролируется на этапе выполнения программы (В! 5.4.1), и операция преобразования сопй сам (В!5.4.2.1), аннулируюшая действие модификаторов сопят и го1ай1е. Язык С++ унаследовал от С конструкцию (2) е, означающую любое преобразование, которое может быть выполнено комбинацией операций маг!с сам, ге1пгегргег саьг и соим сазг, для получения значения типа тиз выражения е (ВВ.2.3). Такой стиль преобразований опаснее рассмотренных выше именованных операций приведения, ибо его труднее находить в больших программах и он не отражает точных намерений программиста, поскольку (2) е может выполнять и переносимое преобразование между родственными типами данных, и непереносимое преобразование несвязанных между собой типов и снятие действия модификатора сопят с указателя.
Без точного знания типа Ти типа выражения е ничего определенного сказать нельзя. 6.3. Обзор операторов языка С++ 183 гоЫ Т(з(оиЫе д) ( тз /=тз ( ); сотрйх з=сотр(ех ( ); У... ) думолчательное целое значение думолчательное значение для типа сотр(ех Для встроенных типов значением по умолчанию является нуль, преобразованный в соответствующий тип (54.9.5).
Таким образом, зпг ( ) есть просто другая форма записи нулевого целого значения. Для пользовательского типа Т, действие выражения Т() определяется так называемым конструктором по умолчанию (если он имеется) (810.4.2). Роль конструирующих выражений для встроенных типов приобретает особую важность в случае определения шаблонов, когда программист не знает, будет ли шаблонный параметр соответствовать встроенному или пользовательскому типам данных (~16.3.4, ~17.4.1.2).
6.3. Обзор операторов языка С++ Ниже приведена компактная сводка операторов языка С++. Синтаксис операторов Иногда конструкцию Т(е) называют приведением типов в функциональном стиле (уипсг(оп-з(у!е саят). К сожалению, для встроенного типа Тзапись Т(е) эквивалентна записи (7) е (86.2.7). Это означает, что для многих встроенных типов применение Т(е) небезопасно. Например, значения арифметических типов могут оказаться усеченными ((галса(ей). Даже явные преобразования от более длинных целых типов к более коротким (например, из 1опя в айаг) приводят к непереносимым, зависящим от реализации результатам. Я стараюсь применять конструкцию Т(е) только в четко определенных случаях: лля сужающих арифметических преобразований (зс.б), для приведения целых к перечислениям (84.8) и для конструирования объектов пользовательских типов (82.5.2, 810.2.3).
Преобразования указателей нельзя выполнять непосредственно в виде Т(е). К примеру, айаг* (2) является синтаксической ошибкой. К сожалению, такую зашиту против опасных приведений указателей можно обойти, используя синонимы указательных типов, вводимые с помощью гуредеу'64.9.7). Конструкция ТН используется для создания значения по умолчанию лля типа Т. Например: !ад Глава б. Выражения и операторы последовательность-операторов: оператор последовательность операторов „ выражение ( последовательность-операторов„,„) ботников, Здесь,р, означает «ор((опа(», то есть «необязательно». Обратите внимание на то, что объявление является оператором, и что нет никаких операторов присваивания и операторов вызова функций; присваивания и вызов функции — это выражения, использующие соответствующие операции. Операторы для обработки исключений, так называемые ггу-блоки («у-Ыос)Ь), будут рассмотрены в 58.3.1. 6.3.1.
Объявления как операторы Объявление является оператором. Если только переменная не объявлена с модификатором иа«с, то инициализатор выполняется всякий раз, как только поток управления проходит через соответствующее объявление (510.4.8). Причина, по которой разрешается применять объявления всюду, где допускается оператор (и еше в некоторых местах; $6.3.2.1, 56.3.3.1), заключается в стремлении уменьшить количество ошибок, связанных с применением неинициализированных переменных, и для лучшей локализации кода. Редко когда возникают причины для объявления переменных ранее момента, когда становятся известными (доступными) их начальные значения.
Например: вой(у(геолог<в(г(пд>а г, (пг О сопл(санг* р) ( К(р==в) ге(игл; б, 3. Обзор операторов языка С++ 185 В (1<0 ( ( г.л(ее () <=1) еггог ("Ьай (пдех" ) в(пни л = и(1); 1У(л == р) ( // ... // ... ) Возможность поместить объявление после некоторого количества исполняемого кода сушественно для констант и вообше для стиля программирования, который можно назвать стилем однократных присваиваний, когда значения объектов после их инициализации не меняются.
Для пользовательских типов с точки зрения эффективности кода также важно отложить определение объектов до момента появления приемлемого инициализатора. Например, лтг/пд ю /* ... */ л=" ТЬе Ьелг и сде гнету оГсде дона." вполне может оказаться менее эффективным, чем л)г(пя л=" Уо(га)ге" г Наиболее обшей причиной, по которой переменные объявляются без инициализации, является необходимость использования отдельных операторов для придания им конкретных значений.
Например, когда значения переменных и массивов извлекаются из потока ввода. 6.3.2. Операторы выбора (условные операторы) Значение выражения может проверяться в операторах (/ илн звчгсЬ: ф'(условие ) оператор (/(условив) оператор е!лв оператор лгг/гсЬ (условие) оператор Операции сравнения « = »= возврашают логическое значение ггие, если сравнение истинно, и логическое/а1ве в противном случае. Для оператора (/ выполняется первый подчиненный оператор (ма(ешеп(), если значение проверяемого выражения (сопй()оп) не равно нулю, или второй подчиненный оператор (если он есть) в противном случае.
Отсюда следует, что любое арифметическое выражение или выражение с указателями может использоваться в качестве проверяемого выражении (условия). Например, если х целое, то (/ (х) //... означает (г'(х! =0) // ... Для указателя р, следуюший код (/(р) Глава б. Выражения и операторы 186 представляет собой прямую проверку «указывает ли р на действительный объект?», в то время как (!" (р! =О) !! ... делает то же самое, но косвенно, сравнивая р с нулевым значением, которое, как известно, не может быть корректным адресом объекта в памяти.
Заметим, что «нулевой» указатель не на всех машинах представляется битовой последовательностью из одних нулей (85.1.1). Все проверенные мной компиляторы для обеих форм сравнения генерировали одинаковый код. Логические операции чаще всего используются именно в проверяемых выражениях (условиях). Операции ьь и ) ) вообще не вычисляют свой второй (правый) аргумент, если вэтом отсутствует необходимость.