При сортировке вы должны четко понимать, как работает каждый из сортирующих алгоритмов, и использовать наиболее дешевый среди тех, которые пригодны для решения вашей задачи.
Предикат представляет собой функциональный объект, который возвращает ответ да/нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов (обратите внимание — в данном случае термин "чистая" не имеет никакого отношения к чисто виртуальным функциям).
Не позволяйте предикатам сохранять или обращаться к состоянию так, чтобы это могло влиять на результат работы оператора operator(); при этом понятие состояния включает как данные-члены, так и глобальные состояния. Для предикатов желательно делать оператор operator() константной функцией-членом (см. рекомендацию 15).
Предпочтительно передавать алгоритмам функциональные объекты, а не функции, а компараторы ассоциативных контейнеров просто должны быть функциональными объектами. Функциональные объекты адаптируемы и, вопреки ожиданиям, обычно дают более быстрый по сравнению с функциями код.
Разрабатывайте функциональные объекты так, чтобы их копирование выполнялось как можно эффективнее. Там, где это возможно, делайте их максимально адаптируемыми путем наследования от unary_function или binary_function.
Избегайте явного выбора типа объекта для настройки поведения. Используйте шаблоны и виртуальные функции для того, чтобы поведение объекта определялось его типом, а не вызывающим кодом.
Не пытайтесь делать какие-то предположения о том, как именно объекты представлены в памяти. Как именно следует записывать и считывать объекты из памяти — пусть решают типы объектов.
Как гласит римская пословица, у лжи короткие ноги. Не пытайтесь использовать reinterpret_cast, чтобы заставить компилятор рассматривать биты объекта одного типа как биты объекта другого типа. Такое действие противоречит безопасности типов.
К указателям на динамические объекты не следует применят преобразование static_cast. Используйте безопасные альтернативы — от dynamic_cast до перепроектирования.
Преобразование типов, отменяющее const, может привести к неопределенному поведению, а кроме того, это свидетельство плохого стиля программирования даже в том случае, когда применение такого преобразования вполне законно.
Возраст не всегда означает мудрость. Старое преобразование типов в стиле С имеет различную (и часто опасную) семантику в зависимости от контекста, спрятанную за единым синтаксисом. Замена преобразования типов в стиле С преобразованиями C++ поможет защититься от неожиданных ошибок.
Не работайте рентгеновским аппаратом (см. рекомендацию 91). Не используйте memcpy и memcmp для копирования или сравнения чего-либо структурированного более, чем обычная память.
Хитрость все равно остается ложью: объединения можно использовать для получения "преобразования типа без преобразования", записывая информацию в один член и считывая из другого. Однако это еще более опасно и менее предсказуемо, чем применение reinterpret_cast (см. рекомендацию 92).
Наличие троеточий в C++ — опасное наследие С. Избегайте их в своих программах; используйте вместо этого высокоуровневые конструкции и библиотеки C++.
Вы же не используете просроченные лекарства? И недействительные объекты, и "антикварные", но небезопасные функции способны навредить здоровью ваших программ.
Полиморфная работа с массивами — большая ошибка. К сожалению, обычно компилятор никак на нее не реагирует. Не попадайтесь в эту ловушку!
Если вы лжете компилятору, он будет мстить.
— Генри Спенсер (Henry Spencer)
Всегда будут вещи, которые мы будем хотеть сказать в наших программах и которые трудно сформулировать на любом языке программирования.
— Алан Перлис (Alan Perlis)
Последней (не по важности) темой книги является корректность типов — очень важное свойство программ, которое вы должны изо всех сил стараться поддерживать. Теоретически корректная с точки зрения типов функция не может обратиться к нетипизированной памяти или вернуть неверные значения. На практике, если ваш код поддерживает корректность типов, он тем самым избегает большого количества неприятных ошибок, от непереносимости программ до порчи содержимого памяти и неопределенного поведения программ.
Основная идея поддержки корректности типов — всегда считывать информацию в том формате, в котором она была записана. Иногда C++ позволяет легко нарушить это правило; приведенные в этом разделе рекомендации помогут вам избежать подобных ошибок.
В этом разделе мы считаем наиболее значимой рекомендацию 91 — "Работайте с типами, а не с представлениями". Система типов — ваш друг и верный союзник. Воспользуйтесь ее помощью и попытайтесь не злоупотреблять ее доверием.
Стр.188
90. Избегайте явного выбора типов — используйте полиморфизм
Избегайте явного выбора типа объекта для настройки поведения. Используйте шаблоны и виртуальные функции для того, чтобы поведение объекта определялось его типом, а не вызывающим кодом.
91. Работайте с типами, а не с представлениями
Не пытайтесь делать какие-то предположения о том, как именно объекты представлены в памяти. Как именно следует записывать и считывать объекты из памяти — пусть решают типы объектов.
92. Избегайте reinterpret_cast
Как гласит римская пословица, у лжи короткие ноги. Не пытайтесь использовать reinterpret_cast, чтобы заставить компилятор рассматривать биты объекта одного типа как биты объекта другого типа. Такое действие противоречит безопасности типов.
93. Избегайте применения static_cast к указателям
К указателям на динамические объекты не следует применят преобразование static_cast. Используйте безопасные альтернативы — от dynamic_cast до перепроектирования.
94. Избегайте преобразований, отменяющих const
Преобразование типов, отменяющее const, может привести к неопределенному поведению, а кроме того, это свидетельство плохого стиля программирования даже в том случае, когда применение такого преобразования вполне законно.
95. Не используйте преобразование типов в стиле С
Возраст не всегда означает мудрость. Старое преобразование типов в стиле С имеет различную (и часто опасную) семантику в зависимости от контекста, спрятанную за единым синтаксисом. Замена преобразования типов в стиле С преобразованиями C+ + поможет защититься от неожиданных ошибок.
96. Не применяйте memcpy или memcmp к не-POD типам
Не работайте рентгеновским аппаратом (см. рекомендацию 91). Не используйте тетеру и тетстр для копирования или сравнения чего-либо структурированного более, чем обычная память.
97. Не используйте объединения для преобразований
Хитрость все равно остается ложью: объединения можно использовать для получения "преобразования типа без преобразования ", записывая информацию в один член и считывая из другого. Однако это еще более опасно и менее предсказуемо, чем применение reinterpret_cast (см. рекомендацию 92).
98. Не используйте неизвестные аргументы (троеточия)
Наличие троеточий в C++ — опасное наследие С. Избегайте их в своих программах; используйте вместо этого высокоуровневые конструкции и библиотеки C++.
99. Не используйте недействительные объекты и небезопасные функции
Вы оке не используете просроченные лекарства? И недействительные объекты, и "антикварные ", но небезопасные функции способны навредить здоровью ваших программ.
100. Не рассматривайте массивы полиморфно
Полиморфная работа с массивами — большая ошибка. К сожалению, обычно компилятор никак на нее не реагирует. Не попадайтесь в эту ловушку!
Стр.219
Мы от всей души благодарны редактору серии Бьярну Страуструпу (Bjarne Stroustrup), редакторам Питеру Гордону (Peter Gordon) и Дебби Лафферти (Debbie Lafferty), а также Тирреллу Албаху (Tyrrell Albaugh), Ким Бодихаймер (Kim Boedigheimer), Джону Фуллеру (John Fuller), Бернарду Гаффни (Bernard Gaffney), Курту Джонсону (Curt Johnson), Чанде Лири-Коту (Chanda Leary-Coutu), Чарли Ледди (Charles Leddy), Хитеру Муллэйн (Heather Mullane), Чути Прасертсиху (Chuti Prasertsith), Ларе Вайсонг (Lara Wysong) и всем остальным работникам издательства Addison-Wesley, помогавшим нам в нашей работе над этим проектом. Нам было очень приятно работать с ними.
На идею и оформление книги нас натолкнули несколько источников, включая такие, как [Cline99], [Peters99], а также работы легендарного и широко цитируемого Алана Перлиса (Alan Perlis).
Особая благодарность тем людям, чьи отзывы помогли нам сделать многие части книги намного лучшими, чем они были бы без этих замечаний. Особенно большое влияние на книгу оказали острые комментарии Бьярна Страуструпа. Мы очень хотим поблагодарить за активное участие в обсуждении материала и высказанные замечания таких людей, как Дэйв Абрамс (Dave Abrahams), Маршалл Клайн (Marshall Cline), Кевлин Хенни (Kevlin Henney), Говард Хиннант (Howard Hinnant), Джим Хайслоп (Jim Hyslop), Николаи Джосаттис (Nicolai Josuttis), Йон Калб (Jon Kalb), Макс Хесин (Max Khesin), Стен Липпман (Stan Lippman), Скотт Мейерс (Scott Meyers) и Дэвид Вандевурд (Daveed Vandevoorde). Кроме того, отдельное спасибо хотелось бы сказать Чаку Аллисону (Chuck Allison), Самиру Байяю (Samir Bajaj), Марку Барбуру (Маге Barbour), Тревису Брауну (Travis Brown), Нилу Кумбесу (Nil Coombes), Дамиану Дечеву (Damian Dechev), Стиву Дьюхарсту (Steve Dewhurst), Питеру Димову (Peter Dimov), Аттиле Фехеру (Attila Feher), Алану Гриффитсу (Alan Griffiths), Мичи Хеннингу (Michi Henning), Джеймсу Канзе (James Kanze), Бартошу Милевски (Bartosz Milewski), Мэтту Маркусу (Mart Marcus), Балогу Палу (Balog Pal), Владимиру Прусу (Vladimir Prus), Дэну Саксу (Dan Saks), Люку Вагнеру (Luke Wagner), Мэтью Вильсону (Matthew Wilson) и Леору Золману (Leor Zolman).
Как обычно, все оставшиеся в книге ошибки и недосмотры — на нашей совести, а не на их.
Герб Саттер (Herb Sutter)
Андрей Александреску (Andrei Alexandrescu)
Сиэтл, сентябрь 2004
Стр.13
Если ваша процедура имеет десять параметров — вероятно, вы где-то ошиблись.
— Алан Перлис (Alan Perlis)
Функции, включая перегруженные операторы, представляют собой фундаментальные единицы работы. Как вы увидите позже в разделе "Обработка ошибок и исключения" (в частности, в рекомендации 70), это непосредственно влияет на наши рассуждения о корректности и безопасности кода.
Но давайте сначала рассмотрим некоторые фундаментальные вопросы написания функций, в том числе операторов. В частности, мы обратим особое внимание на их параметры, семантику и перегрузку.
В этом разделе наиболее важной нам представляется рекомендация 26 — "Сохраняйте естественную семантику перегруженных операторов".
Стр.58
25. Передача параметров по значению, (интеллектуальному) указателю или ссылке
Вы должны четко уяснить разницу между входными, выходными параметрами и параметрами, предназначенными и для ввода, и для вывода информации, а также между передачей параметров по значению и по ссылке, и корректно их использовать.
26. Сохраняйте естественную семантику перегруженных операторов
Программисты ненавидят сюрпризы. Перегружайте операторы только в случае веских на то оснований, и сохраняйте при этом их естественную семантику. Если это оказывается сложным, возможно, вы неверно используете перегрузку операторов.
27. Отдавайте предпочтение каноническим формам арифметических операторов и операторов присваивания
Если можно записать а+b, то необходимо, чтобы можно было записать и а+=Ь. При определении бинарных арифметических операторов одновременно предоставляйте и их присваивающие версии, причем делайте это с минимальным дублированием и максимальной эффективностью.
28. Предпочитайте канонический вид ++ и --, и вызов префиксных операторов
Особенность операторов инкремента и декремента состоит в том, что у них есть префиксная и постфиксная формы с немного отличающейся семантикой. Определяйте операторы operator++ и operator— так, чтобы они подражали поведению своих встроенных двойников. Если только вам не требуется исходное значение — используйте префиксные версии операторов.
29. Используйте перегрузку, чтобы избежать неявного преобразования типов
Не преумножайте объекты сверх необходимости (Бритва Оккама): неявное преобразование типов обеспечивает определенное синтаксическое удобство (однако см. рекомендацию 40), но в ситуации, когда допустима оптимизация (см. рекомендацию 8) и желательно избежать создания излишних объектов, можно обеспечить перегруженные функции с сигнатурами, точно соответствующими распространенным типам аргументов, и тем самым избежать преобразования типов.
30. Избегайте перегрузки &&, II и , (запятой)
Мудрость — это знание того, когда надо воздержаться. Встроенные операторы &&, 11 и , (запятая) трактуются компилятором специальным образом. После перегрузки они становятся обычными функциями с весьма отличной семантикой (при этом вы нарушаете рекомендации 26 и 31), а это прямой путь к трудноопределимым ошибкам и ненадежности. Не перегружайте эти операторы без крайней необходимости и глубокого понимания.
31. Не пишите код, который зависит от порядка вычислений аргументов функции
Порядок вычисления аргументов функции не определен, поэтому никогда не полагайтесь на то, что аргументы будут вычисляться в той или иной очередности.
Думать. Надо добросовестно следовать умным советам, но делать это не вслепую. Во многих разделах этой книги есть подраздел "Исключения", в котором приводятся нестандартные, редко встречающиеся ситуации, когда совет из основного раздела может оказаться неприменим. Никакое количество даже самых хороших (мы на это надеемся) советов не могут заменить голову.
Каждая команда разработчиков отвечает за принятие собственных стандартов и несет ответственность за них и за неукоснительное следование им. Ваша команда — не исключение. Если вы — руководитель, предложите членам своей группы поучаствовать в разработке стандартов, которым группа будет следовать в своей работе. Люди всегда охотнее следуют правилам, которые они сами для себя вырабатывают, чем тем, которые им навязаны.
Стр.10
Эта книга предназначена для того, чтобы послужить основой для вашего стандарта кодирования и быть в той или иной мере включенной в него. Это — не ultima ratio в стандартах кодирования, и ваша группа может разработать (или прибавить) свои правила, в наибольшей степени подходящие для вашей конкретной группы или для конкретной решаемой задачи, так что вы не должны быть скованы этой книгой по рукам и ногам. Тем не менее, мы надеемся, что эта книга поможет вам сберечь время и усилия при разработке собственного стандарта кодирования, а также будет способствовать повышению его качества и последовательности.
Ознакомьте членов своей команды с этой книгой, и после того, как они полностью прочтут ее и познакомятся со всеми рекомендациями и их обоснованиями, решите, какие из них подходят вам, а какие в силу особенностей вашего проекта для вас неприменимы. После этого строго придерживайтесь выбранной стратегии. После того как командный стандарт принят, он не должен нарушаться иначе как по согласованному решению всей команды в целом.
И наконец, периодически творчески пересматривайте собственные стандарты с учетом практического опыта, приобретенного всей командой при работе над проектом.
Если стандарт привел вас к обрыву, это еще не значит, что вы должны прыгнуть с него.
— Норман Даймонд (Norman Diamond)
О Большой Четверке специальных функций было сказано достаточно, чтобы вы не удивлялись тому, что им посвящен отдельный раздел. Здесь собраны знания и практика, связанные с конструкторами по умолчанию, копирующими конструкторами, копирующим присваиванием и деструкторами.
Одна из причин, по которым при работе с этими функциями следует быть особенно внимательными, заключается в том, что если вы дадите компилятору хотя бы полшанса — он тут же напишет эти функции за вас. Еще одна причина состоит в том, что C++ по умолчанию рассматривает классы как типы-значения, но далеко не все типы именно таковы (см. рекомендацию 32). Надо отчетливо понимать, когда следует писать (или запрещать) эти специальные функции явно, и следовать правилам и рекомендациям из этого раздела — это поможет вам в написании корректного, расширяемого и безопасного кода.
В этом разделе мы считаем наиболее значимой рекомендацию 51 — "Деструкторы, функции освобождения ресурсов и обмена не ошибаются".
Стр.100
47. Определяйте и инициализируйте переменные-члены в одном порядке
Переменные-члены всегда инициализируются в том порядке, в котором они объявлены при определении класса; порядок их упоминания в списке инициализации конструктора игнорируется. Убедитесь, что в коде конструктора указан тот же порядок, что и в определении класса.
48. В конструкторах предпочитайте инициализацию присваиванию
В конструкторах использование инициализации вместо присваивания для установки значений переменных-членов предохраняет от ненужной работы времени выполнения при том же объеме вводимого исходного текста.
Стр.212
49. Избегайте вызовов виртуальных функций в конструкторах и деструкторах
Внутри конструкторов и деструкторов виртуальные функции теряют виртуальность. Хуже того — все прямые или косвенные вызовы нереализованных чисто виртуальных функций из конструктора или деструктора приводят к неопределенному поведению. Если ваш дизайн требует виртуальной передачи в производный класс из конструктора или деструктора базового класса, следует воспользоваться иной методикой, например, постконструкторами.
50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными
Удалять или не удалять — вот в чем вопрос! Если следует обеспечить возможность удаления посредством указателя на базовый класс, то деструктор базового класса должен быть открытым и виртуальным. В противном случае он должен быть защищенным и невиртуальным.
51. Деструкторы, функции освобождения ресурсов и обмена не ошибаются
Все запуски этих функций должны быть успешными. Никогда не позволяйте ошибке выйти за пределы деструктора, функции освобождения ресурса (например, оператора delete) или функции обмена. В частности, типы, деструкторы которых могут генерировать исключения, категорически запрещено использовать со стандартной библиотекой C++.
52. Копируйте и ликвидируйте согласованно
Если вы определили одну из следующего списка функций — копирующий конструктор, оператор копирующего присваивания или деструктор — вероятно, вам потребуется определить и обе оставшиеся функции (или по крайней мере одну из них).
Основными принципами дизайна данной книги являются следующие.
Краткость — сестра таланта. Чем больше стандарт кодирования по размеру, тем больше шансов, что он будет благополучно проигнорирован. Читают и используют обычно короткие стандарты. Длинные разделы, как правило, просто просматривают "по диагонали", короткие статьи обычно удостаиваются внимательного прочтения.
Никакой материал не должен вызывать дискуссий. Эта книга документирует широко используемые стандарты, а не изобретает их. Если некоторая рекомендация не применима во всех ситуациях, то мы так и пишем — "подумайте о применении X" вместо "делайте X". Кроме того, к каждому правилу указаны все общепринятые исключения.
Весь материал должен быть обоснован. Все рекомендации в этой книге взяты из существующих печатных работ. В конце книги приведен список использованной литературы по C++.
Материал не должен быть банален. Мы не даем рекомендации, которым вы и так следуете, которые обеспечиваются компилятором или которые уже изложены в других разделах.
Например, "не возвращайте указатель/ссылку на локальную переменную" — хороший совет, но он не включен в данную книгу, поскольку практически все компиляторы выдают соответствующее предупреждение, к тому же этот вопрос раскрывается в первом разделе книги.
Рекомендация "используйте редактор (компилятор, отладчик)" — тоже хороший совет, но, конечно, вы используете эти инструменты и без нашего напоминания. Вместо этого мы рекомендуем использовать автоматизированные системы сборки программ и системы управления версиями.
Еще один совет — "не злоупотребляйте goto" — исходя из нашего опыта, и так широко известен всем программистам, так что нет смысла повторяться.
Каждый раздел состоит из следующих частей.
Заглавие. Краткое название раздела, поясняющее, о чем будет идти речь.
Резюме. Краткое изложение сути вопроса.
Стр.12
Обсуждение. Расширенное пояснение рекомендации. Зачастую включает краткое обоснование, но учтите, что полную информацию по данному вопросу следует искать в приведенных ссылках.
Примеры (если таковые имеются). Примеры, демонстрирующие правило или позволяющие лучше его понять и запомнить.
Исключения (если таковые имеются). Описание ситуаций (обычно редких), когда приведенное правило неприменимо. Однако остерегайтесь попасть в ловушку, думая, что ваш случай особый и что в вашей ситуации это правило неприменимо,— обычно при здравом размышлении оказывается, что ничего особого в вашей ситуации нет и описанное правило может быть с успехом вами применено.
Ссылки. В приведенной в этом подразделе литературе по C++ вы найдете более полный анализ рассматриваемого в разделе вопроса.
В каждой части книги имеется "наиболее важный раздел" — обычно это первый раздел части. Однако иногда это правило нарушалось в связи с необходимостью последовательного и связного изложения материала.
Обработка ошибок — сложная задача, при решении которой программисту требуется вся помощь, которая только может быть предоставлена.
— Бьярн Страуструп (Bjarne Stroustrup),
[Stroustrup94] §16.2
Имеется три способа написать программу без ошибок; но работает только третий способ.
— Алан Перлис (Alan Perlis)
Вопрос не в том, будем ли мы делать программные ошибки. Вопрос в том, будем ли мы что-либо предпринимать, чтобы позволить компилятору и другим используемым инструментам их обнаружить.
В этом разделе документированы добытые трудом множества программистов знания и наилучшие практические подходы, некоторые из которых стоили многих лет работы. Следуйте приведенным правилам и рекомендациям. Когда мы пишем сложную, надежную и безопасную программу — нам требуется вся помощь, которую мы только в состоянии получить.
В этом разделе мы считаем наиболее значимой рекомендацию 69 — "Определите разумную стратегию обработки ошибок и строго ей следуйте".
Стр.144
68.Широко применяйте assert для документирования внутренних допущений и инвариантов
Используйте assert или его эквивалент для документирования внутренних допущений в модуле (т.е. там, где вызываемый и вызывающий код поддерживаются одним и тем же программистом или командой), которые должны всегда выполняться (в противном случае они являются следствием программной ошибки; например, нарушение постусловий функции, обнаруженное вызывающим кодом). (См. также рекомендацию 70.) Убедитесь, что использование assert не приводит к побочным действиям.
69. Определите разумную стратегию обработки ошибок и строго ей следуйте
Еще на ранней стадии проектирования разработайте практичную, последовательную и разумную стратегию обработки ошибок и строго следуйте ей. Убедитесь, что ваша стратегия включает следующее.
Идентификация: какие условия являются ошибкой.
Строгость: насколько важна каждая ошибка.
Обнаружение: какой код отвечает за обнаружение ошибки.
Распространение: какой механизм используется для описания и распространения уведомления об ошибке в каждом модуле.
Обработка: какой код отвечает за выполнение действий, связанных с ошибкой.
Уведомление: каким образом информация об ошибке вносится в журнальный файл или производится уведомление пользователя программы.
Изменяйте механизмы обработки ошибок только в пределах границ модуля.
Стр.215
70. Отличайте ошибки от ситуаций, не являющихся ошибками
Функция представляет собой единицу работы. Таким образом, сбои следует рассматривать либо как ошибки, либо как штатные ситуации, в зависимости от их влияния на функции. В функции/сбой является ошибкой тогда и только тогда, когда он нарушает одно из предусловий/, не позволяет выполнить предусловие вызываемой ею функции, препятствует достижению собственных постусловий f или сохранению инварианта, за поддержку которого отвечает функция/.
В частности, в этой рекомендации мы исключаем внутренние программные ошибки (т.е. те, где за вызывающий и вызываемый код отвечает один и тот же человек или команда, — например, в пределах одного модуля). Они представляют собой отдельную категорию ошибок, для работы с которой используется такое средство, как проверки (см. рекомендацию 68).
Вы, читатель этой книги, и есть главный ее критик. Мы ценим ваше мнение и хотим знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услышать и любые другие замечания, которые вам хотелось бы высказать в наш адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам бумажное или электронное письмо либо просто посетить наш Web-сервер и оставить свои замечания там. Одним словом, любым удобным для вас способом дайте нам знать, нравится ли вам эта книга, а также выскажите свое мнение о том, как сделать наши книги более интересными для вас.
Посылая письмо или сообщение, не забудьте указать название книги и ее авторов, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим мнением и обязательно учтем его при отборе и подготовке к изданию последующих книг. Наши координаты:
E-mail: info@williamspublishing.com
WWW: http://www.wiliamspublishing.com
Информация для писем из
России: 115419, Москва, а/я 783
Украины: 03150, Киев, а/я 152
Стр.220
#
#include, 45
А
Abelson, Harold, 25
ADL, см. Поиск, зависящий от аргумента assert, 45; 112; 144
auto_ptr, 168
В
Bell, Gordon, 25
Bentley, Jon, 25; 29
binary_search, 179
С
Cargill, Tom, 92
const, 42; 142; 194
У входных параметров функций, 58
CVS, 20
D
delete, 95
Dependency inversion principle, 54; 77
deque, 164; 171
Diamond, Norman, 99
dynamic_cast, 41; 193
E
equal_range, 179
errno, 154
explicit, 85; 110
F
find,179
fmd_if, 179
for_each, 28
I
inline, 30
К
Knuth, Donald, 23; 29
L
list, 164
lower_bound, 179
M
map, 164
McConnell, Steve, 25; 144
memcmp, 197
memcpy, 197
mutable, 42
N
new, 95; 97
Размещающий, 98
nth_element, 180
P
partial_sort, 180
partition, 180
Perlis, Alan, 23; 39; 57; 117; 143; 187
Plain Old Data, 190
POD, 190
push_back, 28; 169
R
RAII, 37
realloc, 24
reinterpret_cast, 192
return, 17
Стр.221
s
Schwarz, Jerry, 127
set, 164
shared_ptr, 125; 168
sort, 180
Spencer, Henry, 187
stable_partition, 180
stable_sort, 180
static_cast, 41; 193
string, 167; 171
Stroustrup, Bjarne, 69; 133; 143; 163; 173
struct, 33
Sussman, Gerald Jay, 25
swap, 114; 140
U
upper_bound, 179
using, 119; 122
V
vector, 164; 166; 167; 171
volatile, 49
A
Абельсон, Гарольд, 25
Абстрактный интерфейс, 77; 188
Агрегаты значений, 88
Алгоритмы STL, 173
Аргумент по умолчанию, 81
Атомарные операции, 34
Б
Белл, Гордон,25
Бентли, Ион, 25; 29
В
Венгерская запись, 15
Взаимоблокировка, 34; 36
Вложенность блоков, 50
Внешнее связывание, 32
Время жизни, 47; 200
Г
Гарантии безопасности, 151
Глобальные объекты, 32
Д
Даймонд, Норман, 99
Декремент
Префиксная и постфиксная формы, 62
Дескриптор, 89
Деструктор, 106;151
Виртуальный, 76; 104
Динамическое связывание, 134
Дружба, 73
З
Зависимости определений, 53
Зависимые имена, 55; 138
Заголовочный файл, 16; 55
Защита от множественного включения, 56
Закон Второго Шанса, 78
И
Идиома
Erase-remove, 171
Наиболее важный аспект разработки программного обеспечения — ясно понимать, что именно вы пытаетесь построить.
— Бьярн Страуструп (Bjarne Stroustrup)
Какого вида классы предпочитает разрабатывать и строить ваша команда? Почему?
Интересно, что большинство рекомендаций данного раздела вызваны в первую очередь вопросами зависимостей. Например, наследование— вторая по силе взаимосвязь, которую можно выразить в C++ (первая — отношение дружбы), и такую сильную связь надо использовать очень осторожно и продуманно.
В этом разделе мы сконцентрируем внимание на ключевых вопросах проектирования классов — как сделать это правильно, как не допустить ошибку, избежать ловушек, и в особенности — как управлять зависимостями.
В следующем разделе мы обратимся к Большой Четверке специальных функций — конструктору по умолчанию, копирующему конструктору, копирующему присваиванию и деструктору.
В этом разделе мы считаем самой важной рекомендацию 33 — "Предпочитайте минимальные классы монолитным".
Стр.70
32.Ясно представляйте, какой вид класса вы создаете
Существует большое количество различных видов классов, и следует знать, какой именно класс вы создаете.
Стр.210
33. Предпочитайте минимальные классы монолитным
Разделяй и властвуй: небольшие классы легче писать, тестировать и использовать. Они также применимы в большем количестве ситуаций. Предпочитайте такие небольшие классы, которые воплощают простые концепции, классам, пытающимся реализовать как несколько концепций, так и сложные концепции (см. рекомендации 5 и 6).
34. Предпочитайте композицию наследованию
Избегайте "налога на наследство": наследование— вторая по силе после отношения дружбы взаимосвязь, которую можно выразить в C++. Сильные связи нежелательны, и их следует избегать везде, где только можно. Таким образом, следует предпочитать композицию наследованию, кроме случаев, когда вы точно знаете, что делаете и какие преимущества дает наследование в вашем проекте.
35. Избегайте наследования от классов, которые не спроектированы для этой цели
Классы, предназначенные для автономного использования, подчиняются правилам проектирования, отличным от правил для базовых классов (см. рекомендацию 32). Использование автономных классов в качестве базовых является серьезной ошибкой проектирования и его следует избегать. Для добавления специфического поведения предпочтительно вместо функций-членов добавлять обычные функции (см. рекомендацию 44). Для того чтобы добавить состояние, вместо наследования следует использовать композицию (см. рекомендацию 34). Избегайте наследования от конкретных базовых классов.
36. Предпочитайте предоставление абстрактных интерфейсов
Вы любите абстракционизм? Абстрактные интерфейсы помогают вам сосредоточиться на проблемах правильного абстрагирования, не вдаваясь в детали реализации или управления состояниями. Предпочтительно проектировать иерархии, реализующие абстрактные интерфейсы, которые моделируют абстрактные концепции.
37. Открытое наследование означает заменимость. Наследовать надо
Системы имеют подсистемы, которые в свою очередь состоят из подсистем и так до бесконечности — именно поэтому мы всегда движемся сверху вниз.
— Алан Перлис (Alan Perlis)
Пространство имен — очень важный инструмент для управления именами и снижения количества коллизий имен. То же относится и к модулям, которые, помимо этого, представляют собой инструментарий для работы с версиями. Мы определим модуль как отдельный компонент программы, содержащий тесно связанные между собой ее элементы (см. рекомендацию 5) и поддерживаемый одним и тем же программистом или группой; обычно модуль всегда компилируется одним и тем же компилятором с использованием одних и тех же опций. Модули имеются на разных уровнях детализации в широком диапазоне размеров. С одной стороны, модуль может быть минимального размера, представляя собой отдельный объектный файл, содержащий только один класс; с другой стороны, он может быть, например, отдельной динамической библиотекой, генерируемой из множества исходных файлов, содержимое которых образует подсистему внутри приложения большего размера или выпускается отдельно. Модуль может даже представлять собой огромную библиотеку, состоящую из множества небольших модулей (статических или динамических библиотек), содержащих тысячи разных типов. Несмотря на то, что такие библиотеки в стандарте C++ не упоминаются, программисты постоянно создают и используют библиотеки, и хорошо продуманная модуля-ризация является фундаментальной частью успешного управления зависимостями (см., например, рекомендацию 11).
Трудно представить себе программу значительного размера, которая не использует как пространства имен, так и модули. В этом разделе мы рассмотрим основные рекомендации по использованию двух этих взаимосвязанных инструментов, наряду с их взаимодействием с другими частями языка программирования и среды времени выполнения. Эти рекомендации помогут вам наиболее эффективно воспользоваться таким мощным инструментарием и избежать возможных неприятностей.
В этом разделе мы считаем наиболее значимой рекомендацию 58 — "Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы".
Стр.118
57.Храните типы и их свободный интерфейс в одном пространстве имен
Функции, не являющиеся членами и разработанные как часть интерфейса класса X (в особенности операторы и вспомогательные функции), должны быть определены в том оке пространстве имен, что и X, что обеспечивает их корректный вызов.
58. Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы
Оберегайте ваши типы от непреднамеренного поиска, зависящего от аргументов (argument-dependent lookup — ADL, известный также как поиск Кёнига); однако преднамеренный поиск должен завершаться успешно. Этого можно добиться путем размещения типов в своих собственных пространствах имен (вместе с непосредственно связанными с ними свободными функциями; см. рекомендацию 57). Избегайте помещения типов в те же пространства имен, что и шаблоны функций или операторов).
59. Не используйте using для пространств имен в заголовочных файлах или перед директивой #include
Директива using для пространств имен создана для вашего удобства, а не для головной боли других. Никогда не используйте объявления или директивы using перед директивой #include.
Вывод: не используйте директивы using для пространств имен или using-объявления в заголовочных файлах. Вместо этого полностью квалифицируйте все имена. (Второе правило следует из первого, поскольку заголовочные файлы не могут знать, какие другие директивы #include могут появиться в тексте после них.)
60. Избегайте выделения и освобождения памяти в разных модулях
Золотое правило программиста — положи, где взял. Выделение памяти в одном модуле, а освобождение в другом делает программу более хрупкой, создавая тонкую дальнюю зависимость между этими модулями. Такие модули должны быть компилируемы одной и той же версией компилятора с одними и теми оке флагами (в частности, отладочные версии и версии NDEBUG) и с одной и той же реализацией стандартной библиотеки; кроме того, с практической точки зрения лучше, чтобы модуль, выделяющий память, оставался загружен при ее освобождении.
0.Не мелочитесь, или Что не следует стандартизировать
Скажем кратко: не мелочитесь.
1. Компилируйте без замечаний при максимальном уровне предупреждений
Следует серьезно относиться к предупреждениям компилятора и использовать максимальный уровень вывода предупреждений вашим компилятором. Компиляция должна выполняться без каких-либо предупреждений. Вы должны понимать все выдаваемые предупреждения и устранять их путем изменения кода, а не снижения уровня вывода предупреждений.
2. Используйте автоматические системы сборки программ
Нажимайте на одну (единственную) кнопку: используйте полностью автоматизированные ("в одно действие") системы, которые собирают весь проект без вмешательства пользователя.
3. Используйте систему контроля версий
Как гласит китайская пословица, плохие чернила лучше хорошей памяти: используйте системы управления версиями. Не оставляйте файлы без присмотра на долгий срок. Проверяйте их всякий раз после того, как обновленные модули проходят тесты на работоспособность. Убедитесь, что внесенные обновления не препятствуют корректной сборке программы.
4. Одна голова хорошо, а две — лучше
Регулярно просматривайте код всей командой. Чем больше глаз — тем выше качество кода. Покажите ваш код другим и познакомьтесь с их кодом — это принесет пользу всем.
Место для вашей цитаты.
— Бьярн Страуструп (Bjarne Stroustrup),
[Stroustrup00] §13
Аналогично: место для вашего введения.
В этом разделе мы считаем наиболее значимой рекомендацию 64 — "Разумно сочетайте статический и динамический полиморфизм".
Стр.134
64. Разумно сочетайте статический и динамический полиморфизм
Статический и динамический полиморфизм дополняют друг друга. Следует ясно представлять себе их преимущества и недостатки, чтобы использовать каждый из них там, где он дает наилучшие результаты, и сочетать их так, чтобы получить лучшее из обоих миров.
65. Выполняйте настройку явно и преднамеренно
При разработке шаблона точки настройки должны быть написаны корректно, с особой тщательностью, а также ясно прокомментированы. При использовании шаблона необходимо четко знать, как именно следует настроить шаблон для работы с вашим типом, и выполнить соответствующие действия.
66. Не специализируйте шаблоны функций
При расширении некоторого шаблона функции (включая std::swap) избегайте попыток специализации шаблона. Вместо этого используйте перегрузку шаблона функции, которую следует поместить в пространство имен типа(ов), для которых разработана данная перегрузка (см. рекомендацию 57). При написании собственного шаблона функции также избегайте его специализации.
67. Пишите максимально обобщенный код
Используйте для реализации функциональности наиболее обобщенные и абстрактные средства.
Примечание: для удобства читателей весь список литературы доступен по адресу http://www.gotw.ca/publications/c++cs/bibliography.htm
Ссылки, выделенные подчеркиванием (например, [Abrahams96] ), представляют собой гиперссылки в приведенной выше странице.
| [Abelson96] | Abelson H. and Sussman G. J. Structure and Interpretation of Computer Programs (2nd Edition) (MIT Press, 1996). | ||
| [Abrahams96] | Abrahams D. Exception Safety in STLport. STLport website, 1996. | ||
| [Abrahams01a] | Abrahams D. Exception Safety in Generic Components, in Jazayeri M., Loos R., Musser D. (eds.), Generic Programming: International Seminar on Generic Programming, Dagstuhl Castle, Germany, April/May 1998, Selected Papers, Lecture Notes in Computer Science 1766 (Springer, 2001). | ||
| [Abrahams01b] | Abrahams D. Error and Exception Handling. [Boost] website, 2001. | ||
| [Alexandrescu00a] | Alexandrescu A. Traits: The else-if-then of Types. C++ Report, 12(4), April 2000. | ||
| [Alexandrescu00b] | Alexandrescu A. Traits on Steroids. C++ Report, 12(6), June 2000. | ||
| [Alexandrescu00c] | Alexandrescu A. and Marginean P. Change the Way You Write Exception-Safe Code—Forever. C/C++ Users Journal, 18(12), December 2000. | ||
| [Alexandrescu01] | Alexandrescu A. Modern C++ Design. Addison-Wesley, 2001.
Перевод: Александреску А. Современное проектирование на C++. Серия C+ + In-Depth, т.З. — М.: Издательский дом "Вильямс", 2002. | ||
| [Alexandrescu01a] | Alexandrescu A. A Policy-Based basic_string Implementation. C/C++ Users Journal, 19(6), June 2001. | ||
| [Alexandrescu02a] | Alexandrescu A. Multithreading and the C++ Type System. InformJT website, February 2002. | ||
| [Alexandrescu02b] | Alexandrescu A. "Discriminated Unions (I)," "... (II)," and "... (III)". C/C++ Users Journal, 20(4,6,8), April/June/August 2002. | ||
| [Alexandrescu03a] | Alexandrescu A. Move Constructors. C/C++ Users Journal, 21(2), February 2003. | ||
| [Alexandrescu03b] | Alexandrescu A. Assertions. C/C++ Users Journal, 21(4), April 2003. | ||
| [Alexandrescu03c] | Alexandrescu A. and Marginean P. Enforcements. C/C++ Users Journal, 21(6), June 2003. | ||
| [Alexandrescu03d] | Alexandrescu A. and Held D. Smart Pointers Reloaded. C/C++ Users Journal, 21(10), October 2003. | ||
| [Alexandrescu04] | Alexandrescu A. Lock-Free Data Structures. C/C++ Users Journal, 22(10), October 2004. | ||
| [Allison98] | Allison С. С & C++ Code Capsules. Prentice Hall, 1998. | ||
| [Austern99] | Austern M. H. Generic Programming and the STL. Addison-Wesley, 1999. | ||
| [Barton94] | Barton J. and Nackman L. Scientific and Engineering C++. Addison-Wesley, 1994. | ||
| [Bentley00] | Bentley J. Programming Pearls (2nd Edition). Addison-Wesley, 2000. Перевод: Бентли Дж. Жемчужины программирования. Второе издание. — СПб.: Питер, 2002. | ||
| [BetterSCM] | Web-узел Better SCM Initiative. | ||
| [Boost] | C++ Boost. | ||
| [BoostLRG] | Boost Library Requirements and Guidelines. (Web-узел Boost). | ||
| [Brooks95] | Brooks F. The Mythical Man-Month. Addison-Wesley, 1975; reprinted with corrections in 1995. | ||
| [Butenhof97] | Butenhof D. Programming with POSIX Threads. Addison-Wesley, 1997. | ||
| [Cargill92] | Cargill T. C++ Programming Style. Addison-Wesley, 1992. |
|
[С90] |
ISO/IEC 9899:1990(E), Programming Languages — С ( ISO C90 and ANSI C89 Standard). |
|
[C99] |
ISO/IEC 9899:1999(E), Programming Languages — С (revised ISO and ANSI C99 Standard). |
|
[С++98] |
ISO/IEC 14882:1998(E), Programming Languages—C++ (ISO and ANSI C++ Standard). |
|
[С++03] |
ISO/IEC 14882:2003(E), Programming Languages—C++ (updated ISO and ANSI C++ Standard including the contents of [C++98] plus errata corrections). |
|
[C++TR104] |
ISO/EEC JTC1/SC22/WG21/N1711. (Draft) Technical Report on Standard Library Extensions (ISO C++ committee working document, November 2004). Близкий к окончанию черновик с расширениями стандартной библиотеки C++, включая shared_ptr. |
|
[Cline99] |
Cline M., Lomow G., and Girou M. C++ FAQs (2ndEdition). Addison-Wesley, 1999. |
|
[Constantine95] |
Constantine L. Constantine on Peopleware. Yourdon Press, 1995. |
|
[Coplien92] |
Coplien J. Advanced C++ Programming Styles and Idioms. Addison-Wesley, 1992. |
|
[Cormen01] |
Cormen Т., Leiserson C, Rivest R., Stein C. Introduction to Algorithms (2nd Edition). MIT Press, 2001. |
|
Перевод: Кормен Т., Лейзерсон Ч., Ривест Р., Стейн К. Введение в алгоритмы. 2-е изд. — М.: Издательский дом "Вильямс" (в печати) |
|
|
[CVS] |
Web-узел CVS. |
|
[Cowan01] |
Cowan С, Barringer ML, Beattie S., and Kroah-Hartman G. FormatGuard: Automatic Protection From printf Format String Vulnerabilities. Proceedings of the 2001 VSENIX Security Symposium, August 2001, Washington, D.C. |
|
[Dewhurst03] |
Dewhurst S. C++ Gotchas. Addison-Wesley, 2003. |
|
[Dinkumware-Safe] |
Dinkumware Unabridged Library documentation (Web-узел Dinkumware). |
|
[Ellis90] |
Ellis M. and Stroustrup B. The Annotated C++ Reference Manual. Addison-Wesley, 1990. |
|
Перевод: Эллис М., Страуструп Б. Справочное руководство по языку программирования C++ с комментариями. — М.: Мир, 1992. |
|
|
[Gamma95] |
Gamma E., Helm R., Johnson R., and Vlissides J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. |
|
Перевод: Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. — СПб.: Питер, 2001. |
|
|
[GnuMake] |
Gnu make (Web-узел Gnu). |
|
[GotW] |
Sutter H. Guru of the Week column. |
|
[Henney00] |
Henney K. C++ Patterns: Executing Around Sequences (EuroPLoP 2000 proceedings). |
|
[Henney01] |
Henney K. C++ Patterns: Reference Accounting (EuroPLoP 2001 proceedings). |
|
[Henney02a] |
Henney K. Stringing Things Along. Application Development Advisor, July-August 2002. |
|
[Henney02b] |
Henney K. The Next Best String. Application Development Advisor, October 2002. |
|
[Henricson97] |
Henricson M. and Nyquist E. Industrial Strength C++. Prentice Hall,1997. |
|
[Horstmann95] |
Horstmann С S. Safe STL., 1995. |
|
[Josuttis99] |
Josuttis N. The C++ Standard Library. Addison-Wesley, 1999. |
|
Перевод: Джосьютис Н. C++. Стандартная библиотека. — СПб.: Питер (в печати). |
|
|
[Keffer95] |
Keffer Т. Rogue Wave C++ Design, Implementation, and Style Guide. Rogue Wave Software, 1995. |
|
[Kernighan99] |
Kernighan B. and Pike R. The Practice of Programming. Addison-Wesley, 1999. |
|
[Knuth89] |
Knuth D. The Errors of TeX. Software—Practice & Experience, 19(7), July 1989. |
|
[Knuth97a] |
Knuth D. The Art of Computer Programming, Volume 1: Fundamental Algorithms, 3rd Edition. Addison-Wesley, 1997. |
|
Перевод: Кнут Д. Искусство программирования, том I. Основные алгоритмы, 3-е изд. — М.: Издательский дом "Вильямс", 2000. |
|
[Knuth97b] |
Knuth D. The Art of Computer Programming, Volume 2: Seminumerical Algorithms, 3rd Edition. Addison-Wesley, 1997. |
|
Перевод: Кнут Д. Искусство программирования, том 1. Получисленные алгоритмы, 3-е изд. — М.: Издательский дом "Вильямс", 2000. |
|
|
[Knuth98] |
Knuth D. The Art of Computer Programming, Volume 3: Sorting and Searching, 2nd Edition. Addison-Wesley, 1998. |
|
Перевод: Кнут Д. Искусство программирования, том 1. Сортировка и поиск, 2-е изд. — М.: Издательский дом "Вильямс", 2000. |
|
|
[Koenig97] |
Koenig A. and Moo В. Ruminations on C++. Addison-Wesley, 1997. |
|
[Lakos96] |
Lakos J. Large-Scale C++ Software Design. Addison-Wesley, 1996. |
|
[Liskov88] |
Liskov B. Data Abstraction and Hierarchy. SIGPLAN Notices, 23(5), May 1988. |
|
[Martin96a] |
Martin R. C. The Dependency Inversion Principle. C++ Report, 8(5), May 1996. |
|
[Martin96b] |
Martin R. С Granularity. C++ Report, 8(9), October 1996. |
|
[Martin96c] |
Martin R. С The Open-Closed Principle. C++ Report, 8(1), January 1996. |
|
[Martin98] |
Martin R. C, Riehle D., Buschmann F. (eds.). Pattern Languages of Program Design 3. Addison-Wesley, 1998. |
|
[Martin00] |
Martin R. C. Abstract Classes and Pure Virtual Functions in Martin R. C. (ed.), More C++ Gems. Cambridge University Press, 2000. |
|
[McConnell93] |
McConnell S. Code Complete. Microsoft Press, 1993. |
|
[Metrowerks] |
Metrowerks. |
|
[Meyer00] |
Meyer B. Object-Oriented Software Construction, 2nd Edition. Prentice Hall, 2000. |
|
[Meyers96] |
Meyers S. More Effective C++. Addison-Wesley, 1996. |
|
Перевод: Мейерс С. Наиболее эффективное использование C++. 35 новых рекомендаций по улучшению ваших программ и проектов. — М.: ДМК Пресс, 2000. |
|
|
[Meyers97] |
Meyers S. Effective C++, 2nd Edition. Addison-Wesley, 1997. |
|
Перевод: Мейерс С. Эффективное использование C++. 50 рекомендаций по улучшению ваших программ и проектов. — М.: ДМК Пресс, 2000. |
|
|
[Meyers00] |
Meyers S. How Non-Member Functions Improve Encapsulation. C/C++ Users Journal, 18(2), February 2000. |
|
[Meyers01] |
Meyers S. Effective STL Addison-Wesley, 2001. |
|
Перевод: Мейерс С. Эффективное использование STL. — СПб.: Питер, 2002. |
|
|
[Meyers04] |
Meyers S. and Alexandrescu A. C++ and the Perils of Double-Checked Locking, Part 1 and ...Part 2. Dr. Dobb's Journal, 29(7,8), July and August 2004. |
|
[Milewski01] |
Milewski B. C++ In Action. Addison-Wesley, 2001. |
|
[Miller56] |
Miller G. A. The Magical Number Seven, Plus or Minus Two: Some Limits on Our Capacity for Processing Information. The Psychological Review, 1956, vol. 63. |
|
[MozillaCRFAQ] |
Frequently Asked Questions About mozilla.org 's Code Review Process (Web-узел Mozilla). |
|
[Murray93] |
Murray R. C++ Strategies and Tactics. Addison-Wesley, 1993. |
|
[Musser01] |
Musser D. R., Derge G. J., and Saini A. STL Tutorial and Reference Guide, 2nd Edition. Addison-Wesley, 2001. |
|
[Parnas02] |
Parnas D. The Secret History of Information Hiding. Software Pioneers: Contributions To Software Engineering, Springer-Verlag, New York, 2002. |
|
[Peters99] |
Peters T. The Zen of Python. Сотр.lang.python, June 1999. |
|
[Piwowarski82] |
Piwowarski P. A Nesting Level Complexity Measure. ACM SIGPLAN Notices, 9/1982. |
|
[Saks99] |
Saks D. Thinking Deeply, Thinking Deeper, and Thinking Even Deeper. C/C++ Users Journal, 17(4,5,6), April, May, and June 1999. |
|
[Schmidt01] |
Schmidt D., Stal M., Rohnert H., Buschmann F. Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects, Wiley, 2001. |
|
[SeamonkeyCR] |
Seamonkey Code Reviewer's Guide (Web-узел Mozilla). |
|
[Sedgewick98] |
Sedgewick R. Algorithms in C++, Parts 1-4: Fundamentals, Data Structure, Sorting, Searching, 3rd Edition. Addison-Wesley, 1998. |
|
[STLport-Debug] |
Fomitchev B. STLport: Debug Mode (Web-узел STLport). |
|
[Stroustrup94] |
Stroustrup B. The Design and Evolution of C++. Addison-Wesley, 1994. |
|
Перевод: Страуструп Б. Дизайн и эволюция языка C++. — М.: ДМК Пресс, 2000. |
|
|
[Stroustrup00] |
Stroustrup В. The C++ Programming Language (Special 3rd Edition). Addison-Wesley, 2000. |
|
Перевод: Страуструп Б. Язык программирования C++. Специальное издание. — М.: Бином, 2001. |
|
|
[Sutter99] |
Sutter H. ACID Programming. Guru of the Week #6]. |
|
[Sutter00] |
Sutter H. Exceptional C++. Addison-Wesley, 2000. |
|
Перевод: Саттер Г. Решение сложных задач на C++. Серия C++ In-Depth, т.4. — М.: Издательский дом "Вильямс", 2002. |
|
|
[Sutter02] |
Sutter H. More Exceptional C++. Addison-Wesley, 2002. |
|
Перевод: Саттер Г. Решение сложных задач на C++. Серия C++ In-Depth, т.4. — М.: Издательский дом "Вильямс", 2002. |
|
|
[Sutter03] |
Sutter H. Generalizing Observer. C/C++ Users Journal, 21(9), September 2003. |
|
[Sutter04] |
Sutter H. Exceptional C++ Style. Addison-Wesley, 2004. |
|
Перевод: Саттер Г. Новые сложные задачи на C++. — М.: Издательский дом "Вильямс" (в печати). |
|
|
[Sutter04a] |
Sutter H. Function Types. C/C++ Users Journal, 22(7), July 2004. |
|
[Sutter04b] |
Sutter H. When and How To Use Exceptions. C/C++ Users Journal, 22(8), August 2004. |
|
[Sutter04c] |
Sutter H. Just Enough' Thread Safety. C/C++ Users Journal, 22(9), September 2004. |
|
[Sutter04d] |
Sutter H. How to Provide (or Avoid) Points of Customiza-tion in Templates. C/C+ + Users Journal, 22(11), November 2004. |
|
[SuttHysl01] |
Sutter H. and Hyslop J. Hungarian wartHogs. C/C++ Users Journal, 19(11), November 2001. |
|
[SuttHysl02] |
Sutter H. and Hyslop J. A Midsummer Night's Madness. C/C++ Users Journal, 20(8), August 2002. |
|
[SuttHysl03] |
Sutter H. and Hyslop J. Sharing Causes Contention. C/C++ Users Journal, 21(4), April 2003. |
|
[SuttHysl04a] |
Sutter H. and Hyslop J. Getting Abstractions. C/C++ Users Journal, 22(6), June 2004. |
|
[SuttHysl04b] |
Sutter H. and Hyslop J. Collecting Shared Objects. C/C++ Users Journal, 22(8), August 2004. |
|
[Taligent94] |
Taligent's Guide to Designing Programs. Addison-Wesley, 1994. |
|
[Tsai01] |
Tsai T. and Singh N. Libsafe 2.0: Detection of Format String Vulnerability Exploits. Avaya Labs, March 2001. |
|
[Vandevoorde03] |
Vandevoorde D. and Josuttis N. C++ Templates. Addison-Wesley, 2003. |
|
Перевод: Вандевурд Д., Джосаттис Н. Шаблоны C++. Справочник разработчика. — М.: Издательский дом "Вильямс", 2003. |
|
|
[Webber03] |
Webber А. В. Modern Programming Languages: A Practical Introduction. Franklin, Beedle & Associates, 2003. |
Хорошие стандарты кодирования могут принести немалую выгоду с различных точек зрения.
Повышение качества кода. Работа в соответствии со стандартом приводит к однотипному решению одинаковых задач, что повышает ясность кода и упрощает его сопровождение.
Повышение скорости разработки. Разработчику не приходится решать все задачи и принимать решения "с нуля".
Повышение уровня взаимодействия в команде. Наличие стандарта позволяет уменьшить разногласия в команде и устранить ненужные дебаты по мелким вопросам, облегчает понимание и поддержку чужого кода членами команды.
Согласованность в работе. При использовании стандарта разработчики направляют свои усилия в верном направлении, на решение действительно важных задач.
В напряженной обстановке, при жестких временных рамках люди обычно делают то, чему их учили, к чему они привыкли. Вот почему в больницах в пунктах первой помощи предпочитают опытных, тренированных сотрудников — даже хорошо обученные и знающие новички склонны к панике.
У разработчиков программного обеспечения регулярно возникают ситуации, когда что-то надо было сделать еще вчера — на позавчера. Когда на нас давит график работ (который к тому же имеет тенденцию сдвигаться в одном направлении, и то, что по плану должно было заработать завтра, от нас начинают требовать еще вчера...), мы работаем так, как приучены. Неряшливые программисты, которые даже при обычной неспешной работе не помнят о правильных принципах разработки программного обеспечения (а то и вовсе не знакомы с ними), при нехватке времени окажутся еще небрежнее, а их код будет изобиловать ошибками. Соответственно, программист, который выработал в себе хорошие привычки и регулярно ими пользуется, при "повышенном давлении" будет продолжать выдавать качественный код.
Стандарты кодирования, приведенные в этой книге, представляют собой набор рекомендаций по написанию высококачественного кода на C++. В них сконцентрирован богатый коллективный опыт всего сообщества программистов на C++. Многие из этих знаний разбросаны по частям по самым разным книгам, но не меньшее количество знаний передается изустно. Мы постарались собрать разрозненные сведения в одной книге в виде коллекции ясных, компактных правил с пояснениями, простых для понимания и следования им.
Герб Саттер, Андрей Александреску
Издательский дом "Вильямс"
Москва • Санкт-Петербург • Киев
2005
ББК 32.973.26-018.2.75
С21
УДК 681.3.07
Издательский дом "Вильямс"
Зав. редакцией С.Н. Тригуб
Перевод с английского и редакция канд. техн. наук И.В. Красикова
По общим вопросам обращайтесь в Издательский дом "Вильямс" по адресу:
info@williamspublishing.com, http://www.williamspublishing.com
115419, Москва, а/я 783; 03150, Киев, а/я 152
Саттер, Герб, Александреску, Андрей.
Стандарты программирования на C++.: Пер. с англ. — М.: Издательский дом "Вильямс", 2005.— 224 с. :ил.— Парал. тит. англ.
ISBN 5-8459-0859-0 (рус.)
Эта книга поможет новичку стать профессионалом, так как в ней представлен сконцентрированный лучший опыт программистов на C++, обобщенный двумя экспертами мирового класса.
Начинающий программист найдет в ней простые и понятные рекомендации для ежедневного использования, подкрепленные примерами их конкретного применения на практике.
Опытные программисты найдут в ней советы и новые рекомендации, которые можно сразу же принять на вооружение. Программисты-профессионалы могут использовать эту книгу как основу для разработки собственных стандартов кодирования, как для себя лично, так и для группы, которой они руководят.
Конечно, книга рассчитана в первую очередь на профессиональных программистов с глубокими знаниями языка, однако она будет полезна любому, кто захочет углубить свои знания в данной области.
ББК 32.973.26-018.2.75
Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм.
Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Addison-Wesley Publishing Company. Inc.
Authorized translation from the English language edition published by Prentice Hall, Copyright © 2005
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.
Russian language edition was published by Williams Publishing House according to the Agreement with R&l Enterprises International, Copyright © 2005
© Издательский дом "Вильямс", 2005
© Pearson Education, Inc., 2005
ISBN 5-8459-0859-0 (рус.)
ISBN 0-321-11358-6 (англ.)
Стр.5
Константа для одного является переменной для другого.
— Алан Перлис (Alan Perlis)
В этом разделе мы перейдем от вопросов проектирования к вопросам, которые появляются в основном при реальном кодировании.
Правила и рекомендации из этого раздела применимы безотносительно к конкретной области языка программирования (например, функциям, классам или пространствам имен), но приводят к повышению качества вашего кода. Многие из представленных идиом позволяют вашему компилятору активнее помогать вам в работе, а вам — избежать опасных мест (включая неопределенное поведение), которые компилятор не всегда в состоянии выявить. Все это делает ваш код более надежным.
В этом разделе мы считаем наиболее важной рекомендацию 14: "Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения".
Стр.40
14.Предпочитайте ошибки компиляции и компоновки ошибкам времени выполнения
Не стоит откладывать до выполнения программы выявление ошибок, которые можно обнаружить при ее сборке. Предпочтительно писать код, который использует компилятор для проверки инвариантов в процессе компиляции, вместо того, чтобы проверять их во время работы программы. Проверки времени выполнения зависят от выполнимого кода и данных, так что вы только изредка можете полностью полагаться на них. Проверки времени компиляции, напротив, не зависят от данных и предыстории исполнения, что обычно обеспечивает более высокую степень надежности.
15. Активно используйте const
const — ваш друг: неизменяемые значения проще понимать, отслеживать и мотивировать, т.е. там, где это целесообразно, лучше использовать константы вместо переменных. Сделайте const описанием по умолчанию при определении значения — это безопасно, проверяемо во время компиляции (см. рекомендацию 14) и интегрируемо с системой типов
Стр.208
C++. Не выполняйте преобразований типов с отбрасыванием const кроме как при вызове некорректной с точки зрения употребления const функции (см. рекомендацию 94).
16. Избегайте макросов
Макрос — самый неприятный инструмент С и C++, оборотень, скрывающийся под личиной функции, кот, гуляющий сам по себе и не обращающий никакого внимания на границы ваших областей видимости. Берегитесь его!
17. Избегайте магических чисел
Избегайте использования в коде литеральных констант наподобие 42 или 3.1415926. Такие константы не самоочевидны и усложняют сопровождение кода, поскольку вносят в него трудноопределимый вид дублирования. Используйте вместо них символьные имена и выражения наподобие width*aspectRatio.
18. Объявляйте переменные как можно локальнее
Избегайте "раздувания" областей видимости. Переменных должно быть как можно меньше, а время их жизни — как можно короче. Эта рекомендация по сути является частным случаем рекомендации 10.
19. Всегда инициализируйте переменные
Дураки игнорируют сложности. Прагматики терпят их. Некоторые ухитряются их избегать. И только гении устраняют их.
— Алан Перлис (Alan Perlis)
Я также знал, но забыл афоризм Хоара о том, что преждевременная оптимизация— корень всех зол в программировании.
— Дональд Кнут (Donald Knuth),
The Errors of TeX
[Knuth89]
Очень сложно отделить стиль проектирования от стиля кодирования. Мы попытались поместить в очередном разделе те вопросы, которые обычно проявляются, когда вы начинаете писать реальный код.
В этом разделе внимание уделяется принципам и практическим вопросам, область применения которых больше, чем просто отдельный класс или функция. Классические примеры — баланс между простотой и ясностью (рекомендация 6), избежание преждевременной оптимизации (рекомендация 8) и пессимизации (рекомендация 9). Эти рекомендации применимы не только на уровне кодирования отдельной функции, но и к большим областям — проектированию классов и модулей или к решениям с далеко идущими последствиями по поводу архитектуры приложений. (Они применимы также для всех программистов. Если вы считаете иначе — еще раз прочтите приведенную выше цитату Кнута и даже заучите ее на память.)
Многие из прочих рекомендаций этого и следующего разделов имеют дело с управлением зависимостями — краеугольным камнем проектирования программного обеспечения и часто повторяющейся в данной книге темой. Остановитесь и задумайтесь над произвольной методикой проектирования программного обеспечения — любой хорошей методикой. Какую бы методику вы ни выбрали, так или иначе вы столкнетесь с ослаблением зависимостей. Наследование? Разрабатываемый код должен делать производный класс как можно менее зависимым от базового класса. Минимизация использования глобальных переменных? Снижает дальнодействующие зависимости, осуществляемые посредством широко видимых разным частям программы данных. Абстракция? Устраняет зависимости между кодом, который управляет концепциями, и кодом, который их реализует. Сокрытие информации? Делает код клиента менее зависимым от деталей реализации. Забота об управлении зависимостями отражается в устранении совместного использования состояния (рекомендация 10), применении сокрытия информации (рекомендация 11) и прочем.
В этом разделе самой важной нам кажется рекомендация 6: " Главное — корректность, простота и ясность".
Стр.24
5. Один объект — одна задача
Концентрируйтесь одновременно только на одной проблеме. Каждый объект (переменная, класс, функция, пространство имен, модуль, библиотека) должны решать одну точно поставленную задачу. С ростом объектов, естественно, увеличивается область их ответственности, но они не должны отклоняться от своего предназначения.
6. Главное — корректность, простота и ясность
Корректность лучше быстроты. Простота лучше сложности. Ясность лучше хитроумия. Безопасность лучше ненадежности (см. рекомендации 83 и 99).
7. Кодирование с учетом масштабируемости
Всегда помните о возможном росте данных. Подумайте об асимптотической сложности без преждевременной оптимизации. Алгоритмы, которые работают с пользовательскими данными, должны иметь предсказуемое и, желательно, не хуже чем линейно зависящее от количества обрабатываемых данных время работы. Когда становится важной и необходимой оптимизация, в особенности из-за роста объемов данных, в первую очередь следует улучшать О-сложность алгоритма, а не заниматься микрооптимизациями типа экономии на одном сложении.
Стр.207
8. Не оптимизируйте преждевременно
Как гласит пословица, не подгоняйте скачущую лошадь. Преждевременная оптимизация непродуктивна и быстро входит в привычку. Первое правило оптимизации: не оптимизируйте. Второе правило оптимизации (только для экспертов): не оптимизируйте ни в коем случае. Семь раз отмерь, один раз оптимизируй.
9. Не пессимизируйте преждевременно
То, что просто для вас, — просто и для кода. При прочих равных условиях, в особенности — сложности и удобочитаемости кода, ряд эффективных шаблонов проектирования и идиом кодирования должны естественным образом "стекать с кончиков ваших пальцев" и быть не сложнее в написании, чем их пессимизированные альтернативы. Это не преждевременная оптимизация, а избежание излишней пессимизации.
10. Минимизируйте глобальные и совместно используемые данные
Совместное использование вызывает споры и раздоры. Избегайте совместного использования данных, в особенности глобальных данных. Совместно используемые данные усиливают связность, что приводит к снижению сопровождаемости, а зачастую и производительности.
11. Сокрытие информации
Не выпускайте внутреннюю информацию за пределы объекта, обеспечивающего абстракцию.
12. Кодирование параллельных вычислений
Если ваше приложение использует несколько потоков или процессов, следует минимизировать количество совместно используемых объектов, где это только можно (см. рекомендацию 10), и аккуратно работать с оставшимися.
13. Ресурсы должны быть во владении объектов
Не работайте вручную, если у вас есть мощные инструменты. Идиома C++ "выделение ресурса есть инициализация" (resource acquisition is initialization — RAII) представляет собой мощный инструмент для корректной работы с ресурсами. RAII позволяет компилятору автоматически обеспечить строгую гарантию того, что в других языках надо делать вручную. При выделении ресурса передайте его объекту-владельцу. Никогда не выделяйте несколько ресурсов в одной инструкции.
Предпочитайте алгоритмы циклам.
— Бьярн Страуструп (Bjarne Stroustrup),
[Stroustrup00] §18.12
Алгоритмы представляют собой циклы — только они лучше циклов. Алгоритмы — это "шаблоны" циклов, с добавлением дополнительной семантики по сравнению с простыми for и do. Конечно, начав использовать алгоритмы, вы начнете использовать и функциональные объекты и предикаты; корректно пишите их и широко используйте в своих программах.
В этом разделе мы считаем наиболее значимой рекомендацию 83 — "Используйте отладочную реализацию STL".
Стр.174
83.Используйте отладочную реализацию STL
Безопасность превыше всего (см. рекомендацию 6). Используйте отладочную реализацию STL, даже если она имеется только для одного из ваших компиляторов, и даже если она используется только для отладочного тестирования.
84. Предпочитайте вызовы алгоритмов самостоятельно разрабатываемым циклам
Разумно используйте функциональные объекты. В очень простых случаях написанные самостоятельно циклы могут оказаться более простым и эффективным решением. Тем не менее, вызов алгоритма вместо самостоятельно разработанного цикла может оказаться более выразительным, легче сопровождаемым, менее подверженным ошибкам и не менее эффективным.
При вызове алгоритма подумайте о написании собственного функционального объекта, который инкапсулирует всю необходимую логику. Избегайте объединения связывателей параметров и простых функциональных объектов (например, bind2nd, plus), что обычно снижает ясность кода. Подумайте об использовании лямбда-библиотеки [Boost] , которая автоматизирует задачу написания функциональных объектов.
85. Пользуйтесь правильным алгоритмом поиска
Данная рекомендация применима к поиску определенного значения в диапазоне. При поиске в неотсортированном диапазоне используйте алгоритмы find/find_if или count/count_if. Для поиска в отсортированном диапазоне выберите lower_bound, upper_bound, equal_range или (реже) binary_search. (Вопреки своему имени, binary_search обычно — неверный выбор.)
86. Пользуйтесь правильным алгоритмом сортировки
При сортировке вы должны четко понимать, как работает каждый из сортирующих алгоритмов, и использовать наиболее дешевый среди тех, которые пригодны для решения вашей задачи.
87. Делайте предикаты чистыми функциями
Предикат представляет собой функциональный объект, который возвращает ответ да/ нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов (обратите внимание — в данном случае термин "чистая" не имеет никакого отношения к чисто виртуальным функциям).
Если вам нужен контейнер, по умолчанию используйте vector.
— Бьярн Страуструп (Bjarne Stroustrup),
[Stroustrup00] §17.7
Мы знаем, что вы уже предпочитаете использовать стандартные контейнеры вместо написанных вручную. Но какой именно контейнер следует использовать? Что должно (и что не должно) храниться в контейнерах и почему? Как следует заполнять контейнеры? Какие важные идиомы надо знать?
В этом разделе имеются ответы на все перечисленные (и не только) вопросы. И не случайно, что три первые рекомендации в нем содержат слова "Используйте vector...".
В этом разделе мы считаем наиболее значимой рекомендацию 79 — "Храните в контейнерах только значения или интеллектуальные указатели". К ней мы добавим — если даже вы не планируете применять [Boost] и [C++TR104] для иных целей, все равно воспользуйтесь их интеллектуальным указателем shared_ptr.
Стр.164
76. По умолчанию используйте vector. В противном случае выбирайте контейнер, соответствующий задаче
Очень важно использовать "правильный контейнер". Если у вас есть весомые причины выбрать определенный тип контейнера, используйте тот контейнер, который наиболее подходит для вашей задачи.
Если конкретных предпочтений нет, возьмите vector и спокойно работайте, зная, что вы сделали верный выбор.
77. Вместо массивов используйте vector и string
Избегайте реализации абстракции массива посредством массивов в стиле С, арифметики указателей и примитивов управления памятью. Использование vector или string не только сделает проще вашу жизнь, но и позволит написать более безопасную и масштабируемую программу.
78. Используйте vector (и string::c_str) для обмена данными с API на других языках
vector и string::с_str служат шлюзом для сообщения с API на других языках. Однако не полагайтесь на то, что итераторы являются указателями; для получения адреса элемента, на который ссылается vector<T>:.iterator iter, используйте выражение &*iter.
79. Храните в контейнерах только значения или интеллектуальные указатели
Храните к контейнерах объекты-значения. Контейнеры полагают, что их содержимое имеет тип значения, включая непосредственно хранящиеся значения, интеллектуальные указатели и итераторы.
80. Предпочитайте push_back другим способам расширения последовательности
Используйте push_back везде, где это возможно. Если для вас не важна позиция вставки нового объекта, лучше всего использовать для добавления элемента в последовательность функцию push_back. Все прочие средства могут оказаться как гораздо менее быстрыми, так и менее понятными.
81. Предпочитайте операции с диапазонами операциям с отдельными элементами
При добавлении элементов в контейнер лучше использовать операции с диапазонами (т.е. функцию insert, которая получает пару итераторов), а не последовательность вызовов функции для вставки одного элемента. Вызов функции для диапазона обычно проще написать, легче читать, и он более эффективен, чем явный цикл (см. также рекомендацию 84).
82. Используйте подходящие идиомы для реального уменьшения емкости контейнера и удаления элементов
Для того чтобы действительно избавиться от излишней емкости контейнера, воспользуйтесь трюком с использованием обмена, а для реального удаления элементов из контейнера — идиомой erase-remove.
Стр.217
Если бы строители строили здания так же, как программисты пишут программы,— то первый же залетевший дятел разрушил бы всю цивилизацию.
— Джеральд Вайнберг (Gerald Weinberg)
Следуя великой традиции С и C++, мы начинаем отсчет с нуля. Главный совет — под номером 0 — говорит о том, что основной советчик по поводу стандартов кодирования — наши чувства и ощущения.
Остальная часть этой главы состоит из небольшого количества тщательно отобранных вопросов, которые не имеют прямого отношения к коду и посвящены важным инструментам и методам написания надежного кода.
В этом разделе книги наиболее важной мы считаем нулевую рекомендацию — "Не мелочитесь, или Что не следует стандартизировать".
Стр.14