Для ботаников и лентяев

aa1
aa2
aa3

ООП

1.Особенности языка С++.

-ключевая хар-ка инф технологии, кот является достоинством -С++ основан на С -богатая объектно-ориентир модель реализована на С С++ сложен,но эта слож-ть не синтаксиса а парадигмы программ-ния. Теории ООП основ на:1)моделир-ии слож сист,2)семантич сети, как раздел искусственного интеллекта. -высокая эффект-ть генерируемого кода (от С); компеляторы С++ дают наибол быстрый код из существующих яз прогр-ия. С++ самый быстрый и универсал язык. -независимость от платформы Платформа- совок-ть типов ОС и типов аппарат обеспечения (Linux,Windows) С++ реализ для большинства современ платформ,поэтому прога, напис-я в соотв со стандартами языка м/б перенесена с одной платформы на др.

2. Простейшая прога на С++.

1)#include -подключ заголовочный файл 2)using name space std-подключ пространство имён 3)int main (int ange, char*argv[]){- объявляет функцию main 4)cout<<””<

3. Расширения языка. Функции

Для разн ф-ий,вып-щих аналогич задачи удобно использ одинак имена. void print (char *); void print (int); void f (){ print (“test”); print (10);} В С++ ф-ии могут иметь одинак имена и разн набор формал пар-ров. Это будут обсалют разн ф-ии. При трансляции проги вып-ся поиск по соот-вию фактич пар-ров к формал пар-рам. Возвращ-е значение игнорируется. Для разреш-я неоднознач-тей использ правила: -жёсткое соотв-е типов -если первое не срабатывает, то ведётся поиск соотв-ия достигаемое за счёт стандарт преобраз-ий типов. Стандарт преобраз-я для чисел: char short int long double longdouble int fload double longdouble Стандарт преобраз-я для указателей: все могут быть переведены к void *. -если два не срабатывает, ведётся поиск соотв-ий достигаемых за счёт преобраз-ий пользователей. -если три не срабатывает, ведётся поиск соотв-ий достигаемых за счёт многоточия int print (char *,…); Аргументы по умолчанию в ф-иях. void print (int a, int base=10); void f(){ print (31); //31 print (31,10); //31 print (31,16); //1F print (31,2);} //11111 Приведён ф-ия print м/б вызвана разл спос-ми. Замечан: аргументы по умолчан должны нах в конце списка пар-ров. Встраиваемые ф-ии. inline double sqr (double x) {return x*x;} Ключев слово inline делает ф-ию встраиваемой,т.е. тело ф-ии будет вставлено в точку вызова. Это механизм оптимизации. Замечание:1)для этого чтобы встраивание было возможно объявление д/б видимо в точке вызова,2)дан директива носит рекомендат хар-ер для компелятора.

4.Расширения языка. Объявление переменных.

В С++ объявление может появиться в люб месте, где допустима инструкция: int f(){ cout <<”test”<

5. Расширения языка С. Операции new и delete.

Это средства работы с динамической памятью. New – выделение динам. памяти. Delete – освобождение. void f() { int *p; p=new int; *p=10; delete p; p=new int[5]; p[0]=100; delete[]p; } Обращение к ячейке памяти, на кот. ссылается указатель, наз. разыменование.

6. Расширения языка С. Ссылки.

Ссылки – синоним имени нек. переменной. Ссылки оч. полезны в кач-ве аргументов функции. Это аналог паскалевского Var/ viod f () { int i=1; int &a=i; a=10; //i==10 int b=a; } //b=10 viod f1 (int &t) { t*=100; } viod a () { f1(i); // i==1000; f1(i); // i==10000; f1(5); // Допустимо } В третьем вызове ф-ция f1 будет работать с копией константы, помещенную во временную ячейку.

7. Принцип инкапсуляции.

ООП базируется на неск. принципах, кот-е тесно связаны м/у собой. Инкапсуляция – объединение данных и кода в единую сущность. В рез-те объединения получ-ся объекты, поведение кот. описывают с пом-ю классов. Инкап-я данных означает, что данные явл. не глобальными - доступными всей программе, а локальными - доступными только малой ее части. Инкап-я автом-ки подразумевает защиту данных. Для этого в структуре class исп-ся спецификатор раздела private, содержащий данные и методы, доступные только для самого класса. Если данные и методы содержатся в разделе public, они доступны извне класса. Раздел protected содержит данные и методы, доступные из класса и любого его производного класса. Наличие последних позволяет говорить об иерархии классов, где есть классы - родители – шаблоны для создания классов - потомков. Объекты, полученные из описания класса, называют экземплярами этого класса. Класс – определяемый польз-ем тип, кот. предназначен для создания объекта. Объект – это переменная тап класс. Данные в объекте наз. полями, а выполняемые действия – методами.

8. Классы. Методы и поля.

Пр. Необх. запрограмм-ть работу с календарной датой. struct Date { int d; int m; int y; } Date d1; // d==0; m==0; y==0 void init_date (Date &d); – инициализ-т переем-ю типа Date каким-то значением. Инициал-я кажд. переменой данного типа в проге обязательна. Общепринято иниц-ть значение текущей даты. void add_date (Date &d, int dd) – увелич-ет указанную дату на опред. кол-во дней. void init_date (Date &d, int dd, int mm, int yy); { d.d=dd; d.m=mm; d.y=yy; } (Date &d) – процедурно-ориент. стиль обязывает в кач-ве пар-ра передавать модифицируемую структ. переменную. С++ для таких тип. случаев обеспеч-ет спец. синтаксис, кот. позволяет вкл-ть вункции в состав стр-ры. При этом упомянутый пар-р формир-ся автом-ки. struct Date { int d; int m; int y; void init_date (); void init_date (int dd, int mm, int yy); void add_date (int dd); }; viod Date::init_date (intdd, int mm, int yy) //1 { this->d=dd; //3 this->m=mm; this->y=yy; } void f () { Date d1,d2; d1. init_date (28,12,2005); //2 d2. init_date (5,10,2005); }; //1 – Date:: – этот префикс указывает, что данная ф-ия входит в состав конкр. стр-ры. //2 – вызов такой ф-ии делается путем обращения к соотв. структ. переменной. Др. способы вызова невозможны. //3 – когда произв-ся вызов, показанный на шаге (//2), ф-ция знает, для какой структ. переменной она была вызвана; указатель на эту переменную передается ч/з пар-р this. Пар-р this подставл-ся. в кажд. ф-ию такого типа автом-ки. Если сравнить данный пример с предыд., то видно, что алгоритм не поменялся. Часть работы взял на себя компилятор. Ф-ии такого вида наз-ся методами. Стр-ры такого вида наз. классами. Перем-ые соотв. типов наз. объектами. Замач: 1) :: – оператор разрешения области видимости; 2) Префиксы вида Date:: позволяют исп-ть в разных классах методы с одинаковыми прототипами. Как говорят, класс образует соотв. простр-во имен; 3) Для обращении к эл-м класса изнутра методов данного класса нет необх-ти исп-ть префикс this->. Он подставл. автом-ки. В реал. практике конструкция this-> исп-ся только для разрешения неоднозначности; 4) Методы класса м. вызывать изнутри методов др. классов. При этом вызов будет относиться к одному и тому же объекту.

10. Классы. Конструкторы

Класс- определяемый пользователем тип, кот предназначен для создания объекта. Объект – это переменная типа класс. Текущ реализ класса Date содеож проблему: перед исп-ием все объекты типа Date необх корректно инициализировать. Это осущ вызовом метода init_date Для иниц объектов исп-ся спец. методы, кот наз конструкторами- это метод , кот вызывается компилятором гарантировано при создании объекта, также гарантируется, что вызов будет единственным. Конструктор оформляется, как метод, имя кот совпадает с именем класса и никогда не возвращает зн-ние. Конструктор м/б перегружен, т е конструкторов м/б несколько. Class Date { Private // ….. Public //…..; Date( int dd, int mm, int yy) { // проверка на корректность If ( все ok) { d=dd; m = mm; y=yy;} else { // реакция на ошибку }} Если конструктор объявлен, то синтаксис С++ характеризует его вызов Если конструктор объявлен, то прогр-ст должен явно передать ему пар-ры. Конструктор без пар-ров явл-ся особыи случаем , кот наз конструктор по умолчанию. Его наличие позволяет описывать объекты данного класса как обычные переменные элементарных типов. Но важно: при наличии конструктора по умолчанию из объектов данного класса можно созд массивы . Если конструктора нет вообще , то массивы тоже можно создавать, но при этом компилятор не гарантирует правил иниц полей Class Date { Public //…. Date() //.. } Date date_array [10] Правило : настоятельно рекомендуется задавать для класса хотя бы один конструктор

9. Классы. Управление доступом внутри класса

Класс- определяемый пользователем тип, кот предназначен для создания объекта. Объект – это переменная типа класс. Постановка задачи: необх запрограм работу с календарной датой. Struct Date { Int d; Int m ; Int y ; }; Date d1; //d==0; m==0; y==0; Void init_(Date&,d) Void add__date(int dd) В этом примере имеется ряд недостатков, кот вытекают из след особенностей данной задачи: зн-ние полей нашей стр-ры тесно взаимосвязаны м/у собой. Недостаток реализации в том , что программист , использ нашу стр-ру Date может изменить содержимое полей, разрушив их согласованность, что сделает методы неработоспособными. Для решения проблемы использ управление доступом – спец синтаксис, кот позволяет “прятать” от пользователя кода некот Эл-ты класса , чтобы избежать сбоев в работе метода ПР: Class Date{ Private Int d: Int m: Int y ; Public Void init_date(); Void init_date (int dd); Void init_date( int dd, int mm, int yy); }; Class – ключев слово , для описания классов Private – модификатор доступа, указ, что данные эл-ты видимы только методом данного класса Public – модификатор доступа, кот указыв , что соотв эл-ты доступны всей проге Замечания Управление доступом: 1. позвол реал надежные, удобные в использ классы, кот эфф-ны как стоит. блоки проги 2.Позвол скрывать внутр реализацию класса. Тот факи , что доступ к объекту реализован ч/з методы, позвол менять внутренности класса без модификации польз кода 3.Позвол созд псевдополя. Напр,поле ДЕНЬ НЕДЕЛИ или поле ДЕНЬ НЕДЕЛИ В ГОДУ. Зн-ние этих полей вычисляется на основе реал полей.

11.Принцип композиции

Композиция – принцип ООП, ср-ва кот позволяют исп-ть в кач-ве полей класса зн-ия составных типов таких как классы и массивы. Получ в рез-те сложные типы наз композитными. Class Person { Private: Date First Day of Work; String name; String Last Name; Public: Person ( Date FDOW, string N , string LN); }; Пример показывает , что объекты м/б полями класса и что объекты можно передавать в ф-ии в кач-ве параметров Ограничение: Класс не может иметь поля собств типа, но допустимы указатели и ссылки на самого себя Замечание: Возможна ситуация, когда некий класс А исп-ет для определения полит и/или методовкласс В, а класс В исп-ет для описания полей и/или методов класс А. Такая взаимосвязь порождает ошибки неопред идентификатора. Дл разрешения такой неоднозначности язык С++ позволяет объявить класс без типа. Class B; Class A{ Bb; }; Class B { Aa; }; Недостатки : 1. Можно забыть вызвать метод clear (утечка памяти) ; либо вызвать clear дважды 2. неудобный способ адресации 3. неудобная обработка ошибки Проблема освобождения ресурса устраняется с помощью спец механизма , кот наз деструктором

12. Классы. Диструктор

Класс- определяемый пользователем тип, кот предназначен для создания объекта. Объект – это переменная типа класс. Дистрктор – метод кот гарантировано вызывается компилятором, когда истекает время жизни объекта (когда объект уничтожается) Синтаксис: Имя деструктора совпадает с именем класса, кот добавляется префикс в виде знака ~. Вместо метода clear реазлиз деструктор Class Intarray() { Public: ~IntArray(); }; IntArray :: ~IntArray(){ Delete[] P; } Void f () { Int Array a(100); a.set (10,256) count<

37. Таблица виртуальных методов

Необх знать как происх замещение алг-ма Вирт ф-ий Draw при исследовании. Замещение реализуется с пом алг-ма косв вызова. Если в баз классе объяв-ся вирт метод , то для каждого Вирт метода создается дополн невидимое поле типа указатель на ф-ию. В этом поле хранится адрес на фактич код виртуал метода. Вызов Вирт метода всегда выполн косвенно ч/з упамянутый указатель. Никакой дополн синтаксис не требуется, для Вирт методов компилятор всегда формирует косвенный вызов. Иниц-ия таких указ-лей выполняется на этапе конструирования объекта. Точнее после выполнения тела конструктора Shape Int x Int y - это создано Int color Void(*DrawP)() Circle Int x Int y - это унаследовано Int color Void (*DrawP)() Int R Void f() { Circle с (<…..>) } Вычисления указателя делается в 4 шага : 1. Выполняется конструктор базового класса 2. в указатель записывается адрес ф-ии DRAW для базового класса 3. выполняется конструктор производного класса 4. в указатель записывается адрес виртуальной метода произвоного класса В итоге получается правильн адрес. Этот алг-м модет работать для иерархии любой сложности. Также будет работать в сложных иерархиях создания объекта любого класса из цепочки .

13. Конструирование и уничтожение объектов.

Конструктор и деструктор в алгоритмах раб. в связке. Часто их код получ-ся зеркальным. 1. Именованный лок. объект – объектн. перемен., кот опред-на внутри ф-ции. Такой объект созд-ся, когда ф-ция запуск-ся на выполнение и уничтожается, когда ф-ция заверш-ся. void f() { intArray a(1000); //… } В более общем случае лок. объект созда-ся, когда встреч-ся его объявление, и уничтожается, когда происх. выход из блока, кот. это объявлен. содержит. На практике это позвол. исп-ть объектные перемен. как обычные перемен. элементарных типов, что очень полезно для массивов, списков и т.д. 2. Объекты динамич. памяти. Созд-ся операцией new (с вызовом констр-ра) и уничтож-ся операцией delete (вызов дестр-ра) На практике исп-ся если програм-сту необх. полностью контрол-ть процесс создания и уничтожения объектов. int array *p void g() { p=new intArray (100); } void t() { delete p; } int main() { g(); t(); return 0; } 3. Внестатический объект поля – объект вход-щий в состав композитного класса. Таких объектов м.б. нес-ко. Для этих полей гарантир-ся, что конструкторы будут вызваны до конструкторов главного класса в порядке вписания полей. Деструкторы - в обратном порядке. class club { private: string name; intArray rooms; date founded; }; Конструктор: name, rooms, founded, club Деструктор: club, founded, rooms, name 4. Объект как элем-т массива. Эл-ты массива созд-ся и уничтож-ся вместе с массивом. При этом необходим конструктор по умолчанию. 5. Глоб-ные. объекты и статические объекты модулей. Статич. элем-т модуля: время жизни – все время работы программы, обл. видимости – модуль, где описан элем-т с точки описания и до конца модуля. Упомянутые объекты(глоб. и стат.) созд-ся до начала работы ф-ции main, уничтож-ся после корректного завершения проги. #include using namespace std; //… -определение класса IntArray intArray a(10); int main() { cout << “Test” << endl; cout << a.getlen() << endl; cout << “End” << endl; return 0; } На практ. этот механизм позвол. корректно иниц-ть глоб. окружение, создавая необх. среду выполнения. Реально этим механизмом польз-ся прих-ся редко, т.к. в большинстве случаев он недостаточно гибок. Принцип. проблема здесь – невозможность гарант-ть порядок вызова конструкторов.

14. Особенности конструирования композитных классов.

class club { private: IntArray rooms; string name; date founded; public: club(int cut_rooms, string name, date founded); }; 1. Данный пар-р предназначен для указания числа комнат в клубе. Он исп-ся как длина массива rooms. 2. Имя клуба передается в виде объекта стандартной библиотеки. 3. Дата основания клуба. Класс Date реализован нами. club :: club(int cut_rooms, string name, date founded): rooms(cut_rooms), //1 name(nname), //2 founded(founde.get(), founded.getm(), founded.gety()) //3 { //… } Пример показ-ет синтаксис констр-ра композитного класса с передачей пар-ров конструкторам полей. Порядок указания полей в данном описании не важен, т.к. компилятор все равно произведет вызовы. Рассмотрим нек. ситуации: 1. одно или нес-ко значений элементарн. типов перед-ся конструктору поля. 2. для иниц-ции объектного поля пред-ся объект тукого же типа, при этом класс поля содержит констр-р копирования. В примере исп-ся стандартн. класс string, кот. соотв-щий констр-р содержит, поэтому данный вызов возможен. 3. конструирование объекта, у кот. нет констр-ра копирования: founded – поле класса (founded) – параметр констр-ра Эта строка будет раб. корректно, т.к. в позиции1 мы можем обр-ся только к полю, к параметрам обр-ся нельзя. Позиция 2 считается телом конструктора; в теле метода имена полей перекрываются именами пар-ров, т.е. работает след. правило: class A { private: int c; public: void f(int c); }; void f(int c) { c=10; //1 this -> c=100; //2 } 1. Опред-ние поля перек-ся опр-ем пар-ра. Здесь будет модифицирован пар-р метода. 2. Пример разрешения данной неоднозначности. Обращение к элем-там класса через this позволяет обойти данную проблему. Такой механизм часто исп-ся на практике в след. виде: this -> c=c; Превое с-поле, второе с-параметр.

15. Перегрузка операторов. Общие сведен.

Принципы перегрузки операторов. void f() { //… x=a+b*c; -инструкция } //=,+,* – операторы В базовом С++ такая инструкция будет раб. для элементарных типов, таких как int и double. Хотелось бы, чтобы этот мех-изм работал и для типов польз-ля, таких как комплексное число, матрицы. Это возможно и основано на след. принципах: 1. соотв. тип должен быть классом. 2. перегружать можно только встроен. операторы, новый оператор внести нельзя. 3. нельзя измен. приоритет и число арг-тов операторов. 4. можно измен. семантику оператора, т.е будучи перегруженным оператор может делать все, что угодно, например, можно перегрузить оператор * для мн-ств так, что он будет выполнять их пересечение. 5. перегрузка основана на определении методов и/или ф-ций с фиксир. именами и списком пар-ров, т.е. вместо стандартн. действий компилятор производит вызов польз. ф-ции или метода. 6. в механизме перегрузки сущ-ет много особенностей и неоднозначностей. Перегружать можно: а) арифм операции: *,/,+,-,–унарный минус. б) побитовые опер-ры: ^,<<,>>,~,&,|. в) опер-ры сравнения: !,!=,= =,<=,<,>,>=. г) инкремент, декремент ++,-- различаются префиксная и постфиксная форма. д) нек-ые др.: =,*=,+=,/=,… присваивания

-> выбор элем-та через указател

[] индексации

() вызов ф-ции

(some_type) преобраз-ие типа

new, delete Перегрузка опер-ров – очень мощн. мех-зм, кот. позвол-ет получать типы, удобные в применении. При этом то, что можно перегружать нек-ые экзотическ. опер-ры, вроде преобразования типа или присваи-ия, позвол. тонко управлять принципами языка. Под польз. типами поним-ся все типы, созданные с помощью struct и class, в том числе входящие в состав стандартн. библ. Синтаксис С++ стантартн. библ. и то, что создано програмером. С помощью компилятора С++ можно полностью отказ. от стандартн. библ. либо замен. её своей. Это позвол. разраб-ть ОС и драйвера.

16. Перегрузка операторов. Операторные методы и функции.

Перегр. опер-ров основана на объявлении ф-ций или методов спец. вида. Ключевой признак: слово operator в названии метода или ф-ции, за кот. след. знак соотв. опер-ра. <> operator+ (<>) Эти ф-ции/методы программ-ются по опердел. правилам: правилу необх. следовать, чтобы достичь необх. поведения. Привязка ф-ций к определ. классам достиг-ся через типы параметра. Общие закономерности: 1. есть опер-ры с одним арг-ом, есть–с двумя. 2. многие опер-ры могут реализовать двумя способами: как ф-ция, как метод. 3. перегружаемый оператор относится к конкретн. классу. Для опер-ра с одним арг. – опер-р как метод: class A { public: A operator @(); }; void f1() { A x,y; x=@y; //x=y.operator @() } Оператор с двумя арг-ами как ф-ция: class A { }; A operator @(a arg); void f2() { A x,y; x=@y; //x=operator @(y) } Оператор с двумя арг-ами как метод: class A { public: A operator @(a arg); }; void f3() { A x,y,z; x=y@z; //x=y.operator @(z) } Оператор с двумя арг-ами как ф-ция: class A {}; A operator @(A arg1, A arg2); void f4() { A x,y,z; x=y@z; //x=operator @(y,z) }

17. Тип комплексных чисел. Базовые операторы

Создадим класс комплексных чисел: class complex { private: double re, im; public: complex (double r=0, double i=0); double getRe() (return re;} double getIm() {return im;} void setRe (double r) {re=r;} void setIm (double i) {im=i;} }; **** complex::complex (double r, double i) { re=r; im=i; } void f() { complex a; //a.re=0, a.im=0 complex b(10.3); //b.re=10.3, b.im=0 complex c(11.4,20.5) //c.re=11.4, c.im=20.5 cout<

18. Тип комплексных чисел. Инициализация

Инициализация – это действие, которое задает начальное значение для какой-либо переменной. В простейшем случае инициализация выполняется при определении переменных. В инициализации есть тонкости:

1) Глобальные и статические переменные элементарных типов инициализируются нулями. 2) Остальные переменные элементарных типов заполняются «мусором» 3) Кроме объявлений инициализация выполняется при передаче параметров в ф-ии и методы, при выделении динамической памяти, при создании массивов. 4) Объектные переменные инициализируются с помощью вызова конструктора. 5) Если класс не содержит конструктор, то компилятор формирует конструктор автоматически. Этот конструктор гарантирует вызов других конструкторов (в случаях композиции и наследования) и выполняет инициализацию полей элементарных типов в соответствии с п. 1 и 2. В перегрузке операторов инициализация важна, т.к. в некоторых случаях она позволяет упростить код за счет использования умолчаний. При необходимости умолчание можно предопределить, что в некоторых случаях позволяет оптимизировать код. При необходимости можно задать собственное правило инициализации, которое отсутствует в С++. Эти доп. правила и связанные с ними синтаксические механизмы назыв. преобразованием типа, заданного пользователем. В синтаксисе С++ преобразованием типов можно управлять 2-мя способами: 1) задавая конструктор опред. вида 2) перегрузка собств. оператора. 1-й способ: class A { public: A(B x); }; Если в классе А определен конструктор, которому можно передать в качестве единственного параметра значение типа В, то наличие такого конструктора обеспечивает автоматическое преобразование типа из В в А т.е. если такой конструктор есть, то в любой точке, где необходимо значение типа А можно будет использовать значение типа В, и компилятор будет автоматически выполнять преобразования, формируя вызов соотв. конструктора complex (double r=0, double i=0) Конструктор такого типа м.б. вызван с единственным параметром типа double или типа, совместимого с double. Это значит, что введя такой конструктор, мы определили автоматическое преобразование из double или совместимого с ним типа в complex void f() {

1) complex a; //a.re=0 a.im=0

2) complex b(10.3); // b.re=10.3 b.im=0

3) complex c(10.3,11.4); // c.re=10.3 c.im=11.4

4) complex d=100.1; // d.re=100.1 d.im=0

5) a=10.5; // a=complex(10.5)

6) b=d+10; // b=d+complex(10) }

4) выполняется автоматич. преобразование типа из double в cjmplex т.к. у нас есть конструктор, который можно вызвать с одним пар-ом типа double

5) по синтаксису строка состоит из 2-х шагов: - преобразование double в complex ч/з вызов конструктора. Результат записыв. во временный объект - из времен. объекта результат копируется в переменную а

6) аналогично 5), т.е. константа 10 записыв-ся во времен. объект и затем складывается с переменной в как комплексная. Здесь утеряна оптимальность, т.к. фактически будет выполняться 2 сложения вместо одного.

19. Тип комплексных чисел. Операции сравнения и смешанная арифметика.

Операторы сравнения м.б. реализованы 2-мя способами: в виде ф-ий и в виде методов. Рекомендуется реализовывать в виде ф-ий. bool operator==(complex m, complex n) { return m.getRe()==n.getRe() && m.getIm()== n.getIm(); } void t() { complex var1 (100,4); var2 (5,6); cout<<(var1==var2)<

20. Особенности преобразования типов при перегрузке операторов.

Использовать конструктор для преобразования типа удобно, но с помощью этого механизма нельзя сделать все что хочется: 1) нельзя выполнить преобразование из типа пользователя во встроенный тип 2) нельзя выполнять преобразование из нового класса в ранее определенный класс (без модификации объявления старого класса) В стандартных ОО потоках есть несколько способов обработки ошибок. Например: а) обработка исключений б) методы, возвращающие состояние потока Выполняется действие, далее вызывается метод из потока, который позволяет проверить было ли успешным последнее выполненное действие #include void k() { ifstream f; f.open (“c:\test.txt”); if (f. <>) { //ошибка } } в) сообщение об ошибке ч/з перегрузку операторов. Поток может использоваться как целое число, т.е в нем определен оператор преобразования из типа потока в целое. Т.е. возможно следующее: #include void k() { ifstream f; f.open (“c:\test.txt”); int res=f; if (!res) { //ошибка } } Преобразование такого вида записывается в целочисленную переменную, результат выполнения последнего действия в виде лог. значения, т.е. успех или неуспех. #include void k() { ifstream f; if (!f.open (“c:\test.txt”)) { //ошибка } } #include void k() { int a; if (! (cin>>a)) { //ошибка } } class istream { //…… public: (); 3 1 2 //…………… } 1- ключевое слово 2 – тип, в который происходит преобразование 3 – возвращаемое значение, всегда отсутствует, оно совпадает с тем типом, в который происх. преобразование

23. Тип строк. Копиров. и присваивание.

Копир. осущ-ся , когда новый объект созд. из уже сущ-х объектов. Пример: 1) Объявление объекта; 2) Передача объекта в ф-цию в качестве пар-ра по значению. Присваив-это копир. уже сущ-го объекта. Компилятор отслежив. ситуацию копиров. и присваив. объекта, т.к. объекты представл. сложные стр-ры данных. Для выполн. этих действий формир-ся спец. код. По умолч. использ- ся алгоритм поэлементного копиров./присваив. одного объекта другому. void f(1){ complex a (10,20) ; complex b=a; // b.re=a.re, b.im=a.im complex c(100, 200) ; c=a ; //c.re=a.re, s.im=a.im (старое содержимое с будет уничтожено) } Для больших объектов класса SuperString этот подход будет работать неверно. void f() { SuperString S( “test”) ; SuperString S1=S //* } //* S1.p=S.p; S1.len-S.len Освобождение памяти будет происх. дважды для одного и того же блока памяти. void f2(){ SuperString S(“test”); SuperString S1 (“test2”); S1=S; //* } //* S1.p=S.p; S1.len=S.len; Кроме проблемы второго освоб. блока памяти мы здесь получаем утечку ресурса. Для решения двух проблем применяют спец. способы: 1.Копирующий конструктор позволяет управлять копированием объекта 2.Перегруженный оператор присваив. позволяет управлять процессом присваив. одного объекта другому. Эти способы не явл-ся расшир. синтаксиса С++ Пр: копирование SuperString //*** SuperString (SuperString & w) { len=w.len; if (len) { p=new char [len]; strcpyn (p,w.p,len);} else p=0;} Будучи 1 раз созданным копирующ. конструктор будет автомат. вызван компилят. везде, где это необходимо. Напр, при передаче объекта данного типа в качестве пар-ра по значению. //***SuperString & operator=(SuperString & t) if (len) delete [] p; len=t.len ; if (len){ p=new char[len]; strcpyn (p,t.p, len);} else p=0; return *this Замечание: 1. Оператор присв. всегда возвр. ссылку на самого себя, т.к. таков синтаксис оператора присв. в С++, кот. позволяет исп-ть возвращ. значение для дальнейшего построения выражения. void f(){ SuperString r,r1= “test” , r3= “t3”; r=(r1+= “test”)+r3; } // r = = “test test t3” 2. Копир. конструктор- оператор присв. исп-ся обычн. методом. Такое объявление приведет к тому, что внешн. по отнош. к классу код не сможет выполнить действие копиров. для объектов данного класса. 3. Если класс будет инкапсул. функцион-е ресурса, кот по своей природе нельзя копиров. и присваивать. Пример, сетевое соединение. Правило ООП№4: Рекомендуется большие объекты всегда передавать по ссылке. Правило ООП№5: Рекоменд. для сложных классов определять средства копиров. и присваив. или как минимум блокировать эти механизмы, задав соотв. методы с пустыми телами в секции private.

24. Классы. Дружественные функции и классы.

Необх. операторы сравнения, напр, оператор сравн. равенства. Оператор сравн. должен иметь возможн. добраться до содержимого строк. Возможны 2 варианта решения: 1.Реализ. оператор как метод класса //*** boll operator = =(SuperString & S) ; /-/-/-/--/-/-/-/-/-/ boll SuperSuper::operator= =(SuperString &S){ if (len!= s.len) return false; return strcmpn (p,s.p,len)= = 0;} 2. оператор сравнения м/б реализ. как внешняя ф-ция, но для того, чтобы написать код необх. доступ к полям соотв. объекта. Для того, чтобы ограничение на видимость полей для внешних ф-ций ис-ся дружественность. Ф-ция друг может работать без ограничения при этом правило сокрытия не нарушается, т.к. по синтаксису класс назначает (выбирает) друзей. Использ. ключевое слово friend //*** friend boll operator = = (SuperString $ S1, SuperString &S2); /-/-/-/-/-/-/--/-/ boll operator = = (SuperString & S1, SuperString & S2); if (S1.len != S2.len) return false; return strcmpn (S1.p, S2.p, S1.len)= =0; Понятие дружественности в более общем случае применяется так же к методам класса. Cless ListInterator { publiс : int *next () }; /-/-/-/-/-/-/ class List { friend ListInterator:: next (); }; /-/-/-/-/-- class List { friend class ListInterator; }

21. Перегрузка операт. Большие объекты.

В данном контексте большим объектом наз. объект композитного класса, функц-ость, кот связана с выделением и освобождением ресурсов. а) Особенности работы с большими объектами: Пр: класс строк. # include using namespace std; string S; Наши строки должны уметь: 1.хранить большое кол-во символов (желательно неогранич.) 2.операция конкатенация(слияние строк) 3.оператор индексации с возможностью модификации символа по индексу. class SuperString void f(){ SuperString S1,S2,S3 ; S3=S2+S1; cout<=0 && i

26. Производные классы.

class Shape { public: int color; int x,y; } class Circle:public Shape { public: int R; } class Triangle:public Shape { public: int dx[3],dy[3]; } //:public Shape – указание базового класса В данном примере Circle и Triangle наследуются из Shape, т.е. эти классы будут заимствовать все поля класса Shape. Это приведет к следующему предст. объектов в памяти: Shape a; Circle b; Triangle c; x x x y y y R dx[0] dx[1] dx[2] dy[0] dy[1] dy[2] Одна строка добавляет в класс 3 поля. Если класс Shape будет модифицирован в нем появится еще одно поле (цвет заполнения), то после перетрансляции программы во всех объектах классов-наследников появится это поле. Из рисунка видно, что все наследники класса Shape имеют одинаково организованные участки памяти. Это позволяет писать код, который, модифицируя общие поля, будет приводить к одинаковым результатам для всех объектов классов-наследников. Замечание: такие функции должны получать соотв. объекты в виде указателей или ссылок. void Move(Shape &f, int off_x, int off_y) { f.x+=off_x; f.y+=off_y; } void f(){ Shape s; Move(s,10,15); Circle c; Move(c,-100,200); } Подобный механизм позволяет создавать методы в базовых классах, которые могут наследоваться производными классами. class Shape{ public: int color; int x,y; void Move(int off_x, int off_y); } void Shape::Move(int off_x, int off_y) { x+=off_x; y+=off_y; } void f1(){ shape s; s.Move(20,10); Circle c; c.Move(300,2); } Этот механизм очень удобен, т.к. экономит время разработки сложной программы и позволяет создавать программу с более легкой структурой. Но здесь есть сложности: а) необходимо правильно конструировать иерархию классов. Иногда это требует оригинальных решений; б) рассмотренные ср-ва наследования могут не все из очевидных вещей. Например, возникает трудность с методом рисования.

27. Конструкторы и деструкторы при наследовании.

В связке “базовый-производный” можно использовать методы. Это позволяет исп. правила сокрытия данных. Но возникает вопрос с инициализацией полей базового класса. Возникает вопрос передачи параметров в конструктор базового класса. На самом деле, ситуация немного сложнее. Компилятор C++ гарантирует, что, если у базового класса есть хотя бы один конструктор, то он будет вызван. Возможны 2 ситуации: 1) если у базового класса есть констр. по-умолчанию, и констр. произв. класса не пытается вызвать констр. базового класса, то соответствующий вызов произойдет автоматически; 2) если констр. по-умолчанию нет, то необходимо обязательно сформировать вызов констр. базового класса и обеспечить передачу необх. параметров. class Shape { public: int color; int x,y; Shape (int color_,int x_,int y_); }; Shape::Shape(int color_,int x_,inty_){ x=x_; y=y_; color=color_; } class Circle:public Shape { public: int R; Circle(int x_,int y_,int color_,int R_); } Circle::Circle(int x_,int y_,int color_,int R_) { R=R_; } Подход, использованный в приведенном примере, встречается чаще всего. Констр. произв. класса включает все параметры базового класса + все свои. Синтаксис С++ допускает свободу: class Circle:public Shape{ public: int R; Circle(int color_,int R_); }; Circle::Circle(int color_,int R_):Shape(color_,0,0) { R=R_; } Ситуация с деструкторами проще, т.к. они вызываются автоматически без вмешательства программиста. Но есть некоторые тонкости с динамическими объектами. Конструкторы и деструкторы вызываются в порядке следования классов в иерархии, т.е. конструкторы – от базовых к производным, деструкторы – в обратном порядке. Но полагаться на эту последовательность нельзя, т.к. та нее могут повлиять соответствующие методы полей композитных классов, входящих в состав иерархии. К счастью побочные эффекты появляются, только если программируется что-то системное. 28. Иерархия классов. Производный класс в свою очередь может быть базовым. У класса может быть несколько базовых, т.е. С++ допускает множественное наследование. Пример иерархия служащих. стрелки – от базового к производному. class Temporary{}; class Employce{}; class Secretary: public Employce{}; class Manager: public Employce{}; class Director: public Manager{}; class TSec: public Temporary, public Secretary{}; class Consultant: public Temporary, public Manager{};

29. Управление доступом при наследовании.

Для управл. доступом к проектир. в ЯП С++ вводится дополнит. модификатор protected. Соотв. эл-ты класса будут доступны методом самого класса и методов произв. классов. Так же в наследовании произв. класса дополнит. ограничить доступ к эл-там базового класса, используя идентификатор доступа к наследованию. class Circle: public Shape{}; (1) (2) public protected private private private private private protected protected protected private public protected protected private (1) – модификатор наследования. (2) – модификатор эл-тов в классе. Правило построения таблицы из 2-х модификаторов выбирается более жёсткий.

30. Стратегия обработки ошибок.

Рассм. ситуацию: Программа состоит из 2-х концептуальных частей: 1) Код написанный программистом. 2) Библиотека, кот. программист использует. Библиотеки работают с файлами. Возможна ситуация: Программа не смогла open файл. Возникает ситуация когда нарушается нормальная работа алгоритма. В более общем случае библиотечный код обнаруживает ошибочную ситуацию, но не знает как на неё реагировать. Пользоват. подход не смог обнаружить ошибочную ситуацию, но знает какой д/б реакция. 1) Возвращ. значение 2) Обознач. ошибку 3) Заверш. прогу 4) Возврат. допустимое значение и оставить прогу в неопред. состоянии. 5) Вызвать функию кот. выполняет пользоват. обработку ошибок. Подход №3 делает прогу ненадёжной. В ситуации с обратным вызовом м/б 2 варианта: 1) одна ф-ция на все вызовы. В этом случае алгоритм ф-ции будет запутан. 2) на каждый вызов делает свою ф-цию. Каждый из разработанных подходов обладает своими достоинств. и недостатками. Но все они применяются в реальной практике.

31. Исключения. Синтаксис.

Исключения это такая же инструкция как For, While, Swith. Обработка исключений использ. 3 вида инструкций: 1) помечает (задаёт) участок кода в кот. могут возникать в значит. ситуациях - Try{} 2) Catch – задаёт обработчик исключения. Блок кода кот. получает управление, if возникла исключительная ситуация. Catch (< >) {} 3) throw – ф-ция формир. Исключительную ситуацию. Типовой алгоритм обработки исключений: void f() { try { // очень много действий ….. // ошибка throw <перем. типа А> ….. // очень много действий } catch (<тип А>e) { // реакция на ошибку } } 1) try и catch всегда использ. совместно. Исключ., кот. произошло в некот. ,локе try передаётся в соотв. блок catch. 2) Передача управления осущ. инструкцией throw. Алгоритм внутри try м/б сколь угодно сложным. Throw прерывает выполнение этого алгоритма и следом за ней выполняет next конструкция блока catch. Throw передаёт управление на Catch. 3) Внутри блока Try инструкции Throw м/б любое необх. кол-во. 4) В кач-ве параметра Throw передаётся перем. некотор. типа, при этом для корректной работы тип перем. Throw и тип пар-ра Catch должны совпадать. Тип м/б любым, в том числе составным. Содержимое перем. Из Throw copy в Catch. 5) if Throw вызвано за пределами блока Try и типы в инструкциях не совпадают, то программа завершается. 6) Внутри блока Try могут вызываться ф-ции, кот. содержат инструкцию Throw. В этом случае ф-ция м/б прервана при любой степени сложности вызова, и управление передаётся на Catch. 7) Блоки Try и Catch вкладывать друг в друга. Вложение может отрабатыв. как for обычных блоков и по алгоритму программы.

32. Исключения. Передача информации об ошибке.

Пример: Преобразование целого в символ с контрольным диапазоном при помощи исключений. unsigned char to_char (int i) { if (i<0 || i>255) throw i; Return (unsigned char) i; { int main() { try { to_char(100); to_char(-20); } catch (int e) { cout << e << endl; } return 0; } Пример показывает, что исключения могут использ. для прерывания функций. Так же показана возможность передачи info об ошибке. Catch получает ошибочное значение целого. В кач-ве типа исключения использ. элементарный тип int. Это не всегда удобно, т.к. try и catch могут быть вложены друг в друга и может совпасть тип обработчика. Поэтому рекомендуется использовать классы спец. созданные для обработки ошибок. Класс исключения улучшает читательность кода, так же в класс можно добавить дополнит. функциональность. Например добавление в класс строкового поля содерж. имя той функции, которое сформир. ошибку.

36. Абстрактные классы.

Shape представляет некую вымышленную сущность. Эту абстрактную сущность мы ввели только для удобства программирования, но текущая реализация класса Shape рассм. эту сущность как нечто реальное. Можно создать объекты класса Shape, соотв. объекты можно нарисовать. В С++ существует подобный механизм, кот. наз. абстрактным классом. По синтаксису абстр. класс – это класс, содержащий хотя бы 1 абстр. метод. class Shape { protected: int x; int y; int color; public: Shape (<>); void Draw()=0; - объявление абстр. метода } Абстр. метод наз. чисто виртуальной ф-цией. С практич. т.зр. бывают 2 варианта абстр. классов, предназначенные для построения иерархии: 1) Пример класс Shape. Такие классы явл. абстрактными, содержат некоторую абстр. ф-циональность в виде полей и методов. 2) Интерфейсные классы – содерж. плагинов – динамически загружаемые модули. Пример плагин TotalCommandera В данном случае наследование используется как способ построения межпрограммного интерфейса. Приложение в плагин может иметь весьма сложную ф-циональность и исп-ть управляющую программу как часть пользовательского интерфейса. Типичный интерфейсный класс не содержит никакой конкретики, только объявление абстрактных методов. Плагин представляет реализацию такого класса как правило в виде dll.

35. Виртуальные методы (виртуальные функции).

Термин вирт. ф-ции встречаются наиболее часто, хотя правильнее было бы говорить виртуальные методы. Вирт. ф-ция – это метод, кот. обьявлен в базовом классе как виртуальный и кот. м/б замещён в производном классе. Идея полиморфизма базируется на том что алгоритмы методов базовых классов могут в процессе наследования заменяться алг-ми производных классов, используя механизмы косвенного вызова. Этот процесс наз. замещением. class Shape{ public: virtual void Draw(); }; class Circle: public Shape { public: virtual void Draw(); }; Виртуальный метод определяется с help ключевого слова virtual. Если в базовом классе объявлен виртуальный метод с некоторым прототипом, то методы с таким же прототипом во всех классах наследниках тоже будут виртуальными. При этом слово virtual указывать необязательно. Если метод объявлен как виртуальный, то благодаря замещению его алгоритм работы будет определяться фактическим типом объёкта. Говоря простым языком будет вызываться «правильный» алгоритм вне зависимости от того как производится вызов. void DrawList (int cnt, Shape **mas){ for (int i=0; iDraw(); } Благодаря полиморфизму в DrawList будет вызываться правильный Draw.

34. Принцип полиморфизма.

Идея полиморфизма базируется на том что алгоритмы методов базовых классов могут в процессе наследования заменяться алг-ми производных классов, используя механизмы косвенного вызова. Этот процесс наз. замещением. class Shape{ public: virtual void Draw(); }; class Circle: public Shape { public: virtual void Draw(); };

33. Группировка и перехват исключений.

Возможно не то что надо!!! Рассм. ф-цию, кот. пытается открыть файл. В алг-ме ф-ции возможны 2 принципиально разных ислючительных ситуации: 1) file not open – сбой системы 2) неправильные пар-ры у ф-ции – ошибка логики программы Целесообразно их обрабатывать по-разному. Для каждой ситуации необходим свой класс ошибки. class ParamError; class FIOError; Синтаксис исключений позволяет полноценно исп-ть несколько типов исключений в составе одного алг-ма, т.е. в примере ф-ция может содержать несколько throw разных типов, а блок try – несколько секций catch. int fopen (char *fn, char *mod){ //… if (fn==0) throw ParamError (<..>) //… throw FIOError (<..>) } void m() { try{ fopen (<..>) } catch (ParamError e){ } catch (FIOError e){ } } Необязательно указывать оба catch в одном и том же блоке try. Код может обрабатывать только необходимые исключения, а др. будут передаваться дальше по алг-му. Напр. для данного случая FIOError целесообр. обр. как можно раньше, а ParamError передавать в самый внешний блок алг-ма (напр. в ф-цию main).

bbb
bb1
bb2
bb3
1

На главную

1
1

вверх

2
Используются технологии uCoz