Анимация
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

Оцените приведенный далее исходный текст и найдите:

a) механические ошибки, такие как неверный синтаксис или непереносимые соглашения;

b) стилистические улучшения, которые могут повысить ясность, степень повторного использования и сопровождаемости исходного текста.

Позвольте мне повториться и напомнить, что этот код содержит интересную и весьма полезную идиому. Мне часто требуется доступ к одному и тому же контейнеру разными способами, например, в различном порядке сортировки. По этой причине было бы неплохо иметь один основной контейнер, который хранит данные (например, vector<Employee>) и вторичные контейнеры итераторов, указывающих на элементы основного контейнера и ревизующие различные способы доступа (например, set<vector<Emp1oyee>::iterator,Funct>, где Funct - функтор, который косвенно сравнивает объекты Employee, выполняя упорядочение объектов, отличающееся от того, в котором они физически хранятся в контейнере (в данном случае - в vector)).

Кроме сказанного, имеет значение и стиль. Автор приведенного фрагмента любезно позволил мне воспользоваться им в качестве учебного примера, и я не пытаюсь дразнить его здесь. Я только адаптирую методику, давно открытую таким светилом, как Плоджср (Р. J. Plauger)", пытаясь дать вам рекомендации по кодированию на основе опубликованных исходных текстов. Я критиковал чужие программы и раньше, причем я уверен, что мои программы также подлежат критике.

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

Исправление механических ошибок

а) механические ошибки, такие как неверный синтаксис или непереносимые соглашения

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

#include <algorith>

1. Правильно указывайте стандартные заголовочные файлы. Здесь заголовочный файл <algorithm> ошибочно назван <algorith>. Мое первое предположение было, что это, вероятно, артефакт файловой системы с 8-символьными именами, использовавшейся длл тестирования первоначальной версии кода, но даже моя старая версия VC++ на старой версии Windows (с использованием файловой системы с именами 8.3) отказалась компилировать этот код. В любом случае, это имя не является стандартным, и даже при работе со старыми файловыми системами компилятор должен поддерживать стандартные длинные имена (даже если он затем преобразует их в более короткие имена файлов, или даже если физически файлы не используются вообще).

Теперь обратимся к строке

mai п С)

2. Корректно определяйте функцию та in. Приведенная здесь сигнатура функции main никогда не была стандартом С++ [С++98], хотя и является согласующимся со стандартом расширением (если компилятор выдает соответствующее предупреждение). Такая сигнатура была корректна в языке С до принятия стандарта 1999 года.

Плоджер Ф., Керни1ан Б. Элементы стиля программирования. - М.; Радио и связь, 1984. - Прим. ред.



в котором действовало правило о неявном int, ио ни в С+ + , ни в С99 [С99] она не верна. Из стандарта С+ + :

• §3.6.1/2: переносимый коя должен определять main либо как int main(), либо как int main(int, char*[]).

• §7/7, сноска 78, и §7.1.5/2 сноска 80: неявный i nt запрещен.

• Приложение С (совместимость), комментарий к 7.1.5/4; явно указано, что "голое" объявление main() не корректно в С++, функция main должна определяться какint main().

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

Не полагайтесь на неявный int: стандартом С++ это не предусматривается. В частности, "void mainO" или "maiп()" никогда не были стандартными в С++, хотя многие компиляторы поддерживают их в качестве расширений.

cout « "#################" « end! ;

3. Всегда включайте заголовки для тех типов, определения которых вам требуются. Программа использует cout и endl, но не включает #include <iostream>. Почему, тем не менее, программа работала в системе ее разработчика? Потому что стандартные загологючные файлы С++ могут включать друг друга, но в отличие от С, С++ не определяет, какие стандартные заголовочные файлы включают другие стандартные заголовочные файлы и какие именно.

В нашем случае профамма включает <vector> и <algorithm>, и, видимо, в исходной системе один из этих заголовочных файлов включал также <iostream> -- непосредственно или опосредованно. Такая профамма могла работать в системе у ее автора, может быть, она даже заработает и у меня, но это непереносимый и нездоровый стиль.

4. Следуйте рекомендациям задачи 39 из книги More Exceptional С++ lSutier02] (задача 10.10 русского издания) о пространствах имен. Говоря о cout и endl, программа должна квалифицировать их с использованием std::, либо применить директивы using std:: cout; и using std: :endl ;. К сожалению, это распространено среди программистов - забывать о квалификаторах области видимости пространства имен. Спешу заметить, что автор фрагмента корректно указал пространство имен vector и sort, что является хорошим стилем профаммирования.

Улучшение стиля

б) стилистические улучшения, которые могут повысить ясность, степень повторного

использования и сопровождаемости исходного текста.

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

template <class RAlteo struct sort idxtbl„paiг

RAlter it; int i;

bool operator<C const sort idxtbl pai r & s )

{ return C*it) < (*(s.it)); } void set С const RAlter& it, int i ) { it= it; i= i; } sort idxtbl paiг() {}

5. Корректно и последовательно используйте квалификатор const. В частности, sort idxtbl„pai г: :operator< не модифицирует *this, так что этот оператор следует объявить как константную функцию-член.



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

Последовательно и корректно используйте квалификатор const,

6. Удаление избыточного кода. В программе явным образом определяется конструктор класса sort i dxtbl pai г по умолчанию, хотя он не имеет никаких отличий от неявно генерируемой версии, так что особого смысла в нем нет. Кроме того, поскольку sort i dxbT pai г является структурой с открытыми данными, наличие отдельной операции set, которая вызывается только в одном месте, приводит только к усложнению кода, ничего не давая взамен.

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

Избегайте избыточности и повторения кода.

Теперь перейдем к основной функции sort idxtb!. template <class RAlter>

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::iterator vit = v. beginO;

for( i=0; it<last; it++, vit++, \++ )

(*vit).set(it,i): std: :sort(v.begin О , v.endO); int *pi = pidxtbl ; vit = V.begin О;

forC ; vit<v.endO; pi++, vit++ ) *pi = (*vit).i;

7. Используйте значащие и соответствующие по смыслу имена. В данном случае имя sort i dxtbl вводит в заблуждение, поскольку функция не сортирует индексную таблицу, а создает ее! С другой стороны, имя параметра шаблона RAlter выбрано удачно, так как указывает на использование итератора с произвольным доступом (random-access iterator) - именно то, что и требуется в данном исходном тексте, так что такое имя служит хорошим напоминанием.

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

Используйте понятные и выразительные имена.

8. Будьте последовательны. В функции sort i dxtbl переменные иногда инициализируются в инструкции инициализации цикла, а иногда - нет, что затрудняет чтение кода - по крайней мере, для меня.

9. Избавляйтесь от неоправданной сложности. Эта функция просто обожает излишние локальные переменные! Есть три подтверждения этому. Во-первых, переменная iDst, инициализированная значением last-fi rst и использованная только один раз. Почему бы просто не записать в месте се использования разность last-fi rst и избавиться от нее? Во-вторых, итератор вектора vit создается там, где уже имеется и может использоваться индекс (код от этого станет только понятнее). В-третьих, локальная переменная it инициализируется значением параметра функции, который после этого никогда не используется; лично я предпочитаю в такой ситуации воспользоваться параметром функции (даже если вы измените его - ничего страшного!) вместо введения другого имени.



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