- •1.Краткий обзор агрегатного типа struct.
- •Intcena;
- •Int main()
- •Int cena;
- •Void vyvod(avto s)
- •Int main()
- •3.Понятие объединения union.
- •Void vyvod(avto s, int I)
- •Int main()
- •Intcena;
- •Int main()
- •Int cena;
- •Intmain()
- •Void reset(); …
- •Void Demo::reset() // определениефункции reset( )
- •9.Пример реализации класса для организации обработки символьной строки.
- •Int demochar::length()
- •Int main()
- •10.Пример реализации класса для организации обработки одномерного массива целочисленных значений.
- •11.Инициализация объектов.
- •Int main()
- •12Указатель this.
- •13.Постоянные функции-члены класса.
- •3. Class avto {
- •15. Int main()
- •14.Массив объектов.
- •3. Class avto {
- •24. Int main() {
- •15.Статические переменные-члены и функции-члены класса.
- •17. Staticint GetSumCena() { return SumCena; };
- •19. Staticint SumCena;
- •16 Дружественные функции.
- •8. Friendvoid GetFields(avto&);
- •17 Класс, содержащий переменную-член типа класс.
- •3. Class Date_Avto {
- •16. Class avto {
- •30. Int main()
- •18 Сложность больших программных систем.
- •19 Пять признаков сложной системы.
- •20 Роль декомпозиции, абстракции, иерархии при построении сложных систем.
- •21 Объектная модель.
- •Ood основывается на объектно-ориентированной декомпозиции;
- •25Пример программы с наследованием классов.
- •3. Class avto {
- •18. Class gruz_avto : public avto {
- •36. Int main(){
- •26 Переопределение функций-членов внутри производного класса.
- •3. Class avto {
- •18. Class gruz_avto : public avto {
- •30. Int main()
- •5.Classint_Matrix// класс для обработки целочисленной матрицы
- •45. Int main()
- •Int main()
- •2. Usingnamespace std;
- •3. Int main()
- •18. Return 0;
- •Int main()
- •20. F.Write((char*)&s,sizeof(s));
- •7. Intcena;
- •21. F.Write((char*)&s,sizeof(s)); };
- •22. F.Close();
- •25. F.Seekg(sizeof(s), ios_base::beg);
- •26. F.Read((char*)&s,sizeof(s));
26 Переопределение функций-членов внутри производного класса.
В языке С++ в производном классе разрешается переопределять функции-члены базового класса. В этом случае переопределение функции в производном классе заменяет ее реализацию в базовом классе.
Если в производном классе определяется функция-член с тем же типом возвращаемого значения и сигнатурой, как и у функции-члена базового класса, но которая имеет другое содержимое – “тело”, то эта функция считается переопределенной.
1. #include<iostream>
2. usingnamespace std;
3. Class avto {
4. public:
5. avto() : cena(35700), rashod(17.5), name("FIAT") {};
6. avto(int cena1) : rashod(16.5), name("VOLVO"){cena=cena1;};
7. int GetCena() { return cena; }
8. char* GetName() { return name; }
9. double GetRashod() { return rashod; }
10. void OutPut() { cout <<"CENA="<< cena << endl <<
11. "MARKA="<< name << endl <<
12. "RASHOD="<< rashod << endl; }
13. protected:
14. char* name;
15. int cena;
16. double rashod;
17. };
18. Class gruz_avto : public avto {
19. public:
20. gruz_avto() : avto() { name="KamAZ"; grpodemn=20.5; };
21. gruz_avto(double grp) : avto(38900) { name="MAZ";
22. grpodemn=grp; };
23. void OutPut() { cout <<"CENA="<< cena << endl <<
24. "MARKA="<< name << endl <<
25. "RASHOD="<< rashod << endl <<
26. "GRUZOPODEMN="<< grpodemn << endl; }
27. private:
28. double grpodemn;
29. };
30. Int main()
31. { avto A; A.OutPut(); cout <<" aaaaa\n"<< endl;
32. avto B(33450); B.OutPut(); cout <<" bbbbb\n"<< endl;
33. gruz_avto C; C.OutPut(); cout <<" ccccc\n"<< endl;
34. gruz_avto D(23.5); D.OutPut(); cout <<" ddddd\n"<< endl;
35. return 0;
36. }
Вариант метода OutPut() определен (см. 10-12 строки) и внутри класса gruz_avto(см. 23-26 строки).
В начале функции main() определяются объекты A (см. 31 строку) и B (см. 32 строку) класса avto, а затем вызывается соответствующий вариант метода OutPut().
Ниже в тексте функции main() определяются объектыС и Dкласса gruz_avto, и дважды вызывается уже другой вариант метода OutPut().
Отметим, что внутри класса gruz_avto можно метод OutPut() определить и следующим образом (вместо строк 23-26):
void OutPut() {avto::OutPut();
cout <<"GRUZOPODEMN="<< grpodemn << endl;}
27 Виртуальные функции в языке С++.
Обсуждая реализацию механизма наследования в языке С++ в начале лекции мы определили два класса – avto(базовый класс) и gruz_avto(производный от базового класс). При этом подразумевалось, что объекты класса gruz_avto наследуют все поля и методы базового класса.
Перегруженная функция-член вызывается на основе использования алгоритма соответствия сигнатуре. При этом на этапе компиляции выбирается нужный вариант члена-функции. Использование виртуальных функций-членов класса позволяет выбирать динамически соответствующий вариант реализации функции-члена среди методов базового и производного классов.
Ключевое слово virtual играет роль спецификатора функции и обеспечивает упомянутый механизм. Сочетание виртуальных методов и открытого наследования наиболее полно обеспечивает полиморфизм.
Виртуальная функция-член класса должна представлять собой исполняемый код. В производном классе она может переопределяться, но при этом тип возвращаемого значения и сигнатура должны совпадать с типом и сигнатурой виртуальной функции в базовом классе. Выбор того, какой вариант определения функции вызвать для виртуальной функции, происходит динамически в момент выполнения. Типичным в данном случае является, когда базовый класс содержит виртуальную функцию, а производные классы имеют свои варианты реализации этой функции.
Указатель на базовый класс может указывать либо на объект базового класса, либо на объект производного класса (ниже в примере смотри стр. 37). Выбор функции-члена будет зависеть от класса объекта, на который фактически во время выполнения программы указывает указатель.
!!! Различие в механизме выбора замещенной виртуальной функции и выбором перегружаемой функции заключается в том, что перегружаемая функция выбирается на этапе компиляции, и они (перегружаемые функции) могут иметь различные типы возвращаемых значений. Виртуальная же функция выбирается на этапе выполнения на основе объекта, который передается ей в качестве аргумента-указателя this. Кроме того, будучи однажды объявленной как виртуальная, функция сохраняет это свойство во всех переопределениях в производных классах.
1. #include<iostream>
2. usingnamespace std;
3. class avto {
4. public:
5. avto() : cena(15700), rashod(7.5) { name="Ford";
6. cout <<"Class AVTO !!!"<< endl;};
7. ~avto() { cout <<"Object AVTO-class DESTROY!"<< endl; };
8. void SetCena(int cena) { (*this).cena=cena; }
9. void SetRashod(double rashod) { this->rashod=rashod; }
10. int GetCena() { return cena; }
11. char* GetName() { return name; }
12. double GetRashod() { return rashod; }
13. virtualvoid OutPut() { cout << GetCena() << endl <<
14. GetName() << endl << GetRashod() << endl; }
15. protected:
16. char* name;
17. int cena;
18. double rashod;
19. };
20. class gruz_avto : public avto {
21. public:
22. gruz_avto() : grpodemn(10.5) { cena=25000; rashod=18.5;
23. name="MAZ"; cout <<"Class GRUZ_AVTO !!!"<< endl;};
24. ~gruz_avto() {cout <<"Object GRUZ_AVTO-class DESTROY!"
25. << endl;};
26. void SetGruzopodemnost(double grpodemn)
27. { this->grpodemn=grpodemn;}
28. double GetGruzopodemnost() {return grpodemn;}
29. void OutPut() { cout << GetCena() << endl <<
30. GetName() << endl << GetRashod() <<
31. endl << grpodemn << endl; }
32. private:
33. double grpodemn;
34. };
35. int main()
36. { avto A; A.OutPut();
37. avto* B = new gruz_avto; B->OutPut(); delete B;
38. gruz_avto C; C.OutPut();
39.return 0;
40. }
Внутри базового класса avto определен (см. 13-14 строки) виртуальный метод OutPut(). Вариант метода OutPut() определен (см. 29-31 строки) и внутри класса gruz_avto.
В начале функции main() определяется объект A (см. 36 строку) класса avto, а затем вызывается соответствующий вариант метода OutPut().
Ниже в тексте функции main() определяется указатель B на класс avto, но при этом используется вызов явного конструктора класса gruz_avto, что разрешается в языке С++. При удалении соответствующего объекта будет использоваться только деструктор класса avto.
Далее определяется объектСкласса gruz_avto, и вызывается соответствующий вариант метода OutPut().
28 Перегрузка операторов в языке С++. Общие понятия.
Ранее мы познакомились с механизмом перегрузки функций, когда имеется возможность определять несколько функций, имеющих одно и то же имя при условии, что у них разные сигнатуры (списки аргументов). На основе перегрузки функций в языке С++ реализуется функциональный полиморфизм.
В языке С++ реализован механизм перегрузки операторов. Такая перегрузка представляет собой еще один пример полиморфизма. Перегрузка операторов предоставляет возможность рассматривать операторы языка C++ сразу в нескольких смыслах. Фактически часть операторов языка C++ уже перегружены изначально. Например, оператор *, будучи примененной к адресу, дает значение, которое хранится по этому адресу. Однако, применяя оператор * к паре числовых значений, получаем произведение этих значений. Таким образом, в данном случае в языке C++ используется количество и типы операндов, чтобы решить, какое действие предпринять.
В отличие от функций для операторов языка С++ предусматриваются два дополнительных свойства, существенным образом влияющих на их функциональность: приоритет и ассоциативность. Действительно, встает вопрос как при записи понимать: a+b*c – как (a+b)*c или как a+(b*c)? Выражение a-b+c — это (a-b)+c или a-(b+c)?).
Приоритет (ранг или старшинство оператора) — формальное свойство оператора, влияющее на очередность его выполнения в выражении с несколькими различными операторами при отсутствии явного (с помощью скобок) указания на порядок их вычисления. Например, оператор умножения * обладает изначально более высоким приоритетом, чем оператор сложения +, а потому в выражении a+b*c будет получено сначала произведение b и c, а потом уже сумма.
Операторы могут иметь одинаковый приоритет, тогда они вычисляются по правилу ассоциативности, установленному для них. В программировании ассоциативностью операторов называют последовательность (очередность) их выполнения в случае, когда операторы имеют одинаковый приоритет и отсутствует явное (с помощью скобок) указание на очерёдность их выполнения. Причем различается левая ассоциативность, при которой вычисление выражения происходит слева–направо, и правая ассоциативность — справа–налево. Соответствующие операторы называют левоассоциативными и правоассоциативными. Например, в языке xBaseСУБДVisualFoxPro большинство операторов имеет левую ассоциативность, в то время как возведение в степень правоассоциативно: x2 записывается по правилам xBase в виде x**2 или x^2.
В языках программирования (в том числе в языке С++) применяются два способа задания приоритетов операторов:
Первый из них связан с распределением всех операторов по иерархии приоритетов. Этот способ всегда используется для задания приоритетов по умолчанию и фиксируется в описании языка в виде соглашения, что соответствующим операторам присваивается определенные приоритеты.
Второй способ дает возможность изменять приоритеты по умолчанию, определяя их в явном виде с помощью парных круглых скобок. При этом глубина вложенности прямо пропорциональна величине приоритета, то есть более вложенные внутренние скобки указывают на более высокий приоритет, чем внешние, обрамляющие их.
Поскольку для встроенных в язык операторов всегда предусматриваются приоритеты и ассоциативность, то возникает вопрос: какие приоритеты и ассоциативность будут иметь переопределённые версии этих операторов или, тем более, новые созданные программистом операторы?
Кроме того, имеются и другие “тонкие моменты”, которые могут требовать уточнения. Например, в языке С++ существуют две формы операторов инкремента (увеличения) и декремента (уменьшения) значения ++ и -- — префиксная и постфиксная, поведение которых различается. Как должны вести себя перегруженные версии таких операций? Различные языки по-разному решают приведённые вопросы. Так, в языке C++ приоритет и ассоциативность перегруженных версий операторов сохраняются такими же, как и у определённых в языке, а описания перегрузки префиксной и постфиксной формы операторов инкремента и декремента используют различные сигнатуры.
Итак!!!Перегрузка операторов — это возможность назначать новый смысл операторам при использовании их с определенным классом.
Использование механизма перегрузки операторов позволяет повысить удобочитаемость программ и облегчить их понимание, выражая операторы класса в более понятном виде. При этом:
Чтобы перегрузить оператор, необходимо определить класс, которому оператор будет назначен;
Когда перегружаете оператор, то перегрузка действует только для класса, в котором он определяется. Если программа использует оператор с неклассовыми переменными (например, переменными типа int или float), используется стандартное определение оператора;
Чтобы перегрузить оператор класса, необходимо использовать ключевое слово языка C++ operator для определения метода класса, который вызывается каждый раз, когда переменная класса использует оператор;
В языке С++ разрешается перегружать следующие операторы:
+ - * / % ^ & | ~ !
= <> += -= *= /= %= ^= &=
|= <<>>>>= <<= == != <= >= &&
|| ++ -- ->* , -> [] () newdelete
C++ не позволяет вашим программам перегружать оператор выбора элемента (.), оператор указателя на элемент (.*), оператор разрешения области видимости (::), условный оператор сравнения (?:), оператор sizeof().
29 Пример перегрузки унарных и бинарных операторов в языке С++.
Рассмотрим пример программы, в которой демонстрируется механизм перегрузки операторов. В программе определяется класс int_Matrix для организации обработки целочисленной матрицы. Внутри этого класса реализован явный конструктор, которым предусматривается генерация значений матрицы в диапазоне от 0 до 9 с помощью ГСЗ. Определен метод OutPut() для вывода матрицы на экран. В классе реализована перегрузка бинарных операторов – и +, а также перегрузка унарного оператора –.
1. #include<iostream>
2. #include<stdlib.h>
3. #include<time.h>
4. usingnamespace std;