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

OOP_Bakalavry_-_Laboratornye_raboty

.pdf
Скачиваний:
20
Добавлен:
11.05.2015
Размер:
998.6 Кб
Скачать

3.Убедитесь, что класс PersonListItem является закрытым вложенным классом класса PersonList. Класс может инкапсулировать не только поля и методы, но и целые классы. С точки зрения архитектуры приложения это даёт разработчикам дополнительную выгоду – класс PersonListItem нужен только для внутренней организации списка, и сторонний разработчик не должен ничего о нём знать. В дальнейшем, если вам понадобиться изменить внутреннюю реализацию списка (а вам это понадобиться в следующих лабораторных!), вы сможете легко это сделать, изменив только два класса PersonListItem и PersonList, не затронув всей остальной программы. В этом и состоит главное преимущество инкапсуляции – сокрытие реализации для упрощения дальнейшей модификации кода.

4.Продемонстрируйте правильность работы ваших классов. Для этого:

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

-Выведите содержимое каждого списка на экран с соответствующими подписями списков.

-Добавьте нового человека в первый список.

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

-Удалите второго человека из первого списка. Покажите, что удаление человека из первого списка не привело к уничтожению этого же человека во втором списке.

-Очистите второй список.

Каждый новый шаг должен выполняться по нажатию любой клавиши клавиатуры.

*5. Реализуйте в классе Person методы Read() и Show(), выполняющие чтение персоны с клавиатуры и вывод персоны на экран. В случае использования потокового ввода/вывода вы-

полните перегрузку операторов << и >>. С механизмом перегрузки операторов можно ознакомиться в книге Шилдта «Си++ для начинающих».

*6. Создайте в классе Person статический метод GetRandomPerson(). Реализация данного метода заметно упростит тестирование и демонстрацию программы.

*7. Добавьте в функцию Read() проверки правильности данных:

-Возраст не может быть отрицательным.

-При вводе возраста не должно быть возможности ввода символов.

-Имя и фамилия должны содержать только английские или русские символы.

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

-Учесть возможность ввода двойного имени и двойной фамилии. Для этого нужно учесть множество нюансов: помимо букв алфавита может быть считан символ дефиса “-”, дефис не может быть двойным, дефис не может быть в начале или конце имени (в противном случае он должен быть удален), вторая часть фамилии также должна начинаться с заглавной буквы.

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

Вопросы для проверки:

1.Что такое класс? Что такое объект?

2.Что такое поле объекта? Что такое метод объекта?

3. Что такое интерфейс класса? Что такое реализация класса?

4.Что такое конструктор класса? Чем конструктор отличается от обычного метода? Сколько конструкторов может быть у одного класса? Что такое конструктор по умолчанию? Чем отличается конструктор по умолчанию от конструктора с параметрами по умолчанию?

5.Что такое деструктор? Для чего он нужен? Сколько деструкторов может в одном классе?

6.Что такое инкапсуляция? Какие преимущества с точки зрения разработки ПО позволяет получить инкапсуляция?

7.Что такое модификатор доступа? Какие есть модификаторы доступа в языке Си++? Для чего нужен каждый из них?

8.Что такое статическое поле класса? Что такое статический метод класса? Для чего они нужны?

9.Что такое статический класс?

10.Что такое агрегация? Что такое композиция? Чем они отличаются?

11. Что такое вложенный класс?

12. Оформление исходного кода класса согласно RSDN: как именуется класс? Как именуются закрытые поля? Как именуются открытые поля? Как именуются константные поля? Как именуются методы класса? Какие поля и методы класса должны быть прокомментированы? В каком порядке должны следовать поля и методы внутри класса? Какие ограничения на размер класса существуют в нотации RSDN?

Содержание отчета:

-Титульный лист

-Содержание

1.1Цель работы – освоение базовых понятий создания и определения класса в языке Си++ и изучение механизма инкапсуляции.

1.2Постановка задачи - перечислить все задачи, которые необходимо выполнить в работе – выделены жирным текстом в данном методическом пособии.

1.3Теоретическая часть – представить ответы на вопросы для проверки. Ответы должны быть представлены в виде некоторого единого повествовательного текста без перечисления самих вопросов.

1.4Диаграмма разработанных классов – схема должна соответствовать диаграммам классов UML и отобразить все открытые и закрытые члены классов.

1.5Схема вставки нового элемента в середину двусвязного списка блок-схема подпро-

граммы, выполняющей перемещение человека из одного списка в другой.

1.6Исходный код реализации функций Add() и Remove() класса PersonList.

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

Лабораторная №6 Наследование и полиморфизм

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

– для этого любые поля или методы должны быть отмечены модификатором private. Таким образом, сторонние разработчики вне разрабатываемого класса смогут использовать только интерфейс класса – все члены класса под модификатором public.

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

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

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

Стоит понимать разницу между наследованием и полиморфизмом. Наследование – это приобретение готовой реализации базового класса без возможности её изменения (используется для расширения базовой функциональности), а полиморфизм – это возможность переопределения интерфейса базового класса новой реализацией (используется для изменения базовой функциональности).

1.На основе предыдущих лабораторных создайте путем наследования от класса Person два дочерних класса Adult и Child. В класс Adult добавьте поля, хранящие паспортные данные – Person* MarriedOn (человек, на ком женат/замужем или NULL в ином случае), char[100] WorkPlace

(название места работы). В класс Child добавьте поля Person* Mother, Person* Father, char[100] School (название детского сада/школы).

2.Добавьте в базовый класс Person абстрактный метод char* GetDescription() и сделайте уникальную реализацию в базовом и дочерних классах. Для конкретного экземпляра Person метод

GetDesciption() может вернуть, для примера, следующую строку:

John Carlale, 18 years old, male.

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

Для экземпляра Adult метод описания зависит от конкретных данных. Если человек женат, то в описании должно присутствовать предложение о том, на ком женат, например:

John Carlale, 18 years old, male, married on Jahnet Ogaiyo…

Если человек не женат (MarriedOn == NULL), то описание выглядит следующим образом:

John Carlale, 18 years old, male, single…

По аналогии к описанию должно добавляться и место работы человека, если оно есть, и надпись Unemployee в случае её отсутствия. Например:

John Carlale, 18 years old, male, single, programmer in Microsoft.

Для ребенка стоит учесть различные формулировки при наличии/отсутствии каждого из родителей:

Parents are…

// В случае обоих родителей

Mother

is…

// В случае неуказанного отца

Father

is…

// В случае неуказанной

матери

Parents are not specified…

// В случае неуказанных

обоих родителей

*3. Реализуйте в соответствующих классах статические методы для генерации случайного

взрослого или ребенка. Метод должен возвращать указатель на нового созданного человека.

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

4. Инкапсулируйте поле Age для класса Person и создайте уникальные механизмы задания возраста для каждого класса. В настоящий момент у вас есть два класса, представляющих собой взрослого человека и ребенка. Однако ничто не мешает кому бы то ни было присвоить в поле возраста ребенка 57 лет или в поле возраста взрослого 2 года. Чтобы избежать такого нарушения целостности данных необходимо сделать возраст закрытым полем, при этом добавив в класса два открытых метода int GetAge() и void SetAge(int age). Теперь возраст будет инкапсулированным полем, а метода GetAge() и SetAge(), предоставляющие доступ к закрытому полю, называются аксессорами (от англ. Access - «доступ»).

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

private:

int _age;

public:

int GetAge()

{

Return _age;

}

void SetAge(int age)

{

if (age > 0)

{

_age = age;

}

else

{

_age = 1;

}

}

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

ПРИМЕЧАНИЕ: Учтите, что в данном случае поле Age должно быть не закрытым полем, а защищенным полем (protected), так как дочерний класс всё-таки должен иметь возможность получить значение к этому полю напрямую. При реализации инкапсулированных полей обязательно подумайте, должно ли оно быть закрытым или защищенным.

5.Сделайте методы GetAge() и SetAge() класса Person абстрактными и реализуйте специфические проверки введенного возраста в дочерних классах. Теперь, когда поле Age инкапсулиро-

вано, вы можете создать уникальные проверки для каждого типа человека: в случае Person возраст не может быть отрицательным, в случае Adult возраст не должен быть меньше 18, в случае Child возраст должен быть не больше 18 и положительным. Создание полиморфных аксессоров обеспечит правильность данных каждого типа человека.

6.Продемонстрируйте работу новых классов, выполнив следующую последовательность действий:

1) В функции main() программно создайте список PersonList, в который добавьте семь человек – разное количество взрослых и детей в случайном порядке. (Обратите внимание, что ранее реализованный список, хранящий в себе исключительно класс Person, работает также и для дочерних классов)

2) Выведите на экран описания всех людей списка. Продемонстрируйте, что для различных типов людей описания содержат разную информацию.

3) Программно определите тип четвертого человека в вашем списке.

Стоит пояснить, что одной из проблем при использовании полиморфизма является то, что по указателю на базовый класс нельзя определить экземпляром какого дочернего класса является указанный объект. Например, ваш список PersonList хранит в себе указатели на объекты класса Person. Мы можем поместить в указатель типа Person адрес объект типа Adult или Child. Однако через данный указатель мы уже не можем получить доступ к полям и методам, не относящимся к интерфейсу класса Person (таким, как WorkPlace или Mother). То есть, мы не можем восстановить исходный тип объекта, а явное приведение указателя «наугад» при ошибке приведет к созданию исключения и аварийному завершению программы. В таких случаях программист должен придумать способ, как определить исходный тип объекта наверняка.

В нашем случае, это можно сделать по возрасту:

if (person->GetAge() < 18)

{

cout << “This is a child”;

Child* child = (Child*)person; child->Mother->PrintPerson();

}

else

{

cout << “This is an adult”;

Adult* adult = (Adult*)person; adult->MarriedOn->PrintPerson();

}

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

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

Вопросы для проверки:

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

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

3.Что такое указатель на базовый класс?

4.Что такое виртуальные функции? Что такое чисто виртуальные функции?

5.Что такое полиморфизм? Как реализуется полиморфизм в Си++?

6.Какие проблемы могут возникнуть при работа с полиморфными объектами через указатель на базовый класс?

7.В чем отличие полиморфизма от наследования?

8.Что такое абстрактный класс?

9.Что такое статическое поле, статический метод, статический класс? Для чего они нужны?

10.Что такое инкапсулированное поле? Что такое метод-аксессор? Для чего они нужны?

Содержание отчета:

-Титульный лист

-Содержание

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

1.2Постановка задачи - перечислить все задачи, которые необходимо выполнить в работе – выделены жирным текстом в данном методическом пособии.

1.3Теоретическая часть – представить ответы на вопросы для проверки. Ответы должны быть представлены в виде некоторого единого повествовательного текста без перечисления самих вопросов.

1.4Диаграмма разработанных классов – схема должна соответствовать диаграммам классов UML и отобразить все открытые и закрытые члены классов. На диаграмме должно присутствовать не менее четырех классов: Person, Adult, Child, PersonList.

1.5Механизм определения типа человека через указатель на базовый класс.

1.6Исходный код реализации функций GetDescription() классов Adult и Child.

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

Лабораторная №7 Обобщения

1.Сделать на основе класса PersonList обобщенный класс List, способный хранить список дан-

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

2.Продемонстрируйте работу класса List для следующих типов данных:

-double;

-*Person (работа аналогична классу PersonList);

-double[5];

-List<double>;

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

1.Demonstrate on double

2.Demonstrate on *Person

3.Demonstrate on double[5]

4.Demonstrate on List<double>

--- Choose 1-4:

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

Вопросы для проверки:

1. Что такое обобщение? Для чего нужны обобщения?

Что такое обобщенный метод? Каков синтаксис создания обобщенного метода на языке Си++? Как работает механизм перегрузки функций для обобщенных методов?

Что такое обобщенный класс? Каков синтаксис создания обобщенного класса на языке Си++? Как создать экземпляр обобщенного класса для определенного типа данных?

Можно ли наследоваться от обобщенного класса? Можно ли создать реализацию класса для конкретного типа данных путем наследования от обобщенного класса?

Существуют какие-либо ограничения и особенности, которые необходимо учитывать при создании обобщенных классов и их использовании?

Содержание отчета:

-Титульный лист

-Содержание

1.1Цель работы – изучение понятия обобщений в языке Си++ и их использования на примере ранее созданных классов.

1.2Постановка задачи - перечислить все задачи, которые необходимо выполнить в работе – выделены жирным текстом в данном методическом пособии.

1.3Теоретическая часть – представить ответы на вопросы для проверки. Ответы должны быть представлены в виде некоторого единого повествовательного текста без перечисления самих вопросов.

1.4Диаграмма разработанных классов – схема должна соответствовать диаграммам классов UML и отобразить все открытые и закрытые члены классов.

1.5Исходный код объявления классов List, ListItem и нескольких их методов.

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

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