Я участвовал в разработке стандартов кодирования, которые были довольно сложными. По моему собственному опыту, это было трудно обеспечить, если у вас нет надлежащих процессов для его поддержки и стратегий для его поддержки.
Теперь я работаю и возглавляю среду, в которой еще менее вероятно, что через какое-то время будут иметь место процессы и стратегии выполнения. Тем не менее, я хочу поддерживать некоторый минимальный уровень респектабельного кода. Так что я подумал, что получу здесь хорошие предложения, и мы могли бы вместе создать разумно легкое подмножество наиболее важных стандартных практик кодирования, которое другие могли бы использовать в качестве справочника.
Итак, чтобы подчеркнуть суть здесь:
1 кандидат на ответ, предпочтительно с мотивацией краткий.
Кандидаты Голосовать против, которые фокусируются на правилах стиля и субъективного форматирования. Это не означает, что они неважны, а только то, что они менее актуальны в данном контексте.
Кандидаты Голосовать против фокусируются на том, как комментировать / документировать код. Это более обширная тема, которая, возможно, даже заслуживает отдельного поста.
Кандидаты Проголосовать, которые явно обеспечивают более безопасный код, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.
Не голосуй в любом направлении на кандидатов, в которых вы не уверены. Даже если они звучат разумно и умно или, наоборот, «что-то, что наверняка никто не будет использовать», ваш голос должен основываться на ясном понимании и опыте.
Альтернативный подход - взять существующий стандарт кодирования (например, AV JSF, MISRA C++, www.codingstandard.com), а затем опубликовать правила, которые, по вашему мнению, являются хорошими кандидатами.
как стиль может иметь отрицательное значение. Вы просите лучших практик в стандартах, а затем настаиваете на игнорировании лучших. Код, в котором каждый выбирает свое собственное соглашение об именах (например), является наихудшим из возможных. Голосуйте как хотите, братья!
@gbjbaanb: Пожалуйста, попытайтесь понять цель здесь. Стиль важен, но если вы должны были выбрать (скажем) 10 самых важных вещей, которые нужно иметь в своем легком и удобном для просмотра стандарте, в отсутствие каких-либо процессов, тогда стиль должен иметь довольно низкий приоритет.
Мне нравится, что в настоящее время два верхних варианта «держать функции до 25 строк» и «разбрасывать утверждения повсюду» почти взаимно противоречат друг другу. Если вы хотите сделать и то, и другое, купите более высокий монитор ;-)
@Robert, я считаю процесс частью стиля - это «мягкая» сторона стандартов. Убедитесь, что в стандарте говорится о том, как управлять им более доступным и структурированным способом, не говоря людям использовать утверждения или иметь 25 строковых функций. например. Используемая структура каталогов - мой главный приоритет.





Если используемый набор инструментов (или планируемое использование) имеет неэффективную реализацию исключения, было бы разумно избегать их использования. Я работал в таких условиях.
Обновление: здесь - это чье-то обоснование для «Embedded C++», которое, кажется, исключает исключения. Он отмечает следующие моменты:
На этой странице есть более сложный текст, я не хотел его все копировать. Кроме того, ему 10 лет, поэтому он может больше не пригодиться, поэтому я включил часть о цепочке инструментов. Возможно, здесь также следует читать «если память не считается серьезной проблемой» и / или «если предсказуемая реакция в реальном времени не требуется» и т. д.
Очень интересно. Пожалуйста, отредактируйте и уточните, почему это так.
аргумент, специфичный для домена. если вы не используете исключения, вам нужно понимать, почему они такие классные, иметь метрики, документирующие, почему они не достаточно «какие-то» и насколько еще «какими-то» они должны быть, и быть действительно расстроенным тем, что вы не можете используй их. Только тогда вы должны прекратить их использовать.
Есть очень конкретные причины не использовать исключения, однако EC++ не является исчерпывающим источником, который я бы использовал. EC++ запрещает множество функций C++ и фактически заставляет комитет ISO C++ отреагировать на утверждения, сделанные стандартом.
Единственная веская причина, по которой я читал, чтобы не использовать исключения, - это когда мы имеем дело с устаревшим кодом, который ничего не делает с исключениями - см. Стандарт кодирования Google C++.
Ричар Корден прав. Просто прочитав en.wikipedia.org/wiki/Embedded_C%2B%2B, и то, что они удалили пространства имен, шаблоны и приведения C++, показывают, что AC++ был скорее шуткой, чем что-либо еще
Хорошее замечание о разных стандартах, необходимых для разных сред. Например, для авиационной авионики на веб-сайте потребуются другие.
Имена методов и переменных в общей схеме именования для единообразия; Я не склонен сильно беспокоиться о чем-либо другом при чтении исходного кода.
Нет вкладок (позволяет лучше использовать внешние / другие инструменты) и фиксированные пробелы, вставленные для вкладок.
субъективный стиль / форматирование. Я предпочитаю вкладки.
Я согласен с этим. Вкладки меняют форматирование в разных редакторах, а пробелы единообразны.
-1. Табуляторы всегда должны использоваться для отступов, все остальное может быть пробелами, но табуляции всегда должны использоваться для отступов.
@owens, в том-то и дело, мой редактор может показать мне это так, как я люблю.
Табуляция против пробелов - священная война. Не нужно здесь спорить.
Табуляторы представляют собой форму инкапсуляции и предпочтительнее пробелов. Любой может изменить размер; вкладок, но не пробелов.
Это одна из областей, в которой вам нужно выбрать правило (или на самом деле вставил инструмент, а не просто махать рукой и говорить, что инструмент мог делает это). Код должен состоять либо из табуляции, либо из пробелов, все, что находится между ними, не работает.
@soru: есть также code-block-indent-tabs + statement-indent-space, которые дают вам гибкость табуляции, но не нарушают внешний вид многострочных операторов (например, <tab><tab>const float f = ..., <tab><tab><12spaces>x = ...\n (попробуйте в своем редакторе); к сожалению, отсутствие соблюдения этого правила в редакторах (и отсутствие того, чтобы люди понимали, о чем идет речь) заставило меня "отступить" ко всем пространствам, но я не религиозен в этой теме
Фигурные скобки необходимы, если у вас более одного шага отступа:
if (bla) {
for (int i = 0; i < n; ++i)
foo();
}
Это помогает сохранить отступ в соответствии с тем, как компилятор видит код.
Мне это нравится больше, чем «всегда использовать фигурные скобки».
«не всегда» - это один из вариантов, который усложняет понимание кода мозгом. «всегда» приводит к 1. меньшему выбору => меньшему количеству времени, затрачиваемому на выбор и 2. меньшему количеству причин писать ошибки, упущенные из-за отступов (особенно для людей, которые также пишут на python ...)
формат для удобочитаемости с фигурными скобками или пробелами, но я, конечно, думаю, что их требование произвольно.
@xtofl: даже если вы всегда должны делать это одним способом, компилятор требует больше, поэтому случаи игнорирования неизбежны. Я бы сказал, что брекеты для мозгового анализа не отягощают мозг - они становятся рутинными.
Сохраняйте функции разумного размера. Лично мне нравится, когда функции не превышают 25 строк. Читаемость улучшается, когда вы можете рассматривать функцию как единое целое, вместо того, чтобы сканировать вверх и вниз, пытаясь понять, как она работает. Если вам нужно прокрутить, чтобы прочитать его, это еще больше усугубит ситуацию.
Это действительно важно. И это следствие того, что «функция должна делать только одно, и делать это хорошо».
Я работал над заданиями, в которых был установлен предел сложности McCabe для всех функций. Порой отключение было произвольным, но общий эффект вынудил выполнять более мелкие процедуры, и это было здорово.
Только банальное использование символа? : оператор, т.е.
float x = (y > 3) ? 1.0f : -1.0f;
нормально, но это не так:
float x = foo(2 * ((y > 3) ? a : b) - 1);
Я согласен с духом этого правила, но думаю, что приведенный вами нетривиальный пример вполне подойдет. Я бы запретил вложенные тернарные операторы.
Тернарный оператор следует использовать для случаев, когда результат важнее теста. Кстати, очень полезно использовать const проактивно.
На самом деле, второй пример нарушает мое правило - только одна вещь в строке. Назначение с оператором? сделает этот код читабельным. Короче говоря, это не пример плохого оператора? использовать.
Этого не должно быть в стандарте кодирования, потому что нет объективного определения хорошего / плохого. Это будет рассмотрено во вступительном заявлении «Пишите понятный, поддерживаемый код».
Я согласен с Ферруччо. Этот пример в порядке, это вложенная форма, которая вызывает у меня головную боль.
+0: Я не согласен. Арифметику почти всегда следует предпочесть условным операторам, даже встроенным. Уменьшает цикломатическая сложность
@Ferrucio: вы даже можете сделать вложенный файл читаемым, хотя я бы подумал о замене их именованными функциями. Пример удобочитаемости (откройте любимый редактор и замените \ n на \ n (D'oh): const Foo x = frob ? bar : \n frob ? bar : \n frob ? bar : \n damn; (отредактируйте: и выровняйте их должным образом; SO-комментарии не подходят для кода ...)
Сортировка функций в объявлениях и определениях классов по имени. Это упрощает поиск их в файле .cpp. Кроме того, это освобождает ваш разум, потому что вам не нужно думать о том, где разместить новую функцию.
Я группирую свои методы по функциональности - например, string :: begin () будет рядом с string :: end ()
Почему бы не объединить все ваши функции, определения классов и т. д. В один файл и не расположить их в алфавитном порядке? Почему бы не расположить списки #include в алфавитном порядке? Почему бы не расположить инструкции по алфавиту?
Ага, это глупо. Я согласен с Дастином.
Начиная с 70-х годов (или даже раньше?), Поиск может выполняться с помощью средства поиска вашего любимого редактора, а с 90-х годов все больше и больше IDE имеют первоклассную поддержку для этого. С «find» удобно то, что он работает не только в вашем программном редакторе, но и во всех веб-браузерах, в выбранной вами графической ОС, в текстовом процессоре и многих других. По умолчанию "найти" вызывается нажатием <ctrl> + <f>.
Я думаю, вы, ребята, должны попробовать, прежде чем отклонить это. Code Complete рекомендует это - я пробовал и никогда не оглядывался.
Убедитесь, что уровень предупреждений вашего компилятора установлен достаточно высоко (желательно / Wall), чтобы он обнаруживал такие глупые ошибки, как:
if (p = 0)
когда ты действительно имел в виду
if (p == 0)
так что вам не придется прибегать к еще более глупым уловкам вроде:
if (0 == p)
которые ухудшают читаемость вашего кода.
... и скомпилировать с WISE (Предупреждение - ошибка)
Другой способ: если p равно const, то if (p = 0) все равно не компилируется, но это другой пост ...
Для модерации см. stackoverflow.com/questions/237719 Я сам удивлен и недоволен тем, насколько сильно это задело. Тем не менее, я считаю, что это ценное правило!
К сожалению, библиотеки C++ MSVC содержат много ошибок с /W4 и сотни ошибок с /Wall.
@Mooing Duck: Я столкнулся с этой проблемой. К счастью, это решаемо: stackoverflow.com/questions/525677/…
утверждать все предположения, включая временные предположения, например, нереализованное поведение. утверждать условия входа и выхода из функции, если это нетривиально. утверждать все нетривиальные промежуточные состояния. ваша программа никогда не должна аварийно завершить работу без сбоя утверждения assert. вы можете настроить свой механизм утверждения, чтобы игнорировать будущие события.
Use error-handling code for conditions you expect to occur; use assertions for conditions that should never occur. Error handling typically checks for bad input data; assertions check for bugs in the code.
If error-handling code is used to address an anomalous condition, the error handling will enable the program to respond to the error gracefully. If an assertion is fired for an anomalous condition, the corrective action is not merely to handle an error gracefully—the corrective action is to change the program's source code, recompile, and release a new version of the software. A good way to think of assertions is as executable documentation—you can't rely on them to make the code work, but they can document assumptions more actively than program-language comments can [1].
Я бы даже добавил: compiletime assert, где это возможно.
По поводу программирования по контракту см. Также книгу Б. Мейера и книгу М. Уилсона Imperfect C++.
Я бы даже добавил: проверка времени выпуска почти во всех местах, где у вас есть assert () во время отладки. Единственная причина иметь одно без другого - это производительность, которая редко является предметом оптимизации.
Фигурные скобки для любого управляющего оператора. (Благодаря собственному опыту и усиленному чтением Code Complete v2):
// bad example - what the writer wrote
if ( i < 0 )
printf( "%d\n", i );
++i; // this error is _very_ easy to overlook!
// good example - what the writer meant
if ( i < 0 ) {
printf( "%d\n", i );
++i;
}
если (я <0) printf; \ n ++ i; // легко заметить
Верно. Но обычно не применяется для более длительных условий и заявлений.
пожимать плечами, я думаю, это субъективно. для проверки типа досрочного выхода из строя скобки просто мешают. я был бы раздражен, если бы это было в стандарте кодирования.
отступ более важен, чем фигурные скобки - это то, что показывает вам, где что-то должно быть, "++ i;" всегда будет выделяться в коде с хорошим отступом.
Я согласен. Я не вижу пользы в том, чтобы не использовать брекеты, только потенциальные проблемы.
-1 код сокращения важен. Правильный отступ, кажется, решает эти проблемы, а проблемы с неправильным отступом легко обнаружить во время CR.
это субъективно, но в моей книге избегать потенциальных ошибок важнее, чем иметь меньше фигурных скобок, на которые можно смотреть
Предпочитаю RAII.
Указатели STL auto (и используемые в boost и C++ 0x) могут помочь.
я бы сказал «предпочитаю RAII» - я бы точно не стал требовать его в стандарте.
Согласовано. В одном месте, где я работал, люди спорили с симантиками вроде «перфер». В конце концов, наш стандарт был написан так, как будто все было абсолютным требованием (несмотря на возможность отклониться от него после проверки).
Без RAII довольно сложно написать безопасный от ошибок код, который легко поддерживать.
До недавнего времени я даже не знал термина «RAII». Я всегда считал это понятие «здравым смыслом».
Меня поражает, сколько программистов на C++ не знают идиомы RAII. Мне пришлось закрепить это с помощью правила, гласящего: «Никогда не используйте« новый », не посоветовавшись с руководителем группы».
@kris, мне это очень нравится
По возможности используйте ссылки вместо указателей. Это предотвращает постоянные защитные проверки NULL.
Но если вы разыменовываете указатель для инициализации ссылки, сначала убедитесь, что указатель не NULL!
Да, но если бы вы всегда использовали ссылки, у вас не было бы этого указателя для начала! Я шучу ... Меня много раз кусала та самая проблема, о которой ты говоришь.
+1 к эффекту, а не к рассуждениям. я иногда возвращаю нулевой итератор, например найти(). это плохо? Языки более высокого уровня, использующие ссылочные типы, обычно имеют объект None.
@Dustin Getz - так какие доводы вы хотели бы видеть? Я отредактирую.
@Dustin, @Aardvark: Проблема в том, что если вы возвращаете ссылку, тогда вы как бы вынуждены использовать исключения для обозначения сбоя, а не проверять возвращаемый указатель на NULL. Однако, если вам не нужна проверка ошибок, используйте ссылку, потому что она предотвращает множество глупых ошибок. Это также упрощает чтение кода. Я также рекомендую использовать ссылки для моделирования with Паскаля, например SubClassType &sub = SomePtr->SomeLowerLevel[index]->member;, а затем вы используете sub.
Используйте инструмент для удаления ворса - например, PC-Lint. Это позволит уловить многие из проблем руководящих принципов «структурного» кодирования. Имеются в виду вещи, которые указывают на фактические ошибки, а не на проблемы стиля / читабельности. (Не то чтобы удобочитаемость не важна, но это меньше, чем фактические ошибки).
Пример, вместо того, чтобы требовать этот стиль:
if (5 == variable)
Чтобы предотвратить ошибку «непреднамеренное назначение», позвольте lint ее найти.
Именно это я пропагандирую в своей компании. Всегда следует использовать линт, и он решит эту проблему. Кроме того, ни один опытный разработчик больше не совершит эту ошибку (даже опечатку). И, наконец, визуально стиль с постоянным первым делом мне неприятен ... так что спасибо, я согласен.
Размещение константы в первую очередь вредит удобочитаемости, рассеивая поток информации. Я абсолютно согласен с тем, чтобы инструменты выполняли здесь свою работу.
Какими бы ни были рекомендации, сделайте это очень легкий, чтобы признать применимость: чем меньше у вас выбора, тем меньше времени вы теряете. И тем легче становится анализировать код.
Примеры «трудно распознать»:
По умолчанию использовать идентификаторы const. Они предоставляют гарантии для читателя / сопровождающего, и их намного проще встроить, чем вставить потом.
И переменные-члены, и методы будут объявлены const, а также аргументы функции. Переменные-члены const обеспечивают правильное использование списка инициализаторов.
Побочный эффект этого правила: избегайте методов с побочными эффектами.
------ РЕДАКТИРОВАТЬ
Участник constпеременные казался хорошей идеей; это не всегда так: они фактически делают объект Любые неизменяемым. Это становится проблематичным, например, для сортировка вектора.
struct A {
const int i;
};
bool operator<(const A& lhs, const A& rhs) {
return lhs.i < rhs.i;
}
int main() {
std::vector<A> as;
as.emplace_back(A{1});
std::sort(begin(as), end(as));
}
сообщение об ошибке:
... note: copy assignment operator of 'A' is implicitly deleted because
field 'i' is of const-qualified type 'const int'
...
in instantiation of function template specialization 'std::sort<...>'
requested here
std::sort(begin(as), end(as));
Так легко использовать и гораздо больше безопасности в коде. +1. На самом деле, это более важно, чем забавное «используйте if (0 == p) вместо if (p == 0)» ...
В самом деле, имейте p const, и вам не придется беспокоиться о if (p=0).
Проголосовали, но я бы посоветовал прояснить, что вы говорите о методы, а также просто об элементах данных.
Хороший момент для вставки const позже - это больно ... +1
Запретить t[i]=i++;f(i++,i); и т. д., Поскольку нет (переносимых) гарантий относительно того, что выполняется первым.
Я должен был бы поискать это, но я не могу поверить, что Стандарт оставляет эту конструкцию неопределенной.
«Неопределенный» - вот это слово. То, как упорядочиваются инструкции, - это точки последовательности, и в обоих примерах нет точки последовательности между двумя оценками i и i ++. На самом деле это довольно известная проблема в C и C++. См. FAQ C++ lite §39.15 и §39.16
Опять же, этого не должно быть в письменном стандарте кодирования, потому что он просто загромождает документ очевидной информацией.
К сожалению, это не так очевидно для всех. Тем не менее ваш комментарий интересен. Возникает новый вопрос: где начинается очевидное и где оно заканчивается? Потому что, в конце концов, стандарт - это правила безопасности, которые хорошо известны и понятны нескольким людям.
Какие? с таким же успехом вы можете запретить мою любимую: const double PI = 3.141592653658979;*(double*)&PI = 4; Серьезно, если вы собираетесь говорить о фактических ошибках, запрещенных в документе стилей, документ будет бесконечно длинным!
использовать:
static_castconst_castreinterpret_castdynamic_castно никогда не использует приведение в стиле C.
Как это явно способствует более безопасному коду, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.
Каждый каст имеет ограниченные возможности. Например, если вы хотите удалить константу (по какой-либо причине), const_cast не изменит тип одновременно (что может быть трудно найти ошибку).
Кроме того, это позволяет рецензенту искать их, а затем кодировщику обосновывать их, если это необходимо.
Более того, приведение C всегда обрабатывается как reinterpret_cast <> при работе с прямыми объявлениями. И C castts не может правильно преобразовать или перекрестно.
Да, конечно, но я не знаю, что рекомендовал бы reinterpret_cast ...
Это противоречит правилу «использовать самое слабое возможное приведение», поскольку reinterpret_cast сильнее, чем приведение в стиле C, и иногда между ним и static_cast нет ничего, кроме приведения в стиле c. Например, преобразование указателя на байтовый буфер в структуру {} (обычный двоичный шаблон синтаксического анализа). Вы бы порекомендовали reinterpret_cast? Думаю, нет.
@PavelRadzivilovsky, это как раз один из чехлов для reinterpret_cast<>. reinterpret_cast<> предназначен для преобразования между Foo* и Bar*, где Foo и Bar - это совершенно разные типы (в вашем примере std::uint8* и struct foo. reinterpret_cast<> также слабее, чем приведение в стиле C, потому что он не может отбросить const или volatile.
Боковое примечание: не навязывайте SESE (Единый вход Единый выход) (т.е. не запрещайте более одного return, использование break / continue / ...)
В C++ это утопия, поскольку throw - еще одна точка возврата.
У SESE было два преимущества в C и языках без исключений:
Я не согласен с тем, что единственная точка возврата когда-либо делала функции проще в обслуживании. Обычно это просто излишне усложняет тело функции.
Грег, я полностью с тобой согласен. Я не думаю, что функцию SESE легче читать. Некоторые так думают, но это тоже не мой случай.
Модифицируйте, но я обычно не возражаю против таких требований, потому что существует почти никогда больше, чем одна точка выхода из подпрограммы или цикла. Может быть несколько мест выхода пришли из, но никогда не более одной точки, куда он переходит. Без goto подпрограммы все являются единственной точкой выхода.
..другой способ взглянуть на это - если у вас есть исключения, все ваши подпрограммы и циклы в любом случае являются многократным выходом. :-)
Используйте std :: vector всякий раз, когда вам нужно создать буфер данных, даже если размер фиксирован.
Используйте std :: string всякий раз, когда вам нужна строка.
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
std :: vector: Пользователь вектора всегда может найти его размер, и при необходимости размер вектора можно изменить. Его можно даже передать (через обозначение (& (myVector [0]))) в C API. Конечно, вектор уберет за собой.
std :: string: Практически по тем же причинам, что и выше. И тот факт, что он всегда будет правильно инициализирован, что его нельзя переполнить, что он будет изящно обрабатывать модификации, такие как конкатенации, присвоение и т. д., И естественным образом (используя операторы вместо функций )
Проблема в том, что я пишу много кода интерфейса ОС, в котором интенсивно используются массивы символов. Наличие какого-то стандарта, требующего от меня использования std :: string для объектов, к которым никогда не обращаются как к чему-либо, кроме массивов символов, привело бы к большому количеству глупого кода.
Таким образом, решение состоит в том, чтобы связать API ОС с методами, удобными для C++. Я работал над такой штукой, чтобы связать мой код C++ с необработанным Win32 API, и обычно это нормально. И мой интерфейс C++ проверял наличие "GetLastError" и регистрировал информацию, что ни один разработчик не делал сам по себе!
для решений, специфичных для предметной области (драйверов и т. п.), вы, конечно, можете предпочесть собственные API-интерфейсы, если можете это оправдать.
@ Дастин Гетц: Конечно, ты прав. Драйверы сами по себе являются низкоуровневыми API, ограничения очень специфичны, поэтому решение обычно тоже
Что такое std::string? Может ты имел ввиду std::wstring? :)
@ 280Z28: Вы, конечно, шутите. Для тех, кто не знает разницы между std :: string и std :: wstring, первый - это std :: basic_string, шаблон для "char", а второй - тот же std :: basic_string, но на этот раз шаблон для "wchar_t".
Я знаю, что это спустя годы, но я думаю, что @ 280Z28 шутил, что никто не должен использовать в новом коде ничего, кроме широких символов, чтобы код поддерживал интернационализацию.
Структуры - это допустимые конструкции C++, используемые для объединения данных. Тем не менее, данные всегда должны быть правильно инициализированы.
Все структуры C++ должны иметь как минимум конструктор по умолчанию, который установит для своих агрегированных данных значения по умолчанию.
struct MyStruct // BAD
{
int i ; bool j ; char * k ;
}
struct MyStruct // GOOD
{
MyStruct() : i(0), j(true), k(NULL) : {}
int i ; bool j ; char * k ;
}
И если они обычно каким-либо образом инициализируются, предоставьте конструктор, позволяющий пользователю избежать инициализации структуры C-стиля:
MyStruct oMyStruct = { 25, true, "Hello" } ; // BAD
MyStruct oMyStruct(25, true, "Hello") ; // GOOD
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
Наличие структуры без надлежащего конструктора оставляет пользователю этой структуры задачу ее инициализации. Итак, следующий код будет скопирован из функции в функцию:
void doSomething()
{
MyStruct s = { 25, true, "Hello" } ;
// Etc.
}
void doSomethingElse()
{
MyStruct s = { 25, true, "Hello" } ;
// Etc.
}
// Etc.
Это означает, что в C++, если вам нужно добавить поле в структуру или изменить порядок внутренних данных, вы должны пройти все эти инициализации, чтобы убедиться, что каждая из них по-прежнему верна. При наличии подходящего конструктора изменение внутренних компонентов структур не связано с их использованием.
В этом ответе интересно то, что я не согласен с заголовком, но полностью согласен с текстом.
Вы правы: я поменяю название ...
Конструктор делает тип не-POD. Как создать структуру POD?
@MSalters: Хороший вопрос, ответ уже дан выше, но не совсем ясно, это правда: в C++ избегайте структур POD, насколько это возможно ...
Обратите внимание, что структуры / классы «POD» в C++ практически не имеют преимуществ перед не-POD. Единственный момент, когда вы рассмотрите возможность использования POD (или подобного POD), - это взаимодействие с C API.
Если вам действительно нужен POD, вы можете использовать наследование для создания версии с конструктором. Также полезно, если структура является частью библиотеки или API, которые вы не можете изменить.
@Mark Ransom: Да, это решение было исследовано для структуры Win32 API в сообщении: stackoverflow.com/questions/112085/…
Если вы просто инициализируете значения i и k по умолчанию для данного типа, вам следует вместо этого использовать i(), k(). Таким образом, элементы в списке инициализаторов, которые имеют конкретное значение для этого конструктора, будут единственными с конкретными значениями в круглых скобках.
@ 280Z28: помните, что типы i и k являются встроенными типами C / C++, что означает, что они НЕ инициализируются по умолчанию. Таким образом, использование обозначения «i ()» во встроенных типах C / C++ означает, что у вас, вероятно, возникнут проблемы (то есть ошибки) позже из-за выскакивания полуслучайных значений ... ^ _ ^ ...
В C++ 03 POD являются единственным способом переносимого создания структур данных staticconst (и их массивов), которые не требуют инициализации во время выполнения. Игнорируйте на свой страх и риск (или используйте C++ 0x constexpr).
@parcebal: проверьте разницу между инициализация по умолчанию и инициализация значения. @ 280Z28 может быть немного расплывчатым в своей терминологии, но код, который он рекомендует, правильный, а не случайный или неинициализированный.
Лучшая версия этого правила: используйте структуры Aggregate / POD только там, где это необходимо. Затем, если его еще нет (я только наполовину ознакомился с этим списком), может быть правило, которое говорит, что все определяемые пользователем типы, не относящиеся к POD / Aggregate, должны иметь конструктор. Как написано, я не думаю об этом особо.
Следует выделить момент, чтобы объяснить разницу между семантикой значения и семантикой сущности. Он может предоставить типичные фрагменты кода о том, как обрабатывается копия в различных случаях.
См. Также Контрольный список для написания конструктора копирования и оператора присваивания на C++
Публичное наследование должно моделировать принцип замещения Лискова (LSP).
Повторное использование / импорт кода без возможности замены должно быть реализовано с частным наследованием, когда имеет смысл очень сильная связь, или с агрегацией в противном случае.
Не добавляйте типы или функции в глобальное пространство имен.
Это хорошее предложение, но отсутствует логическое обоснование: вы загрязняете глобальное пространство имен, делая это и тем самым потенциально вызывая множество проблем, если имя существует где-то еще.
C API может быть очень эффективным, но для этого потребуются открытые необработанные данные (например, указатели и т. д.), Что не поможет безопасности кода. Вместо этого используйте существующий C++ API или инкапсулируйте C API с кодом C++.
например.:
// char * d, * s ;
strcpy(d, s) ; // BAD
// std::string d, s ;
d = s ; // GOOD
strtok не реентерабелен. Это означает, что если один strtok запущен, а другой не завершен, один повредит «внутренние данные» другого.
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
Использование C API означает использование необработанных типов, что может привести к интересным ошибкам, таким как переполнение буфера (и потенциальное повреждение стека), когда sprintf заходит слишком далеко (или обрезка строки при использовании snprintf, что является своего рода повреждением данных). Даже при работе с необработанными данными можно легко злоупотребить malloc, как показано в следующем коде:
int * i = (int *) malloc(25) ; // Now, I BELIEVE I have an array of 25 ints!
int * j = new int[25] ; // Now, I KNOW I have an array of 25 ints!
И т. Д. И т. Д.
Что касается strtok: C и C++ - это языки с поддержкой стека, которые позволяют пользователю не заботиться о том, какие функции в стеке находятся выше его собственных, а какие функции будут вызываться ниже его собственных в стеке. strtok убирает эту свободу "не заботиться"
Сначала напишите безопасный и правильный код.
Затем, если у вас есть проблемы с производительностью, и если ваш профилировщик сказал вам, что код работает медленно, вы можете попробовать его оптимизировать.
Никогда не верьте, что вы оптимизируете фрагменты кода лучше, чем компилятор.
В поисках оптимизации изучите используемые алгоритмы и, возможно, лучшие альтернативы.
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
Обычно «оптимизированный» (или предположительно оптимизированный) код намного менее ясен и, как правило, выражается необработанным, почти машинным способом, а не более ориентированным на бизнес способом. Некоторые оптимизации полагаются на переключатели, if и т. д., И тогда их будет труднее протестировать из-за нескольких путей кода.
И, конечно же, оптимизация перед профилированием часто приводит к нулевому увеличению производительности.
Лучшие стандарты - это те, которые невелики и сосредоточены на том, что действительно важно для создания качественного кода. Они не пытаются учить кодированию, они не пытаются навязывать определенный способ кодирования. Обычно они придерживаются функций согласованности и субъективных обзоров (например, если остальная часть вашей команды считает, что часть кода читабельна, соответствует правилам согласованности и прокомментирована, то это всегда будет хороший код)
Итак, чтобы еще раз подчеркнуть: согласованность - соглашение об именах, управление пробелами, блоки комментариев, структура каталогов. Больше ничего не имеет значения В самом деле
Редактировать для Дастина: большая проблема со стандартами связана с исключениями. Если у вас есть стандарт, который гласит «1 оператор на строку», вы не можете написать следующий выдуманный пример:
SetColText(1,"col1"); SetColWidth(1, 10);
SetColText(2,"col1"); SetColWidth(2, 10);
...
SetColText(9,"col1"); SetColWidth(9, 10);
Но я бы сказал, что это было более читабельно и, следовательно, менее подвержено ошибкам, которые их разделяли. (Я уверен, что вы можете придумать лучшие примеры).
Это моя точка зрения - рассказывать людям, как писать код и как отформатировать его в соответствии со строгими правилами, всегда будет провалено способами и в местах, которых вы не ожидали. Так что гораздо лучше доверить своим программистам сделать это сразу после соблюдения нескольких правил. Если им нужно следовать нескольким правилам, они напишут хороший, дисциплинированный код, так что вам не понадобятся остальные дрянные правила.
Вы видите некоторые стандарты, которые применяются для страниц и страниц. (В Philips C# one 48 страниц!)
Итак, учитывая, что у вас есть команда качественных программистов, что вам нужно сделать, чтобы упростить работу с их кодом? ответ всегда заключается в том, «где» они помещают код, а не в том, как они его пишут. например. у вас всегда есть корзина, и каталог obj в вашем проекте - хороший стандарт. Вы можете выбрать любой проект и узнать, где что находится ... в отличие от тех, кто собирает все свои двоичные файлы в своем каталоге c: / mybin, потому что ему так проще.
я не знаю, чувак. тот, который вы можете проанализировать с ++, вы можете проанализировать ЛЮБОЙ C++. Я думаю, что важные ценности связаны с управлением сложностью. Я думаю, я согласен с частью субъективного обзора, но я уверен, что не согласен с последней частью
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
Отсутствие четкой философии владения памятью приводит к интересным ошибкам или утечкам памяти, и теряется время на размышления о том, должен ли char *, возвращаемый этой функцией, быть освобожден пользователем или нет, или возвращен специальной функции освобождения памяти и т. д.
Насколько это возможно, функция / объект, выделяющий память, должен быть функцией / объектом, освобождающим ее.
Я бы добавил, что политика «владельца» должна также включать политику «передачи права собственности».
Если вам нужно использовать целочисленный тип, выберите его и оставьте. Это позволит избежать проблем, связанных с смешиванием типов short, int, long и т. д.
// BAD
int i ;
long j ;
short k ;
// GOOD (if you choose the "int" as integer)
int i ;
int j ;
int k ;
То же самое и с настоящими типами: выберите один (например, двойной) и не используйте другой.
И т.п.
Примечание. По-прежнему существует проблема подписанных / неподписанных, которых не всегда можно избежать, и тот факт, что STL использует свои собственные целочисленные типы (например, std :: vector :: size_type), но весь оставшийся код не должен смешиваться.
Примечание 2: вы можете использовать typedef для «выбора» предпочтительного типа для целых и действительных чисел со знаком. Это позволило бы при необходимости внести изменения с небольшими затратами.
Как это явно способствует созданию более безопасного кода, что сводит к минимуму риск загадочных ошибок, что увеличивает ремонтопригодность и т. д.?
Некоторые ошибки возникают из-за сравнения беззнакового типа со знаковым, загадочной потери точности или целочисленного недо / переполнения.
Компиляторы обычно отправляют предупреждения во время компиляции, но тогда обычно ответ заключается в том, чтобы «отбросить» предупреждение, что может помочь скрыть ошибку.
плинтус сделал полезный комментарий, я скопирую вставку сюда:
Having written a lot of code that has to interact with things at the hardware level, I can't say much for this guideline. For this level of work, I prefer the integral types to be abstracted to names that include the precision (ie, int16, uint16, int32, uint32, etc.) – plinth Aug 18 at 20:50
плинтус правильный, конечно. Иногда приходится иметь дело с int16, uint8 и другими «точно определенными» типами.
Это не делает сообщение выше недействительным, только завершите его.
Источник ошибки - смешивание разных типов (например, преобразование unsigned char в int), поэтому такого смешивания следует избегать. Таким образом, применяются следующие правила:
Ниже приведен пример кода, который может сломаться:
void * doAllocate(uint32 i)
{
// try to allocate an array of "i" integers and returns it
}
void doSomething()
{
uint32 i0 = 225 ;
int8 i1 = 225 ; // Oops...
doAllocate(i0) ; // This will try to allocate 255 integers
doAllocate(i1) ; // This will TRY TO allocate 4294967265
// integers, NOT 225
}
Написав много кода, который должен взаимодействовать с вещами на аппаратном уровне, я не могу много сказать по этому руководству. Для этого уровня работы я предпочитаю, чтобы интегральные типы были абстрагированы до имен, которые включают точность (например, int16, uint16, int32, uint32 и т. д.)
@plinth: это другая проблема. В общих случаях кодировщик хочет поместить в переменную целое число. Смешение типов, как по размеру, так и по знаку, - это путь к катастрофе. Обычно кодировщику нужен простой тип, способный хранить все возможные значения. Выбор одного и ТОЛЬКО одного - это хорошее дело. <продолжение следует> ...
@plinth: <continuing> ... В вашем случае, поскольку вы работаете рядом с оборудованием, вам НЕОБХОДИМО использовать точный необходимый тип, таким образом, "int" или "short", которые потенциально зависят от каждого компилятора, неверны дело в том, что нужно использовать точный тип, например int32 или uint8. <продолжение следует> ...
@plinth: <продолжение> ... Но учтите, что даже рядом с оборудованием преобразование int8 в uint32 сломается, если вы не будете очень осторожны. Это означает, что в вашем случае вместо того, чтобы полагаться на ОДИН и ТОЛЬКО ОДИН целочисленный тип, как я советовал, вы должны быть осторожны, чтобы не смешивать их, или быть очень очень осторожными, когда вы их смешиваете. Заключение: даже если решение, которое я дал в моем сообщении (с использованием только одного целочисленного типа), не может быть применено в вашем конкретном случае, причина (т.е. источник ошибок при смешивании) все еще действительна в вашем конкретном случае и, следовательно, требует дополнительная работа / осторожность.
@plinth: <продолжение> ... Но учтите, что даже рядом с оборудованием преобразование int8 в uint32 сломается, если вы не будете очень осторожны. Это означает, что в вашем случае вместо того, чтобы полагаться на ОДИН и ТОЛЬКО ОДИН целочисленный тип, как я советовал, вы должны быть осторожны, чтобы не смешивать их, или быть очень очень осторожными, когда вы их смешиваете. <продолжение следует> ...
@plinth: <continuing> ... Заключение: даже если решение, которое я дал в моем сообщении (с использованием только одного целочисленного типа), не может быть применено в вашем конкретном случае, причина, описанная в моем сообщении (т.е. источник ошибок при смешивании ) по-прежнему действует в вашем конкретном случае и, следовательно, требует дополнительной работы / осторожности. Этот пост в основном нацелен на программистов, которые будут думать, что они умнее только потому, что они использовали беззнаковое сокращение вместо int для «целей оптимизации памяти». Я встречал их, и я встречал их ошибки, и это было неприятно ... ^ _ ^ ...
Принцип наименьшего удивления.
Возможно, вы ищете не тот «вкус» правил, но я бы определенно поставил его на первое место.
Это не только проверка на корень, причину и работоспособность всех скучных вещей, таких как рекомендации по форматированию и комментированию, но - что более важно для меня - делает упор на то, чтобы код читался и понимался, а не просто компилировался.
Он также охватывает единственную разумную меру качества кода, с которой я когда-либо сталкивался - WTF в минуту.
Я бы использовал этот первый пункт, чтобы подчеркнуть важность и ценность ясного, последовательного кода и мотивировать следующие элементы в стандарте кодирования.
Предпочитайте код, соответствующий стандарту. Предпочитаю использовать стандартные библиотеки.
Жаль, что этот пункт оправдан. Слишком много людей, которые оправдывают свой код единственным выражением «работает на меня» ...
Я думаю, что стандартный документ кодирования не решит эту проблему. Решение состоит в том, чтобы мотивировать ваш труд изучать / заботиться о человеческой стороне программирования - «сначала код для людей, а потом компьютеры».
Очевидно, невозможно просто уволить тех, кому все равно, но стандартный документ им тоже не сильно поможет.
Частично согласен. Мотивация программистов к правильному кодированию хороша для ограничения списка исправлений, созданного при проверке кода. Однако я считаю, что некоторые части стандарта не достигаются автоматически, если полагаться на здравый смысл, например правильность констант.
Конечно, но если они мотивированы и внимательны, научите их константе один раз, и они будут использовать ее вечно из гордости. если им все равно, это будет наполовину оценено в соответствии с минимальной спецификацией.
Я предлагаю просто потребовать от разработчиков прочитать кучу руководств и книги Мейерса по эффективному C++ и более эффективному C++.
Если вам нужен легкий вес, вам придется полагаться на здравый смысл и общий идеал.
Проверки кода также помогают добиться этого.
Чтобы сделать его легким, я бы избегал полиции по документам и кодам. Публично хвалите хороший код.
РЕДАКТИРОВАТЬ - я начал с комментария здесь, но для удобства просмотра добавлю его в ответ:
правильно сделанные обзоры творит чудеса, но вы не можете разрешить иерархию отчетов в обзоре, и никакая статистика с именами людей не может быть в результатах обзора.
Убедитесь, что документ небольшой, и обязательно укажите ПРИЧИНЫ для "правила" / руководства. Без этого вы просто требуете слепого повиновения. С обоснованием и причинами вы обучаете так, чтобы на самом деле публикация / запись «правила» стала ненужной. (поскольку концепция будет усвоена)
Легкость действительно сильно зависит от здравого смысла. На мой взгляд, документ должен существовать, чтобы каждый мог читать и все знали кадры (особенно юниоры). Проверка полицейской работы и судебного процесса - это тень от неэффективного руководства и коммуникации.
Всегда, всегда, всегда выполняйте правильную инициализацию элемента данных при построении объекта.
Я столкнулся с проблемой, когда конструктор объекта полагался на некоторую инициализацию "по умолчанию" для своих членов данных. Сборка кода для двух платформ (Windows / Linux) дала разные результаты и обнаруживала труднообнаруживаемую ошибку памяти. В результате элемент данных не был инициализирован в конструкторе и использовался до его инициализации. На одной платформе (Linux) компилятор инициализировал это значение по умолчанию, которое автор кода считал подходящим. В Windows значение было инициализировано чем-то - но мусором. При использовании элемента данных все пошло наперекосяк. Как только инициализация была исправлена - больше никаких проблем.
Наверное, простое, но все же важное правило:
Избегайте неопределенного поведения.
Этого очень много в C++, и, вероятно, невозможно написать нетривиальное приложение, которое как-то от него не зависит, но общим правилом все равно должно быть «неопределенное поведение - это плохо». (Потому что, к сожалению, есть программисты на C++, которые считают, что «это работает на моей машине / компиляторе» - это достаточно хорошо).
Если на это приходится полагаться, дайте всем понять, что, почему, где и как.
Я думаю, что разработал много нетривиальных приложений на C++, которые не зависят от неопределенного поведения.
Я думаю, вы просто не понимаете, что они зависят от неопределенного поведения. ;)
Я думаю, что разработал много нетривиальных приложений на C++, которые не зависят от неопределенного поведения, и я почти уверен в этом. Если вы очень хорошо знаете C++, это очень и очень осязаемая задача;)
@phresnel: все ли ваши приложения были однопоточными? Многопоточность - это неопределенное поведение в C++ 03. Просто пример;)
?? Является ли многопоточность неопределенным поведением или нет, зависит исключительно от того, как реализована библиотека многопоточности. «Многопоточность не определена в C++ 2003» полностью отличается от «Многопоточность - неопределенное поведение». Установленная фраза и значение «неопределенного поведения» четко определены в стандарте C++, это нет некоторый разговорный термин.
@phresnel: C++ 03 не имеет модели памяти. Никакая библиотека не может восполнить этот фундаментальный недостаток. Сама концепция одновременного выполнения нескольких запусков одной и той же программы и совместного использования данных не определена. В языке есть ничего такого, который говорит, когда, как и должны ли данные, записанные одним потоком, когда-либо стать видимыми для другого. Все, что полагается на такой обмен данными, основывается на неопределенном поведении.
Как насчет трюка объединения для преобразования (в частности, переинтерпретации базовых битов) между типами (скажем, int и float? Вы когда-нибудь использовали это? Это неопределенное поведение. Когда вы дойдете до него, C++ содержит множество сомнительных битов и частей, которые должен в быть точными, но не определенными.
@jalf: Итак, если я взаимодействую с синхронной, основанной на задачах многопоточной библиотекой, написанной на haskell, через несколько функций extern "C", которым я передаю некоторые данные только для записи, а затем ожидаю возвращаемого значения синхронно, это в любом случае поведение undefined? Вы правы, если приписываете своей фразе «возможно», но это определенно не необходимость. C - не единственный способ писать C-вызываемые функции. (...)
(...) Кроме того, нетривиальность не подразумевает многопоточность. И нет, я не использую трюк union в переносимых вариантах моего кода, я использую его максимум в необязательном коде # ifdef-imprisoned. Я плохо выразился, но я никогда не использую код, специфичный для компилятора, но обычно я не использую полагаться в нем. Вы, конечно, не совсем ошибаетесь в своем примере многопоточности, но я также прав в том, что это возможный для вызова многопоточности без входа в Темное царство, и в этом я написал много нетривиальных переносимых приложений.
@jalf: Кстати, у C++ 03 делает есть модель памяти, даже если она "битовая" симплицистическая. C++ 0x «просто» восстановит его.
@phresnel: тогда покажите мне, где в стандарте это указано. (подсказка: это не так)
ре. многопоточность, ваш пример haskell может сойти с рук, потому что код C++ является однопоточным и просто вызывает другую библиотеку, написанную на другом языке, которая имеет четко определенную семантику потоковой передачи. Я уверен, что вы написали много портативных приложений, но это не значит, что в них нет неопределенного поведения. Как насчет подписанного целочисленного переполнения? Используете operator < для сравнения указателей, указывающих на разные массивы? a[i] == ++i?
@jalf: ANSI ISO IEC 14882 2003, 1.7 «Модель памяти» [intro.memory], как уже упоминалось, это упрощенно: The fundamental storage unit in the C + + memory model is the byte. A byte is at least large enough to contain any member of the basic execution character set and is composed of a contiguous sequence of bits, the number of which is implementation-defined. The least significant bit is called the low-order bit; the most significant bit is called the high-order bit. The memory available to a C + + program consists of one or more sequences of contiguous bytes. Every byte has a unique address.
@jalf: я не сравниваю указатели (в основном потому, что я их обычно не использую) и не полагаюсь на переполнение. Если мне действительно нужны подписанные типы, которые не подчиняются закону арифметики по модулю (согласно разделу [basic.fundamental]), я напишу класс, реализующий это поведение. Далее идет перекрытие массивов, изменение значения const_casted, которое было объявлено как const, бесконечная рекурсия шаблона, выброс из деструкторов, доступ к нестатическим элементам данных в блоках function-try-блоках, указание числа <= 0 или> 32767 в #line. Знаю, знаю. Это становится немного смешным.
@phresnel: достаточно честно. Я, очевидно, не могу знать, есть ли в коде неопределенное поведение, которое я не читал. Я думаю, что моя точка зрения ясна, что есть много тонких ловушек, на которые почти каждый случайно полагается, но которые являются неопределенным поведением. Но вы можете быть исключением из одного миллиона. ;)
Под «моделью памяти» мы обычно подразумеваем семантику того, что означает чтение из памяти и запись в память: в частности, когда запись становится видимой для других потоков. В C++ 03 нет такой модели памяти Любые. А без этого любая попытка сделать запись из одного потока видимой для другого принципиально не определена.
(Википедия описывает «модель памяти» следующим образом: «В вычислениях модель памяти описывает: как потоки взаимодействуют через память, или в более общем плане: укажите, какие допущения разрешено делать компилятору при генерации кода для сегментированной памяти или платформ страничной памяти». C++ 03 ничего из этого не описывает. Он просто говорит, что такое бит и байт. Обычно это не считается моделью памяти, независимо от заголовка раздела.;)
Честно говоря, язык C++ иногда отличается от общепринятого языка ИТ. Думаю, что я не исключение, но нам не хватает статистики для этого обсуждения;)
Убедитесь, что деструкторы определены как виртуальный:
class GoodClass {
public:
GoodClass();
virtual ~GoodClass()
};
class BadClass {
public:
BadClass();
~BadClass()
};
Нет. Нет смысла иметь деструкторы из классов значений виртуальными. Более того, в / C++ Coding Standards / H.Sutter и A.Alexandrecu рекомендуют делать деструктор из базовый класс либо публичным и виртуальным, либо защищенным и не виртуальным.
Я согласен с Саттером / Александреку, за исключением того, что я бы пошел дальше и сказал, что это пустое правило для всех деструкторов. Мой аргумент в том, что класс, возможно, не является базовым классом сегодня, но нечего сказать, что он не будет базовым классом завтра. В этом сценарии отсутствующий виртуальный объект - это ожидающая своего появления ошибка.
Когда известно, что класс является классом значений, его последующее наследование вызовет множество других проблем. Четко сказав: «Нет, этот класс НЕ предназначен для (публичного) наследования» (благодаря невиртуальному деструктору и комментарию), у нас есть шанс избежать проблем.
Если позже он станет базовым классом, он также получит виртуальные методы. Тогда вы еще можете сделать dtor виртуальным. В противном случае, по крайней мере, gcc сгенерирует предупреждение.
Избегайте использования сгенерированного конструктора копирования и оператора = по умолчанию.
class foo
{
//...
private:
foo( const foo& );
const foo& operator=( const foo& );
};
Или в более чистый способ, если вы используете boost:
class foo : private boost::noncopyable
{
...
};
Это два упрощенных правила. Когда все атрибуты можно копировать и присваивать, И когда мы хотим, чтобы новый класс был копируемым и присваиваемым, мы должны предпочесть созданный по умолчанию конструктор копирования и оператор присваивания. В таких случаях нам следует подумать о том, чтобы оставить комментарий, в котором говорится, что это осознанный выбор, поскольку все члены можно копировать и назначать. Возвращаемся к моему ответу: stackoverflow.com/questions/242728/…
Да, если комментарий достаточно ясен, чтобы никто не мог добавить в класс новый атрибут нетривиальной копии, нарушающий исходные предположения.
Отредактировано, чтобы уточнить, что это правило говорит вам, что вам следует избегать их использования по умолчанию и сделать поведение без копирования параметром по умолчанию.
Фолиант "Искусство компьютерного программирования" {1,2,3}
Передайте входные аргументы по константной ссылке, а выходные или входные-выходные аргументы по указателю. Это правило взято из руководства по стилю Google.
Раньше я испытывал абсолютное отвращение к указателям и предпочитал использовать ссылки, когда это возможно (как было предложено одним из плакатов в этом потоке). Однако принятие этого соглашения о выводе аргументов в качестве указателя сделало мои функции более удобочитаемыми. Например,
SolveLinearSystem(left_hand_side, right_hand_side, ¶ms);
дает понять, что в "params" записывается.
Основывайте свой стиль кодирования на «Стандарте кодирования C++» (Саттер / Александреску) и документируйте только там, где вы отклоняетесь от него. Я не могу серьезно относиться к любому «Стандарту кодирования C++», который этого не делает.
Правило 0 всегда должно быть:
Некоторые проблемы могут потребовать несоблюдения определенного стандарта.
Эти случаи должен должны быть задокументированы и обоснованы, поскольку они указывают на отклонение от ожидаемых идиом в кодовой базе.
Отсутствие их документирования может вызвать ошибки, потому что:
Будущий сопровождающий может привести код в соответствие без понимание того, почему он вообще не соответствует требованиям.
Будущий разработчик может ожидать, что код будет соответствовать требованиям, и поэтому сделает неверные предположения о его значении.
Вполне вероятно, что здесь вы создадите бессвязный набор правил, большинство из которых не будут решать проблемы, приводящие к неопределенному поведению.