Вы отметили это тремя языками, и ответы на них действительно сильно различаются. Обсуждение C ++ более или менее подразумевает обсуждение приведений C, что дает (более или менее) четвертый ответ.
Поскольку это тот, который вы не упомянули явно, я начну с C. Приведения C имеют ряд проблем. Во-первых, они могут делать что угодно. В некоторых случаях приведение не делает ничего, кроме как сказать компилятору (по сути): «Заткнись, я знаю, что делаю», т. Е. Гарантирует, что даже когда вы выполняете преобразование, которое может вызвать проблемы, компилятор не будет предупреждать вас об этих потенциальных проблемах. Например char a=(char)123456;
,. Определен точный результат этой реализации (зависит от размера и подписиchar
), и, за исключением довольно странных ситуаций, вероятно, бесполезен. C-приведения также различаются в зависимости от того, происходит ли это только во время компиляции (то есть вы просто указываете компилятору, как интерпретировать / обрабатывать некоторые данные) или что-то, что происходит во время выполнения (например, фактическое преобразование из двойного в длинный).
C ++ пытается справиться с этим, по крайней мере, до некоторой степени, добавляя ряд «новых» операторов приведения, каждый из которых ограничивается только подмножеством возможностей приведения C. Это затрудняет (например) случайное выполнение преобразования, которое вы действительно не планировали - если вы намереваетесь только отбросить константу на объекте, вы можете использовать const_cast
и быть уверены, что единственное, на что это может повлиять, - это то, объект находится const
, volatile
или нет. И наоборот, a static_cast
не может влиять на то, является ли объект const
илиvolatile
. Короче говоря, у вас есть большинство тех же типов возможностей, но они разделены на категории, поэтому одно приведение обычно может выполнять только один вид преобразования, где одно приведение в стиле C может выполнять два или три преобразования за одну операцию. Основным исключением является то, что вы можете использовать a dynamic_cast
вместо a static_cast
по крайней мере в некоторых случаях, и, несмотря на то, что он был написан как a dynamic_cast
, он действительно закончится как static_cast
. Например, вы можете использовать его dynamic_cast
для перемещения вверх или вниз по иерархии классов, но приведение иерархии вверх всегда безопасно, поэтому его можно выполнять статически, в то время как приведение иерархии вниз не обязательно безопасно, поэтому сделано динамически.
Java и C # намного больше похожи друг на друга. В частности, для них обоих приведение (фактически?) Всегда является операцией времени выполнения. Что касается операторов приведения C ++, он обычно наиболее близок к a dynamic_cast
с точки зрения того, что на самом деле сделано - то есть, когда вы пытаетесь привести объект к некоторому целевому типу, компилятор вставляет проверку во время выполнения, чтобы увидеть, разрешено ли это преобразование. , и выбросить исключение, если это не так. Точные детали (например, имя, используемое для исключения "плохого приведения") варьируются, но основной принцип остается в основном аналогичным (хотя, если память обслуживает, Java действительно применяет приведение к нескольким типам, не являющимся объектами, например, int
намного ближе к C приведение типов - но эти типы используются достаточно редко, чтобы 1) я этого точно не помню и 2) даже если это правда, это все равно не имеет большого значения).
Если смотреть на вещи в более общем плане, ситуация довольно проста (по крайней мере, IMO): приведение (очевидно, достаточно) означает, что вы конвертируете что-то из одного типа в другой. Когда / если вы это делаете, возникает вопрос: «Почему?» Если вы действительно хотите, чтобы что-то было определенным типом, почему вы не определили его для начала? Это не значит, что никогда не было причин для такого преобразования, но в любое время, когда это происходит, это должно вызывать вопрос о том, можно ли изменить дизайн кода, чтобы во всем использовался правильный тип. Даже кажущиеся безобидными преобразования (например, между целым числом и с плавающей запятой) следует исследовать гораздо более внимательно, чем это принято. Несмотря на кажущеесяподобия, целые числа действительно должны использоваться для «подсчитываемых» типов вещей и с плавающей запятой для «измеренных» типов вещей. Игнорирование различия - вот что приводит к некоторым сумасшедшим заявлениям вроде «в средней американской семье 1,8 ребенка». Несмотря на то, что все мы видим, как это происходит, факт остается фактом: ни в одной семье нет 1,8 детей. У них может быть 1, может быть 2 или больше, но никогда не будет 1,8.