Что такое неявное и явное приведение типов
Еще раз про приведение типов в языке С++ или расстановка всех точек над cast
Этот пост попытка кратко оформить все, что я читал или слышал из разных источников про операторы приведения типов в языке C++. Информация ориентирована в основном на тех, кто изучает C++ относительно недолго и, как мне кажется, должна помочь понять cпецифику применения данных операторов. Старожилы и гуру С++ возможно помогут дополнить или скорректировать описанную мной картину. Всех интересующихся приглашаю под кат.
Приведение типов в стиле языка C (C-style cast)
Приведение типов в стиле языка C может привести выражение любого типа к любому другому типу данных (исключение это приведение пользовательских типов по значению, если не определены правила их приведения, а также приведение вещественного типа к указателю или наоборот). К примеру, unsigned int может быть преобразован к указателю на double. Данный метод приведения типов может быть использован в языке C++. Однако, метод приведения типов в стиле языка C не делает проверки типов на совместимость, как это могут сделать static_cast и dynamic_cast на этапе компиляции и на этапе выполнения соответственно. При этом все, что умеют const_cast и reinterpret_cast данный метод приведения типов делать может.
Общий вид приведения:
, где new_type – новый тип, к которому приводим, а exp – выражение, которое приводится к новому типу.
Т.к. данный оператор не имеет зарезервированного ключевого слова (например, static_cast) найти все места приведения типов в тексте программы будет не очень удобно, если это потребуется.
const_cast
Оператор приведения const_cast удаляет или добавляет квалификаторы const и volatile с исходного типа данных (простые типы, пользовательские типы, указатели, ссылки). Например, был const int, а после преобразования стал int или наоборот. Квалификаторы const и volatile называют cv-квалификаторы (cv-qualifiers). Данные квалификаторы указываются перед именами типов. Как ни трудно догадаться квалификатор const задает константность, т.е. защищает переменную от изменения. Квалификатор volatile говорит о том, что значение переменной может меняться без явного выполнения присваивания. Это обеспечивает защиту от оптимизации компилятором операций с данной переменной.
Общий вид приведения:
Дополнительный пример от пользователя 5nw
Квалификаторы const и volatile можно удалить или добавить только с помощью оператора приведения const_cast и приведения типов в стиле языка C. Другие операторы приведения типов не влияют на квалификаторы const и volatile (reinterpret_cast, static_cast, dynamic_cast).
reinterpret_cast
Оператор приведения reinterpret_cast используется для приведения несовместимых типов. Может приводить целое число к указателю, указатель к целому числу, указатель к указателю (это же касается и ссылок). Является функционально усеченным аналогом приведения типов в стиле языка С. Отличие состоит в том, что reinterpret_cast не может снимать квалификаторы const и volatile, а также не может делать небезопасное приведение типов не через указатели, а напрямую по значению. Например, переменную типа int к переменной типа double привести при помощи reinterpret_cast нельзя.
Общий вид приведения:
static_cast
Оператор приведения static_cast применяется для неполиморфного приведения типов на этапе компиляции программы. Отличие static_cast от приведения типов в стиле языка C состоит в том, что данный оператор приведения может отслеживать недопустимые преобразования, такие как приведение указателя к значению или наоборот (unsigned int к указателю на double не приведет), а также приведение указателей и ссылок разных типов считается корректным только, если это приведение вверх или вниз по одной иерархии наследования классов, либо это указатель на void. В случае фиксации отклонения от данных ограничений будет выдана ошибка при компиляции программы. При множественном наследовании static_cast может вернуть указатель не на исходный объект, а на его подобъект.
Общий вид приведения:
dynamic_cast
Оператор приведения dynamic_cast применяется для полиморфного приведения типов на этапе выполнения программы (класс считается полиморфным, если в нем есть хотя бы одна виртуальная функция). Если указатель, подлежащий приведению, ссылается на объект результирующего класса или объект класса производный от результирующего то приведение считается успешным. То же самое для ссылок. Если приведение невозможно, то на этапе выполнения программы будет возвращен NULL, если приводятся указатели. Если приведение производится над ссылками, то будет сгенерировано исключение std::bad_cast. Несмотря на то, что dynamic_cast предназначен для приведения полиморфных типов по иерархии наследования, он может быть использован и для обычных неполиморфных типов вверх по иерархии. В этом случае ошибка будет получена на этапе компиляции. Оператор приведения dynamic_cast может приводить указатель на полиморфный тип к указателю на void, но не может приводить указатель на void к другому типу. Способность dynamic_cast приводить полиморфные типы обеспечивается системой RTTI (Run-Time Type Identification), которая позволяет идентифицировать тип объекта в процессе выполнения программы. При множественном наследовании dynamic_cast может вернуть указатель не на исходный объект, а на его подобъект.
Руководство по программированию на C#. Приведение и преобразование типов
Неявные преобразования. Специальный синтаксис не требуется, так как преобразование всегда завершается успешно и данные не будут потеряны. Примеры включают преобразования из меньших в большие целочисленные типы и преобразования из производных классов в базовые классы.
Пользовательские преобразования. Такие преобразования выполняются специальными методами, которые можно определить для включения явных и неявных преобразований между пользовательскими типами без связи «базовый класс — производный класс». Дополнительные сведения см. в разделе Операторы пользовательского преобразования.
Преобразования с использованием вспомогательных классов. Чтобы выполнить преобразование между несовместимыми типами, например целыми числами и объектами System.DateTime или шестнадцатеричными строками и массивами байтов, можно использовать классы System.BitConverter и System.Convert, а также методы Parse встроенных числовых типов, такие как Int32.Parse. Дополнительные сведения см. в руководствах по преобразованию массива байтов в значение типа int, преобразованию строки в число и преобразованию из шестнадцатеричных строк в числовые типы.
Неявные преобразования
Полный список всех неявных числовых преобразований см. в разделе Таблица неявных числовых преобразований в статье Встроенные числовые преобразования.
Для ссылочных типов неявное преобразование всегда предусмотрено из класса в любой из его прямых или косвенных базовых классов или интерфейсов. Никакой специальный синтаксис не требуется, поскольку производный класс всегда содержит все члены базового класса.
Явные преобразования
Тем не менее если преобразование нельзя выполнить без риска потери данных, компилятор требует выполнения явного преобразования, которое называется приведением. Приведение — это способ явно указать компилятору, что необходимо выполнить преобразование и что вам известно, что может произойти потеря данных или приведение может завершиться сбоем во время выполнения. Чтобы выполнить приведение, укажите тип, в который производится приведение, в круглых скобках перед преобразуемым значением или переменной. В следующей программе выполняется приведение типа double в int. Программа не будет компилироваться без приведения.
Полный список всех поддерживаемых явных числовых преобразований см. в разделе Таблица явных числовых преобразований в статье Встроенные числовые преобразования.
Для ссылочных типов явное приведение является обязательным, если необходимо преобразовать базовый тип в производный тип:
Операция приведения между ссылочными типами не меняет тип времени выполнения базового объекта; изменяется только тип значения, который используется в качестве ссылки на этот объект. Дополнительные сведения см. в разделе Полиморфизм.
Исключения преобразования типов во время выполнения
В некоторых преобразованиях ссылочных типов компилятор не может определить, будет ли приведение допустимым. Есть вероятность, что правильно скомпилированная операция приведения завершится сбоем во время выполнения. Как показано в следующем примере, приведение типа, завершившееся сбоем во время выполнения, вызывает исключение InvalidCastException.
Преобразования типов и безопасность типов
В этом документе описаны распространенные проблемы преобразования типов и описывается, как избежать их использования в коде C++.
Когда компилятор обнаруживает ненадежное преобразование, он выдает ошибку или предупреждение. Произошла ошибка при остановке компиляции. Предупреждение позволяет продолжить компиляцию, но указывает на возможную ошибку в коде. Однако даже если программа компилируется без предупреждений, она по-прежнему может содержать код, который вызывает неявные преобразования типов, приводящие к неправильным результатам. Ошибки типов также могут вводиться явными преобразованиями или приведениями в коде.
Неявные преобразования типов
Если выражение содержит операнды различных встроенных типов и явные приведения отсутствуют, компилятор использует встроенные стандартные преобразования для преобразования одного из операндов, чтобы типы совпадали. Компилятор пытается выполнить преобразования в четко определенной последовательности, пока она не завершится успешно. Если выбранное преобразование является повышением, компилятор не выдает предупреждение. Если преобразование является узким, компилятор выдает предупреждение о возможной утрате данных. Происходит ли фактическая потери данных, зависит от фактических значений, но рекомендуется считать это предупреждение как ошибку. Если включен определяемый пользователем тип, компилятор пытается использовать преобразования, указанные в определении класса. Если не удается найти допустимое преобразование, компилятор выдает ошибку и не компилирует программу. Дополнительные сведения о правилах, регулирующих стандартные преобразования, см. в разделе стандартные преобразования. Дополнительные сведения о пользовательских преобразованиях см. в разделе пользовательские преобразования (C++/CLI).
Расширяющие преобразования (продвижение)
В расширяющем преобразовании значение меньшей переменной присваивается более крупной переменной без потери данных. Поскольку расширяющие преобразования всегда являются надежными, компилятор выполняет их автоматически и не выдает предупреждения. Следующие преобразования являются расширяющими преобразованиями.
Сужающие преобразования (приведение)
Компилятор выполняет сужающие преобразования неявным образом, но предупреждает о возможной потере данных. Выведите эти предупреждения очень серьезно. Если вы уверены, что не произойдет потери данных, так как значения в переменной большего размера всегда помещаются в меньшую переменную, добавьте явное приведение, чтобы компилятор больше не выдавал предупреждение. Если вы не уверены, что преобразование является надежным, добавьте в код какую-либо проверку среды выполнения для обработки возможной потери данных, чтобы она не вызывала неправильные результаты.
Преобразование из типа с плавающей запятой в целочисленный тип является узким преобразованием, так как дробная часть значения с плавающей запятой отбрасывается и теряется.
В следующем примере кода показаны некоторые неявные сужающие преобразования и предупреждения, которые возникают компилятором.
Преобразования со знаком — без знака
Компилятор не предупреждает о неявных преобразованиях между целыми типами со знаком и без знака. Поэтому рекомендуется полностью избегать беззнаковых преобразований. Если вы не можете избежать их, добавьте проверку среды выполнения, чтобы определить, является ли преобразуемое значение большим или равным нулю и меньше или равно максимальному значению типа со знаком. Значения в этом диапазоне будут передаваться из входных файлов в неподписанный или из неподписанных в подписывание без переинтерпретации.
Преобразования указателей
Явные преобразования (приведения)
С помощью операции приведения можно указать компилятору преобразовать значение одного типа в другой тип. В некоторых случаях компилятор вызовет ошибку, если эти два типа полностью не связаны, но в других случаях не вызывает ошибку, даже если операция не является строго типизированной. Используйте приведение с осторожностью, так как любое преобразование из одного типа в другой является потенциальным источником ошибок программы. Однако иногда требуется выполнить приведения, а не все приведения являются опасными. Одно эффективное использование приведения заключается в том, что в коде выполняется понижающие преобразования и известно, что преобразование не приводит к созданию неверных результатов в программе. Фактически, это говорит компилятору о том, что вы делаете, а также о том, что вы выполняете предупреждения. Другой способ заключается в приведении из класса указателя на класс, производный от указатель на базовый. Другой способ — приведение к переменной постоянной, чтобы передать ее в функцию, для которой требуется аргумент, не являющийся константой. Большинство этих операций приведения к некоторым рискам требует определенного риска.
В программировании в стиле C для всех типов приведений используется один и тот же оператор приведения в стиле C.
Этот оператор приведения не используется так часто, как другие, и не гарантирует перенос в другие компиляторы.
Дополнительные сведения см. в разделе оператор.
Преобразование типов в C++
С++ позволяет нам преобразовывать данные одного типа в данные другого – это известно как преобразование типов.
В C++ существует два типа преобразования:
Неявное преобразование типов
Преобразование типа, которое автоматически выполняется компилятором, известно как неявное преобразование. Этот тип также известен, как автоматическое преобразование.
Давайте посмотрим на два примера неявного преобразования типов.
Пример 1: из int в double
В программе мы присвоили данные типа int переменной типа double.
Пример 2: из double в int
В программе мы присвоили данные типа double переменной типа int.
Примечание. Поскольку int не может иметь десятичную часть, цифры после десятичной точки в приведенном выше примере усекаются.
Потеря данных (сужение преобразования)
Как мы видели из приведенного выше примера, преобразование из одного типа данных C++ в другой в С++ подвержено потере данных. Это происходит, когда данные большего типа преобразуются в данные меньшего типа.
Явное преобразование
Когда пользователь в C++ вручную меняет данные с одного типа на другой, это называется явным преобразованием. Этот тип преобразования также известен, как приведение типов.
Есть три основных способа использования явного преобразования в C++:
C-style
Как следует из названия, этому типу приведения предпочитает язык программирования C. Это также известно как обозначение приведения.
Синтаксис этого стиля:
Использование функционального стиля
Мы также можем использовать такую функцию, как нотация, для преобразования данных из одного типа в другой.
Пример 3: приведение типов
Мы использовали преобразование типа в стиле C и преобразование в функциональный стиль, и отобразили результаты. Поскольку они выполняют одну и ту же задачу, оба дают нам одинаковый результат.
Операторы преобразования типов
Помимо этих двух приведений типов, C++ также имеет четыре оператора преобразования типов:
Приведение типа
Приведе́ние ти́па (type conversion) — преобразование значения переменной одного типа в значение другого типа. Выделяют явное и неявное приведения типов.
Содержание
Неявное приведение
Само приведение происходит как во время присваивания значения переменной, так и при операциях сравнения, вычисления выражения. При использовании в выражении несколько различных типов значения одного или нескольких подтипов может быть осуществлено преобразование к более общему типу (супертипу), с большим диапазоном возможных значений.
Каждый раз при выполнени операции сравнения или присваивания переменные разных типов будут приведены к единому типу. Следует с осторожностью использовать неявное приведение типа. При переводе числа из вещественного типа в целочисленный, дробная часть отсекается. Обратное приведение из целочисленного типа к вещественному также может привести к понижению точности, что связано с различным представлением вещественных и целочисленных чисел на машинном уровне. К примеру, вещественный тип single стандарта IEEE 754 не может точно представить число 16777217, в то время как 32-битный целочисленный тип может. Это может привести к ситуациям, когда сравнение на равенство одного и того же числа, представленного типами (int и single) будет выдавать ложный результат (числа не равны друг другу).
Явное приведение типа
В языке C
Для явного приведения типов некоторой переменной перед ней следует указать в круглых скобках имя нового типа, например:
В языке C++
В языке C++ существует четыре разновидности приведения типа. Все четыре типа записываются в виде
Громоздкие ключевые слова являются напоминанием программисту, что приведение типа чревато проблемами.
static_cast
dynamic_cast
const_cast
reinterpret_cast
См. также
Полезное
Смотреть что такое «Приведение типа» в других словарях:
Приведение типов — Приведение типа (type conversion) весьма важная часть многих языков программирования, в особенности функциональных. Означает этот термин следующее: если в какой то момент тип переменной в составе выражения не задан, он просчитывается (выводится)… … Википедия
Понижающее приведение — В объектно ориентированном программировании, понижающее приведение (англ. downcasting) или «уточнение типа» (англ. type refinement) акт приведения типа, ссылающегося на базовый класс, к одному из его производных классов. В… … Википедия
Санкт-Петербургская психиатрическая больница специализированного типа с интенсивным наблюдением — Санкт Петербургская психиатрическая больница (стационар) специализированного типа с интенсивным наблюдением … Википедия
Динамическая идентификация типа данных — Не следует путать с динамической типизацией. Динамическая идентификация типа данных (англ. Run time type information, Run time type identification, RTTI) механизм в некоторых языках программирования, который позволяет определить тип… … Википедия
Динамическая идентификация типа — данных (англ. Run time Type Information, Run time Type Identification, RTTI) механизм, реализованный в языках программирования, который позволяет определить тип данных переменной или объекта во время выполнения программы. Содержание 1 Реализация … Википедия
ГОСТ 22615-77: Система «человек-машина». Выключатели и переключатели типа «Тумблер». Общие эргономические требования — Терминология ГОСТ 22615 77: Система «человек машина». Выключатели и переключатели типа «Тумблер». Общие эргономические требования оригинал документа: 1. Выключатель (переключатель) типа «Тумблер» выключатель (переключатель),… … Словарь-справочник терминов нормативно-технической документации
Сравнение C Sharp и Java — Правильный заголовок этой статьи Сравнение C# и Java. Он показан некорректно из за технических ограничений. Сравнения языков программирования Общее сравнение Основной синтаксис Основные инструкции Массивы Ассоциативные массивы Операции со… … Википедия
Тип данных — (встречается также термин вид данных) фундаментальное понятие теории программирования. Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям и, возможно, способ реализации хранения значений и… … Википедия
Валютный тип — Тип данных Содержание 1 История 2 Определение 3 Необходимость использования типов данных … Википедия
Типизация данных — Тип данных фундаментальное понятие теории программирования. Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям, и, возможно, способ реализации хранения значений и выполнения операций. Любые… … Википедия