Анимация
JavaScript
|
Главная Библионтека Это означает, что каждое расширение такой функции будет идентично по содержанию любому другому ее расширению. Из функций, которые не похожи на функцию successor() , большинство будут подобны find() , использующей информацию о типе, но которую легко изменить так, чтобы ее не использовать. Вы можете решить эту проблему, используя механизм шаблонов для создания производного класса. Основываясь на предыдущей реализации, не использующей шаблоны, вы можете сделать следующее: template <class t key> class storable tem : public storable t key key; public: Замещение базового класса virtual int operator==( const storable &r ) const; ... template <class t key> /* виртуальный */ int storable tem<t key>:: operator==( const storable &r ) const t key *right = dynamic cast<t key *>( &r ); return right ? (s == r.s) : NULL; Выбрав другой путь, я сосредоточил в базовом классе все функции, которые не зависят от типа key. Затем я использовал механизм шаблонов для создания определения производного класса, реализующего только те функции, которым нужно знать тип key. Полезным результатом является существенное сокращение размера кода. Механизм шаблонов может рассматриваться как средство автоматизации производства шаблонных производных классов. Часть 8и. Исключения 159. Назначение исключений - не быть пойманными Как правило, исключение должно быть возбуждено, если: • Нет другого способа сообщить об ошибке (например, конструкторов, перегруженных операций и т.д.). • Ошибка неисправимая (например, нехватка памяти). • Ошибка настолько непонятная или неожиданная, что никому не придет в голову ее протестировать (например, printf). Исключения были включены в язык для обработки ошибочных ситуаций, которые иначе не могут быть обработаны, таких, как ошибка, случающаяся в конструкторе или перегруженной операции. Без использования исключений единственным способом обнаружения ошибки в конструкторе будет передача этому объекту сообщения: some obj x; if( x.is invalid() ) конструктор не выполнился. что, по меньшей мере, неаккуратно. Перегруженные операции являют собой ту же проблему. Единственным способом, которым использованная в x=a+b; функция operator+() может сообщить об ошибке, является возврат неверного значения, которое будет скопировано в x. Вы могли бы затем написать: if( x == INVALID ) ... или нечто подобное. Снова весьма неаккуратно. Исключения также полезны для обработки ошибок, которые обычно являются фатальными. Например, большинство программ просто вызовут exit() , если функция malloc() не выполнится. Все проверки типа: if( !(p = malloc(size)) ) fatal error( E NO MEMORY ); бесполезны, если оператор new просто не возвратит значения, когда ему не хватит памяти. Так как new на самом деле возбуждает исключение (по сравнению с вызовом exit() ), то вы можете перехватить это исключение в тех редких случаях, когда вы можете что-то сделать в такой ситуации. Также имеется и другая проблема. Одной из причин того, что комитет ISO/ANSI по Си++ требует, чтобы оператор new возбуждал исключение, если он не может выделить память, заключается в том, что кто-то провел исследование и обнаружил, что какая-то смехотворная доля ошибок времени выполнения в реальных программах вызвана людьми, не побеспокоившимися проверить, не вернула ли функция malloc() значение NULL. По причинам, обсуждаемым позже, я не думаю, что исключение должно быть использовано вместо возврата ошибки просто для защиты программистов от себя самих, но оно срабатывает с new, потому что эта ошибка обычно в любом случае неисправима. Лучшим примером может быть функция printf() . Большинство программистов на Си даже не знают, что printf() возвращает код ошибки. (Она возвращает количество выведенных символов, которое может быть равно 0 , если на диске нет места). Программисты, которые не знают о возврате ошибки, склонны ее игнорировать. А это не очень хорошо для программы, которая осуществляет запись в перенаправленный стандартный вывод, продолжать, как будто все в порядке, поэтому можно считать хорошей идеей возбудить здесь исключение. Итак, что же плохого в исключениях? На самом деле существует две проблемы. Первой является читаемость. Вам будет тяжело меня убедить, что: some class obj; try obj.fO; catch( some class::error &r ) выполнить действие в случае ошибки лучше читается, чем: if ( obj.f() == ERROR ) выполнить действие в случае ошибки В любом случае, если try-блок содержит более одного вызова функций, вы не сможете просто исправить ошибку, потому что вы не сможете узнать, где возникла ошибка. Следующий пример демонстрирует вторую проблему. Класс CFile, реализующий основной ввод/вывод двоичных файлов, возбуждает 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 |