Описания лабораторных работ / Лаб. 3 (new)
.docxЛАБОРАТОРНАЯ РАБОТА №3.
ОТНОШЕНИЕ НАСЛЕДОВАНИЯ.
Постановка задачи.
Вариант |
Класс №2 |
Свойства (Класс №2) |
Класс №3 |
|
1 |
CStudent (Студент) |
String Surname (Фамилия, например – Иванов) CGroup* G |
Дополнить систему, состоящую из двух классов “Класс №1” и “Класс №2”, которые были разработаны в лабораторной работе 2, новым классом “Класс №3”. Новый класс должен быть связан public наследованием с классом “Класс №2”. Класс “Класс №3” должен иметь одно поле, которое выбирается студентом самостоятельно. Для разрабатываемого класса написать конструкторы умолчания, с параметрами и конструктор копирования, деструктор, методы доступа и метод print(). Написать тестовую программу для проверки работоспособности разработанных классов.
Дополнить систему, состоящую из двух классов “Класс №1” и “Класс №2”, которые были разработаны в лабораторной работе 2, новым классом “Класс №3”. Новый класс должен быть связан public наследованием с классом “Класс №2”. Класс “Класс №3” должен иметь одно поле, которое выбирается студентом самостоятельно. Для разрабатываемого класса написать конструкторы умолчания, с параметрами и конструктор копирования, деструктор, методы доступа и метод print(). Написать тестовую программу для проверки работоспособности разработанных классов.
Дополнить систему, состоящую из двух классов “Класс №1” и “Класс №2”, которые были разработаны в лабораторной работе 2, новым классом “Класс №3”. Новый класс должен быть связан public наследованием с классом “Класс №2”. Класс “Класс №3” должен иметь одно поле, которое выбирается студентом самостоятельно. Для разрабатываемого класса написать конструкторы умолчания, с параметрами и конструктор копирования, деструктор, методы доступа и метод print(). Написать тестовую программу для проверки работоспособности разработанных классов.
Дополнить систему, состоящую из двух классов “Класс №1” и “Класс №2”, которые были разработаны в лабораторной работе 2, новым классом “Класс №3”. Новый класс должен быть связан public наследованием с классом “Класс №2”. Класс “Класс №3” должен иметь одно поле, которое выбирается студентом самостоятельно. Для разрабатываемого класса написать конструкторы умолчания, с параметрами и конструктор копирования, деструктор, методы доступа и метод print(). Написать тестовую программу для проверки работоспособности разработанных классов.
|
|
2 |
CWorker (Сотрудник) |
String Surname (Фамилия, например – Иванов) CProfession* P |
||
3 |
CFile (Файл) |
String Name (Имя, например – Иван) CCharacteristic * S |
||
4 |
CCar (Машина) |
String Brand (Бренд, например – LADA) CWheel* W |
||
5 |
CRadioDetal (Радиодеталь) |
String Name (Наименование, например – Транзистор) CComponent* C |
||
6 |
CBook (Книга) |
String Name (Имя, например – Александр Сергеевич Пушкин) CAuthor* A |
||
7 |
CFilm (Фильм) |
Double Rating (Рейтинг фильма, например – 8.61) CDescription* D |
||
8 |
СRailwayWagon (Железн. Вагон) |
String Company (Компания, например - Алтайвагон) СProperties obj |
||
9 |
CAircraft (Самолёт) |
String Company (Компания в которой используются, например – Аэрофлот) CType* T |
||
10 |
CLanguageProg (Язык программ.) |
String Name (Название, например – Prolog) CDescription* D |
||
11 |
CClientBank (Клиент банка) |
String Name (Название банка, например – Сбербанк) CCardClient obj |
||
12 |
CParts (Комплектующие) |
Double Cost (Цена, например – 16660.6) CDetail obj |
||
13 |
CFootbalPlayer (Игрок футбол.ком.) |
String FullName (Имя футболиста, например – Криштиану Роналду) CDescription obj |
||
14 |
CProgramPocket (Програм.пакет) |
String Firm (Компания, разработавшая, например – Microsoft) CCharacteristic obj |
||
15 |
CApartment (Квартира) |
Double Cost (Цена квартиры (в млн. руб., например – 3.41) CAdvertisement* A |
||
16 |
CTV (Телевизор) |
Double Cost (Цена, например -13990) CCharacteristic obj |
||
17 |
CPassport (Паспортные дан.) |
String Country (Страна держателя, например – Россия) CData* D |
||
18 |
CMobilePhone (Мобильный тел.) |
String Brand (Производитель устройства, например – Samsung) CCharacteristic obj |
||
19 |
CWebsite (Сайт) |
String Name (Название сайта, например – Ulmart) CLogs* L |
||
20 |
CPlanets (Планеты СС) |
String Name (Название, например – Земля) CDescription obj |
||
21 |
CMuseumsSPB (Музеи СПБ) |
String Thematics (Тематика, например – Исторический) CGuide* G |
||
22 |
CTheatresSPB (Театры СПБ) |
String Name (Имя театра, например – МДТ) CBrochure obj |
||
23 |
CHotelSPB (Отели СПБ) |
String Name (Название, например – RadisonRoyal) CAdvertisement obj |
||
24 |
CSmartphone (Смартфон) |
Double Cost (Цена телефона, например – 13990) CCharacteristic* С |
||
25 |
CTransport (Транспортное ср. ) |
String Brand (Бренд автомобиля, например – Rolls-Royce) CEngine* E |
||
26 |
CPersonalPC (Персональный ПК) |
String Brand (Производитель компьютера, например – InvasionLabs) CCharacteristic obj |
||
27 |
CNotebook (Ноутбук) |
String Brand (Производитель ноутбука, например – Acer) CCharacteristic* С |
||
28 |
CTV (Телевизор) |
String Brand (Производитель, например – Samsung) CCharacteristic* C |
||
29 |
CPlants (Растение) |
String Name (Название, например – Туя обыкновенная) CDescription* D |
||
30 |
CAnimals (Животные) |
String Name (Название, например – Панда) CDescription obj |
||
|
Класс №2 |
Свойства (Класс №2) |
Класс №3 |
Свойства (Класс №3) |
Пример |
CMammal (Млекопитающее) |
Float WeightMammal (Вес млекопитающего, например – 25) CSpine* MammaFeatures |
СDog (Собака) |
String NameDogs (Имя собаки, например – Ушастик) CMammal obj
|
Методические указания.
Доступность элементов базового класса в порожденном классе зависит от вида наследования.
Вид наследования |
Права доступа в базовом классе |
Права доступа в порожденном классе |
public |
private |
private |
protected |
protected |
|
public |
public |
|
Protected |
private |
private |
protected |
protected |
|
public |
||
Private |
private |
private |
protected |
||
Public |
Обсуждение.
В языке C++ для обеспечения сокрытия информации используется принцип управления доступом, а не управление видимостью. Для уяснения различия между этими двумя подходами, рассмотрим следующий пример.
// Файл Сlasses.h class A { private: int n; }; class B : public A { public: void foo(); }; // Файл B.cpp int n; void B :: foo() { int n = 5; // ... }
При компиляции рассматриваемого программного кода будет выведено сообщение об ошибке Дело в том, что в теле функции foo() видна закрытая переменная – член базового класса А. Эта переменная скрывает глобальную переменную n, объявленную в файле B.cpp. Если бы сокрытие информации осуществлялось за счет управления видимостью, то сообщение об ошибке появляться не должно.
Итак, очень важный вывод состоит в том, что все закрытые элементы базового класса в порожденном классе видны, но недоступны. Заметим, что в других объектно-ориентированных языках могут быть приняты свои способы сокрытия информации. Например, в Object Pascal Delphi это осуществляется за счет управления видимостью.
Требует осуждения следующий вопрос. Связи с чем функциям порожденного класса не предоставлен доступ к реализации базового класса. На это есть определенные причины. Дело в том, что в противном случае клиент также может сравнительно легко получить такой же доступ. Для этого ему достаточно породить класс.
В разделе protected следует размещать те элементы базового класса, которые должны быть доступны порожденному классу, но не должны быть доступны его клиентам. Не рекомендуется в этот раздел базового класса включать элементы его реализации.
При public – наследовании открытый интерфейс базового класса входит составной частью в интерфейс порожденного класса. Можно говорить о том, что при public – наследовании наследуется интерфейс базового класса.
При использовании protected – наследования и public – наследования интерфейс базового класса клиентам базового класса не предоставляется. В этом случае можно говорить о наследовании реализации.
Имеется ряд элементов базового класса, которые не наследуются. К их числу относятся:
-
конструкторы.
-
Деструктор.
-
Перегруженный оператор присваивания.
Основным видом наследования в языке C++ является public – наследование.
Виртуальный деструктор оказывается необходимым в тех случаях, когда необходимо предусмотреть полиморфную работу с объектами через указатель или ссылку на базовый класс.
Пусть имеются два класса B и D, связанные public – наследованием.
class B { public: B(); ~B(); }; class D : public B { public: D(); ~D(); }
Предположим теперь, что имеется следующий клиентский программный код
int main() {
B* pb = new B; // ... delete pb; // ... }
Рассмотрим два случая. Вначале предположим, что деструктор в классе B является виртуальным. В этом случае оператор delete вызовет деструктор класса D, который после завершения своей работы вызовет деструктор базового класса. Это обусловлено тем обстоятельством, что при работе с виртуальными функциями определяющим является динамический тип указателя.
Перейдем к рассмотрению второго случая, когда деструктор базового класса является невиртуальным. В этом случае определяющим является статический тип указателя. В результате будет вызван только деструктор базового класса.
Множественное наследование используется в тех случаях, когда возникает необходимость воспользоваться возможностями, которые могут предоставить несколько классов. Подобная ситуация характерна при использовании нескольких библиотек.
При применении множественного наследования возникает ряд проблем.
Возможны следующие разновидности наследования:
-
Шаблон класса может быть порожден от шаблонного класса.
-
Шаблон класса может быть порожен от нешаблонного класса.
-
Нешаблонный класс может быть порожден от шаблонного класса.
Пример 1. Наследование шаблона класса от шаблона класса.
// базовый класс template <typename T> class Array { // ... private: T* p; int size_; }; // Порожденный класс template <typename T> class GrowArray : public Array<T> { // ... };
Пример 2. Шаблон класса порождается от нешаблонного класса
Рассматриваемый пример представляет интерес по нескольким причинам:
-
Отсутствует «разбухания» кода.
-
Демонстрируется целесообразность использования private-наследования.
-
Используется вложенный класс.
// Базовый класс Файл BaseStack.h // Назначение класса состоит в реализации функциональности // стека class BaseStack { protected: BaseStack(); ~BaseStack(); void push(void* Object); void* pop(); bool isEmpty()const; private: struct Node // Вложенный класс: Узел { void* data_; Node* next_; Node(void* data, Node* next) : data_(data), next_(next) { } }; Node* top; BaseStack(const BaseStack& rhs); BaseStack& operator=(const BaseStack& rhs); }; // Реализация базового класса BaseStack. Файл BaseStack.cpp // Программный код не приводится // Порожденный с помощью private-наследования шаблон класса // Stack<T>. Назначение шаблона состоит в реализации // контроля типов. Файл Stack.h #include “BaseStack.h” template <typename T> class Stack : public BaseStack { public: void push(T* ObjectPtr); { BaseStack :: push(ObjectPtr); } T* pop() { return static_cast<T”>(BaseStack :: pop()); } bool isEmpty()const { return BaseStack :: isEmpty(); } };
Обсуждение.
Реализация стека состоит из трех классов:
-
BaseStack – нешаблонный класс, реализующий функциональность стека;
-
Node – нешаблонный класс, вложенный в класс BaseStack.
-
Stack – шаблонный класс, порожденный с помощью private-наследования от класса BaseStack.
Обращает на себя внимание отсутствие у класса BaseStack интерфейса пользователя. Интерфейс, который предоставляется этим классом, может быть назван интерфейсом наследования. Его элементы находятся в protected-секции. В частности объекты типа BaseStack могут создавать только функции-члены классов, порожденные от рассматриваемого класса. Для этой цели порожденные классы могут воспользоваться защищенным конструктором умолчания.
В private-секции рассматриваемого класса содержится объявление класса Node. Этот класс можно рассматривать как элемент реализации класса BaseStack. Дело в том, что объекты класса Node должны создаваться только функциями-членами класса BaseStack.
Интерфейс пользователя, обеспечивающий контроль типов, создается шаблоном класса, который порождается от класса BaseStack с помощью private-наследования. Следует отметить, что функции этого класса чрезвычайно просты. Поэтому они могут быть реализованы встроенными.
Пример 3. Порождение нешаблонного класса от шаблона класса.
template <typename T> class B { // ... }; class D : public B<int> { // ... };
Пример.
Пример на основе трех классов: Spine, Mammal и Dog
#include "pch.h"
#include <iostream>
#include <string>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
/*Создаем класс Spine(Позвоночник)*/
class Spine
{
/*Объявляем переменную lengthSpine(Длина позвоночника) типа float и
переменную WallColor(Название позвоночника) типа string c модификатором доступа private */
private:
float lengthSpine;
string nameSpine;
public:
/*Объявляем конструктор по умолчанию*/
Spine()
{
/*Производим отладочную печать работы конструктора по умолчанию и инициализируем переменные lengthSpine и nameSpine */
cout << "Конструктор по умолчанию Spine" << endl;
lengthSpine = 0.6;
nameSpine = "Грудной";
}
/*Объявляем конструктор, который принимает в качестве параметров две переменные:
lengthSpine типа float и nameSpine типа string.
В инициализации конструктора инициализируем переменные lengthSpine и nameSpine полученными значениями */
Spine(float lengthSpine, string nameSpine) : lengthSpine(lengthSpine), nameSpine(nameSpine)
{
/*Производим отладочную печать работы конструктора с параметром*/
cout << "Конструктор c параметрами Spine" << endl;
}
/*Объявляем конструктор копирования Spine, который принимает в качестве параметра объект Spine с именем cpySpine
В инициалиации конструктора копируем поля объекта cpySpine в lengthSpine и nameSpine соответственно скопированным значениями*/
Spine(const Spine& cpySpine) : lengthSpine(cpySpine.lengthSpine), nameSpine(cpySpine.nameSpine)
{
/*Производим отладочную печать работы конструктора копирования*/
cout << "Конструктор копирования Spine" << endl;
}
/*Объявляем перегруженный оператор присваивания*/
Spine& operator=(const Spine &spine)
{
/*Производим отладочную печать работы перегруженного оператора присваивания
Копируем поля объекта cpySpine в lengthSpine и nameSpine соответственно*/
cout << "Перегруженный оператор присваивания Spine" << endl;
this->lengthSpine = spine.lengthSpine;
this->nameSpine = spine.nameSpine;
/*Возвращаем текущий объект*/
return *this;
}
/*Объявляем деструктор*/
~Spine()
{
/*Производим отладочную печать работы деструктора*/
cout << "Деструктор Spine" << endl;
}
/*Объявляем setter для записи значения в private lengthSpine*/
void setLengthSpine(float lengthSpine)
{
this->lengthSpine = lengthSpine;
}
/*Объявляем getter для получения значения из private lengthSpine*/
float getLengthSpine()
{
return lengthSpine;
}
/*Объявляем setter для записи значения в private nameSpine*/
void setNameSpine(string nameSpine)
{
this->nameSpine = nameSpine;
}
/*Объявляем getter для получения значения из private nameSpine*/
string getNameSpine()
{
return nameSpine;
}
/*Метод вывода print полей класса*/
void print()
{
cout << "Длина позвоночника: " << lengthSpine << " м." << endl;
cout << "Название позвоночника: " << nameSpine << endl;;
}
};
/*Создаем класс Mammal(Млекопитающее)*/
class Mammal
{
/*Объявляем переменную weightMammal(Вес млекопитающего) типа float
и объявляем указатель mammalFeatures(Особенности млекопитающего) на класс Spine c модификатором доступа private */
private:
float weightMammal;
Spine* mammalFeatures;
public:
/*Объявляем конструктор по умолчанию*/
Mammal()
{
/*Производим отладочную печать работы конструктора по умолчанию и инициализируем переменную weightMammal значением,
и выделяем память для mammalFeatures */
cout << "Конструктор по умолчанию Mammal" << endl;
weightMammal = 25.5;
mammalFeatures = new Spine[2];
}
/*Объявляем конструктор, который принимает в качестве параметров три переменные:
lengthSpine типа float, nameSpine типа string и weightMammal типа float.
В инициализации конструктора инициализируем переменную weightMammal полученным значением */
Mammal(float lengthSpine, string nameSpine, float weightMammal) : weightMammal(weightMammal)
{
/*Производим отладочную печать работы конструктора с параметрами, выделяем память для mammalFeatures,
передавая в качестве размерности 2 и вызываем setter`ы setLengthSpine и setNameSpine класса Spine,
передавая в качестве параметров переменные lengthSpine и nameSpine соответственно */
cout << "Конструктор с параметром Mammal " << endl;
mammalFeatures = new Spine[2];
mammalFeatures->setLengthSpine(lengthSpine);
mammalFeatures->setNameSpine(nameSpine);
}
/*Объявляем конструктор копирования Mammal, который принимает в качестве параметров объект Mammal
с именем cpyMammal.
В инициализации конструктора инициализируем переменную weightMammal скопированным значением */
Mammal(const Mammal& cpyMammal) : weightMammal(cpyMammal.weightMammal)
{
/*Производим отладочную печать работы конструктора копирования. Выделяем память для mammalFeatures, передавая в качестве размерности 1
Копируем поля объекта cpyMammal в mammalFeatures соответственно*/
cout << "Конструктор копирования Mammal" << endl;
this->mammalFeatures = new Spine[1];
this->mammalFeatures[0] = cpyMammal.mammalFeatures[0];
}
/*Объявляем перегруженный оператор присваивания*/
Mammal& operator=(const Mammal &mammal)
{
/*Производим отладочную печать работы перегруженного оператора присваивания*/
cout << "Перегруженный оператор присваивания Mammal" << endl;
/*Проверка на выделенность памяти. Если выделено-очищаем*/
if (this->mammalFeatures != nullptr)
{
delete[] this->mammalFeatures;
}
/*Выделяем память*/
this->mammalFeatures = new Spine[1];
/*Копируем поля объекта mammal в weightMammal и в цикле mammalFeatures соответственно*/
weightMammal = mammal.weightMammal;
this->mammalFeatures[0] = mammal.mammalFeatures[0];
/*Возвращаем текущий объект*/
return *this;
}
/*Объявляем деструктор*/
~Mammal()
{
/*Производим отладочную печать работы деструктора и освобождаем выделенную память для mammalFeatures*/
cout << "Деструктор Mammal" << endl;
delete[] mammalFeatures;
}
/*Объявляем setter для записи значения в private weightMammal*/
void setWeightMammal(float weightMammal)
{
this->weightMammal = weightMammal;
}
/*Объявляем getter для получения значения из private weightMammal*/
float getWeightMammal()
{
return weightMammal;
}
/*Метод вывода print полей класса*/
void print()
{
/*Вызываем метод print из класса Spine*/
mammalFeatures->print();
cout << "Вес млекопитающего: " << weightMammal << " кг." << endl;
}
};
/*Объявляем класс Dog(Собака), который наследуется с видом наследования public от класса Mammal*/
class Dog : public Mammal
{
/*Объявляем переменную nameDogs типа string с модификатором доступа private*/
private:
string nameDogs;
public:
/*Объявляем конструктор по умолчанию, который,
в свою очередь, в инициализации конструктора инициализирует конструктор по умолчанию базового класса(Mammal)*/
Dog() : Mammal()
{
/*Производим отладочную печать работы конструктора по умолчанию и инициализируем переменную nameDogs*/
cout << "Конструктор по умолчанию Dog" << endl;
nameDogs = "Петруша";
}
/*Объявляем конструктор с параметрами, который принимает в качестве параметров четрые переменные:
lengthSpine типа float, nameSpine типа string и weightMammal типа float и nameDogs типа string,
в свою очередь, в инициализации конструктора инициализирует конструктор с параметрами базового класса(Mammal).
Также в конструкторе инициализации инициализируем nameDogs полученным значением*/
Dog(float lenghtSpine, string nameSpine, float weightMammal, string nameDogs) :
Mammal(lenghtSpine, nameSpine, weightMammal), nameDogs(nameDogs)
{
/*Производим отладочную печать работы конструктора с параметрами*/
cout << "Конструктор с параметрами Dog" << endl;
}
/*Объявляем конструктор копирования Dog, который принимает в качестве параметров объект Dog
с именем cpyPug.
В инициализации конструктора инициализируем конструктор базового класса(Mammal) и переменную nameDogs скопированным значением */
Dog(const Dog& cpyDog) : Mammal(cpyDog), nameDogs(cpyDog.nameDogs)
{
/*Производим отладочную печать работы конструктора копирования*/
cout << "Конструктор копирования Dog" << endl;
}
/*Объявляем деструктор класса Dog*/
~Dog()
{
/*Производим отладочную печать работы деструктора*/
cout << "Деструктор Dog" << endl;
}
/*Объявляем setter для записи значения в private nameDogs*/
void setNameDogs(string nameDogs)
{
this->nameDogs = nameDogs;
}
/*Объявляем getter для получения значения из private nameDogs*/
string getNameDogs()
{
return nameDogs;
}
/*Метод вывода print полей класса*/
void print()
{
/*Вызываем функция print базового класса(Mammal)*/
Mammal::print();
cout << "Кличка собаки: " << nameDogs << endl;
}
};
int main()
{
/*Объявляем переменные lengthSpine и weightMammal типа float и nameSpine типа string*/
float lengthSpine, weightMammal;
string nameSpine, nameDogs;
/*Устанавливаем русский язык*/
system("chcp 1251");
/*Очищаем окно консоли*/
system("cls");
cout << "Введите название позвоночника: ";
/*Считываем переменную nameSpine, введенной с клавиатуры*/
cin >> nameSpine;
cout << "Введите длину позвоночника: ";
/*Считываем переменную lengthSpine, введенной с клавиатуры*/
cin >> lengthSpine;
cout << "Введите вес млекопитающего: ";
/*Считываем переменную weightMammal, введенной с клавиатуры*/
cin >> weightMammal;