Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Triangle t; square s; circle c(10); // объекты производных типов



p = & t; p-> set(1, 1);

res = p-> area(); // находим площадь треугольника

p = & s; p-> set(2, 2);

res = p-> area(); // находим площадь четырехугольника

p = & c; p-> set(20, 30);

res = p-> area(); // находим площадь окружности

}

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

Когда виртуальная функция не переопределена в производном классе, то при вызове ее в объекте производного класса вызывается версия из базового класса. Однако во многих случаях невозможно ввести содержательное определение виртуальной функции в базовом классе. Например, при объявлении класса figure в предыдущем примере реализация функции area() не несет никакого смысла. Могут быть также такие виртуальные функции, которые обязательно должны быть переопределены в производных классах, без чего эти классы не будут иметь никакого значения. В таких случаях необходим метод, гарантирующий, что производные классы действительно определят все необходимые функции. Язык С++ предлагает в качестве решения этой проблемы чисто виртуальные функции. Это такие функции, которые объявлены в базовом классе, но не имеют в нем определения. Т.к. они не имеют определений, т.е. тел в этом базовом классе, то всякий производный класс обязан иметь свою собственную версию реализации. Для объявления чистой виртуальной функции используется следующая общая форма:

virtual < тип возвращаемого значения> < имя функции> (< список параметров> ) = 0;

Например, для определения чисто виртуальной функции area() в классе figure необходимо написать следующее: virtual double area() = 0;

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

Порядок выполнения работы.

1. При домашней подготовке необходимо изучить литературу по теме лабораторной работы.

2. Получить задание у преподавателя.

3. Разработать алгоритм решения задачи и написать программу, реализующую задание.

4. Проверить правильность ее работы.

5. Составить отчет и защитить работу.

Требования к отчету.

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

Варианты заданий.

1. Разработать иерархию классов, которые позволяют оперировать с геометрическими фигурами: точка, линия, фигура, окружность, треугольник, четырехугольник, многоугольник, пирамида и призма. Реализация должна допускать создание объектов с различными параметрами, вычисление различных геометрических характеристик фигур, сопоставление одноименных фигур друг с другом. Интерфейс должен содержать виртуальные функции.

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

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

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

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

12. Контрольные вопросы.

1. Что такое полиморфизм? Как он поддерживается в С++?

2. Чем полиморфизм времени выполнения программы отличается от полиморфизма времени компиляции?

3. Что такое наследование классов? Для чего оно применяется?

4. В чем заключаются особенности описания конструкторов и деструкторов при наследовании? В каком порядке они вызываются при создании и удалении объектов производного типа?

5. Объяснить механизмы множественного наследования.

6. В чем заключаются особенности использования указателей на базовые и производные классы?

7. Что такое виртуальные функции? Для чего они применяются?

8. Механизмы работы с виртуальными функциями.

9. В чем отличия чисто виртуальных функций от виртуальных?

10. Стандартные классы языка С++.


 

Лабораторная работа № 14.
Организация ввода-вывода в С++

Цели и задачи работы

Теоретические положения.

Вывод

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

Например:

put(cerr, " x = " ); // cerr - выходной поток ошибокput(cerr, x); put(cerr, '\n');

Тип аргумента определяет какую функцию надо вызывать в каждом случае.Такой подход применяется в нескольких языках, однако, это слишком длинная запись. За счет перегрузки операции < < , чтобы она означала" вывести" (" put to" ), можно получить более простую запись и разрешить программисту выводить в одном операторе последовательность объектов, например так:

cerr < < " x = " < < x < < '\n';

Здесь cerr обозначает стандартный поток ошибок. Так, если х типа intсо значением 123, то приведенный оператор выдаст

x = 123

и еще символ конца строки в стандартный поток ошибок. Аналогично, если химеет пользовательский тип complex со значением (1, 2.4), то указанный оператор выдаст

x = (1, 2.4)

в поток cerr. Такой подход легко использовать пока x такого типа, для которого определена операция < <, а пользователь может просто доопределить < < для новых типов.

Мы использовали операцию вывода, чтобы избежать многословности, неизбежной, если применять функцию вывода. Но почему именно символ < <?. Невозможно изобрести новую лексему. Кандидатом для ввода и вывода была операция присваивания, но большинство людей предпочитает, чтобы операции ввода и вывода были различны. Более того, порядок выполнения операции = неподходящий, так cout=a=b означает cout=(a=b).Пробовали использовать операции < и >, но к ним так крепко привязано понятие " меньше чем" и " больше чем", что операции ввода-вывода с ними во всех практически случаях не поддавались прочтению.

Операции < < и > > похоже не создают таких проблем. Они асиметричны, что позволяет приписывать им смысл " в" и " из". Они не относятся к числу наиболее часто используемых операций над встроенными типами, а приоритет < < достаточно низкий, чтобы писать арифметические выражения в качестве операнда без скобок:

cout < < " a*b+c=" < < a*b+c < < '\n';

Скобки нужны, если выражение содержит операции с более низким приоритетом:

cout < < " a^b|c=" < < (a^b|c) < < '\n';

Операцию сдвига влево можно использовать в операции вывода, но, конечно, она должна быть в скобках:

cout < < " a< < b=" < < (a< < b) < < '\n';

Вывод встроенных типов

Для управления выводом встроенных типов определяется класс ostreamс операцией < < (вывести):

class ostream: public virtual ios { //...public: ostream& operator< < (const char*); //строки ostream& operator< < (char); ostream& operator< < (short i) { return *this < < int(i); } ostream& operator< < (int); ostream& operator< < (long); ostream& operator< < (double); ostream& operator< < (const void*); // указатели //... };

Естественно, в классе ostream должен быть набор функций operator< < ()для работы с беззнаковыми типами.

Функция operator< < возвращает ссылку на класс ostream, из которого она вызывалась, чтобы к ней можно было применить еще разoperator< <. Так, если х типа int, то

cerr < < " x = " < < x;

понимается как

(cerr.operator< < (" x = " )).operator< < (x);

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

Функция ostream:: operator< < (int) выводит целые значения, а функция ostream:: operator< < (char) - символьные. Поэтому функция

void val(char c){ cout < < " int('" < < c < < " ') = " < < int(c) < < '\n'; }

печатает целые значения символов и с помощью программы

main(){ val('A'); val('Z'); }

будет напечатано

int('A') = 65int('Z') = 90

Здесь предполагается кодировка символов ASCII, на вашей машине может быть иной результат. Обратите внимание, что символьная константа имеет тип char, поэтому cout< < 'Z' напечатает букву Z, а вовсе не целое 90.

Функция ostream:: operator< < (const void*) напечатает значение указателя в такой записи, которая более подходит для используемой системы адресации.

Программа

main(){ int i = 0; int* p = new int(1); cout < < " local " < < & i < < ", free store " < < p < < '\n'; }

выдаст на машине, используемой автором,

local 0x7fffead0, free store 0x500c

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

Обсуждение базового класса ios отложим до 10.4.1.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-05-03; Просмотров: 1125; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.022 с.)
Главная | Случайная страница | Обратная связь