Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
- Название:Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
- Автор:
- Жанр:
- Издательство:Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7
- Год:2006
- Город:Москва
- ISBN:5-94074-304-8
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ краткое содержание
Эта книга представляет собой перевод третьего издания американского бестселлера Effective C++ и является руководством по грамотному использованию языка C++. Она поможет сделать ваши программы более понятными, простыми в сопровождении и эффективными. Помимо материала, описывающего общую стратегию проектирования, книга включает в себя главы по программированию с применением шаблонов и по управлению ресурсами, а также множество советов, которые позволят усовершенствовать ваши программы и сделать работу более интересной и творческой. Книга также включает новый материал по принципам обработки исключений, паттернам проектирования и библиотечным средствам.
Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
void advance(IterT& iter, DistT d)
{
doAdvance( // вызвать версию
iter, d, // doAdvance
typename // соответствующую
std::iterator_traits::iterator_category() // категории
); // итератора iter
}
Подведем итоги – как нужно использовать класс-характеристику:
• Создать набор перегруженных «рабочих» функций либо шаблонов функций (например, doAdvance), которые отличаются параметром-характеристикой. Реализовать каждую функцию в соответствии с переданной характеристикой.
• Создать «ведущую» функцию либо шаблон функции (например, advance), которая вызывает рабочие функции, передавая информацию, предоставленную классом-характеристикой.
Классы-характеристики широко используются в стандартной библиотеке. Так, класс iterator_traits, помимо iterator_category, представляет еще четыре вида информации об итераторах (наиболее часто используется value_type; в правиле 42 показан пример его применения). Есть еще char_traits, который содержит информацию о символьных типах, и numeric_limits, который хранит информацию о числовых типах, например минимальных и максимальных значениях и т. п. Имя numeric_limits немного сбивает с толку, поскольку нарушает соглашение, в соответствии с которыми имена классов-характеристик должны оканчиваться на «traits», но тут уж ничего не поделаешь, придется смириться.
В библиотеке TR1 (см. правило 54) есть целый ряд новых классов-характеристик, которые предоставляют информацию о типах, включая is_fundamental (где T – встроенный тип), is_array (где T – тип массива) и is_base_of (то есть является ли T1 тем же, что и T2, либо его базовым классом). Всего TR1 добавляет к стандартному C++ более 50 классов-характеристик.
• Классы-характеристики делают доступной информацию о типах во время компиляции. Они реализованы с применением шаблонов и их специализаций.
• В сочетании с перегрузкой классы-характеристики позволяют проверять типы во время компиляции.
Правило 48: Изучите метапрограммирование шаблонов
Метапрограммирование шаблонов (template metaprogramming – TMP) – это процесс написания основанных на шаблонах программ на C++, исполняемых во время компиляции. На минуту задумайтесь об этом: шаблонная метапрограмма – это программа, написанная на C++, которая исполняется внутри компилятора C+ +. Когда TMP-программа завершает исполнение, ее результат – фрагменты кода на C++, конкретизированные из шаблонов, – компилируется как обычно.
Если эта идея не поразила вас до глубины души, значит, вы недостаточно напряженно думали о ней.
C++ не предназначался для метапрограммирования шаблонов, но с тех пор, как технология TMP была открыта в начале 90-х годов, она оказалась настолько полезной, что, вероятно, и в сам язык, и в стандартную библиотеку будут включены расширения, облегчающие работу с TMP. Да, TMP было именно открыто, а не придумано. Средства, лежащие в основе TMP, появились в C++ вместе с шаблонами. Нужно было только, чтобы кто-то заметил, как они могут быть использованы изобретательным и неожиданным образом.
Технология TMP дает два преимущества. Во-первых, она позволяет делать такие вещи, которые иными способами сделать было бы трудно либо вообще невозможно. Во-вторых, поскольку шаблонные метапрограммы исполняются во время компиляции C++, они могут переместить часть работы со стадии исполнения на стадию компиляции. В частности, некоторые ошибки, которые обычно всплывают во время исполнения, можно было бы обнаружить при компиляции. Другое преимущество – это то, что программы C++, написанные с использованием TMP, можно сделать эффективными почти во всех смыслах: компактность исполняемого, код быстродействия, потребления памяти. Но коль скоро часть работы переносится на стадию компиляции, то, очевидно, компиляция займет больше времени. Для компиляции программ, в которых применяется технология TMP, может потребоваться намного больше времени, чем для компиляции аналогичных программ, написанных без применения TMP.
Рассмотрим псевдокод шаблонной функции advance, представленный на стр. 227 (см. правило 47; возможно, имеет смысл перечитать это правило сейчас, поскольку ниже я предполагаю, что вы знакомы с изложенным в нем материалом). Я выделил в этом фрагменте часть, написанную на псевдокоде:
template
void advance(IterT& iter, DistT d)
{
if ( iter является итератором с произвольным доступом ) {
iter += d; // использовать итераторную арифметику
} // для итераторов с произвольным доступом
else {
if(d>=0) {while (d–) ++iter;} // вызывать ++ или – в цикле
else {while(d++) –iter;} // для итераторов других категорий
}
}
Мы можем использовать typeid, чтобы заменить псевдокод реальным кодом. Тогда задача будет решена «нормальным» для C++ способом – вся работа выполняется во время исполнения:
template
void advance(IterT& iter, DistT d)
{
if (typeid(typename std::iterator_traits::iterator_category)==
typeid(std::random_access_iterator_tag))
iter += d; // использовать итеративную арифметику
} // для итераторов с произвольным доступом
else {
if(d>=0) {while (d–) ++iter;} // вызывать ++ или – в цикле
else {while(d++) –iter;} // для итераторов других категорий
}
}
В правиле 47 отмечено, что подход, основанный на typeid, менее эффективен, чем при использовании классов-характеристик, поскольку в этом случае: (1) проверка типа происходит во время исполнения, а не во время компиляции, и (2) код, выполняющий проверку типа, должен быть включен в исполняемую программу. Фактически этот пример показывает, как технология TMP может порождать более эффективные программы на C++, потому что характеристики – это и есть частный случай TMP. Напомню, что характеристики делают возможным вычисление выражения if…else во время компиляции.
Я уже отмечал выше, что некоторые вещи технология TMP позволяет сделать проще, чем «нормальный» C++, и advance можно считать иллюстраций этого утверждения. В правиле 47 упоминается о том, что основанная на typeid реализация advance может привести к проблемам во время компиляции, и вот вам пример такой ситуации:
std::list::iterator iter;
...
advance(iter, 10); // сдвинуть iter на 10 элементов вперед
// не скомпилируется для приведенной
// выше реализации
Рассмотрим версию advance, которая будет сгенерирована для этого вызова. После подстановки типов iter и 10 в качестве параметров шаблона IterT и DistT мы получим следующее:
Читать дальшеИнтервал:
Закладка: