Использование в шаблонных классах аргументов по умолчанию



 

Шаблонный класс может по умолчанию определять аргумент, соответствующий обобщенному типу. Например, в результате такой template ‑спецификации

 

 

будет использован тип int , если при создании объекта класса myclass отсутствует задание какого‑то бы то ни было типа.

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

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

 

 

Обратите особое внимание на эту строку:

 

 

Здесь параметр AType по умолчанию заменяется типом int , а параметр size по умолчанию устанавливается равным числу 10 . Как показано в этой программе, объекты класса atype можно создать тремя способами:

■ путем явного задания как типа, так и размера массива;

■ задав явно лишь тип массива, при этом его размер по умолчанию устанавливается равным 10 элементам;

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

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

 

Явно задаваемые специализации классов

 

Подобно шаблонным функциям можно создавать и специализации обобщенных классов. Для этого используется конструкция template<> , которая работает по аналогии с явно задаваемыми специализациями функций. Рассмотрим пример.

 

 

При выполнении данная программа отображает такие результаты.

 

 

В этой программе обратите особое внимание на следующую строку.

 

 

Она уведомляет компилятор о том, что создается явная int ‑специализация класса myclass . Тот же синтаксис используется и для любого другого типа специализации класса.

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

 

Глава 17: Обработка исключительных ситуации

 

Эта глава посвящена обработке исключительных ситуаций. Исключительная ситуация (или исключение ) – это ошибка, которая возникает во время выполнения программы. Используя С++‑подсистему обработки исключительных ситуаций, с такими ошибками вполне можно справляться. При их возникновении во время работы программы автоматически вызывается так называемый обработчик исключений . Теперь программист не должен обеспечивать проверку результата выполнения каждой конкретной операции или функции вручную. В этом‑то и состоит принципиальное преимущество системы обработки исключений, поскольку именно она "отвечает" за код обработки ошибок, который прежде приходилось "вручную" вводить в и без того объемные программы.

В этой главе мы также возвращаемся к С++‑операторам динамического распределения памяти: new и delete . Как разъяснялось выше в этой книге, если оператор new не может выделить требуемую память, он генерирует исключение. И здесь мы узнаем, как именно обрабатывается такое исключение. Кроме того, вы научитесь перегружать операторы new и delete , что позволит вам определять собственные схемы выделения памяти.

 

Основы обработки исключительных ситуаций

 

Обработка исключений – это системные средства, с помощью которых программа может справиться с ошибками времени выполнения.

Управление С++‑механизмом обработки исключений зиждется на трех ключевых словах: try , catch и throw . Они образуют взаимосвязанную подсистему, в которой использование одного из них предполагает применение другого. Для начала будет полезно получить общее представление о роли, которую они играют в обработке исключительных ситуаций. Если кратко, то их работа состоит в следующем. Программные инструкции, которые вы считаете нужным проконтролировать на предмет исключений, помещаются в try ‑блок. Если исключение (т.е. ошибка) таки возникает в этом блоке, оно дает знать о себе выбросом определенного рода информации (с помощью ключевого слова throw ). Это выброшенное исключение может быть перехвачено программным путем с помощью catch ‑блока и обработано соответствующим образом. А теперь подробнее.

Инструкция throw генерирует исключение, которое перехватывается catch‑инструкцией.

Итак, код, в котором возможно возникновение исключительных ситуаций, должен выполняться в рамках try ‑блока. (Любая функция, вызываемая из этого try ‑блока, также подвергается контролю.) Исключения, которые могут быть выброшены контролируемым кодом, перехватываются catch ‑инструкцией, непосредственно следующей за try ‑блоком, в котором фиксируются эти "выбросы" исключений. Общий формат try ‑ и catch ‑блоков выглядит так.

 

 

Блок try должен содержать код, который, по вашему мнению, должен проверяться на предмет возникновения ошибок. Этот блок может включать лишь несколько инструкций некоторой функции либо охватывать весь код функции main() (в этом случае, по сути, "под колпаком" системы обработки исключений будет находиться вся программа).

После "выброса" исключение перехватывается соответствующей инструкцией catch , которая выполняет его обработку. С одним try ‑блоком может быть связана не одна, а несколько catch ‑инструкций. Какая именно из них будет выполнена, определяется типом исключения. Другими словами, будет выполнена та catch ‑инструкция, тип исключения которой (т.е. тип данных, заданный в catch ‑инструкции) совпадает с типом сгенерированного исключения (а все остальные будут проигнорированы). После перехвата исключения параметр arg примет его значение. Таким путем могут перехватываться данные любого типа, включая объекты классов, созданных программистом.

Чтобы исключение было перехвачено, необходимо обеспечить его "выброс" в try‑блоке.

Общий формат инструкции throw выглядит так:

 

 

Здесь с помощью элемента exception задается исключение, сгенерированное инструкцией throw . Если это исключение подлежит перехвату, то инструкция throw должна быть выполнена либо в самом блоке try , либо в любой вызываемой из него функции (т.е. прямо или косвенно).

На заметку. Если в программе обеспечивается "выброс" исключения, для которого не предусмотрена соответствующая catch‑инструкция, произойдет аварийное завершение программы, вызываемое стандартной библиотечной функцией terminate(). По умолчанию функция terminate() вызывает функцию abort() для остановки программы, но при желании можно определить собственный обработчик ее завершения. За подробностями относительно обработки этой ситуации следует обратиться к документации, прилагаемой к вашему компилятору.

Рассмотрим простой пример обработки исключений средствами языка C++.

 

 

При выполнении эта программа отображает следующие результаты.

 

 

Рассмотрим внимательно код этой программы. Как видите, здесь try ‑блок содержит три инструкции, а инструкция catch(int i) предназначена для обработки исключения целочисленного типа. В этом try ‑блоке выполняются только две из трех инструкций: cout и throw . После генерирования исключения управление передается catch ‑выражению, при этом выполнение try ‑блока прекращается. Необходимо понимать, что catch ‑инструкция не вызывается, а просто с нее продолжается выполнение программы после "выброса" исключения. (Стек программы автоматически настраивается в соответствии с создавшейся ситуацией.) Поэтому cout ‑инструкция, следующая после throw ‑инструкции, никогда не выполнится.

После выполнения catch ‑блока управление программой передается инструкции, следующей за этим блоком. Поэтому ваш обработчик исключения должен исправить ошибку, вызвавшую его возникновение, чтобы программа могла нормально продолжить выполнение. В случаях, когда ошибку исправить нельзя, catch ‑блок обычно завершается обращением к функциям exit() или abort() . (Функции exit() и abort() описаны в разделе "Копнем глубже" ниже в этой главе.)

Как упоминалось выше, тип исключения должен совпадать с типом, заданным в catch ‑инструкции. Например, если в предыдущей программе тип int , указанный в catch ‑выражении, заменить типом double , то исключение перехвачено не будет, и произойдет аварийное завершение программы. Вот как выглядят последствия внесения такого изменения.

 

 

Такие результаты выполнения этой программы объясняются тем, что исключение целочисленного типа не перехватывается инструкцией catch (double i) .

 

Функции exit() и abort()

 

Функции exit() и abort() входят в состав стандартной библиотеки C++ и часто используются в программировании на C++. Обе они обеспечивают завершение программы, но по‑разному.

Вызов функции exit() немедленно приводит к "правильному" прекращению программы. ("Правильное" окончание означает выполнение стандартной последовательности действий по завершению работы.) Обычно этот способ завершения работы используется для остановки программы при возникновении неисправимой ошибки, которая делает дальнейшее ее выполнение бессмысленным или опасным. Для использования функции exit() требуется включить в программу заголовок <cstdlib> . Ее прототип выглядит так.

 

 

Поскольку функция exit() вызывает немедленное завершение программы, она не передает управление вызывающему процессу и не возвращает никакого значения. Тем не менее вызывающему процессу в качестве кода завершения передается значение параметра status . По соглашению нулевое значение параметра status говорит об успешном окончании работы программы. Любое другое его значение свидетельствует о завершении программы по ошибке. Для индикации успешного окончания можно также использовать константу EXIT_SUCCESS , а для индикации ошибки– константу EXIT_FAILURE . Эти константы определены в заголовке <cstdlib> .

Прототип функции abort() выглядит так:

 

 

Аналогично exit() функция abort() вызывает немедленное завершение программы. Но в отличие от функции exit() она не возвращает операционной системе никакой информации о статусе завершения и не выполняет стандартной ("правильной" ) последовательности действий при остановке программы. Для использования функции abort() требуется включить в программу заголовок <cstdlib> . Функцию abort() можно назвать аварийным "стоп‑краном" для С++‑программы. Ее следует использовать только после возникновения неисправимой ошибки.

Последнее сообщение об аварийном завершении программы (Abnormal program termination ) может отличаться от приведенного в результатах выполнения предыдущего примера. Это зависит от используемого вами компилятора.

Исключение, сгенерированное функцией, вызванной из try ‑блока, может быть перехвачено этим же try ‑блоком. Рассмотрим, например, следующую вполне корректную программу.

 

 

Эта программа генерирует такие результаты.

 

 

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

 

 

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

 

 

Как видите, программа сгенерировала три исключения. После каждого исключения функция Xhandler() передавала управление в функцию main(). Когда она снова вызывалась, возобновлялась и обработка исключения.

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

 


Дата добавления: 2018-09-22; просмотров: 413; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!