Есть много способов решить одну и ту же проблему в C или C++. Это и хорошо, и плохо; это хорошо, потому что у вас есть гибкость. Но гибкость — это в то же время и плохо, потому что приходится выбирать различные решения одной и той же проблемы, когда она появляется в разных местах. Это сбивает с толку, потому что скрывает фундаментальное сходство между проблемами.
Использование функций
В отличие от прозы, где повторение одних и тех же слов или фраз может показаться излишним, в программировании можно использовать одну и ту же конструкцию снова и снова. Конечно, вы, возможно, захотите превратить повторяющийся кусок кода в функцию: это даже более читабельно, поскольку дает блоку кода наглядное имя. (По крайней мере, вы должны сделать его наглядным!)
Вы также можете улучшить читабельность с помощью стандартных функций и структур данных (таких как STL). Это позволяет избежать смятения, если кто-то спросит: «Почему вы создаете новую функцию, когда у вас уже есть достаточно хорошая?» Проблема в том, что люди считают, что для новой функции должна быть причина, и что она должна как-то отличаться от стандартной версии.
Кроме того, с помощью стандартных функций вы помогаете читателю понять имена аргументов в функции. Нет необходимости смотреть на прототип функции чтобы увидеть, что означают аргументы или их порядок, или имеют ли некоторые аргументы значения по умолчанию.
Используйте соответствующие функции языка
Есть некоторые очевидные вещи, которых следует избегать: не используйте цикл в качестве условного оператора. Выбирайте правильный тип данных для хранения данных: если вам не нужны знаки после запятой в числе, используйте целое число. Если вам нужно беззнаковое значение, используйте число без знака. Если вы хотите указать, что значение никогда не должно меняться, используйте константы.
Старайтесь избегать необычных конструкций, если только у вас нет на то веской причины, говоря другими словами, не используйте функцию просто потому, что она существует. Первое правило — избегайте циклов do while
, если только они вам не крайне необходимы. Люди не привыкли их видеть и, в теории, будут плохо понимать их. Я никогда не сталкивался с этой проблемой сам, но подумайте, на самом ли деле нужен цикл do while
. Аналогичным образом, хотя тернарный оператор является отличным способом выразить некоторые идеи, он также может быть очень сложным для программистов, которые не используют его часто. Хорошим правилом является использование его только в случае необходимости (например, в списке инициализации конструктора) и оставить более стандартную конструкцию if else
для всего остального. Конечно, это сделает вашу программу на четыре строки больше, но большинству людей будет легче ее читать.
Есть некоторые менее очевидные способы использования стандартных функций. Когда вы создаете цикл, тщательно выбирайте между while
, do while
и for
. Цикл for
лучше всего использовать, когда вы можете заполнить каждую его часть (начальная инициализация счётчика, условное выражение и инкремент) довольно коротким выражением. Циклы while
хорошо подходят для просмотра сигнальных переменных, значение которых может быть установлено в нескольких местах или величина которых зависит от некоторых внешних событий. Циклы while
также хороши, когда шаг обновления на самом деле не прямое «обновление» управляемой переменной — например, при чтении строк из текстового файла может быть более целесообразно использовать цикл while
, чем цикл for
, потому что продолжительность цикла зависит от результата вызова метода, а не от значения управляющей переменной:
while ( fgets(buf, sizeof(buf), fp) != NULL) { /* работа с буфером */ }
Нет смысла писать цикл for
. (Если не верите, просто попробуйте, и если у вас это получится, то вы поймете, что в этом случае цикл for лучше не использовать).
Разделение сложных выражений
Нет причин писать все на одной строке. Если у вас есть сложные вычисления в несколько шагов и уровней скобок, имеет смысл перейти от однострочных вычислений на те, что используют временные переменные. Это дает два преимущества: во-первых, легче следить за выражением. Во-вторых, вы можете дать уникальное имя каждому промежуточному шагу, который может помочь читателю видеть что происходит. В любом случае, вы можете захотеть повторно использовать эти промежуточные вычисления. В дополнение к математическим расчетам, этот принцип также распространяется на вложенные вызовы функций. Чем меньше событий, которые происходят на одной строке кода, тем легче видеть то, что происходит.
Еще одно преимущество разделения выражения в том, что вы можете написать больше комментариев в строке, чтобы объяснить, что происходит и почему.
Избегайте магических чисел
Так называемые, магические числа — это числа, которые появляются непосредственно в коде без причины. Например, что означает число 80 в следующем выражении?
for( int counter = 0; counter < 80; ++counter ) { printf( "-" ); }
Это может быть ширина экрана, но также это может быть ширина карты, на которой рисуется стена. Вы просто не знаете, почему в цикле стоит значение 80. Лучшим решением является использование макросов в C или констант в C++. Это дает вам возможность наглядно называть ваши числа. Это также помогает легче обнаружить использование определенного числа и различать числа с тем же значением, но разным смыслом. Кроме того, если вы решите, что нужно изменить значение, у вас есть одно место, куда вы можете внести изменения, вместо того, чтобы просматривать весь код.