|
Программирование >> Обобщенные обратные вызовы
> Рекомендация Последовательно и корректно используйте квалификатор const, 6. Удаление избыточного кода. В программе явным образом определяется конструктор класса sort i dxtbl pai г по умолчанию, хотя он не имеет никаких отличий от неявно генерируемой версии, так что особого смысла в нем нет. Кроме того, поскольку sort i dxbT pai г является структурой с открытыми данными, наличие отдельной операции set, которая вызывается только в одном месте, приводит только к усложнению кода, ничего не давая взамен. > Рекомендация Избегайте избыточности и повторения кода. Теперь перейдем к основной функции sort idxtbT template <class RAlteo void sort idxtbl ( RAlter fi rst, RAlter last, int* pidxtbl ) int iDst = last-fi rst; typedef std:: vector< sort idxtbl pai r <RAlter> > V; V v( iDst ); int i=0; RAlter it = first; v: literator vit = v.beginC); for С i=0; it<last; it++, vit++, \++ ) C*vit).setCit,i): std: rsortCv.begin C) , v.endO); int *pi = pidxtbl ; vit = V.beginC); for С ; vit<v.endC); pi++, vit++ ) *pi = C*vit).i; 7. Используйте значащие и соответствующие по смыслу имена. В данном случае имя sort i dxtbl вводит в заблуждение, поскольку функция не сортирует индексную таблицу, а создает ее! С другой стороны, имя параметра шаблона RAlter выбрано удачно, так как указывает на использование итератора с произвольным доступом (random-access iterator) - именно то, что и требуется в данном исходном тексте, так что такое имя служит хорошим напоминанием. > Рекомендация Используйте понятные и выразительные имена. 8. Будьте последовательны. В функции sort i dxtbl переменные иногда инициализируются в инструкции инициализации цикла, а иногда - нет, что затрудняет чтение кода - по крайней мере, для меня. 9. Избавляйтесь от неоправданной сложности. Эта функция просто обожает излишние локальные переменные! Есть три подтверждения этому. Во-первых, переменная iDst, инициализированная значением last-fi rst и использованная только один раз. Почему бы просто не записать в месте се использования разность last-fi rst и избавиться от нее? Во-вторых, итератор вектора vit создается там, где уже имеется и может использоваться индекс (код от этого станет только понятнее). В-третьих, локальная переменная it инициализируется значением параметра функции, который после этого никогда не используется; лично я предпочитаю в такой ситуации воспользоваться параметром функции (даже если вы измените его - ничего страшного!) вместо введения другого имени. 10. Повторное использование, часть 1: интенсивнее используйте стандартную библиотеку. В приведенной программе вполне правильно используется функция std::sort, и это хорошо. Но что такое последний цикл, как не копирование, которое вполне можно выполнить при помоши функции std: :сору? Зачем вводить специальный класс sort idxtbl pai г, если его единственное отличие от std::pai г в наличии функции сравнения? Помимо простоты, повторное использование стандартной библиотеки делает код более удобочитаемым. Будьте скромнее и используйте результаты чужого труда! > Рекомендация Следует хорошо знать и использовать в своем коде возможности стандартной библиотеки вместо написания собственного кода. И. Повторное использование, часть 2: убить двух зайиев. повышая степень повторного использования своего кода. В исходном фрагменте ничто, кроме самой функции, не может быть использовано повторно. В с п ом о гате л ь н ы й класс sort i dxtbl pai г написан сугубо для использования в данной функции и не может быть повторно использован независимо от функции. 12. Повторное использование, часть 3: улучшение сигнатуры. Исходная сигнатура template <class RAlter> void sort idxtbl С RAiter first, RAlter last, int* pidxtbl ) получает простой указатель i nt* на выходную область, чего я обычно стараюсь избегать в пользу более управляемого представления (например, vector). В конце концов, пользователь должен иметь возможность вызвать sort idxtbl и поместить выходные данные в обычный массив, вектор или куда-то еше. Требование иметь возможность поместить данные в произвольный контейнер просто кричит о том, что надо воспользоваться итератором, правда? (См. также задачи 5 и 6.) tempiate< class RAin, class Out > void sort idxtbl( RAin fi rst, RAin last, Out result ) > Рекомендация Избегайте жесткого указания типов там, где без этого можно обойтись. Это позволит расширить возможности повторного использования вашего кода. 13. Повторное использование, часть 4. или Сравнивайте итераторы при помощи !=. При сравнении итераторов всегда используйте оператор 1= (который работает для всех типов итераторов) вместо < (который работает только для итераторов с произвольным доступом), конечно, кроме тех случаев, когда вы действительно нуждаетесь в использовании < и будете работать исключительно с итераторами с произвольным доступом. В приведенной программе для сравнения итераторов применен оператор <, что подра зумеваст использование итераторов с произвольным доступом, для которых программа изначально и предназначена: для создания индексов в векторах и массивах (которые оба поддерживают итераторы с произвольным доступом). Но нет причин, по которым мы бы не могли захотеть сделать то же с контейнерами другого вида, например, 1 i st или set (которые не поддерживают итераторы с произвольны.м доступом), и единственная причина, по которой исходная программа не может с ними работать, - это использование оператора < для сравнения итераторов вместо оператора !=. Вот как по этому поводу пишет Скотт Мейерс в 32 разделе своей книги [Meyers96]: Хорошее программное обеспечение приспособлено к изменениям. Оно в состоянии включать новые возможности, его можно перенести на новые платформы, оно допускает подгонку под новые требования, может работать с новыми входными данными. Такие гибкие, интеллектуальные и надежные программы не получаются случайно - это результат конструирования и реализации специалистами, которые, помня о нуждах дня сегодняшнего, не забывают о дне завтрашнем и его возможных требованиях к программе. Такие программы, приспособленные к видоизменению, создаются программистами, которые закладывают в свои программы будущее. > Рекомендация Предпочтительно сравнивать итераторы при помощи оператора ! =, а не <. 14. Если только вам не требуется прежнее значение итератора, используйте префиксный инкремент (декремент). При работе с итераторами использование префиксной формы инкремента (++i), а не постфиксной (i++) должно стать привычкой; см. [SutterOO] (задача 1.6 русского издания). Конечно, существенного различия в приведенном фрагменте может и не быть, поскольку vector<T>: :iterator может представлять собой простой указатель т* (хотя это может быть и не так), но если мы реализуем пункт 13, то код не будет ограничен использованием одного лишь типа vector<T>: .-iterator, а большинство итераторов других типов представляют собой классы. Возможно, их копирование не требует много процессорного времени - но зачем напрасно допускать даже такое малое снижение производительности программы? > Рекомендация Если только вам не требуется прежнее значение итератора, используйте префиксный инкремент. На этом заканчивается наше рассмотрение наиболее важных вопросов, связанных с приведенным исходным текстом. Есть и другие замечания, но я не считаю их столь важными. Например, код промышленного уровня должен содержать комментарии, документирующие предназначение каждого класса и каждой функции и их семантику; однако -это требование не имеет смысла в статье, в которой все проблемы описаны гораздо лучше простым человеческим языком и существенно детальнее, чем это можно сделать в комментариях. Я сознательно не рассматривал стиль написания данного фрагмента, поскольку, в конце концов, его основным предназначением была всего лишь демонстрация читателям журнальной статьи принципа работы игщексных таблиц. Резюме Давайте попытаемся сохранить базовый интерфейс исходного кода . Ограничимся только коррекцией механических ошибок и стиля, и рассмотрим три альтернативные улучшенные версии приведенного в условии исходного текста. Каждая из них имеет свои преимущества, свои недостатки и свои предпочтения в стиле. Общее у всех трех версий то, что они более ясны и понятны, и содержат более переносимый код - так что при желании вы можете даже использовать их в своей собственной работе. Улучшенная версия кода из [HicksOO]. #inciude <vector> #include <map> #include <algorithm> Автор исходного текста сообщил об отзывах некоторых читателей, которые пошли по другому, но не менее элегантному пути - создавая контейнерообразный объект, который представляет собой обертку вокруг исходного контейнера, включая его итераторы, и обеспечивает итерирование с использованием различных способов сортировки.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |