Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
shprora_po_proge.docx
Скачиваний:
5
Добавлен:
23.09.2019
Размер:
518.78 Кб
Скачать

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;

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