Программирование >>  Разработка устойчивых систем 

1 ... 6 7 8 [ 9 ] 10 11 12 ... 196


Обработчики 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():



1 ... 6 7 8 [ 9 ] 10 11 12 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика