|
Программирование >> Включение нужных заголовков
Ниже приведен один из возможных вариантов (комментарии приводятся не только для книги - я бы включил их и в программу). typedef vector<int>::iterator Veclnter; Инициализировать rangeBegin первым элементом v. большим или равным последнему вхождению у. Если такой элемент не существует. rangeBegin инициируется значением v.beginO Veclntlter rangeBegin = find if(v.rbeginO.v.rendO, bind2nd(greater equal<int>().y)).base(); Удалить от rangeBegin до v.end все элементы со значением, меньшим х V.eraseC renrave i f(rangeBegi n.v.end().bi nd2nd(1ess<i nt>().x)).v.end()); Возможно, даже этот вариант кое-кого смутит, поскольку он требует понимания идиомы erase-remove, но при наличии комментариев в программе и хорошего справочника по STL (например, The C-i~i- Standard Library* [3] или web-сайта SGI [21]) каждый программист C-i-i- без особых усилий разберется, что же происходит в программе. Обратите внимание: в процессе модификации я не отказался от использования алгоритмов и не попытался заменить их циклами. В совете 43 объясняется, почему алгоритмы обычно предпочтительнее циклов, и приведенные аргументы действуют и в этом случае. Основная цель при программировании заключается в создании кода, понятного как для компилятора, так и для читателя-человека, и обладающего приемлемым быстродействием. Алгоритмы почти всегда лучше подходят для достижения этой цели. Тем не менее, совет 43 также объясняет, почему интенсивное использование алгоритмов естественным образом приводит к частому вложению вызовов функций и использованию адаптеров функторов. Вернемся к постановке задачи, с которой начинается настоящий совет. Допустим, имеется вектор vector<int>. Из этого вектора требуется удалить все элементы, значение которых меньше х, но оставить элементы, предшествующие последнему вхождению значения, не меньшего у. Нетрудно придти к общей схеме решения: поиск последнего вхождения значения в векторе требует применения f 1 nd или f 1 nd i f с обратными итераторами; удаление элементов требует erase или идиомы erase-remove. Объединяя эти две идеи, мы получаем следующий псевдокод, где нечто обозначает код, который еще не был наполнен смысловым содержанием: v.erase(remove if(findJf(v.rbeginO. v.rendO. нечто).baseO, V.endO. нечто)). V.endO); При наличии такой схемы рассчитать, что же кроется за нечто , совсем несложно. Вы не успеете опомниться, как придете к решению из исходного примера. Во время написания программы подобные решения выглядят вполне логичными, поскольку в них отражается последовательное применение базовых принципов (например, идиомы erase-remove плюс использование find с обратными итераторами). К сожалению, читателю вашей программы будет очень трудно разобрать готовый продукт на те идеи, из которых он был собран. Нечитаемый код легко пишется, но разобраться в нем трудно. Впрочем, нечитаемость зависит от того, кто именно читает программу. Как упоминалось выше, некоторые программисты С++ вполне нормально относятся к конструкциям вроде приведенной в начале этого совета. Если такая картина типична для среды, в которой вы работаете, и вы ожидаете, что она останется таковой в будущем, не сдерживайте свои творческие порывы. Но если ваши коллеги недостаточно уверенно владеют функциональным стилем программирования и не столь хорошо разбираются в STL, умерьте свои амбиции и напишите что-нибудь вроде альтернативного решения, приведенного выше. Банальный факт из области программирования: код чаще читается, чем пишется. Хорошо известно также, что на сопровождение программы уходит значительно больше времени, чем на ее разработку. Если программу нельзя прочитать и понять, ее нельзя и успешно сопровождать, а такие программы вообще никому не нужны. Чем больше вы работаете с STL, тем увереннее себя чувствуете и тем сильнее хочется использовать вложенные вызовы функций и создавать объекты функций на лету . В этом нет ничего плохого, но всегда следует помнить, что написанную сегодня программу завтра придется кому-то читать - может быть, даже вам. Приготовьтесь к этому дню. Да, используйте STL в своей работе. Используйте хорошо и эффективно... но избегайте написания нечитаемого кода. В долгосрочной перспективе такой код будет каким угодно, но только не эффективным. Совет 48. Всегда включайте нужные заголовки При программировании в STL нередко встречаются программы, которые успешно компилируются на одной платформе, но требуют дополнительных директив #include на другой. Этот раздражающий факт связан с тем, что Стандарт С++ (в отличие от Стандарта С) не указывает, какие стандартные заголовки могут или должны включаться в другие стандартные заголовки. Авторы реализаций пользуются предоставленной свободой и выбирают разные пути. Попробую пояснить, что это значит на практике. Однажды я засел за пять платформ STL (назовем их А, В, С, D и Е) и попробовал экспериментальным njrreM определить, какие стандартные заголовки можно убрать, чтобы программа при этом нормально компилировалась. По этим данным становится ясно, какие заголовки включают другие заголовки директивой #i ncl ude. Вот что я узнал: на платформах А и С <vector> включает <stri ng>; на платформе С <algorithm> включает <string>; на платформах С и D <iostream> включает <iterator>; на платформе D <i ostream> включает <stri ng> и <vector>; на платформах D и Е <string> включает <algorithm>; во всех пяти реализациях <set> включает <functional>. За исключением последнего случая мне так и не удалось провести программу с убранным заголовком мимо реализации В. По закону Мэрфи вам всегда придется вести разработку на таких платформах, как А, С, D и Е, и переносить программы на такие платформы, как В, особенно когда это очень важная работа, которую необходимо сделать как можно скорее. Так бывает всегда. Но не стоит осуждать компиляторы или разработчиков библиотек за трудности с переносом. Пропущенные заголовки на вашей ответственности. При каждой ссылке на элементы пространства имен std вы также отвечаете за включение соответствующих заголовков. Если заголовки опущены, программа теоретически может откомпилироваться, но другие платформы STL имеют полное право отвергнуть ваш код. Чтобы вам было проще запомнить необходимые заголовки, далее приведена краткая сводка содержимого всех стандартных заголовков, относящихся к STL. Почти все контейнеры объявляются в одноименных заголовках, то есть vector объявляется в заголовке <vector>, 1 i st объявляется в заголовке <1 i st> и т. д. Исключениями являются <set> и <тар>. В заголовке <set> объявляются контейнеры set и multiset, а в заголовке <тар> объявляются контейнеры тар и multimap. Все алгоритмы, за исключением четьфех, объявляются в заголовке <al догi thm>. Исключениями являются алгоритмы accumulate (см. совет37), inner product, adjacent di fference и partial sum. Эти алгоритмы объявляются в заголовке <numeric>. Специализированные разновидности итераторов, включая i streami terator и istreambuf i terator (см. совет 29), объявляются в заголовке <iterator>. Стандартные функторы (например less<T>) и адаптеры функторов (например notl и bind2nd) объявляются в заголовке <functional>. Не забывайте включать соответствующую директиву #i ncl ude при использовании любых из перечисленных компонентов, даже если платформа разработки позволяет обойтись и без нее. Ваше прилежание непременно окупится при переносе программы на другую платформу. Совет 49. Научитесь читать сообщения компилятора При определении вектора в программе вы имеете полное право указать конкретный размер: vector<int> v(10): Создать вектор из 10 элементов Объекты string имеют много общего с vector, поэтому кажется, что следующая команда тоже допустима: string s(lO); Попытаться определить string из 10 элементов
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |