|
Программирование >> Разработка устойчивых систем
Обработчики unexpectedO void my uhandlerl() { throw A(): void my uhandler2() { throw; Если включить эту команду throw в f или g. компилятор распознает нарушение и сообщит об ошибке. поэтому мы выделяем ее в отдельную функцию, void to { throw ВО; void f() throw(A) { tO; void gO throwCA. bad exception) { tO; int mainO { set terminate(my thandler); set unexpected(my uhandlerl); try { fO: catch (A&) { cout caught an A from f\n : set unexpected(my uhand1er2); try { gO; catch (bad exception&) { cout caught a bad exception from g\n ; try { fO: catch (...) { cout This will never printVn ; } /:- Обработчик my uhandler() запускает допустимое ис1С71ючение (A), поэтому выполнение успешно продолжается в первой секции catch. Обработчик my uhandler2() запускает недопустимое исключение (В), но в спецификации д() присутствует класс bad exception, поэтому исключение В заменяется объектом bad exception, и выполнение успешно продолжается во второй секции catch. Поскольку спецификация f() не содержит объекта bad exception, функция my thandler() вызывается как обработчик завершения. Результат выполнения программы выглядит так: caught an А from f caught a bad exception from g terminate called Улучшим спецификации исключений? На первый взгляд может показаться, что существующий синтаксис спецификаций исключений не очень надежен, и что следующая запись должна означать, что функция не запускает никаких исключений: void fO: Если программист хочет указать, что функция может запускать произвольные ис1С71Ючения, ему следовало бы записать это объявление так: void fO throwC...); Только не в С++ Наверное, такой синтаксис был бы более логичным, поскольку объявления функции стали бы более однозначными. К сожалению, при взгляде на код функции не всегда понятно, запускает ли она исключения, например, исключение может возникнуть из-за нехватки памяти. Что еще хуже, существующие функции, написанные до появления в языке обработки исключений, могут непреднамеренно запускать исключения из-за вызываемых ими функций (при их включении в новые версии программ с обработкой исключений). Из-за этого следующая неинформативная запись означает: Возможно, я буду запускать исключения... а может, и нет : void fO; Подобная неоднозначность неизбежна, чтобы не препятствовать эволюции программ. Если вы хотите указать, что функция f() не запускает исключения, задайте спецификацию с пустым списком: void fO throwO: Спецификации исключений и наследование Каждая открытая функция класса участвует в формировании контракта с пользователем; если передать ей некоторые аргументы, функция выполнит те или иные операции и/или вернет результат. Контракт должен соблюдаться и в производных классах, в противном случае нарушится основное правило производный класс является частным случаем базового класса . Поскольку спецификации исключений являются логической частью объявления функции, они тоже должны сохраняться в иерархиях наследования. Например, если функция базового класса объявляет, что она запускает только исключения типа А, переопределение этой функции в производном классе не должно добавлять новые типы исключений в спецификацию, поскольку это нарушит работу любых программ, зависящих от интерфейса базового класса. С другой стороны, количество запускаемых исключений можно уменьшить и даже вовсе запретить их, так как это не приведет к расширению интерфейса базового класса. В спецификации производной функции вместо А также можно использовать тип, производный от А . Рассмотрим пример: C01;Covariance.cpp {-хо} Должна происходить ошибка компиляции. {-[nwcc}{-msc} #include <iostream> using namespace std: class Base { public: class BaseException {}: class Derived : public Base { public: void f() throw (BaseException) { throw BaseExceptionO: virtual void gO throw (DerivedException) { throw DerivedException(): }: III:- Компилятор должен отвергнуть переопределение Derived::f() как ошибочное (или по крайней мере выдать предупреждение), поскольку оно изменяет спецификацию исключений Base::f(). Спецификация Derived::g() допустима, потому что DerivedException является частным случаем BaseException (а не наоборот). Base/ Derived и BaseException/DerivedException можно рассматривать как параллельные иерархии классов; в контексте Derived ссылки на BaseException в спецификациях исключений и возвращаемых значениях могут заменяться на DerivedException. Такое поведение называется ковариттным (оба набора классов одновременно изменяются при перемещении по соответствующим иерархиям). Как было показано в первом томе, типы параметров не ковариантны - вы не можете изменять сигнатуры переопределенных виртуальных функций. Когда спецификации исключений не используются Просматривая объявления функций в стандартной библиотеке С++, вы не обнаружите в ней ни одной спецификации исключений! Хотя это может показаться странным, на самом деле такая внешняя небрежность объясняется вескими причинами: библиотека состоит в основном из шаблонов, а ее автору неизвестно, как будут работать конкретные типы или функции. Допустим, вы создаете обобщенный шаблон стека и пытаетесь включить в функцию рор() спецификацию исключений: Т рорО throw(logic error): Единственная потенциальная ошибка, которую можно себе представить, - попытка извлечения элемента из пустого стека, поэтому кажется, что вполне безопасно задать исключение logic error или другого подходящего типа. Но копирующий конструктор типа Т тоже может запустить исключение! В этом случае будет вызвана функция unexpectedO, а программа завершится. Не стоит давать обещания, которые невозможно выполнить. Если вы не знаете, какие исключения могут возникнуть в программе, не используйте спецификации исключений. Вот почему в шаблонах, составляющих основную часть стандартной библиотеки С++, отсутствуют спецификации исключений - известные им исключения указываются в документации, а остальное остается на ваше усмотрение. Спецификации исключений требуются главным образом в нешаблонных классах. class DerivedException : public BaseException {}: virtual void f() throw (DerivedException) { throw DerivedException(): virtual void gO throw (BaseException) { throw BaseException():
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |