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

Я буду выкладывать сразу по 5 советов, чтобы не утомлять вас — в книге много ссылок на другие интересные статьи, видео и т. д. Однако, если вам не терпится, сразу перейти к полной версии книги: 60 ужасные советы для разработчика C++». В любом случае, приятного чтения!

Ужасный совет N46. Пишите свой код так, как будто вы тренируетесь для IOCCC.

Напишите свой код так, как будто его прочитает председатель жюри IOCCC и как будто они знают, где вы живете (чтобы прийти и вручить вам приз).

Это ссылка на цитату: Всегда кодируйте так, как будто парень, который в конечном итоге будет поддерживать ваш код, будет жестоким психопатом, который знает, где вы живете. Это фраза Джона Ф. Вудса, однако иногда ее приписывают Стиву МакКоннеллу, который процитировал ее в своей книге Code Complete.

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

IOCCC (International Obfuscated C Code Contest) — соревнование по компьютерному программированию. Участникам необходимо написать наиболее творчески обфусцированный код C в пределах размера исходного кода.

Кажется очевидным, почему плохо написанный код — это плохо. Но все же — почему? Программисты тратят большую часть своего времени не на написание кода, а на его чтение. Я не могу вспомнить источник и точные цифры, но, кажется, они тратят более 80% своего времени на чтение.

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

Ужасный совет N47. Получайте удовольствие при написании кода

Если разрывы строк и отступы в C++ несущественны, почему бы не написать код в виде кролика или белки?

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

Но я не собираюсь быть снобом. Я иногда писал креативные и юмористические комментарии, так что не вижу в этом никакого вреда. Все мы люди, и у всех нас есть эмоции. Так что все в порядке, если мы выражаем свои чувства в комментариях к коду. И все мы знаем, насколько сильными могут быть эти эмоции, когда дело доходит до кодинга :).

Ключевые принципы: не ломайте код, не перекомментируйте код и никого не оскорбляйте.

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

//        .==.        .==.          
//       //`^\\      //^`\\         
//      // ^ ^\(\__/)/^ ^^\\        
//     //^ ^^ ^/6  6\ ^^ ^ \\       
//    //^ ^^ ^/( .. )\^ ^ ^ \\      
//   // ^^ ^/\| v""v |/\^ ^ ^\\     
//  // ^^/\/ /  `~~`  \ \/\^ ^\\    
//  -----------------------------

Кстати, рекомендую взглянуть на разные необычные вещи, которые могут встречаться в комментариях к коду. Мой коллега написал об этом статью: «Комментарии к коду как произведение искусства».

Ужасный совет N48. У каждого свой стиль

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

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

Этого делать не следует по следующим причинам:

  • Ваш код, написанный в привычном для вас индивидуальном стиле, легче читать и понимать. Однако проект не является индивидуальной деятельностью. В результате работа с кодом, написанным разными способами, будет утомительна для всех членов вашей команды. Это просто пустая трата времени и умственной энергии. Даже если вы примете один стиль кодирования, поначалу все равно будут терпеть боль. Однако по мере того, как ваша команда со временем адаптируется к общему стилю, чтение и написание кода станет намного проще. Общая результативность команды также улучшится.
  • Столкновение стилей. Иногда необходимо исправить определенный фрагмент кода. Если каждый разработчик сделает это по-своему, то код будет выглядеть как лоскутное одеяло. Это приводит к отсутствию стиля кодирования — чистая анархия. Вы можете переписать чужой код под себя, но это займет много времени. С точки зрения автора кода, переписывание кода на самом деле вредно. Это, в общем-то, становится бессмысленным источником личных раздоров. Конечно, вы можете следовать стилю чужого кода при его редактировании. Но это сложно, и вы еще точно не знаете всех нюансов причуд кодинга вашего коллеги.
  • То, что кому-то нравится их стиль кодирования, не означает, что он хорош. Существует множество методов написания кода, которые могут помочь вам предотвратить ошибки. Некоторые из этих подходов описаны в моей мини-книге Главный вопрос программирования, рефакторинга и всего остального. Таким образом, опытные команды разработчиков выиграют от разработки и внедрения общего стандарта кодирования.

Надеюсь, я был достаточно убедителен. Единый стандарт кодирования в команде — очень полезная практика. За основу можно взять Руководство по стилю Google C++ или любое другое руководство по стилю. Затем адаптируйте его под себя. Еще один источник вдохновения можно найти здесь:

  • книга, о которой я упоминал здесь ранее, — «Code Complete» Стива МакКоннелла (ISBN 978–0735619678);
  • «Достаточно веревки, чтобы выстрелить себе в ногу» Аллена И. Холуба. Мне это не очень понравилось, но я не мог не упомянуть об этом. Это классика;
  • Стандартная основа С++. Часто задаваемые вопросы: стандарты кодирования.

Тема форматирования кода связана со стилем кодирования и стандартом кодирования. Разработчики могут проверять имена сущностей во время проверки кода. Что касается форматирования кода, то они могут использовать различные полезные инструменты. Самое главное договориться о том, как команда форматирует код, а дальше уже дело техники:

Ужасный совет N49. Перегрузить все

Для максимально возможного количества типов перегрузите как можно больше операторов, включая неарифметические. Придавая операторам другое значение, вы приближаетесь к созданию собственного языкового диалекта. Создание собственного языка — это весело. А что, если вы также добавите макросы…

Перегрузка оператора означает более элегантный вызов функции. Например, он позволяет использовать operator+ и operator= для строк. Это помогает написать элегантный код конкатенации строк:

result = str1 + str2;

Вы должны признать, что этот код намного аккуратнее, чем следующий:

result.assign(str1.strcat(str2));

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

Допустим, A, B, C являются экземплярами матричного класса. В этом случае следующий код понятен:

A = B * C;

Это другой случай:

A = B && C;

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

P.S. Кстати, однажды Бьерн Страуструп в шутку написал о включении перегрузки по пробелам: Обобщение перегрузки для C++2000.

Ужасный совет N50. Не верьте в эффективность std::string

Универсальный std::string неэффективен. realloc, strlen, strncat работают быстрее и эффективнее.

Тот факт, что производительность программы можно значительно повысить, отказавшись от класса std::string, является мифом. Однако для появления этого мифа должна быть причина.

Дело в том, что распространенные ранее реализации std::string были далеки от удовлетворительных. Так что, возможно, мы имеем дело не с мифом, а с устаревшей информацией.

Позвольте мне поделиться своим собственным опытом. С 2006 года мы разрабатываем статический анализатор PVS-Studio. В 2006 году она называлась Viva64, но это не имеет значения. Изначально мы широко использовали в анализаторе класс std::string.

Время прошло. Анализатор развивался, диагностик появлялось все больше, и с каждым релизом он работал все медленнее и медленнее :). Пришло время подумать об оптимизации кода. Профайлер указал на одно из узких мест — работу со строками. И тут мне пришла в голову цитата: «в любом проекте рано или поздно появляется пользовательский класс string». К сожалению, я не помню, откуда взялась эта цитата и когда именно это произошло. Кажется, это был 2008 или 2009 год.

Анализатор в процессе своей работы создает много пустых или очень коротких строк. Мы создали собственный класс строк — vstring, эффективно выделяющий память для таких строк. С точки зрения публичного интерфейса наш класс повторял std::string. Пользовательский строковый класс увеличил скорость работы анализатора примерно на 10%. Прохладный!

Этот строковый класс прослужил нам много лет, пока я не прослушал доклад Антона Полухина на конференции C++ Russia 2017 — Чего делать нельзя: как профессионалы C++ изобретают велосипед [RU]». В своем выступлении он сказал, что класс std::string был хорошо оптимизирован в течение многих лет. А те, кто использует свой строковый класс, — непрогрессивные динозавры :).

Антон рассказал аудитории, какие оптимизации сейчас используются в классе std::string. Например, из самого простого — про конструктор перемещения. Особенно меня интересовала Оптимизация малых строк.

Итак, мы больше не хотели быть динозаврами. Наша команда провела эксперимент — мы начали переключаться с пользовательского класса vstring обратно на std::string. Сначала мы просто закомментировали класс vstring и написали using vsstring = std::string;. К счастью, после этого потребовались небольшие правки кода в других местах, так как интерфейсы классов по-прежнему почти полностью совпадали.

И как изменилось время работы? Он вообще не изменился! Это означает, что для нашего проекта универсальный std::string стал таким же эффективным, как и наш собственный пользовательский класс, который мы создали около дюжины лет назад. Удивительный! Минус одно бессмысленное изобретение.

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

Во-первых, обработка C-строк (строки с завершающим нулем) провоцирует частое вычисление их длины. Если вы не будете хранить длину строк отдельно, будет сложно написать высокопроизводительный код. А если сохранить длину, то опять переходим к аналогу класса string.

Во-вторых, сложно написать надежный код с такими функциями, как realloc, strncat и т. д. Ошибки, обнаруженные PVS-Studio в различных проектах, подчеркивают, что код, использующий эти функции, напрямую притягивает ошибки. Вот шаблоны ошибок, обнаруженные при использовании strlen, strncat, realloc.

Автор: Андрей Карпов. Электронная почта: karpov[@] viva64.com.

Более 15 лет опыта работы в области статического анализа кода и качества программного обеспечения. Автор большого количества статей о качестве кода C++. Microsoft MVP for Developer Technologies с 2011 по 2021 год. Андрей Карпов — один из основателей проекта PVS-Studio. Долгое время был техническим директором компании и разработчиком ядра анализатора C++. В настоящее время он в основном занимается управлением командой, обучением сотрудников и DevRel.

Полный текст по ссылке: 60 ужасных советов C++-разработчику.

Подпишитесь на ежемесячную рассылку, чтобы быть в курсе последних статей автора и его коллег в PVS-Studio.