Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

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>: nterator может представлять собой простой указатель т* (хотя это может быть и не так), но если мы реализуем пункт 13, то код не будет ограничен использованием одного лишь типа vector<T>: .-iterator, а большинство итераторов других типов представляют собой классы. Возможно, их копирование не требует много процессорного времени - но зачем напрасно допускать даже такое малое снижение производительности программы?

> Рекомендация

Если только вам не требуется прежнее значение итератора, используйте префиксный инкремент.

На этом заканчивается наше рассмотрение наиболее важных вопросов, связанных с приведенным исходным текстом. Есть и другие замечания, но я не считаю их столь важными. Например, код промышленного уровня должен содержать комментарии, документирующие предназначение каждого класса и каждой функции и их семантику; однако .это требование не имеет смысла в статье, в которой все проблемы описаны гораздо лучше простым "человеческим" языком и существенно детальнее, чем это можно сделать в комментариях.

Я сознательно не рассматривал стиль написания данного фрагмента, поскольку, в конце концов, его основным предназначением была всего лишь демонстрация читателям журнальной статьи принципа работы игщексных таблиц.

Резюме

Давайте попытаемся сохранить базовый интерфейс исходного кода". Ограничимся только коррекцией механических ошибок и стиля, и рассмотрим три альтернативные улучшенные версии приведенного в условии исходного текста. Каждая из них имеет свои преимущества, свои недостатки и свои предпочтения в стиле. Общее у всех трех версий то, что они более ясны и понятны, и содержат более переносимый код - так что при желании вы можете даже использовать их в своей собственной работе.

Улучшенная версия кода из [нтскьОО].

#include <vector>

#include <map>

#include <algorithm>

Автор исходного текста сообш;ил об отзывах некоторых читателей, которые пошли по другому, но не менее элегантному пути - создавая контейнерообразный объект, который представляет собой обертку вокруг исходного контейнера, включая его итераторы, и обеспечивает итерирование с использованием различных способов сортировки.



Решение 1 является несколько "подчищенной" версией кода, сохраняющей общую структуру исходного варианта, код сократился с 23 до 17 строк (даже если считать "public:" и "private:" отдельными строками), namespace Solutionl {

tempiate<class lter>

class sort„idxtbl paiг {

public:

void setCconst lter& it, int i) {it = it; i = i;} bool operator<(const sort idxtbl pair& other) const

{ return *it < *other.it ; } operator int() const { return i ; } private:

Iter it ; int i ;

Эта функция претерпела самые существенные изменения, сократившись с 13 строк до 5. Для сравнения я сохранил весь старый код. Старайтесь писать программы так, чтобы в них не было излишней сложности

tempiate<class iterin, class iterout>

void sort idxtbl(iterin fi rst, iterin last, iterout out){

std::vector<sort idxtbl pair<iterin> > v(last-first); int iDst = 1ast-fi rst;

typedef std::vector< sort idxtbl pair<RAlter> > V; V v(iDst);

for( i nt i=0; i < 1ast-fi rst; ++i )

v[i].set( first+i, i ); . int i=0; RAlter it = first; v::iterator vit = v.begin(); for (i=0; it<last; it++, vit++, i++) (*vit).set(it,i); std: :sort( v.beqin() , v.endO ) ; std::sort(v.begi n(), v.end()); std: : copy( v. begi n() , v.endO, out ) ; int *pi = pidxtbl; vit = v.beginO ; for (; vit<v.end(); pi++, vit++) *pi = (*vit) .i;

Решение 2 использует класс pair вместо вспомогательного класса, код уменьшился до 15 строк (из них 2 - продолжение предыдущих из-за ширины страницы). 8 строк специфичны для данного кода, а 4 строки в принципе можно непосредственно использовать и в других контекстах.

namespace Solution2 {

tempiate<class т, class u> struct ComparePai rlstDeref {

bool operatorO(const std::paiг<т,и>& a,

const std::pair<T,u>& b ) const { return *a.first < *b.first; }

tempiate<class Iterin, class Iter0ut>

void sort idxtbl(Iterin fi rst, Iterin last, Iterout out){ std::vector< std::pair<iterin,int> > s( last-first ); for( int i=0; i < s.sizeO; ++i )

s[i] = std::make pair( first+i, i );



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85