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

currtype= STRING; return *ptype; } else

Маленькое замечание: комментарий el se ничего не дает и совершенно бесполезен.

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

Пишите (только) полезные комментарии. Никогда не пишите комментарии, которые повторяют код. Комментарии должны пояснять код и причины, по которым вы написали его именно так.

Но здесь еще есть три более серьезные проблемы. Первая заключается в том, что функции не написаны симметрично, и в то время как первое использование списка или строки дает объект, построенный при помоши конструктора по умолчанию, первое использование int дает нам неинициализированный объект. Если это сделано преднамеренно, чтобы отразить обычную семантику неинициализируемых переменных типа i nt, то это следовало документировать; в противном случае int также должен быть инициализирован. Например, если вызывающая функция обращается к getint и пытается сделать копию (неинициализированного) значения, го в результате мы получаем неопределенное поведение - не все платформы поддерживают копирование произвольных некорректных значений int и могут отвергнуть такие инструкции в процессе работы программы.

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

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

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

Третья проблема заключается в хрупкости такого подхода при необходимости внесения изменений. Он основан на переключении типов, и поэтому очень легко случайно рассинхронизировать функции при удалении или добавлении нового типа.

Теперь остановитесь и задумайтесь: как бы вы поступили при необходимости добавить новый тип? Перечислите по возможности полно все необходимые для этого действия.

Вы готовы? Давайте сравним наши результаты. Чтобы добавить новый тип, вы должны помнить о том, что:

• надо добавить новое значение в перечисление;

• надо добавить новую функцию доступа;

• надо обновить функцию cleanup для удаления нового типа; и

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

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

void MYUNION::cleanup О

switch( currtype ) { case list: {

list& ptype = getlistC);



ptype.-LIST О; break; } case case STRING: {

STRINGS ptype = getstringO; ptype.-STRING О; break; } case default: break; } switch currtype=NONE;

Здесь опять можио высказать уже знакомое замечание по поводу комментариев - case и swi tch не несут смысловой нафузки. Лучше не иметь комментариев вовсе, чем такие комментарии, которые только отвлекают внимание.

Но здесь есть и более серьезный вопрос: вместо простого default: break; было бы лучше использовать полный список типов (включая int) и сигнализировать о логической ошибке в случае неизвестного типа - вероятно, посредством assert или throw std::logic error(...);.

И вообще, переключение типов является злом само по себе. Поиск "switch С++ Dewhurst" в Google дает массу ссылок на эту тему, включая [Dewhurst02. Если вас интересует более детальная информация по этому вопросу, чтобы убедить коллег не использовать эту методику, - обратитесь к найденным ссылкам.

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

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

Эти хитрые имена

Есть еще одна механическая проблема, которую я еще не раскрыл в полном объеме. Впервые она проявляет себя во всей красе (вернее, уродстве) в строке

enum uniontype {NONE, INT, LIST, STRING};

Никогда, никогда, вы слышите? - никогда! - не используйте имена, которые начинаются с подчеркивания или содержат двойное подчеркивание, эти имена зарезервированы для исключительного использования вашим компилятором и разработчиками стандартной библиотеки, чтобы избежать столкновения с такими же именами в вашем коде. Не трогайте их имен, и они не будут трогать ваши!"*

Не останавливайтесь! Читайте дальше! Вы могли встретить этот совет раньше. Вы могли даже услышать его от меня или прочесть в моих книгах. Это могло настолько вам надоесть, что, зевнув, вы решили перейти к следующему подразделу. Так вот, это не просто теоретические измышления!

Строка с определением enum компилируется большинством компиляторов, которыми я компилировал данную программу: Borland 5.5, Comeau 4.3.0.1, gcc 2.95.3 / 3.1.1 / 3.4, Intel 7.0 и Microsoft Visual С++ от 6.0 по 8.0 (2005) beta. Но два из них -Metrowerks CodeWarrior 8.2 и EDG 3.0.1 со стандартной библиотекой Dinkumware 4.0 - не смогли с ней справиться.

Если быть более точным, то правило гласит, что любое имя с двойным подчеркиванием в

любом месте, такое как 1 iке this, или начинающееся с подчеркивания и прописной буквы

наподобие Li keThi s. является зарезервированным. Если хотите - запомните это правило, но, на мой взгляд, проще просто полностью избегать двойных подчеркиваний и имен, начинающихся с подчеркивания.



Metrowerks CodeWarrior 8 останавливался, сообщив о 52 ошибках (это не опечатка). 225 строк (это тоже не опечатка) сообщений об ошибках начинались со следующей диагностики, прямо указывающей на одну из запятых.

### гтмсс compiler:

# File: 36.срр

#--------------

# 17: enum uniontype {none, int, list, String};

# Error: л

# identifier expected ### mwcc Compiler:

# 18: uniontype currtype;

# Error: ЛЛЛЛЛЛЛЛЛ

# declaration syntax error

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

Но что же случилось с определением uniontype? Указанная запятая вроде бы находится на положенном месте? Перед ней находится нормальный идентификатор, все, как положено... Все становится понятным, если попросить компилятор Metrowerks вывести исходный текст после обработки препроцессором. Пропустив много-много строк, мы встретим следующую, которая и поступает на вход компилятора.

enum uniontype {none, int, , };

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

Но что же произошло с.....list и string? Можно предположить, что ими перекусил голодный зверь по кличке Препроцессор. Это произошло просто потому, что реализация Metrowerks определяет макрос, который при обработке препроцессором удалил имена list и string, что вполне законно, поскольку стандарт позволяет этой реализации определять собственные имена „Names - как и other names.

Итак, компилятор Metrowerks просто съел как LIST, так и string. Это объясняет первую часть чудес. А как насчет второй части - реализации EDGs/Dinkumware? Судите сами.

"1.срр", 1iпе 17: error: trailing comma is nonstandard enum uniontype {NONE, INT, LIST, STRING};

"l.cpp", line 58: error: expected an expression if( currtype== STRING) {

"1.срр", line 63: error: expected an expression

currtype= STRiNG;

"1.срр", line 76: error: expected an expression case STRING: { л

4 errors detected in the compilation of "Зб.срр".

Что произошло в этот раз, можно понять даже без генерации исходного текста препроцессором и его проверки. Компилятор ведет себя так, как если бы слово string просто отсутствовало. Это связано с тем, что, как не сложно догадаться, оно тоже съедено все еще голодным Препроцессором.

Я надеюсь, что эти при.меры убедили вас, что использовать в своих программах

„Имена наподобие этого не стоит и что данная проблема вовсе не так надуманна,

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



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