ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ МОСКОВСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ВЫСШАЯ ШКОЛА ПЕЧАТИ И МЕДИАИНДУСТРИИ Институт Принтмедиа и информационных технологий Кафедра Информатики и информационных технологий направление подготовки 09.03.02 «Информационные системы и технологии» ЛАБОРАТОРНАЯ РАБОТА № _9_ Дисциплина: Тема: Объектно-ориентированное программирование Виртуальные функции Выполнил: студент группы 211-729 Мороз Анастасия Сергеевна (Фамилия И.О.) Дата, подпись 02.06.2022 (Дата) (Подпись) Проверил: (Фамилия И.О., степень, звание) (Оценка) Дата, подпись (Дата) Замечания: Москва 2022 (Подпись) Лабораторная работа №9 Цель: получить практические навыки в создании программ, использующих структуры и перечисления. Постановка задачи: 1. Разработать программы, реализующие задания, приводимые ниже. 2. Оформить отчет, содержащий следующие пункты: 1. Титульный лист. 2. Название и цель работы. 3. Постановка задачи. 4. Листинг программы с комментариями. Листинг программы: 5) Предположим, что в функции main() определены три локальных массива одинакового размера и типа (скажем, float). Первые два уже инициализированы значениями. Напишите функцию addarrays(), которая принимает в качестве аргументов адреса грех массивов, складывает соответствующие элементы двух массивов и помещает результат в третий массив. Четвертым аргументом этой функции может быть размерность массивов. На всем протяжении программы используйте указатели. #include <iostream> using namespace std; const int N = 5; void Function(int p1[], int p2[], int p3[]); int main() { setlocale(LC_ALL, ""); int p1[N]{ 1,2,3,4,5 }, p2[N]{ 2,3,4,5 ,6}, p3[N]; Function(p1, p2, p3); } void Function(int p1[], int p2[], int p3[]) { for (int i = 0; i < N; i++) { p3[i] = p1[i] + p2[i]; } for (int i = 0; i < N; i++) { cout << "№ : " << i << " Элемент : " << p3[i] << endl; } } 6) Создайте свою версию библиотечной функции strcmp(s1, s2), которая сравнивает две строки и возвращает -1, если s1 идет первой по алфавиту, О, если в s1 и s2 одинаковые значения, и 1, если s2 идет первой по алфавиту. Назовите вашу функцию compstr(). Она должна принимать в качестве аргументов два указателя на строки char *, сравнивать эти строки посимвольно и возвращать число int. Напишите функцию main() для проверки работы вашей функции с разными строками. Используйте указатели во всех возможных ситуациях. #include <iostream> using namespace std; const int N = 3; void Function(char p1[], char p2[]); //объявление функции int main() { setlocale(LC_ALL, ""); char p1[N] = "GH", p2[N] = "MH"; // ввод строчек для сравнения Function(p1, p2); // применение функции } void Function(char p1[], char p2[]) { int m = 0; for (int i = 0; i < N - 1; i++) // запуск цикла для прогонки функции по каждой букве { if ((int)p1[i] > (int)p2[i]) { m++; cout << "-1 "; exit(0); // если первая сторока по алфовиту первыя, вывод 1, цикл прерывается } if ((int)p1[i] < (int)p2[i]) { m++; cout << "1 "; exit(0); // если вторая сторока по алфовиту первыя, вывод -1, цикл прерывается } } if (m == 0) { cout << "0 "; // если строки иденичны } } 7) Модифицируйте класс person из программы, представленной ниже, чтобы он включал в себя не только имя человека, но и сведения о его зарплате в виде поля salary типа float. Вам будет необходимо изменить методы setName() и printName() на setData() и printData(), включив в них возможность ввода и вывода значения salary, как это можно сделать с именем. Вам также понадобится метод getSalary(). Используя указатели, напишите функцию salsortQ, которая сортирует указатели массива persPtr по значениям зарплаты. Попробуйте вместить всю сортировку в функцию salsort(), не вызывая других функций, как это сделано в программе PERS0RT. При этом не забывайте, что операция -> имеет больший приоритет, чем операция *, и вам нужно будет написать #include <iostream> #include <iomanip> #include <conio.h> #include <sstream> #include <string.h> using namespace std; class person // создание класса { protected: string name; float salary; public: void setData() // метод заполнения { cout << "Введите имя: "; cin >> name; cout << "ВВедите зарплату : "; cin >> salary; } void printData() // метод для вывода данных класса { cout << endl << "Имя: " << name << ", Зарплата: " << salary; } string getName() // метод воззвращающий имя { return name; } float getSalary()// метод воззвращающий зарплату { return salary; } }; void bsort(person** pp, int n) // метод для сортировки { void order(person**, person**); int j, k; for (j = 0; j < n - 1; j++) for (k = j + 1; k < n; k++) order(pp + j, pp + k); } void salsort(person** pp, int n) { int j, k; for (j = 0; j < n - 1; j++) for (k = 1; k < n; k++) if ((*(pp + j))->getSalary() > (*(pp + k))->getSalary()) { swap(*(pp + j), *(pp + k)); } } void order(person** pp1, person** pp2) // метод меняющий классы местами { if ((*pp1)->getName() > (*pp2)->getName()) { swap(*pp1, *pp2); } } /////////////////////////////////////////////////////////////////////////7 int main() { setlocale(LC_ALL, ""); void bsort(person**, int); void salsort(person**, int); person* persPtr[100]; int n = 0; char choice; do { persPtr[n] = new person; persPtr[n]->setData(); n++; cout << "Продолжать ввод (y/n)?"; cin >> choice; } while (choice == 'y'); cout << "\nНеотсортированный список :"; for (int j = 0; j < n; j++) persPtr[j]->printData(); salsort(persPtr, n); cout << "\nОтсортированный список :"; for (int j = 0; j < n; j++) persPtr[j]->printData(); cout << endl; } 8) Исправьте функцию additem() из программы связного списка так, чтобы она добавляла новый элемент в конец списка, а не в начало. Это будет означать, что первый вставленный элемент будет выведен первым и результат работы программы будет следующим: #include <iostream> using namespace std; int n = 0; //////////////////////////////////////////////////////////////// struct link //один элемент списка { int data; //элемент данных link* next; //указатель на следующую ссылку }; //////////////////////////////////////////////////////////////// class linklist //список ссылок { private: link* first; //указатель на первую ссылку public: linklist() //конструктор по умолчанию { first = NULL; } //нет первой ссылки void additem(int d); //добавить элемент данных (одна ссылка) void display(); //добавить элемент данных (одна ссылка) }; //-------------------------------------------------------------void linklist::additem(int d) { link* newlink = new link; newlink->data = d; if (first) { link* p = first; while (p->next) p = p->next; p->next = newlink; } else first = newlink; newlink->next = NULL; } //-------------------------------------------------------------void linklist::display() //показать все ссылки { link* current = first; //установить ptr на первую ссылку while (current != NULL) //выйти по последней ссылке { cout << current->data << endl; //распечатать данные current = current->next; //перейти к следующей ссылке } delete current; } //////////////////////////////////////////////////////////////// int main() { linklist l; //сделать связанный список l.additem(25); l.additem(36); l.additem(49); l.additem(64); //добавить четыре элемента в список l.display(); return 0; //показать весь список } 9) Допустим, что нам нужно сохранить 100 целых чисел так, чтобы иметь к ним легкий доступ. Допустим, что при этом у нас есть проблема: память нашего компьютера так фрагментирована, что может хранить массив, наибольшее количество элементов в котором равно десяти (такие проблемы действительно появляются, хотя обычно это происходит с объектами, занимающими большое количество памяти). Вы можете решить эту проблему, определив 10 разных массивов по 10 элементов в каждом и массив из 10 указателей на эти массивы. Массивы будут иметь имена а0, a1, а2 и т. д. Адрес каждого массива будет сохранен в массиве указателей типа int*, который называется ар. Вы сможете получить доступ к отдельному целому используя выражение ap[j] [к], где j является номером элемента массива указателей, а к — номером элемента в массиве, на который этот указатель указывает. Это похоже на двумерный массив, но в действительности является группой одномерных массивов. Заполните группу массивов тестовыми данными (скажем, номерами 0, 10, 20 и т. д #include <iostream> #include <string> #include <iomanip> #include <cmath> using namespace std; const int N = 10; int main() { int A = 0; setlocale(LC_ALL, ""); int a[N], b[N], c[N], d[N], e[N], f[N], g[N], h[N], r[N], t[N]; // создание 10 массивов размера N int* p[] = { a,b,c,d,e,f,g,h,r,t }; // создание массива указателей for (int i = 0; i < 10; i++) {// функция заполнения массива указателей на массивы for (int q = 0; q < 10; q++) { p[i][q] = A++; } } for (int i = 0; i < 10; i++) {// функция вывода массива указателей на массивы cout << "Массив " << i << ": "; for (int q = 0; q < 10; q++) { cout << p[i][q] << " "; } cout << endl; } } 10) Описанный в упражнении 9 подход нерационален, так как каждый из 10 массивов объявляется отдельно, с использованием отдельного имени, и каждый адрес получают отдельно. Вы можете упростить программу, используя операцию new, которая позволит вам выделить память для массивов в цикле и одновременно связать с ними указатели: for ( j = 0: j < NUMARRAYS; j++ ) // создаем NUMARRAYS массивов *( ар + j ) = new int [ MAXSIZE ]: //no MAXSIZE целых чисел в каждом Перепишите программу упражнения 9, используя этот подход. Доступ к отдельному элементу массивов вы сможете получить, используя то же выражение, что и в упражнении 9, или аналогичное выражение с указателями: *(*(ap+j)+k). #include <iostream> #include <string> #include <iomanip> #include <cmath> using namespace std; int main() { int A = 0; setlocale(LC_ALL, ""); int n = 10, m = 10; int** a = new int* [n]; // создание указателя на указатель (массив указателей) int* ap; // объявление указателя ap = *a; // присвоение указателя for (int j = 0; j < n; j++) { // цикл заполнения ap = new int[m] - j; a[j] = (ap + j); for (int i = 0; i < m; i++) { a[j][i] = A++; } } for (int i = 0; i < n; i++) {// цикл вывода cout << "Массив " << i << ": "; for (int q = 0; q < m; q++) { cout << a[i][q] << " "; } cout << endl; } delete a; } 11) Создайте класс, который позволит вам использовать 10 отдельных массивов из упражнения 10 как один одномерный массив, допуская применение операций массива. То есть мы можем получить доступ к элементам массива, записав в функции main() выражение типа a[j], а методы класса могут получить доступ к полям класса, используя двухшаговый подход. Перегрузим операцию [ ], чтобы получить нужный нам результат. Заполним массив данными и выведем их. Хотя для интерфейса класса использованы операции индексации массива, вам следует использовать указатели внутри методов класса. #include<iostream> using namespace std; class tenmas // создания класса для работы с массиврм { private: int** mas; public: tenmas() // создание конструктора по умолчанию { mas = new int* [10]; // освоюождение памяти для 10 элементов памяти for (int i = 0; i < 10; i++) { *(mas + i) = new int[10]; } } void input(int x, int y, int n) { *(*(mas + x) + y) = n; } void input(int* x, int y, int n) // перегрузка функции input { *(x + y) = n; } void print(int x, int y, int n) { cout << *(*(mas + x) + y) << endl; } void print(int* x, int y) // перегрузка функции print { cout << *(x + y) << endl; } int* operator [](int a) { return *(mas + a); } }; int main() { setlocale(LC_ALL, "Russian"); tenmas tm; tm.input(tm[2], 3, 4); tm.print(tm[2], 3); return 0; } 12) Указатели сложны, поэтому давайте посмотрим, сможем ли мы сделать работу с ними более понятной (или, возможно, более непонятной), используя их симуляцию в классе. Для разъяснения действия наших доморощенных указателей мы смоделируем память компьютера с помощью массивов. Так как доступ к массивам всем понятен, то вы сможете увидеть, что реально происходит, когда мы используем для доступа к памяти указатели. Мы будем использовать один массив типа char для хранения всех типов переменных. Именно так устроена память компьютера: массив байтов (тип char имеет тот же размер), каждый из которых имеет адрес (или, в терминах массива, индекс). Однако C++ не позволит нам хранить данные типа float или int в массиве типа char обычным путем (мы можем использовать объединения, но это другая история). Поэтому мы создадим симулятор памяти, используя отдельный массив для каждого типа данных, которые мы хотим сохранить. В этом упражнении мы ограничимся одним типом float, и нам понадобится массив для него. Назовем этот массив fmemory. Однако значения указателей (адреса) тоже хранятся в памяти, и нам понадобится еще один массив для их хранения. Так как в качестве модели адресов мы используем индексы массива, то нам потребуется массив типа int, назовем его pmemory, для хранения этих индексов. #include <iostream> #include <iomanip> #include <conio.h> #include <sstream> #include <string.h> using namespace std; float fmemory[100]; int fmem_top = 0; int pmemory[100]; int pmem_top = 0; class Float { int addr = -1; public: Float(float f) { fmemory[fmem_top] = f; addr = fmem_top++; } int operator & () { return addr; } }; class ptrFloat { int addr; public: ptrFloat(int i) { pmemory[pmem_top] = i; addr = pmem_top++; } float& operator * () { return fmemory[pmemory[addr]]; } }; int main() { Float f1 = 1.234; Float f2 = 5.678; ptrFloat p1 = &f1; ptrFloat p2 = &f2; cout << "*p1 = " << *p1 << endl << "*p2 = " << *p2 << endl; *p1 = 7.123; *p2 = 8.456; cout << "*p1 = " << *p1 << endl << "*p2 = " << *p2 << endl; }