CHAP8_3 (1018811), страница 3

Файл №1018811 CHAP8_3 (Сборник литературы - С и С++) 3 страницаCHAP8_3 (1018811) страница 32017-07-08СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 3)

{

storable *head;

public:

// ...

storable *find( const storable &a_match_of_this ) const;

};

storable *collection::find( const storable &a_match_of_this ) const

{

// Послать сообщение объекту начала списка, указывающее, что

// список просматривается на совпадение со значением a_match_of_this;

return head ? head->find( a_match_of_this )

: NULL

;

}

Механизм поиска нужных объектов скрыт внутри класса storable. Вы можете изменить лежащую в основе структуру данных, поменяв определение storable, и эти изменения совсем не затронут реализацию класса collection.

Затем давайте реализуем класс storable, использующий простой связанный список в качестве лежащей в основе структуры данных:

class storable

{

storable *next, *prev;

public:

storable *find ( const storable &match_of_this ) const;

storable *successor ( void ) const;

virtual int operator== ( const storable &r ) const;

};

storable *storable::find( const storable &match_of_this ) const

{

// Возвращает указатель на первый элемент в списке с корнем

// на себя самого, имеющий тот же ключ, что и "r". Обычно,

// объект-коллекция должен послать это сообщение объекту начала

// списка, указатель на который хранится в классе коллекции.

storable *current = this;

for( ; current; current = current->next )

if( *current == match_of_this ) // найдено совпадение

return current;

}

storable *storable::successor( void ) const

{

// Возвращает следующее значение в последовательности.

return next;

}

Функция operator==() должна быть чисто виртуальной, потому что отсутствует возможность ее реализации на уровне класса storable. Реализация должна быть выполнена в производном классе13 :

class storable_string : public storable

{

string s;

public:

virtual int operator==( const storable &r ) const;

// ...

};

virtual int operator==( const storable &r ) const

{

storable_string *right = dynamic_cast<storable_string *>( &r );

return right ? (s == r.s) : NULL;

}

Я здесь использовал предложенный в ISO/ANSI C++ безопасный механизм нисходящего приведения типов. right инициализируется значением NULL, если передаваемый объект (r) не относится к типу storable_string. Например, он может принадлежать к некоторому другому классу, также являющемуся наследником storable.

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

template <class t_key>

class storable

{

storable *next, *prev;

t_key key;

public:

// ...

storable *find ( const storable &match_me ) const;

storable *successor ( void ) const;

int operator==( const storable &r ) const;

};

template <class t_key>

int storable<t_key>::operator==( const storable<t_key> &r ) const

{

return key == r.key ;

}

template <class t_key>

storable<t_key> *storable<t_key>::successor( void ) const

{

return next;

}

template <class t_key>

storable *storable<t_key>::find( const storable<t_key> &match_me ) const

{

storable<t_key> *current = this;

for( ; current; current = current->next )

if( *current == match_me ) // найдено совпадение

return current;

}

Проблема здесь в непроизводительных затратах. Функции-члены шаблона класса сами являются шаблонами функций. Когда компилятор расширяет шаблон storable, он также расширяет варианты всех функций-членов этого шаблона. Хотя я их не показал, вероятно, в классе storable определено множество функций. Многие из этих функций будут похожи на функцию 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.И(I). Исключения

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.f();

}

catch( some_class::error &r )

{

// выполнить действие в случае ошибки

}

лучше читается, чем:

if( obj.f() == ERROR )

// выполнить действие в случае ошибки

В любом случае, если try-блок содержит более одного вызова функций, вы не сможете просто исправить ошибку, потому что вы не сможете узнать, где возникла ошибка.

Следующий пример демонстрирует вторую проблему. Класс CFile, реализующий основной ввод/вывод двоичных файлов, возбуждает исключение в случае переполнения диска при записи, чего легко добиться на дискете. Более того, функция write() не возвращает никакого кода ошибки. Перехват исключения является единственным способом обнаружения ошибки. Вот пример того, как вы должны обнаруживать ошибку чтения:

char data[128];

Cfile f( "some_file", CFile::modeRead );

try

{

f.Write( data, sizeof(data) );

}

catch( CFileException &r )

{

if( r.m_cause == CfileException::diskFull )

// что-то сделать

}

Имеется две проблемы. Первая явно связана с уродливостью этого кода. Я бы гораздо охотнее написал:

bytes_written = f.Write( data, sizeof(data));

if( bytes_written != sizeof(data) )

// разобраться с этим

Вторая проблема одновременно более тонкая и более серьезная. Вы не сможете исправить эту ошибку. Во-первых, вы не знаете, сколько байтов было записано перед тем, как диск переполнился. Если Write() возвратила это число, то вы можете предложить пользователю сменить диск, удалить несколько ненужных файлов или сделать еще что-нибудь для освобождения места на диске. Вы не можете тут сделать это, потому что не знаете, какая часть буфера уже записана, поэтому вы не знаете, откуда начинать запись на новый диск.

Даже когда Write() возвратила количество записанных байтов, то вы все еще не можете исправить ошибку. Например, даже если функцию CFile переписать, как показано ниже, то она все равно не будет работать:

char data[128];

CFile f( "some_file", CFile::modeRead );

int bytes_written;

try

{

bytes_written = f.Write( data, sizeof(data) );

}

catch( CFileException &r )

{

if( r.m_cause == CFileException::diskFull )

// что-то выполнить.

// при этом переменная bytes_written содержит мусор.

}

Управление передается прямо откуда-то изнутри Write() в обработчик catch при возбуждении исключения, перескакивая через все операторы return внутри Write(), а также через оператор присваивания в вызывающейся функции; переменная bytes_written остается неинициализированной. Я думаю, что вы могли бы передать Write() указатель на переменную, которую она могла использовать для хранения числа записанных байтов перед тем, как выбросить исключение, но это не будет значительным улучшением. Лучшим решением будет отказ от возбуждения исключения и возврат или числа записанных байтов, или какого-то эквивалента индикатора ошибки.

Последней проблемой являются непроизводительные затраты. Обработка исключения вызывает очень большие непроизводительные затраты, выражающиеся в возрастании в несколько раз размера кода и времени выполнения. Это происходит даже в операционных системах типа Microsoft NT, которая поддерживает обработку исключений на уровне операционной системы. Вы можете рассчитывать на 10-20% увеличение размера кода и падение скорости выполнения на несколько процентов при интенсивном использовании исключений.14 Следовательно, исключения должны использоваться лишь тогда, когда непроизводительные затраты не берутся в расчет, обычно при наличии возможности лучше предпочесть возврат ошибки.

160. По возможности возбуждайте объекты типа error.

Характеристики

Тип файла
Документ
Размер
70,5 Kb
Тип материала
Высшее учебное заведение

Список файлов книги

С и С++ - сборник литературы
C++ Бархатный путь - Марченко А
cpp_001.shtml
cpp_002.shtml
cpp_003.shtml
cpp_004.shtml
cpp_005.shtml
cpp_006.shtml
cpp_007.shtml
cpp_008.shtml
cpp_009.shtml
cpp_010.shtml
cpp_011.shtml
cpp_012.shtml
cpp_013.shtml
cpp_014.shtml
cpp_015.shtml
cpp_016.shtml
cpp_017.shtml
cpp_018.shtml
cpp_019.shtml
cpp_020.shtml
cpp_021.shtml
cpp_022.shtml
cpp_023.shtml
cpp_024.shtml
cpp_025.shtml
cpp_026.shtml
cpp_027.shtml
cpp_030.shtml
cpp_034.shtml
Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
7029
Авторов
на СтудИзбе
260
Средний доход
с одного платного файла
Обучение Подробнее