Герб Саттер - Стандарты программирования на С++. 101 правило и рекомендация
- Название:Стандарты программирования на С++. 101 правило и рекомендация
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2005
- Город:Москва
- ISBN:5-8459-0859-0
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Герб Саттер - Стандарты программирования на С++. 101 правило и рекомендация краткое содержание
Эта книга поможет новичку стать профессионалом, так как в ней представлен сконцентрированный лучший опыт программистов на С++, обобщенный двумя экспертами мирового класса.
Начинающий программист найдет в ней простые и понятные рекомендации для ежедневного использования, подкрепленные примерами их конкретного применения на практике.
Опытные программисты найдут в ней советы и новые рекомендации, которые можно сразу же принять на вооружение. Программисты-профессионалы могут использовать эту книгу как основу для разработки собственных стандартов кодирования, как для себя лично, так и для группы, которой они руководят.
Конечно, книга рассчитана в первую очередь на профессиональных программистов с глубокими знаниями языка, однако она будет полезна любому, кто захочет углубить свои знания в данной области.
Стандарты программирования на С++. 101 правило и рекомендация - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
find_if(v.begin(), v.end(), not1(IsHeavy())) ; // OK
Еще более важно то, что для определения сравнения в ассоциативных контейнерах вам нужен именно функциональный объект, а не функция. Это связано с тем, что нельзя инстанцировать шаблон с функцией в качестве параметра:
bool CompareThings(const Thing&, const Thing&);
set s; // Ошибка
Вместо этого следует написать:
struct CompareThings
: public binary_function {
bool operator()( const Thing&, const Thing& ) const;
};
set s; //OK
Наконец, имеется еще одно преимущество функциональных объектов — эффективность. Рассмотрим следующий знакомый алгоритм:
template
Iter find_if(Iter first, Iter last, Compare comp);
Если мы передадим алгоритму в качестве компаратора функцию
inline bool Function(const Thing&) { /* ... */ }
find_if(v.begin(), v.end(), Function);
то на самом деле будет передана ссылка на функцию. Компиляторы редко встраивают вызовы таких функций (за исключением некоторых относительно свежих компиляторов, которые в состоянии провести анализ всей программы в целом), даже если они объявлены как таковые и видимы в момент компиляции вызова find_if
. Кроме того, как уже упоминалось, функции не адаптируемы.
Давайте передадим алгоритму find_if
в качестве компаратора функциональный объект:
struct FunctionObject : unary_function {
bool operator()(const Thing&) const { /* ... */ }
};
find_if(v.begin(), v.end(), FunctionObject());
Если мы передаем объект, который имеет (явно или неявно) встраиваемый оператор operator()
, то такие вызовы компиляторы С++ способны делать встраиваемыми уже очень давно.
Примечание. Эта методика не является преждевременной оптимизацией (см. рекомендацию 8); ее следует рассматривать как препятствие преждевременной пессимизации (см. рекомендацию 9). Если у вас имеется готовая функция — передавайте указатель на нее (кроме тех ситуаций, когда вы должны обязательно обернуть ее в ptr_fun
или mem_fun
). Но если вы пишете новый код для использования в качестве аргумента алгоритма, то лучше сделать его функциональным объектом.
[Austern99] §4, §8, §15 • [Josuttis99] §5.9 • [Meyers01] §46 • [Musser01] §8 • [Sutter04] §25
89. Корректно пишите функциональные объекты
Разрабатывайте функциональные объекты так, чтобы их копирование выполнялось как можно эффективнее. Там, где это возможно, делайте их максимально адаптируемыми путем наследования от unary_function
или binary_function
.
Функциональные объекты моделируют указатели на функции. Подобно указателям на функции, они обычно передаются в функции по значению. Все стандартные алгоритмы передают объекты по значению, и то же должны делать и ваши алгоритмы, например:
template
Function for_each(InputIter first, InputIter last, Function f);
Следовательно, функциональные объекты должны легко копироваться и быть мономорфными (для защиты от срезки), так что избегайте виртуальных функций (см. рекомендацию 54). Конечно, у вас могут быть большие и/или полиморфные функциональные объекты — их тоже вполне можно использовать; просто скройте их размер с помощью идиомы Pimpl (указателя на реализацию; см. рекомендацию 43). Эта идиома позволяет, как и требуется, получить внешний мономорфный класс малого размера, обеспечивающий доступ к богатой функциональности. Внешний класс должен удовлетворять следующим условиям.
• Быть адаптируемым. Наследуйте его от unary_function
или binary_function
.
• Использовать идиому Pimpl. Такой класс содержит указатель (например, shared_ptr
) на (возможно, большого размера) реализацию необходимой функциональности.
• Иметь оператор(ы) вызова функции. Эти операторы передают вызовы объекту-реализации.
Этим ограничиваются требования к внешнему классу (не считая возможного наличия собственных (не генерируемых компилятором) конструкторов, оператора присваивания и/или деструктора.
Функциональные объекты должны быть адаптируемы. Стандартные связыватели и адаптеры полагаются на наличие определенных инструкций typedef
, обеспечить которые легче всего при наследовании ваших функциональных объектов от unary_function
или binary_function
. Инстанцируйте unary_function
или binary_function
с теми типами, которые получает и возвращает ваш оператор operator()
(при этом у каждого типа, не являющегося указателем, следует убрать все спецификаторы const
верхнего уровня, а также все &
).
Постарайтесь избежать наличия нескольких операторов operator()
, поскольку это затрудняет адаптируемость. Дело в том, что обычно оказывается невозможно обеспечить корректные инструкции typedef
, необходимые для адаптирования, поскольку один и тот же синоним типа, определяемый через инструкцию typedef
, имеет разные значения для разных операторов operator()
.
Не все функциональные объекты являются предикатами — предикаты представляют собой подмножество функциональных объектов (см. рекомендацию 87).
[Allison98] §15, §C • [Austern99] §4, §8, §15 • [Gamma95] Bridge • [Josuttis99] §8.2.4 • [Koenig97] §21, §29 • [Meyers97] §34 • [Meyers01] §38, §40, §46 • [Musser01] §2.4, §8, §23 • [Sutter00] §26-30 • [Vandevoorde03] §22
Безопасность типов
Если вы лжете компилятору, он будет мстить.
— Генри Спенсер (Henry Spencer)Всегда будут вещи, которые мы будем хотеть сказать в наших программах и которые трудно сформулировать на любом языке программирования.
— Алан Перлис (Alan Perlis)Последней (не по важности) темой книги является корректность типов — очень важное свойство программ, которое вы должны изо всех сил стараться поддерживать. Теоретически корректная с точки зрения типов функция не может обратиться к нетипизированной памяти или вернуть неверные значения. На практике, если ваш код поддерживает корректность типов, он тем самым избегает большого количества неприятных ошибок, от непереносимости программ до порчи содержимого памяти и неопределенного поведения программ.
Основная идея поддержки корректности типов — всегда считывать информацию в том формате, в котором она была записана. Иногда С++ позволяет легко нарушить это правило; приведенные в этом разделе рекомендации помогут вам избежать подобных ошибок.
В этом разделе мы считаем наиболее значимой рекомендацию 91 — "Работайте с типами, а не с представлениями". Система типов — ваш друг и верный союзник. Воспользуйтесь ее помощью и попытайтесь не злоупотреблять ее доверием.
90. Избегайте явного выбора типов — используйте полиморфизм
Избегайте явного выбора типа объекта для настройки поведения. Используйте шаблоны и виртуальные функции для того, чтобы поведение объекта определялось его типом, а не вызывающим кодом.
Читать дальшеИнтервал:
Закладка: