Является C++ явным преобразованием действительно это плохо?

Мое знание C++ в этой точке является более академическим, чем что-либо еще. Во всем моем чтении к настоящему времени, использовании явного преобразования с именованными бросками (const_cast, static_cast, reinterpret_cast, dynamic_cast) шел с большой маркировкой предупреждения (и легко видеть, почему), который подразумевает, что явное преобразование является симптоматическим для плохого дизайна и должно только использоваться как последнее прибежище при отчаянных обстоятельствах. Так, я должен спросить:

Явное преобразование с именованными бросками является действительно просто жюри, подстраивающим код, или является там более корректным и положительным приложением к этой функции? Существует ли хороший пример последнего?

10
задан Kevin Panko 15 June 2010 в 15:33
поделиться

6 ответов

Бывают случаи, когда без него просто не обойтись. Например, этот. Проблема в том, что у вас множественное наследование и вам нужно преобразовать указатель this в void*, при этом гарантируя, что указатель, переходящий в void*, будет по-прежнему указывать на правильный подобъект текущего объекта. Использование явного приведения - единственный способ добиться этого.

Существует мнение, что если вы не можете обойтись без приведения, то у вас плохой дизайн. Я не могу с этим полностью согласиться - возможны разные ситуации, в том числе и упомянутая выше, но, возможно, если вам приходится слишком часто использовать явные касты, то вы действительно плохо проектируете.

19
ответ дан 3 December 2019 в 13:44
поделиться

IMO, как и большинство вещей, это инструменты, с подходящим и несоответствующим использованием. Приведение, вероятно, является областью, в которой инструменты часто используются ненадлежащим образом, например, для приведения типов между int и типом указателя с reinterpret_cast (который может сломаться на платформах, где они разные sizes), или в const_cast для удаления константности просто как хакерство, и так далее.

Если вы знаете, для чего они нужны и для чего они предназначены, нет ничего плохого в том, чтобы использовать их по назначению.

3
ответ дан 3 December 2019 в 13:44
поделиться

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

С другой стороны, разработчик, который понимает их цель, когда их использовать, а когда нет, и каковы альтернативы, не пишет код, требующий большого количества приведений!


Ознакомьтесь с более подробными вариантами этих преобразований, такими как polymorphic_cast , в библиотеке ускоренных преобразований, чтобы получить представление о том, насколько осторожны программисты на C ++, когда дело касается преобразования типов.

1
ответ дан 3 December 2019 в 13:44
поделиться

Насколько плохое приведение обычно зависит от его типа. У всех этих слепков есть законное применение, но одни пахнут хуже, чем другие.

const_cast используется для отбрасывания const сущности (поскольку добавление не требует преобразования). В идеале это никогда не следует использовать. Это упрощает вызов неопределенного поведения (попытки изменить объект, изначально обозначенный const ), и в любом случае нарушает const -корректность программы. Иногда это необходимо при взаимодействии с API, которые сами не являются const -правильными, что может, например, запрашивать char * , когда они собираются рассматривать его как const char * , но поскольку вы не должны писать API таким образом, это признак того, что вы используете действительно старый API или кто-то облажался.

reinterpret_cast всегда будет зависеть от платформы и, следовательно, в лучшем случае вызывает сомнения в переносимом коде. Более того, если вы не выполняете низкоуровневые операции с физической структурой объектов, это не сохраняет смысла. В C и C ++ тип должен иметь смысл. int - это число, которое что-то означает; int , который по сути является конкатенацией char s, на самом деле ничего не значит.

dynamic_cast обычно используется для понижения; например от Base * до Derived * , при условии, что либо он работает, либо возвращает 0. Это подрывает объектно-ориентированный объект во многом так же, как оператор switch в тег типа выполняет: он перемещает код, определяющий класс, в сторону от определения класса. Это объединяет определения классов с другим кодом и увеличивает потенциальную нагрузку на обслуживание.

static_cast используется для преобразований данных, которые, как известно, являются в целом правильными, таких как преобразования в и из void * , известные безопасные преобразования указателей в иерархии классов и тому подобное. Худшее, что вы можете сказать о нем, - это то, что он в некоторой степени подрывает систему типов. Вероятно, это понадобится при взаимодействии с библиотеками C или частью C стандартной библиотеки, поскольку void * часто используется в функциях C.

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

6
ответ дан 3 December 2019 в 13:44
поделиться

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

1
ответ дан 3 December 2019 в 13:44
поделиться

Бывают ситуации, когда вы не можете избежать явного приведения. Особенно при взаимодействии с библиотеками C или плохо спроектированными библиотеками C++ (как COM библиотека sharptooth, использованная в качестве примера).

В целом, использование явных приведений - это красная селедка. Это не обязательно означает плохой код, но привлекает внимание к потенциально опасному использованию.

Однако не стоит бросать 4 каста в один мешок: static_cast и dynamic_cast часто используются для up-casting (от Base к Derived) или для навигации между родственными типами. Их появление в коде вполне нормально (действительно, трудно написать Visitor Pattern без одного из них).

С другой стороны, использование const_cast и reinterpret_cast гораздо опаснее.

  • использование const_cast для попытки изменить объект, доступный только для чтения, является неопределенным поведением (спасибо Джеймсу МакНеллису за исправление)
  • reinterpret_cast обычно используется только для работы с необработанной памятью (аллокаторами)

Они, конечно, имеют свое применение, но не должны встречаться в обычном коде. Однако для работы с внешними или C API они могут быть необходимы.

По крайней мере, это мое мнение.

12
ответ дан 3 December 2019 в 13:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: