Если я использую C-Style в моем проекте на С++, стоит ли рефакторинг выполнять С++?

Я использую C-style cast в моем проекте LOC С++ 15K, 90% времени для отливок между дочерними и базовыми классами.

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

У меня пока не было одной ошибки в моем проекте, которая была вызвана, например, случайно запутанным стилем C-Style.

Есть две основные причины, по которым я их не использовал:

  • Я еще не знал о них достаточно.
  • Мне не понравился их синтаксис, они более подробные и трудные для чтения для меня.

Мои вопросы:

  • (Почему) Должен ли я реорганизовать мой проект на использование стилей в стиле С++?
  • Почему я должен использовать С++-стиль для моих будущих проектов?

Я использую так же хорошо, как и все другие преимущества, предлагаемые С++ из OOP, включая виртуальные и абстрактные базовые классы, пространства имен, STL и т.д., а не синтаксис нового типа. Аргумент "Почему вы не используете C тогда?" не работает для меня.

Ответ 1

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

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

T* ptr = (T*) var;

Они могут не сразу определить, является ли это

  • Приведение из базового класса в производный класс или наоборот.
  • Отбрасывание const отбрасывается var
  • Приведение из интегрального типа в указатель.

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

T* ptr = static_cast<T*>(var);

или

T* ptr = const_cast<T*>(var);

Еще одна причина, чтобы предпочесть операторы литья в стиле С++, заключается в том, что они делают код более эластичным для изменения. Например, предположим, что у меня есть эта функция:

void DoSomething(Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Теперь предположим, что я понимаю, что эта функция не должна вносить какие-либо изменения в свой аргумент, поэтому я решил отметить ее const. Например:

void DoSomething(const Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Но теперь у нас есть проблема - мой стиль C-стиля, который использовался только для down down, теперь также удаляет const ness. Это может привести к легкой ошибке, когда DoSomethingElse мутирует указатель, который я передаю, хотя сам метод DoSomething promises не выполняет этого. Если вместо этого я написал этот код как

void DoSomething(Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

И затем измените код, добавив const:

void DoSomething(const Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

Теперь я получу ошибку компилятора, сообщающую мне, что мой старый бросок сломан, что может заставить меня обнаружить, что в коде есть логическая ошибка (а именно, что DoSomethingElse мутирует его аргумент, поэтому я могу " t наивно сделать ptr указатель на const.

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

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

Ответ 2

В компании, с которой я работал, мы как-то пришлось перенести несколько миллионов строк приложения кода на новую платформу. То, что начиналось как "просто еще один порт для еще одной платформы Unix" (код, уже запущенный на Windows, OSX и полдюжины Unix-подобных платформ), оказался огромной проблемой. IIRC, причина заключалась в том, что на этой платформе были строгие требования к выравниванию, и некоторые из наших бросков отключали аппаратные исключения из-за несоосности.

Это было бы достаточно легко исправить, если бы не тот факт, что определенный процент этих прикладов был C-style cast. Для них было невозможно эффективно grep. Поиски показали буквально десятки тысяч строк кода, которые все должны были быть проверены вручную человеческими существами, 99,99% которых были абсолютно неактуальны.
Это не помогло, чтобы мы люди склонны пропускать несколько из этих труднодоступных бросков, которые затем должны были бы быть найдены в другом раунде рассмотрения десятков тысяч строк кода, 99,99% из них точно такие же, как и в первом раунде.

Эта задача была почти невозможна для завершения в указанное время, а почти сломал компанию, потому что они столкнулись с серьезным штрафом по контракту, если мы пропустили крайний срок.

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

Ответ 3

(Почему) Должен ли я реорганизовать мой проект на использование стилей в стиле С++?

Да. По той же причине вы должны использовать их в будущем.

Почему я должен использовать С++-стиль для моих будущих проектов?

Список на самом деле довольно длинный, но я рассмотрю наиболее важные.

Во-первых, они четко заявляют о намерении броска. Читая "static_cast", вы знаете, что автор намеревался выполнить статическую актерскую интерпретацию, а не реинтерпрет или const.

Во-вторых, они делают одно и только одно. Например, вы не можете случайно отбросить constness.

Их легко найти. Найдите "const_cast", чтобы найти все приведения в/из const. Как вы это сделаете с помощью C-стиля? Вы не могли.

Они не меняют вид каста при изменении семантики локального кода. Например, (Widget*)(blah) бесшумно изменится на реинтерпрет, если Widget перестанет наследовать от любого типа, на котором был указатель blah. Он также изменяется на реинтерпрет, если определение Widget не доступно локально. Если вы используете приведения нового стиля, вместо этого вы получите рвоту компилятора, а не молчаливую интерпретацию оператора.

Вы можете сделать динамический бросок. C-style casts не может этого сделать.

Ответ 4

Ну две части:

(Почему) Должен ли я реорганизовать мой проект на использование стилей в стиле С++?

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

Почему я должен использовать С++-стиль для моих будущих проектов?

Потому что C-броски легко ошибаются.

Ответ 5

Если 90% ваших приведений относятся к подклассу к базовому классу, то удаление их будет началом - они не должны быть нужны. Если у вас много отливок с базы на суб, тогда вы делаете что-то еще не так.

В остальном вы говорите компилятору, что что-то странное происходит, и вы нарушаете правила - либо правила системы типов с reinterpret_cast, либо const_cast, вы рискуете представлять со статическим_cast до меньший интегральный тип, или вы нарушаете LSP и должны знать о конкретном типе подкласса при заданном базовом классе.

Сделать эти случаи явными - это хорошо.

Ответ 6

(Why) Should I refactor my project to use C++-style casts?
Why should I use C++-style casts for my future projects?

Есть несколько хороших ответов (включая мою собственную ИМО) на второй вопрос на этот вопрос.

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

Если у вас есть время для повторной факторизации, это будет хорошим кандидатом для этого.

Ответ 7

Если бы я был, я бы продолжал использовать C-style cast. У вас есть веские причины: вы не испытываете предполагаемых недостатков C-style casts, приведения С++ более подробные, С++-трансляции сложнее читать и менее знакомы вам.

В более общем плане, как программист на С++, вы часто будете читать или рассказывать, что какой-то способ делать вещи - это старое, неправильное, способное делать вещи и что вы должны использовать способ New, Correct, С++. Некоторые примеры:

  • вы должны использовать придания стилей С++ вместо стилей стиля C
  • вы должны использовать ссылки вместо указателей.
  • вам следует использовать ключевое слово "const" много
  • вы должны использовать generics вместо указателей void или типов
  • вы должны использовать списки инициализаторов вместо инициализации переменных-членов в теле конструкторов
  • вы должны использовать конструкторы вместо функции инициализации после построения
  • вы никогда не должны использовать макросы

Вам обычно будет сказано, что новый способ безопаснее, что позволяет компилятору улавливать больше ошибок и делает код более понятным.

Я предлагаю вам с подозрением посмотреть такой совет. Вместо этого учтите: использует ли с помощью новой функции С++ проблему, которая у вас есть на самом деле? Это создает новые проблемы (кстати, увеличение детализации кода и снижение удобочитаемости - это проблемы)? Если ответ на первый вопрос отсутствует, или ответ на первый вопрос - да, вы должны подумать о том, чтобы придерживаться способа "Старый, неправильный", "C".

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

Раньше я был тем человеком, который всегда пытался использовать Новый, Правильный, C + + способ. Я уже не настолько догматичен. Эссе на приведенной ссылке было большим фактором в изменении моего ума.

http://yosefk.com/c++fqa/