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

Программирование на языке высокого уровня Лекции 09.03.01

.pdf
Скачиваний:
29
Добавлен:
10.01.2021
Размер:
1.1 Mб
Скачать

доступ к элементам данных с помощью операций . или ->, применяемых соответственно к представителю или указателю на представитель класса.

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

class ttt { int x,y; public:

void Setxy (int xx, int yy) {x=xx;y=yy;}

void Getxy (int &xx, int &yy) {xx=x;yy=y;}

};

void main(void)

{ttt o1;

ttt *o2=&o1;

o1.Setxy(10,10); int st1,st2; o2->Getxy(st1,st2);

}

Темы 35-38. Специальные функции-элементы класса.

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

конструктор - инициализирует представитель класса;

конструктор копии - инициализирует новый представитель класса, используя значение уже существующего объекта;

операция присваивания - присваивает содержимое представителя другому представителю класса;

деструктор - производит очистку представителей класса;

операция new - выделяет память для динамически создаваемого представителя класса;

операция delete - освобождает динамическую память, занятую представителем класса;

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

Конструктор является функцией-элементом с тем же именем, что

и ее класс. Он вызывается компилятором всегда, когда создается представитель класса. Если никаких конструкторов не определено,

Программирование на языке высокого уровня 09.03.01

41

то компилятор генерирует конструктор по умолчанию (не имеющий параметров). Для конструктора не указывается возвращаемый тип и он не может возвращать значение. Элементы-данные класса обычно инициализируются в теле конструктора. Однако, определение конструктора может также содержать список инициализации элементов. Список инициализации элементов отделяется двоеточием от заголовка определения функции и содержит элементы данных, разделенные запятыми.

class qqq { int x,y;

public: qqq(int xx, int yy):x(xx),y(yy)

{}

};

Конструктор копии является конструктором специального вида: он воспринимает в качестве аргумента ссылку на объект класса. Ссылка передается всякий раз, когда новый объект инициализируется значениями уже существующего. Если конструктор копии не предусмотрен, то компилятор генерирует конструктор копии по умолчанию. Такой конструктор создает буквальную копию объекта. Если конструктор копии объявлен в разделе private, то компилятор будет генерировать сообщение об ошибке, если кто-то попытается создать копию объекта.

class proverka { int a,b;

public:

proverka (int aa, int bb):a(aa),b(bb)

{}

proverka (const proverka &zaval)

{a=zaval.a;

b=zaval.b;

}

};

void main() { proverka q(10,10); proverka t=q;

}

Операция присваивания является функцией-элементом с именем operator=, которая принимает в качестве своего единственного параметра ссылку или константную ссылку на объект данного класса. Операция присваивания вызывается компилятором, когда объекту присваивается значение другого объекта. Компилятор генерирует присваивание по умолчанию, если операция присваивания не определена явно. Присваивание по умолчанию производит поразрядное копирование.

Программирование на языке высокого уровня 09.03.01

42

class proverka { int a,b;

public:

proverka (int aa, int bb):a(aa),b(bb)

{}

proverka& operator= (const proverka &); };

proverka& proverka:: operator= (const proverka &zaval) { a=zaval.a;

b=zaval.b; return *this;

}

void main()

{proverka q (10,10); proverka w (7,7);

q=w;

}

Конструктор копии отличается от операции присваивания тем, что операция присваивания вызывается, если изменяется содержимое

существующего

объекта, а конструктор копии вызывается, если

новый объект инициализируется значениями другого объекта.

Указатель

this является скрытым параметром, который

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

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

(~). Деструктор не может иметь аргументов, не может возвращать значение и не наследуется.

Объекты класса могут быть преобразованы в другие типы (или созданы из других типов) с помощью операций приведения типа или конструкторов преобразования.

Если конструктор некоторого класса имеет только один параметр, то объект типа, совпадающего с типом этого параметра, при помощи такого конструктора можно неявно преобразовать в объект класса.

class stroka { char *str;

public:

stroka (const char*);

...

};

Программирование на языке высокого уровня 09.03.01

43

void main() {

stroka msg= "Экзамен не сдать.";

...

}

При необходимости программист может запретить вызов конструктора с одним параметром в качестве конструктора преобразования. Для этого в объявлении конструктора должно быть добавлено ключевое слово explicit. Такой конструктор можно будет вызывать только обычным (явным) образом:

stroka msg("Экзамен не сдать.");

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

operator имя_типа();

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

# include <stdio.h> class treyg {

int s1,s2,s3;

public: treyg(int a, int b, int c): s1(a),s2(b),s3(с) { }

operator int ();

};

treyg :: operator int()

{return (s1+s2+s3); } void main()

{int w,e,r;

printf("Введи три числа\n"); scanf("%d,%d,%d",&w,&e,&r); treyg ABC(w,e,r); printf("perimetr=%d",(int)ABC);

}

Тема 39. Перегрузка операций.

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

Программирование на языке высокого уровня 09.03.01

44

поставки С++ используют перегруженные операции. В частности, так сделано в классе потоков iostream.h:

cout<<"Долой двоечников"<<endl; cin>>x;

Функции-операции, реализующие перегрузку операций, имеют

вид

operator операция (операнды);

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

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

class proverka { int a,b;

public:

proverka (int aa, int bb):a(aa),b(bb)

{}

proverka operator + (const proverka &); };

proverka proverka:: operator + (const proverka &zaval) { proverka marazm(0,0);

marazm.a=a+zaval.a;

marazm.b=b+zaval.b; return marazm;

}

void main()

{proverka q(10,10); proverka w(7,7);

proverka summa(0,0); summa=q+w;

}

Очень важно, что если для представителей класса определен оператор +, то это не значит, что можно пользоваться оператором +=. Если есть желание или необходимость пользоваться таким оператором, то его нужно перегружать отдельно.

Программирование на языке высокого уровня 09.03.01

45

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

proverka proverka:: operator + (int arg)

{ proverka marazm(0,0); marazm.a=a+arg; marazm.b=b+arg; return marazm;

}

void main()

{proverka q(10,10); proverka w(7,7);

proverka summa(0,0),summa1(0,0); summa=q+w;

summa1=q+5;

}

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

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

Чтобы узнать, какая именно исключительная ситуация возникает при выполнении той или иной функции, и возникает ли она вообще,

необходимо воспользоваться системой поиска.

 

Блок

операторов,

который

будет

контролироваться

программистом, берется в фигурные скобки, а перед этим блоком указывается ключевое слово try. Сразу за проверяемым блоком указывается ключевое слово catch, после которого в круглых скобках указывается const Eимя_класса &, а затем в отдельном логическом блоке следует обработчик исключительной ситуации.

С++ позволяет применять несколько catch-блоков, каждый из которых определяет способ обработки своей конкретной ситуации.

try {

Symma->Text = FloatToStrF( StrToFloat (Tsena->Text)* StrToFloat(Ves->Text),ffFixed,7,2);

}

catch ( const EConvertError &)

Программирование на языке высокого уровня 09.03.01

46

{ShowMessage ("Ты ввел текст, дружок");} catch ( const Eoverflow &)

{ShowMessage

("Опять опозорился - слишком большое число");} Если во время выполнения программы возникает исключительная

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

Программирование на языке высокого уровня 09.03.01

47

Раздел 6. Введение в объектно-ориентированные элементы языка С++ (часть 2) Лекции 31-34.

Тема 41. Динамическое распределение памяти.

Вязыке С++ для управления динамическим распределением

памяти существуют операции new и delete для простых переменных, а также new[] и delete[] для массивов. Наряду с ними сохранена возможность вызова стандартных функций языка С - malloc(), calloc() и free().

int *ykaz; ykaz= new int;

....

delete ykaz; ykaz= new int[S];

....

delete[] ykaz;

Для простых переменных существует возможность одновременной инициализации переменных при динамическом выделении памяти:

int *tt=new int(90); cout<<*tt<<endl;

Если операция new вызывается для представителя какого-либо класса, то одновременно вызывается конструктор:

proverka *att;

att = new proverka(50,50);

Аналогично, при вызове операции delete для представителя какого-либо класса будет вызван деструктор. Для переменных, для которых память выделяется подобным образом, конструктор не занимается выделением памяти для объектов, а деструктор ее не освобождает. Этим занимаются операции new и delete.

Класс может определять свои собственные операции new и delete (или new[] и delete[] для массивов). Синтаксис заголовка таких перегруженных функций традиционен, например:

void * proverka::operator new[](...)

Тема 42. Понятие о дружественности.

Специальные спецификаторы доступа класса позволяют указывать, могут ли функции вне определенного класса обращаться к его элементам. Может, тем не менее, возникнуть ситуация, когда потребуется обеспечить определенной функции или классу доступ к элементам данного класса, определенным как private или protected. Язык С++ позволяет особо разрешить доступ к любым элементам класса другому классу или функции с помощью ключевого

Программирование на языке высокого уровня 09.03.01

48

слова friend. Это ключевое слово встраивается в качестве оператора описания в определение данного класса:

class proverka { ...

friend class rabota;

... };

Можно также разрешить обычной функции или функции-элементу другого класса полный доступ к элементам класса, объявленным private или protected c помощью описания friend в определении данного класса:

class proverka

{ friend void rabota::tfunc(int); friend void print(const proverka&); };

void print(const proverka& zaval) { cout<<zaval.a<<"#"<<zaval.b<<endl;

}

К друзьям и дружественности приложены следующие правила:

на описания friend не влияют спецификаторы public, protected или private;

описания friend не взаимны, если A объявляет В другом, то это не означает, что А является другом для В;

дружественность не наследуется: если А объявляет В другом, то классы, производные от В, не будут

автоматически получать доступ к элементам А; дружественность не является переходным свойством: если А

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

Тема 43. Наследование классов.

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

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

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

элементы базового, наследуя, тем не менее, основной

объем

свойств базового класса.

 

Синтаксис наследования имеет следующий вид:

 

Программирование на языке высокого уровня 09.03.01

49

class osnova {..........};

class potomok: ключ_доступа osnova {..........};

Ключ доступа может быть private, protected или public. Он может отсутствовать, и в этом случае доступ принимается по умолчанию private для классов и public для структур.

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

Существуют следующие варианты доступа:

Наследование

Доступ в

базовом

Доступ в производном

 

классе

 

классе

 

 

 

 

public

public

 

 

 

protected

 

не меняется

 

private

 

 

 

 

 

 

protected

public

 

protected

 

protected

 

не меняется

 

private

 

не меняется

 

 

 

 

private

public

 

private

 

protected

 

private

 

private

 

не меняется

 

 

 

 

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

class prolet: public proverka { char * stroka;

public: prolet(char *,int,int);

friend void print(const proverka&); void print1 ()

{proverka zaval(a,b); print(zaval); printf("%s",stroka);}

};

prolet::prolet(char *str,int a1,int b1):proverka(a1,b1) {strcpy(stroka,str);}

void main(){

prolet t("Psel von",40,40); t.print1();

}

Программирование на языке высокого уровня 09.03.01

50