Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

chast2

.pdf
Скачиваний:
9
Добавлен:
10.06.2015
Размер:
431.28 Кб
Скачать

-абстрактный класс не может участвовать в операциях приведения типов

-абстрактный класс может быть производным как от абстрактного класса так и от обычного класса.

-допускается создавать указатели на объекты абстрактного класса.

Например формальный параметр какой-то функцииможет быть таким указателем,но при обращении к этой функции аргумент, т.е. фактический параметр,должен быть адресом какого-то конкретного объекта производного неабстрактного класса.

Именно последняя особенность обеспечивает эффективную реализацию принципа полиморфизма.

Рассмотрим пример двухуровневой иерархии. На первом уровне будет абстрактный базовый класс Abstr_bas,а на втором - производные.

class Pr1:public Abstr_bas{ //производный класс№1 public:

virtual void f(){cout<<"эта функция f призводного класса №1"<<endl;} };

class Pr2:public Abstr_bas{ //производный класс№2 public:

virtual void f(){cout<<"эта функция f призводного класса №2"<<endl;} };

Помимо указанной иерархии классов опредилим общую автономную функцию.

void F_common(Abstr_bas* ptr) {ptr->f();};

ПРи обращении к этой функции на место формального параметра должен ставится адрес конкретного объекта производного класса. В теле функции по указателю с помощью оператора ptr->f() будет вызван нужный вариант виртуальной функции.

Pr1 obj1; //создаем объект производного класса №1

F_common(& obj1); //внутри функции F_common срабатывает функция f класса Pr1 Pr2 obj2;

F_common(& obj2);

Abstr_bas* ptrA;

ptrA=&obj1; //передаем этому указателю адрес объекта производного класса №1, а значит динамическим типом этого указателя будет тип Pr1*

F_common(ptrA); //внутри функции F_common срабатывает функция f класса Pr1

Данный пример предельно упрощен, однако пустьнапример:

-производных классов не 2 а 20

-в функцию F_common передается еще ряд параметров

-в ее теле помимо вызова функции f осуществляется еще много действий общих для обработки объектов всех производных классов.

Тогда мы благодаря позднему связыванию сможемобойтись одним универсальным и компактным кодом функции F_common,а так же простй логикой её применения. Добиться такого же с помощью раннего связывания нельзя. Функционально подобный алгоритм будет существенно более грамоздким и запутанным.

___________________________________________________________________________________

70.Особенности адресации виртуальных функций.

___________________________________________________________________________________

Механизм реализации виртуальных функций,в частности аспект позднего связывания предполагает наличие специальной системы адресации таких функций.

Наиболее распространенный подход к решению проблемы адресации предполагает построение в каждом классе таблицы виртуальных функций (или методов) VMT.

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

Пример опредедления класса

class B{... //базовый класс public:

B(){...} //невиртуальный конструктор virtual ~B(){} // виртуальный диструктор virtual void f1(){} // виртуальный метод virtual int f2(){} // виртуальный метод virtual char f3(){}//виртуальный метод double f4(){} //невиртуальный метод }; //конец определения класса

Логическая структура VMT для этого класса показана в левой части рис 5.1,а правая часть схематически иллюстрирует часть памяти на которую будут ссылаться уазатели из VMT.

Коды невиртуальных методов в VMT не адресуются, в нашем примере это конструктор B() и метод f4. На базе класса В определим следующий производный класс.

class D:public B{... //производный класс public:

virtual ~D(){}// перекрытый виртуальныйдиструктор virtual void f1(){}//перекрытый виртуальный метод virtual char f3(){}//перекрытый виртуальный метод virtual void f5(){}// новый виртуальный метод double f6(){}// невиртуальный метод

};

таблица виртуальных методов для класса Д будет иметь логическую структуру,показанную в левой части рис 5.2. В правой - фрагмент оперативной памяти компьютера,на отдельные части которого ссылаются указатели из VMT.

В этой VMT помимо адресов перекрытых виртуальными методами

D::~D

D::f1

D::f3

и новый виртуальный метод D::f5,который явно определен в производном классе содержится также иадрес метода B::f2,которая унаследована от базового класса и явно не упоминается в определении производного класса.

Проиллюстрировать применение VMT можно используя пример из предыдущего пункта. При вызове функции F_common в зависимости от реализации компелятора может выполнится такая последовательность действий:

-По динамическому типу аргумента определяется адрес VMT соответствующего класса

-в таблице ищется указатель на нужную вртуальную функцию, согласно рис 5.2 это может быть как метод базового так и производного класса.

-По найденному указателю управление передаетсякоду нужной функции

__________________________________________________________________________________

71.Исключения - основные понятия.

__________________________________________________________________________________

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

Типичные примеры:

-деление на 0

-ошибка преобразования типа данных

-ошибки ввода - вывода

и т.д.

В тоже время в современных языках программироания этому понятию придают существенно более широкую трактовку:

Под исключением понимают любую ситуацию которую программист по каким-либо причинам считает особой (требующей специальной обработки). Очевидно, что обычные ошибки попадают подширокое определение исключения.

Основная идея механизма обработки исключительной ситуации в ООП в том числе в С++ заключается в отделении процесса обработки исключительной ситуации от процесса выявления ее наличия.

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

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

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

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

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

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

Мы рассмотрим только базовые возможности ориентируясь на консольные приложения С++.

В начале рассмотрим служебные слова испоьзуемые в С++ при выявлении,пересылке и собственно обработки (перехвата) исключения.

-try(контролировать, пытаться)

-throw (бросать, генерировать)

-catch (ловить, перехватывать)

Общая схема пересылки,выявления и перехвата исключения такова

try{ //(1) начало блока контроля try <операторы>//(2)

if(<условие 1>)throw<выражение 1>;//(3) <операторы>//(4)

if(<условие 2>)throw<выражение 2>;//(5) }//(6) конец блока try

1-6 - блок контроля

catch<спецификация исключения 1>//(8) {<операторы обработки исключения>}//(9)

catch<спецификация исключения 2>//(10) {<операторы обработки исключения>}//(11)

8-11 - перехватчики исключения

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

throw<выражение>

имено специальный оператор вида throw<выражение> предназначен для генерирования и пересылки исключения. В этом операторе <выражение> стоящие после слова throw формирует специальный объект который принято называть исключением. Тип и значение этого объекта исключения определяется формирующим его выражением.

В данном случае термин "объект" понимается в общем смысле слова как выделенная область памяти хранящая значение (п. 4.1 из первой части курса),но в частном члучае это может быть и объект в узком смысле слова, т.е переменаая классового типа.

Поскольку генерировать объект исключение имеет смысл только если складывается определенная ситуация соответствующая тому,что <условия> принимает значение true, то throw логично разсещать в составе условного оператора (если условие true - это признак реализации особой ситуации,то сгенерировать объект исключения).

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

После создания объекта исключения оператор throw передает управление первому оператору стоящему после фигурной скобки закрывающей блок контроля. Причем таким оператором обязательно должен быть перехватчик catch какого-то исключения. Если по схеме работы программы в данном блоке контроля предполагается выявлять несколько разных исключений разных типов, то после блока контроля должен быть уаазан перехватчик всех этих исключений и между ними нельзя располагать другие операторы.

Перехватчики расположенные после данного блока контроля должны обрабатывать исключения разных типов.

Последовательность работы конструкции схематически показаная в строках 1-11 такова:

-сначала выполняются операторы из строки 2

-если <условие 1> имеет значение true, то генерируется объект исключение, тип и значение которого задается <выражением 1>. На этом работа блока контроля прерывается и управление передается первому перехватчику после блока контроля.

-если <условие 1> = false, то выполняются операторы из строки 4, после чего проверяем <условие 2> и т.д.

Таким оьразом в блоке контроля либо не будет сгенерировано ни одного объекта исключения либо только один, а именнно это тот объект, который будет сгенерирован первым.

После окончания работы блока контроля:

-если объект исключения не был сгенерирован, то управление передается первому обычному оператору после списка операторов catch.

-если объект исключения был сгенерирован и среди перехватчиков catch имеется такой,в котором спецификация исключения совпадает по типу со сгенерированным исключением, то срабатывает тело этого перехватчика (строка 9 или 11), после чего управление передается первому обычному оператору после списка операторов catch.

-если объект исключения был сгенерирован, но среди перехватчиков catch нет подходящего по спецификации, то вызывается специальнаябиблиотечная функция terminatr(),которая завершает выполнение программы.

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

Рассмотрим пример иллюстрирующий рассмотренные выше понятия:

const double pi=arccos(-1.0); //(12)

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

double Sqr_Ellips(double a,double b){ //(13) if ((a==0)&&(b==0))throw 0;//(14)

if (a==0)throw a;//(15)

if (b==0)throw "полуось b=0";//(16)

if ((a<0)||(b<0))throw string("есть отрицательные полуоси");//(17) return pi*a*b;}//(18)

Укажем типы генерируемых в теле функции исключений (типы выражений стоящих после слова throw):

-в ст14 тип int

-в ст15 тип double

-в ст16 тип char* (т.к. соответствующее выражение - это строка символов)

-в ст17 тип string (это стандартный строковый класс в С++)

Приведем фрагмент тела функции main в котором в соответствии с рассмотренной выше общей схемы организован перехват исключений.

try{ //(19) cout<<"Sqr_Ellips(1,1)="<<Sqr_Ellips(1.1)<<endl; //(20) } //(21) конец блока try

//далее идут обработчики исключений этого блока try

catch(const int i){cout<<"обе полуоси равны"<<i<<endl;} //(22) catch(const double x){cout<<"полуось а="<<x<<endl;} //(23) catch(const char*){cout<<"полуось b=0"<<endl;} //(24) catch(const string msg){cout<<msg<<endl;} //(25)

...

Обратить внимание,что все спецификации исключений перехватчика catch различны.

В приведенном примере при заданных аргументах функции Sqr_Ellips(1,1) ни один из перехватчиков не сработает, а значит управление будет передано первому оператору после строки 25. Если в строке 20 обращение к функции будет например таким: Sqr_Ellips(-1,1),то работа функции прервется в строке 17, будет сгенерирован объект исключение типа string который в обработчике в строке 25 получит имя msg,а далее в теле обработчика значение этой строки будет выведено на экран дисплея.

Пользуясь приведенным примером дополним записанное выше полное описание последовательности работы механизма обработки исключения:

объект-исключения формируются локально в одном блоке (в нашем примере это в теле функции Sqr_Ellips), но при этом они доступны в другом блоке (у нас в теле функции main). Это особое правило работы с объектами исключения. Они создаются как временные статические объекты в одном блоке и благодаря этому могут обрабатываться в другомблоке. После обработки эти объекты уничтожаются.

_________________________________________________________________________________

72.Особенности синтаксиса и симантика исключений.

__________________________________________________________________________________

перечень особенностей:

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

Обычно для переноса больших объемов информации в качестве <выражения> размещаемого после throw используют объект специально определяемого класса. Конкретно после throw стоит конструктор создающий объект класса и необходимую для обработки исключения информацию размещает в полях.

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

3.Существует 3 формы спецификации исключенияв обработчиках:

-catch(<тип><имя>){операторы} //(1)

-catch(<тип>){операторы} //(2)

-catch(...){операторы} //(3)

Здесь <тип> - это тип объекта исключения.

<имя> - это имя которое внутри обработчика получает ранее созданный объект исключения,это имя можно использовать в операторах обработчика (см ст 22,23,25 в предыдущем примере)

Второй вариант спецификации не предполагает использование значения объекта исключения,важен только его тип (ст 24).

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

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

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

Если например обработчики ст 22 поставить после ст 23 и 24, то до него дело никогда не дойдет, т.е. ситуация "обе полуоси =0" реально может сложится ,но соответствующий обработчик никогда на неене отреагирует. В частности обработчик вида 3, если он нужен, всегда должен ставится последним.

5.Проверка соответствия спецификации обработчика и тип исключения осуществляется по следующему правилу:

пусть обработчик имеет вид catch(T x){...} //(4) T-тип, х-имя

Тогда этот обработчик перехватит исключения сл типов:

-Т

-const T

-const T& (ссылка на объекты типа Т)

-T&

и в том сл,если его тип может быть стандартным образом приведен к типу параметра-обработчика.

6. Оператор имеющий сл формы

-throw<выражение>; //(5)

-throw; //(6)

первую форму мы разобрали (см пример), она предназначена именно для генерации объекта исключения. Копия этого объекта передается за пределы блока контроля throw, и эта копия существует, пока соответствущее исключение ни будет обработано.

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

вышестоящем. Тогда оператор в форме 6 выполнит роль ретранслятора, т.е. он передаст "наверх" полученное исключение.

Если оператор throw в форме 6 попытаться использовать вне блока контроля и вне обработчика исключения, то будет вызвана функция terminate(), которая завершит работу программы.

7. В заголовке функции можно указывать какие по типу исключения эта функция порождает. Соответствующий формат заголовка функции таков:

<тип><имя функции>(<спкцификация параметров>)//(7) throw(<список типов>){<тело фукции>}//(8)

КОнструкция вида throw (<список типов>) называется спецификацией исключений данной функции. Исключение типов указанных в списке могут передаваься за пределы функций. Если внутри функций будет сгенерирован объект исключенияне входящий в список и он не будет обработан внутри функции, то управление будет передано специальной системной функции unexpected() которая в свою очередь вызывает функцию terminate().

Если список импов будет пустым,то все исключения генерируемые в теле функции должны быть обработаны там же.

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

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

//определяем специальный класс для обработки исключений

struct EXCEPT{ //(9) начало класса double a,b; //(10)

string S; //(11)

EXCEPT(double a1,double b1,string S1):a(a1),b(b1),S(S1){} //(12) };//(13)

// далее определяем функцию для рассчета площади описанного вокруг эллипса прямоугольника. Внутр этой функции будет вызвана функция Sqr_Ellips(см п 6.1)

double Sqr_Rect(double a,double b){ //(14) отсутствие суфикса throw в заголовке функции указывает, что фукция может пересылать во внешнюю среду любые исключения

try{ //(15)

if(a<b)throw EXCEPT(a,b,"полуось а меньше полуоси b"); //(16) вызываем констрктор класса EXCEPT, т.е. в качестве исключения здесь будет объект класса EXCEPT.

double pl_El=Sqr_Ellips(a,b); //(17) если в теле функции Sqr_Ellips реализуется какое-то исключение,то следующий оператор не выполнится и управление будет передано первому catch.

cout<<"площадь эллипса="<<pl_El<<endl;//(18) }//(19) конец блока try

catch(EXCEPT Ex){cout<<"пересылаю выше"<<endl; throw;} //(20) здесь осуществляется ретрансляция исключения вида "a<b"

// далее идут перехватчики исключений которые могут быть сгенерированны в теле функции Sqr_Ellips. При этом перехватчик исключения вида "есть отрицательные полуоси" играет роль ретранслятора.

catch(const int){cout<<"оби полуоси=0"<<endl;} //(21) catch(const double x){cout<<"полуось а="<<x<<endl;} //(22) catch(const char*){cout<<"полуось b=0"<<endl;} //(23)

catch(const string msg){const<<msg<<endl; throw;} //(24) здесь ретрансляция

if(a==b)throw EXCEPT(a,b,"полуоси а и b равны"); //(25) для перехвата этого исключения оператор вызова функции Sqr_Rect должен размещаться в блоке try более высокого уровня

return 4*a*b;} //(26) площадь прямоугольника

Рассмотрим фрагмент функции main с оператором try в тело которого будет вложен вызов функции Sqr_Rect и который слодовательно будетблоком более высокого уровня (внешним блоком) по отношению к блоку try, организованному в теле самой функции Sqr_Rect. При этом будем руководствоваться следующей логикой:

если исключения видов "a<b","a=b" и "есть отрицательные полуоси" реализовать, то результат расчета площади прямоугольника выводить в консольное окно не нужно.

try{ //(27)

double xx=Sqr_Rect(1,2);} //(28) конец внешнего блока try

//далее идут перехватчики тех исключений которые могут быть переданы во внешний блок try из функции

Sqr_Rect (см ст 20,24,25)

catch(EXCEPT Ex){ //(29) перехват исключений вида "a<b" и "a=b" cout<<Ex.S<<". расчет площади прямоугольника не проводится" //(30) <<endl; //(31)

_getch(); //(32) return 0;} //(33)

catch(const string msg){ //(34) перехват исключения вида "есть отрицательные полуоси" cout<<msg<<". расчет площади прямоугольника непроводится" ..(35)

<<endl; //(36) _getch(); //(37) return 0;} //(38)

cout<<"площадь прямоугольника ="<<xx<<endl; //(39)

Необходимо обратить внимание на то,как устроены тела 2х последних обработчиков исключения: наличае оператора return 0. Если этого не сделать,то согласно схеме функционирования механизма обработки исключения,после выполнения обработчика управлениебудет передано первому оператору стоящему после списка обработчиков (ст 39), а значит в консольное окно попадет бессмысленный результат. Первый перехватчик (29-33) предназначен для перехвата 2х видов исключения (тип у них один и тот же), причем сообщение о том како именно исключениереализовалось будет передано через поле х объекта. Аналогично можно было бы организовать обработку 200т различных видов...

___________________________________________________________________________________

73.Исключения в конструкторах.

___________________________________________________________________________________

Язык С++ предусматривает две схемы обработки исключений сгенерированных в конструкторе:

-обычная схема,как для функции

-спкциальная схема, в соответствии с которой к.оформляется как блок try со своими перехватчиками.

Структура:

<имя класса>::<имя класса>(<спецификация параметров>)//(1) try:<список инициализаторов>//(2)

{<тело конструктора>}//(3)здесь могут генерироваться исключения <последовательность перехватчиков исключений>//(4)

Обратить внимание,что во второй схеме телом блока try служит тело конструктора.

Если какие-то исключения сгенерированные в к. (ст3) не будут перехвачены ни одним из перехватчиков самого конструктора (ст4), то они должны быть перехвачены во внешнемблоке,в теле которого вызывается конструктор.

Рассмотрим пример в котором разработаем класс подобный тому, что мы делали в п.4.2.1. С одной стороны мы упростим этот класс, а с другой - добавим элементы необходимые для иллюстрации исключений в конструкторе.

class point{ //(5) public: //(6)

double x,y,z; //(7) координаты и модуль радиусвектора точки

double vx,vy,mod_v; //(8) компоненты и модуль вектора начальной скорости point(double x1=0,double y1=0,double vx1=0, double vy1=0); //(9) к.умолчания

}; //(10) конец определения класса point

//определение конструктора

point::point(double x1,double y1, double vx1,double vy1); //(11) try:x(x1),y(y1),vx(vx1),vy(vy1){ //(12) обратить внимание на слово try перед инициализацией r=sqrt(x*x+y*y); //(13)

mod_v=sqrt(vx*vx+vy*vy); //(14)

if(r>1)throw r; //(15) это исключение предполагается перехватываться в самом конструкторе if(mod_v>10)throw string("скорость точки слишком велика"); //(16)

} //(17)конец конструктора

catch(const double rp){cout<<"точка вышла за пределы круга: r="<<rp<<endl;} //(18) перехватчик конструктора

Приведем фрагмент функции main с оператором try в теле которого будет создаваться объект класса point.

try{ //(19)

point P1(1,1,20,20); //(20) } //(21)конец блока try

catch(conststring msg){cout<<msg<<endl;} //(22) перехват исключения "скорость велика"

Прокомментируем всё при таком обращении к конструктору (ст20) и модуль радиусвектора и модуль скорости выйдут за

допустимые пределы,но сгенерированобудет только первое исключение.Соответственно сработает только перехватчик определенный в самом конструкторе. В консольном окне появится такст "точка вышла за пределы круга: r=1.41421".

Если же строку 20 изменить так: point P1(0,0,20,20);

то сгенерировано будет второе исключение,сработает перехватчик из ст22, определенный во внешнем блоке,и в консольном окне появится фраза:

"Скорость точки слишком велика".

___________________________________________________________________________________

74.Шаблоны классов.

___________________________________________________________________________________

В данном разделе мы рассмотрим ряд только основных аспектов общего понятия "шаблоны классов".

Шаблоны классов предназначены для создания семейств родственных классов (шаблоны иногданазывают родовыми или параметризованными типами).

Общий синтаксис определения шаблона семейства классов таков:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]