Преобразование типов C++ — различия между версиями
Ulysses (обсуждение | вклад) |
Ulysses (обсуждение | вклад) м (→Указатели) |
||
(не показаны 3 промежуточные версии этого же участника) | |||
Строка 1: | Строка 1: | ||
− | + | __TOC__ | |
{{цитата|автор=[http://ru.wikipedia.org/wiki/Бьярне_Страуструп Бьярне Страуструп]|Фундаментальные типы могут преобразовываться один в другой невероятно большим количеством способов. По-моему, дозволяется слишком много преобразований.}} | {{цитата|автор=[http://ru.wikipedia.org/wiki/Бьярне_Страуструп Бьярне Страуструп]|Фундаментальные типы могут преобразовываться один в другой невероятно большим количеством способов. По-моему, дозволяется слишком много преобразований.}} | ||
− | Интегральные типы (<tt>int</tt>, <tt>char</tt>, <tt>bool</tt>, etc.) и типы с плавающей точкой в присваиваниях и арифметических выражениях можно свободно смешивать. Когда возможно, значения преобразуются так, чтобы не потерять информации. Неявные преобразования, сохраняющие значения, (например, <tt>bool</tt> к <tt>int</tt>) обычно называют ''продвижениями'' (promotion). К сожалению, преобразования, приводящие к потере значения, (например, присваивание вещественного числа целочисленной переменной, которое производится отбрасыванием дробной части) также могут осуществляться неявно. | + | Интегральные типы (<tt>int</tt>, <tt>short</tt>, <tt>char</tt>, <tt>bool</tt>, etc.) и типы с плавающей точкой в присваиваниях и арифметических выражениях можно свободно смешивать. Когда возможно, значения преобразуются так, чтобы не потерять информации. Неявные преобразования, сохраняющие значения, (например, <tt>bool</tt> к <tt>int</tt>) обычно называют ''продвижениями'' (promotion). К сожалению, преобразования, приводящие к потере значения, (например, присваивание вещественного числа целочисленной переменной, которое производится отбрасыванием дробной части) также могут осуществляться неявно. |
Полный список правил преобразований можно прочитать в книге Б. Страуструпа<ref name="tcpppl">''Страуструп Б.'' Язык программирования C++, 3-е изд.</ref>, Приложение B, раздел 6. | Полный список правил преобразований можно прочитать в книге Б. Страуструпа<ref name="tcpppl">''Страуструп Б.'' Язык программирования C++, 3-е изд.</ref>, Приложение B, раздел 6. | ||
Строка 23: | Строка 23: | ||
В целом, для ''неявного'' преобразования указателей отведено существенно меньше свободы, чем для обычных базовых типов. | В целом, для ''неявного'' преобразования указателей отведено существенно меньше свободы, чем для обычных базовых типов. | ||
− | Имеется специальный тип указателей: <tt>void *</tt> (подробнее см. <ref name="tcpppl" />, п. 5.6) — любой указатель может быть неявно преобразован к нему. Однако это правило не действует для указателей на функцию или на член класса. (<small>Последний факт иногда используется при реализации сложных приёмов программирования на C++, таких как « | + | Имеется специальный тип указателей: <tt>void *</tt> (подробнее см. <ref name="tcpppl" />, п. 5.6) — любой указатель может быть неявно преобразован к нему. Однако это правило не действует для указателей на функцию или на член класса. (<small>Последний факт иногда используется при реализации сложных приёмов программирования на C++, таких как «[http://ru.wikipedia.org/wiki/Умный_указатель умные указатели]», чтобы обезопасить программиста от выполнения [http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/shared_ptr.htm#conversions неявных преобразований].</small>). |
Пару указателей <tt>void *</tt> можно сравнивать на равенство и неравенство, присваивать один указатель другому. Наконец, <tt>void *</tt> можно преобразовывать к другому типу указателя, при этом следует использовать ''явное преобразование типа'' (cast) одним из следующих способов: | Пару указателей <tt>void *</tt> можно сравнивать на равенство и неравенство, присваивать один указатель другому. Наконец, <tt>void *</tt> можно преобразовывать к другому типу указателя, при этом следует использовать ''явное преобразование типа'' (cast) одним из следующих способов: | ||
Строка 37: | Строка 37: | ||
В общем случае рекомендуется использовать форму (2). | В общем случае рекомендуется использовать форму (2). | ||
− | == | + | Чаще всего можно обойтись преобразованиями <tt>T* → void *</tt> и <tt>void * → T*</tt> (где <tt>T</tt> это некоторый тип), потому что в аргументах функций, выполняющих разные преобразования типов, обычно указывается <tt>void *</tt>. Однако бывают случаи, когда хочется выполнить преобразование <tt>T* → S*</tt>, минуя тип <tt>void *</tt>. В таких случаях есть две возможности: использовать старый стиль преобразований C (который вообще преобразует всё во всё) или новый стиль C++: |
+ | <source lang=Cpp> | ||
+ | char * pc; | ||
+ | int * pi; | ||
+ | // ... | ||
+ | pc = (char *)pi; // (1) старый стиль C | ||
+ | pc = reinterpret_cast<char *>(pi); // (2) новый стиль C++ | ||
+ | </source> | ||
+ | Обратите внимание, что такое преобразование считается менее безопасным и потому может быть произведено только с помощью <tt>reinterpret_cast</tt>, но не <tt>static_cast</tt>. | ||
+ | |||
+ | == Литература == | ||
<references /> | <references /> | ||
[[Категория:C++]] | [[Категория:C++]] |
Текущая версия на 19:33, 6 октября 2013
Содержание
Фундаментальные типы могут преобразовываться один в другой невероятно большим количеством способов. По-моему, дозволяется слишком много преобразований. |
Интегральные типы (int, short, char, bool, etc.) и типы с плавающей точкой в присваиваниях и арифметических выражениях можно свободно смешивать. Когда возможно, значения преобразуются так, чтобы не потерять информации. Неявные преобразования, сохраняющие значения, (например, bool к int) обычно называют продвижениями (promotion). К сожалению, преобразования, приводящие к потере значения, (например, присваивание вещественного числа целочисленной переменной, которое производится отбрасыванием дробной части) также могут осуществляться неявно.
Полный список правил преобразований можно прочитать в книге Б. Страуструпа[1], Приложение B, раздел 6.
Арифметические преобразования
В арифметических выражениях преобразования выполняются для того, чтобы привести значения к общему типу, который потом используется как тип результата. Если не учитывать беззнаковые типы, то можно удовлетвориться следующим набором неформальных правил:
- Если какой-либо из операндов принадлежит типу double, то и другой приводится к double.
- В противном случае, если какой-либо из операндов принадлежит типу float, то и другой приводится к float.
- В противном случае операнды типов short, char, bool приводятся к int (в случае bool значение false переводится в 0, а true — в 1).
- Наконец, если один из операндов типа long, то и другой приводится к long.
Условный оператор и условная операция (?:)
Следует помнить, что в качестве условия для условного оператора или условной операции может использоваться любое арифметическое выражение. В этом случае ненулевое значение трактуется как true, а 0 — как false.
Указатели
В целом, для неявного преобразования указателей отведено существенно меньше свободы, чем для обычных базовых типов.
Имеется специальный тип указателей: void * (подробнее см. [1], п. 5.6) — любой указатель может быть неявно преобразован к нему. Однако это правило не действует для указателей на функцию или на член класса. (Последний факт иногда используется при реализации сложных приёмов программирования на C++, таких как «умные указатели», чтобы обезопасить программиста от выполнения неявных преобразований.).
Пару указателей void * можно сравнивать на равенство и неравенство, присваивать один указатель другому. Наконец, void * можно преобразовывать к другому типу указателя, при этом следует использовать явное преобразование типа (cast) одним из следующих способов:
char * pc;
void * pv;
// ...
pc = (char *)pv; // (1) старый стиль C
pc = static_cast<char *>(pv); // (2) новый стиль C++
Конструкция (2) выглядит более громоздкой, но так сделано неспроста:
Подобная форма явного преобразования типа [от void * к char * в примере] небезопасна и некрасива. Соответственно, использованный в примере тип преобразования static_cast был спроектирован при разработке языка таким образом, чтобы явно напоминать об этом. Бьярне Страуструп
|
В общем случае рекомендуется использовать форму (2).
Чаще всего можно обойтись преобразованиями T* → void * и void * → T* (где T это некоторый тип), потому что в аргументах функций, выполняющих разные преобразования типов, обычно указывается void *. Однако бывают случаи, когда хочется выполнить преобразование T* → S*, минуя тип void *. В таких случаях есть две возможности: использовать старый стиль преобразований C (который вообще преобразует всё во всё) или новый стиль C++:
char * pc;
int * pi;
// ...
pc = (char *)pi; // (1) старый стиль C
pc = reinterpret_cast<char *>(pi); // (2) новый стиль C++
Обратите внимание, что такое преобразование считается менее безопасным и потому может быть произведено только с помощью reinterpret_cast, но не static_cast.