МІНІСТЕРСТВО ОСВІТИ ТА НАУКИ УКРАЇНИ ДЕРЖАВНИЙ ВИЩИЙ НАВЧАЛЬНИЙ ЗАКЛАД ДОНЕЦЬКИЙ НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ Факультет “Комп’ютерних наук і технологій” Кафедра “Комп’ютерної інженерії” МЕТОДИЧНІ ВКАЗІВКИ Й ЗАВДАННЯ ЩОДО ВИКОНАННЯ ЛАБОРОТОРНИХ РОБІТ З ДИСЦИПЛІНИ «ОБ’ЄКТНО – ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ» для студентів очної, заочної та очно-заочної форм навчання з напрямку «Комп’ютерна інженерія» Затверджено на засіданні методичної комісії з напрямку «Комп’ютерна інженерія» Протокол N_7_от «_15_»_02_2010 р. Донецьк, ДонНТУ, 2010 УДК 681.3 Методичні вказівки й завдання щодо виконання лабораторних робіт з дисципліни «Об’єктно – орієнтоване програмування» (для студентів очної, заочної та заочної прискореної з наданням денних освітніх послуг форм навчання з напрямку «Комп’ютерна інженерія») / Уклад.: Р.В. Мальчева, Т.О. Приходько. - Донецьк, ДонНТУ, 2010. - 80 с. Надано перелік та зміст лабораторних робіт для вивчення об’єктно – орієнтованої технології програмування на мові С++. Наведені методичні вказівки щодо виконання лабораторних робіт, теоретичний матеріал, варіанти завдань й приклади розробки програмних модулів. Видання містить термінологічний словник з об’єктно – орієнтованої технології, стислий опис системи позначок. Призначено для студентів вищих навчальних закладів, які навчаються за напрямком підготовки «Комп’ютерна інженерія». Укладач: к.т.н , доцент Р.В. Мальчева к.т.н., асистент Т.О. Приходько Рецензент: Н.Н. Дацун, доцент Відпов. за випуск: В.А. Святний, зав. каф., проф. 2 Аннотация Курс «Объектно-ориентированное программирование» базируется на разделе «Структуры данных и алгоритмы» курса «Программирование». Целью курса является введение студентов в развитие формальных концепций структур данных, алгоритмов и их реализации. Изучение курса помогает развить аналитические основы проектирования алгоритмов и типов данных, также фокусирует внимание на комплексном анализе процесса проектирования программного продукта. Эффективность алгоритмов во временной и пространственной (пространство памяти) общности, использование абстрактных типов данных. После изучения этого модуля студенты будут знакомы с инструментами определения пригодности различных алгоритмов для решения конкретных проблем. Лабораторные работы выполняются на языке программирования С++. Курс покрывает все главные аспекты синтаксиса языка, показывает как он реализует особенности ООП. В конце курса обсуждаются предпосылки и типовые задачи, приведшие к появлению объектно-ориентированного подхода, а также дается краткий обзор других объектно-ориентированных языков. Анотація Курс «Об'єктно-орієнтоване програмування» базується на розділі «Структури даних і алгоритми» курсу «Програмування». Метою курсу є введення студентів в розвиток формальних концепцій структур даних, алгоритмів і їх реалізації. Вивчення курсу допомагає розвинути аналітичні основи проектування алгоритмів і типів даних, також фокусує увагу на комплексному аналізі процесу проектування програмного продукту. Ефективність алгоритмів в тимчасовій і просторовій (простір пам'яті) спільності, використання абстрактних типів даних. Після вивчення цього модуля студенти будуть знайомі з інструментами визначення придатності різних алгоритмів для вирішення конкретних проблем. Лабораторні роботи виконуються на мові програмування С++. Курс покриває всі головні аспекти синтаксису мови, показує як вона реалізує особливості ООП. В кінці курсу обговорюються передумови і типові задачі, що привели до появи об'єктно-орієнтованого підходу, а також дається короткий огляд інших об'єктно-орієнтованих мов. 3 СОДЕРЖАНИЕ стр. Введение 5 Лабораторная работа №1 “Одномерный массив - вектор” 6 Лабораторная работа №2 “Двумерный массив – матрица” 17 Лабораторная работа №3 “Обработка строк” 26 Лабораторная работа №4 “Перегрузка операций” 41 Лабораторная работа №5 “ Механизм создания порожденных классов. Наследование” 54 Лабораторная работа №6 «Множественное наследование» 59 Рекомендованная литература 64 Приложение А: Система обозначений ООП 65 Приложение В: Терминологический словарь 69 4 ВСТУП Настоящий практикум составлен в соответствии с программами курса “Объектно-ориентированное программирование” и предназначен для студентов специальности “Компьютерная инженерия” как дневного, так и заочного ускоренного отделений. Цель практикума – закрепить знания, полученные при изучении теоретической части курсов и получить практические навыки разработки объектно-ориентированных программ. Практикум охватывает все разделы объектно-ориентированного программирования на языке С++ и включает выполнение шести лабораторных работ. Лабораторные работы посвящены базовыми понятиями С++, такими как объекты и классы, наследование, полиморфизм, виртуальные функции, перегрузка операций, шаблоны функций и классов. В пособии для каждой лабораторной работы указаны цель и основное содержание работы. Приведены теоретические сведения, необходимые для проведения работы, порядок выполнения работы и методические указания. 5 ЛАБОРАТОРНАЯ РАБОТА 1 “Одномерный массив - вектор” Цель. Освоение основ технологии объектно-ориентированного программирования на примере создания класса «вектор» для описания одномерного массива. 1. Общие указания По определению одномерный массив – это последовательность элементов памяти (т.е. элементы имеют соседние адреса) одного типа. Таким образом, одномерный массив определяется тройкой: 1) Начальный адрес (логическое имя массива). 2) Тип элемента 3) Кол-во элементов данного типа (размерность). Память класса имеет вид [private: ] <тип> *<имя>; int dim; // размерность Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть ввод, вывод элементов вектора; 3 функции обработки в соответствии с вариантом задания. 2. Пример реализации ЗАДАНИЕ 1: Создать класс «вектор» для описания одномерного массива и вычислить сумму его элементов. Шаг 1. Создать файл “vect.hpp” с описанием класса «Vect». # include <stdio.h> class Vect { int *v; // начальный адрес одномерного массива int dim; // размерность public: Vect(int); // конструктор int dimen() // функция, возвращающая размерность массива {return dim;} void in_val_a(FILE *fin); // ввод элементов массива из файла void display(FILE *fout); // вывод массива в файл void show();// вывод массива на экран int sum(); // вернуть сумму элементов массива 6 }; // конец описания класса Шаг 2. Создать файл “vect.сpp” с реализацией интерфейса класса «Vect». # include <iostream.h> # include "vect.hpp" Vect::Vect(int d) // конструктор { if (d<=0) printf("\n Size error:"); else { dim=d; v=new int[d];};//задам размерность вектора, выделяем память } void Vect:: in_val_a(FILE *fin) { int i; for (i=0; i<dim; i++) { fscanf(fin,"%d",&v[i]); //инициализация массива элементами из файла } } int Vect::sum() // подсчет суммы элементов вектора { int i,s=0; for (i=0; i<dim; i++) { s=s+v[i]; } return s; } void Vect:: display(FILE *fout) // вывод вектора в файл { int i; for (i=0; i<dim; i++) fprintf(fout,"\n member i=%d equal a=%d- ",i,v[i]); } void Vect::show() { int i; for (i=0; i<dim; i++) cout<<"num"<<i<<"="<<v[i]<<"\n"; } Шаг 3. Главная программа (файл “vect_main.cpp”) #include "vect.сpp" void main() { FILE *ff,*fin; int i,n; 7 int sx; ff=fopen("rez.dat","w+"); fin=fopen("init.dat","r"); Vect x(10); x.in_val_a(fin); x.display(ff); x.show(); getch(); sx=x.sum(); fprintf(ff,"\n sum=%d",sx); fin=fopen("int2.dat","r"); Vect y(10); y.in_val_a(fin); y.display(ff); у.show(); getch(); sx=y.sum(); fprintf(ff,"\n sum=%d",sx); fclose(ff); fclose(fin); } ЗАДАНИЕ 2: Создать класс «вектор» для описания одномерного массива и реализовать следующие функции его обработки: - вычислить сумму его элементов; - вычислить сумму элементов, имеющих четное значение; - вычислить сумму элементов, имеющих нечетное значение; - вычислить произведение элементов; - вычислить произведение элементов, имеющих отрицательное значение; - удалить элемент с индексом К. Файл “ar_ext.hpp” // описание класса # include<stdio.h> #include <conio.h> class Vect { int *q; int dim; public: Vect(int); int dmen() {return dim;} int& operator[](int); int& elem(int i) {return q[i];} void in_val_a(FILE *fin); void display(FILE *fout); void show(); int sum(); int sum_odd(); 8 int sum_even(); int product(); int pr_neg(); int del_el(int k); }; // конец описания класса Файл “ar_ext.сpp” // реализация класса #include “ar_ext.hpp” // Implementation Vect::Vect(int d) { if (d<=0) printf("\n Size error:"); else { dim=d; q=new int[d];}; } int& Vect::operator[](int i) { if(i<0||dim<=i) printf("Index error"); return q[i]; } void Vect:: in_val_a(FILE *fin) { int i; for (i=0; i<dim; i++) { fscanf(fin,"%d",&q[i]); } } int Vect::sum() { int i,s=0; for (i=0; i<dim; i++) { s=s+q[i]; } return s; } int Vect::del_el(int k) // удалить элемент с индексом k { int i; for (i=k+1; i<dim; i++) { q[i-1]=q[i]; } dim--; 9 return dim; } int Vect::sum_odd() // сумма нечетных элементов { int t,i,s=0; for (i=0; i<dim; i++) { t=q[i] % 2; if (t!=1) { s=s+q[i]; } } return s; } int Vect::sum_even() // сумма четных элементов { int t,i,s=0; for (i=0; i<dim; i++) { t=q[i] % 2; if (t>0) { s=s+q[i]; } } return s; } int Vect::product() // произведение всех элементов { int i; int p=1; for (i=0;i<dim; i++) { p=p*(q[i]); } return p; } int Vect::pr_neg() //произведение отрицательных элементов { int i; int p=1; for (i=0;i<dim; i++) { if (q[i]<0) p=p*(q[i]); } return p; } 10 void Vect:: display(FILE *fout) // вывод в файл { int i; for (i=0; i<dim; i++) fprintf(fout,"\n member i=%d equal a=%d- ",i,q[i]); } void Vect::show() { int i; for (i=0; i<dim; i++) cout<<"num"<<i<<"="<<v[i]<<"\n"; } Файл “ar2_main.cpp”) #include “ar_ext.сpp” void main(void) { FILE *f1,*f2,*fr; int a,s; int Sx; int Px; fr=fopen("O.dat","w+"); f1=fopen("A1.dat","r"); clrscr(); fprintf(fr,"\n VECT X:"); Vect x(6); x.in_val_a(f1); x.display(fr); x.show(); getch(); Sx=x.sum_odd(); Px=x.product(); fprintf(fr,"\n sum of odd=%d,\n product=%d",Sx,Px); Sx=x.sum_even(); fprintf(fr,"\n sum of even=%d",Sx); s=x.del_el(4); fprintf(fr,"\n VECT X (after deleting of 4-th element: dim=%d",x.dmen()); x.display(fr); fprintf(fr,"\n VECT Y:"); f2=fopen("B2.dat","r"); Vect y(5); y.in_val_a(f2); y.display(fr); у.show(); getch(); Sx=y.sum(); Px=y.pr_neg(); fprintf(fr,"\n sum=%d,\n product(for negatives) =%d",Sx,Px); fclose(f1); fclose(f2); fclose(fr); } 11 3. Варианты задания Создать класс «вектор» для описания одномерного массива и произвести с ним следующие операции, согласно варианту: № 1. 2. 3. 4. 5. 6. Задание Дан массив целых чисел X=(x1,x2,...,xn). Сформировать массив Y=(y1,y2,...,ym), поместив в него в порядке убывания все различные (неповторяющиеся) числа, входящие в массив X. Определить, насколько отличаются средние арифметические значения элементов массивов X и Y. Определить наибольший общий делитель всех чисел массива. Массив Х=(x1,x2,...,xn) содержит большое количество нулевых элементов. Определить положение и размер наиболее длинной серии таких элементов. И удалить ее из состава массива. (Длина массива при этом уменьшится) Удалить из нового массива X=(x1,x2,...,xn) все элементы, превышающие его среднее арифметическое значение S, кроме первого такого элемента, и определить, как при этом изменилось значение S. Буферный массив не использовать. Заданы два массива X=(x1,x2,...,xn) и Y = (y1,y2,...,ym), в состав которых входят натуральные числа, причем в каждом из этих массивов нет повторяющихся элементов. Сформировать массив Z, включив в него все элементы, которые одновременно содержатся в массиве X и массиве Y. Подсчитать количество неповторяющихся элементов в обоих массивах. Все положительные числа в массиве Z переставить в обратном порядке, не изменяя положения остальных чисел. Буферный массив не использовать. Задан целочисленный массив X=(x1,x2,...,xn), в котором могут быть одинаковые числа. Найти максимальный и минимальный элементы среди неповторяющихся чисел. и обменять их местами. Учесть частный случай, когда в массиве нет неповторяющихся чисел. Подсчитать количество повторяющихся элементов. Из массива целых положительных чисел X=(x1,x2,...,xn) удалить все четные по значению элементы, кроме последнего, после чего оставшиеся числа расположить в порядке возрастания. Учесть частные случаи ( в массиве нет четных элементов, имеется только один четный элемент, все элементы - четные). Буферный массив не использовать. При однократном просмотре массива X найти два максимальных по модулю элемента, кратных соответственно числам 2 и 3, и, если такие элементы существуют и они не совпадают друг с другом, переставить их местами в массиве. В массиве X=(x1,x2,...,xn) поменять местами первый и второй отрицательные элементы, третий и четвертый отрицательные элементы и т.д. Если количество отрицательных элементов в массиве меньше двух, преобразование массива не производить. Определить, как изменилось положение минимального и максимального элементов массива Х при его преобразовании, для этого написать функции нахождения минимума и максимума. 12 № 7. 8. 9. 10. 11. 12. 13. Задание Элементы массива X = (x1,x2,...,xn) - это последовательность цифр целого числа, записанного в системе счисления с основанием q, 1 < q <= 10, 0 <= x[i] < q. Переставить цифры числа в обратном порядке и отпечатать десятичное значение этого числа до и после перестановки. Найти максимальный и минимальный элементы среди неповторяющихся чисел. Известно, что в целочисленном массиве X=(x1,x2,...,xn) три и только три числа равны между собой. Найти эти числа и переместить их в начало массива, сдвинув остальные числа к концу этого массива. найти самый длинный подмассив, который является арифметической прогрессией. За однократный просмотр массива найти его максимальный положительный элемент Xmax и определить среднее арифметическое значение Найти НОК всех элементов массива, за исключением элементов, равных Xmax. Указание. В программе должны быть учтены частные случаи, в том числе: - в массиве нет положительных элементов; - все элементы массива положительны и равны друг другу. В целочисленном массиве X=(x1,x2,...,xn) каждую пару x[i] и x[j] (i,j=1,...,n, i<>j) нечетных элементов преобразовать в четные элементы по формулам: x[i]:=x[i]+1, x[j]:=x[j]-1. Пары элементов i,j выбирать в порядке их следования в массиве X. Определить, как при этом изменились среднее арифметическое массива X. Подсчитать количество повторяющихся элементов. Даны два целочисленных массива X=(x1,x2,...,xn) и Y=(y1,y2,...,ym). Пусть в массиве Х имеется k1 четных элементов, а в массиве y - k2 нечетных элементов. Обменять местами k=min(k1,k2) четных элементов массива Х с нечетными элементами массива Y (в порядке их следования в массивах Х и Y). Учесть, что в частном случае может быть k=0. Создать третий массив, в который записать элементы двух исходных массивов в порядке чет-нечет. Найти сумму элементов двух исходных массивов. Преобразовать массив X, расположив вначале его отрицательные, а затем неотрицательные элементы, сохранив при этом в группе отрицательных элементов их исходный относительный порядок, а в группе неотрицательных элементов изменив его на обратный. Определить, как при этом изменилось положение минимального по модулю элемента массива X. Буферный массив не использовать. Создать второй массив, в который записать произведения i-го и n-го, (i+1)-го и (n1)-го и .т.д. элементов. Заданный вещественный массив X=(x1,x2,...,xn) усреднить следующим образом: максимальный и минимальный элементы заменить их средним арифметическим значением, то же сделать по отношению к максимальному и минимальному элементам преобразованного массива Х и т.д. n/2 раз. Если в очередном цикле обработки массива Х обнаружится, что его максимальный и минимальный элементы отличаются между собой не более чем на значение eps (eps - достаточно малая величина), то дальнейшее преобразование массива Х не производить. 13 № 14. 15. 16. 17. 18. 19. Задание Определить, как изменились среднее арифметическое значение S И среднее квадратическое отклонение G элементов массива Х после его преобразования. Вещественный массив X=(x1,x2,...,xn) содержит несколько отрицательных элементов, разделяющих его на отдельные подмассивы. Сгруппировать элементы каждого подмассива в порядке возрастания. Учесть частные случаи ( в массиве нет отрицательных элементов; подмассив пустой или содержит только один элемент). Сформировать два отдельных массива для положительных и отрицательных элементов. Подсчитать наименьшее общее кратного для каждого из них. В вещественном массиве X=(x1,x2,...,xn) каждую пару x[i] и x[j] (i,j=1,...,n; i<>j) отрицательных элементов преобразовать в положительные элементы по формуле x[i],x[j]:=sqrt(x[i]*x[j]). Пары элементов (i,j) выбирать в порядке их следования в массиве X. Определить, как при этом изменилось среднее арифметическое значение элементов массива X. Сформировать массив Y, включив в него все неповторяющиеся элементы из массива X. Заданы два массива X=(x1,x2,...,xn) и Y = (y1,y2,...,ym), в состав которых входят натуральные числа, причем в каждом из этих массивов нет повторяющихся элементов. Сформировать массив Z, объединив массивы X и Y; при этом в массиве Z также не должно быть повторяющихся элементов. Найти монотонные (т.е. либо неубывающие, либо невозрастающие) подпоследовательности в массиве Z. Упорядочить их в порядке убывания их длины. Заданы массивы A=(a1,a2,...,an) и B=(b1,b2,...,bn), C =(c1,c2,...,cn), элементы которых представляют собой координаты вершин треугольника на плоскости (a1,b1,c1)-координата х 1-го треугольника, (a2,b2,c2)- его координата у. и.т.д. Найти в массивах отрицательные элементы, преобразовать их в их модули. Определить порядковые номера треугольников с максимальным и минимальным периметрами. В массиве X=(x1,x2,...,xn) расположить в порядке убывания входящие в его состав положительные элементы, а затем в порядке возрастания - отрицательные элементы. Нулевые элементы, если они имеются в массиве X, расположить между группой положительных и группой отрицательных элементов. Определить, как при этом изменилось положение максимального и минимального элементов массива Х.Буферный массив не использовать. При однократном просмотре исходного массива X определить значение и положение (индекс) трех наименьших элементов, после чего переставить их местами в обратном порядке. Заданы два целочисленных массива Х=(x1,x2,...,xn) и Y=(y1, y2,...,ym). B состав массива Х дополнительно включить те элементы из массива Y, которые отсутствуют в массиве X. Определить, как при этом изменилось среднее арифметическое значение элементов массива X. 14 № 20. 21. 22. 23. 24. Задание В массиве X определить положение и размер последней серии положительных элементов, в состав которой входит от двух до пяти элементов, после чего переставить элементы серии в обратном порядке. При однократном просмотре целочисленного массива X найти два максимальных числа Xmax1 и Xmax2 соответственно среди четных и нечетных по значению элементов массива, после чего переставить в обратном порядке элементы подмассива, расположенного между этими числами, включая элементы Xmax1 и Xmax2. Все четные числа в целочисленном массиве X=(x1,x2,...,xn) переставить в обратном порядке, не изменяя положение остальных чисел. Элементы вещественного массива X = (x1,x2,...,xn) строго упорядочены по возрастанию, т.е. x1 < x2 < x3 < ... < xn , при этом x[i+1]-x[i] > eps ( i = 1,...,n-1; eps - малое число, например, 0.001). Элементы вещественного массива Y=(y1,y2,...,ym), m <= n расположены в произвольном порядке. Включить в состав массива X те элементы y[j], j = 1..m, которые отличаются от элементов x[i] не менее чем на eps, сохранив при этом упорядоченность массива X. Буферный массив не использовать. подсчитать количество положительных k1 и количество отрицательных k2 элементов массива X. Если k=k1-k2<>0, то изменить знаки стольких положительных или отрицательных элементов, чтобы выполнялось условие abs(k)<=1. Задан целочисленный массив X=(x1,x2,...,xn), в котором могут быть одинаковые числа. Определить есть ли повторяющиеся числа. Найти максимальный и минимальный элементы среди повторяющихся чисел и обменять их местами. Учесть частные случаи (в массиве нет повторяющихся чисел, максимальный и минимальный элементы равны друг другу). Буферный массив не использовать. Выполнить циклический сдвиг массива X=(x1,x2,...,xn) на k элементов (0 <= k <= n) по направлению, которое определяется значением переменной s ( s = 0 - влево, s = 1 - вправо). Например, при циклическом сдвиге массива 4 -8 6 12 1 0 7 9 влево на 3 элемента получим 12 1 0 7 9 4 -8 6 . Значения переменных k и s ввести с клавиатуры. Найти серии отрицательных элементов. Элементы каждой серии отрицательных элементов вещественного массива X=(x1,x2,...,xn) переставить в обратном порядке. Элементы массивов X=(x1,x2,...,xn) и Y=(y1,y2,...,yn) определяют координаты точек ломаной линии. Удалить из состава ломаной отрезок минимальной длины и отрезок максимальной длины. Определить, как при этом изменилась общая длина ломаной линии и средняя длина ее отрезков. 15 № 25. Задание Значение целой части неотрицательного вещественного числа задано в виде массива двоичных цифр a[n],a[n-1],a[n-2],...,a[1],a[0], значение его дробной части - в виде массива двоичных цифр b[1],b[2],...,b[m]. По отношению к этому числу выполнить следующее: - удалить незначащие нули в целой и дробной части, если они имеются; - округлить дробную часть до четырех двоичных цифр. Учесть частные случаи: - все элементы a[i] = 0; - все элементы b[j] = 0; - все элементы a[i] и b[j] равны нулю. Отпечатать полученное десятичное значение. 4. Требования к отчету 1. 2. 3. 4. 5. Обоснование выбора структуры памяти класса и его интерфейса. Блок-схемы алгоритмов функций обработки (ПО3). Описание класса + схемы (ПО2, ПО4). Результаты тестирования класса. Заключение и рекомендации по совершенствованию класса и/или тестирующей программы. 5. Контрольные вопросы 1. 2. 3. 4. Что значит в ООП понятие «класс», и какой формат его объявления в программе? Что такое объект класса, что он содержит? Какие существуют уровни доступа к объектам и методам класса (дать характеристику каждому)? Что такое операция привязки, ее основное назначение? 16 ЛАБОРАТОРНАЯ РАБОТА 2 “Двумерный массив - матрица” Цель. Освоение основ технологии объектно-ориентированного программирования на примере создания класса «матрица» для описания двумерного массива. 1. Общие указания По определению двумерный массив (как и одномерный) – это последовательность элементов памяти (т.е. элементы имеют соседние адреса) одного типа. Таким образом, двумерный массив определяется четверкой: 1) Начальный адрес (логическое имя массива); 2) Тип элемента; 3) Кол-во элементов данного типа в строке; 4) Кол-во элементов данного типа в столбце; Рисунок иллюстрирует двухмерный массив а. Массив содержит три строки и четыре столбца, так что, как говорят, - это массив три на четыре. Вообще, массивы с m строками и n столбцами называют массивами m на n. Каждый элемент в массиве а определяется именем элемента в форме а[di][dj]; а - это имя массива, di и dj - индексы, которые однозначно определяют каждый элемент в а. Память класса имеет вид [private: ] <тип> *<имя>; int dm_l; // число строк int dm_c; // число столбцов Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть ввод, вывод элементов массива; 3 функции обработки, в соответствии с вариантом задания. 17 2. Пример реализации ЗАДАНИЕ 1: Создать класс «матрица» для описания двумерного массива и реализовать следующие операции: - вычислить сумму его элементов; - заменить отрицательные элементы нулевыми значениями. Файл “matr.hpp” – класс matrix # include<stdio.h> class matrix { int *m; // адрес матрицы int dm_l; // количество строк – 1-я размерность int dm_c; // количество столбцов – 2-я размерность public: //функции matrix(int,int); // конструктор // получить размерности int dmen_l() {return dm_l;} int dmen_c() {return dm_c;} // ввод void in_val_a(FILE *fin); void in_val(); //вывод void display(FILE *fout); void screen(); // сумма int sum(); // замена отрицательных элементов нулями void edit(); }; // конец описания класса Файл “matr.сpp” – реализация интерфейса класса matrix #include "matr.hpp" // Implementation matrix::matrix(int d_l, int d_c) { if ( (d_l<=0) || (d_c<=0) ) printf("\n Size error:"); else { dm_l=d_l; dm_c=d_c; m=new int[d_l*d_c];}; } void matrix:: in_val_a(FILE *fin) { int i,j; 18 for (i=0; i<dm_l; i++) for (j=0; j<dm_c; j++) { fscanf(fin,"%d",m+i*dm_c+j); } } void matrix:: in_val() { int i,j; for (i=0; i<dm_l; i++) for (j=0; j<dm_c; j++) { printf("\n Input value:"); scanf("%d",(m+i*dm_c+j)); } } int matrix::sum() { int i,j,s=0; for (i=0; i<dm_l; i++) for (j=0; j<dm_c; j++) { s=s+*(m+i*dm_c+j); } return s; } void matrix::edit() { int i,j; for (i=0; i<dm_l; i++) for (j=0; j<dm_c; j++) { if (*(m+i*dm_c+j)<0) *(m+i*dm_c+j)=0; } } void matrix:: display(FILE *fout) { int i,j; for (i=0; i<dm_l; i++) {fprintf(fout,"\n"); for (j=0; j<dm_c; j++) fprintf(fout," %d ",*(m+i*dm_c+j)); } } void matrix::screen() { int i,j; 19 for (i=0; i<dm_l; i++) { printf("\n"); for (j=0; j<dm_c; j++) printf(" %d ",*(m+i*dm_c+j)); } } Главная программа - файл “Matrix_m.cpp” #include <stdio.h> #include "matr.сpp" void main() { FILE *ff,*fin; int i,n; int sx; ff=fopen("m_rez.dat","w+"); fin=fopen("m_init.dat","r"); matrix x(4,4); printf("\n Dimension: %d %d",x.dmen_l(),x.dmen_c()); x.in_val_a(fin); x.display(ff); sx=x.sum(); fprintf(ff,"\n sum=%d",sx); edit(); printf("\n Dimension: %d %d",x.dmen_l(),x.dmen_c()); x.display(ff); sx=x.sum(); fprintf(ff, "\n New sum=%d",sx); fclose(ff); fclose(fin); } Data-file “M-init.dat” -1 -2 -3 -4 -2 -3 3 4 -5 -3 -4 -5 -6 -4 -5 -6 Sum=-46 Result file “M_res.dat” 0 0 0 0 0 0 3 4 0 0 0 0 0 0 0 0 Sum=7 3. Варианты задания Создать класс «матрица» для описания двумерного массива и произвести с ним следующие операции, согласно варианту: 20 № 1. 2. 3. 4. 5. 6. 7. Задание Задана квадратная матрица. Переставить в обратном порядке элементы тех столбцов матрицы, которые расположены ниже ее главной диагонали. В каждой строке матрицы отрицательным элементам присвоить нулевое значение, после чего перенести все положительные элементы в начало строки в порядке их исходного относительного расположения. Каждую элемент строки прямоугольной матрицы заменить суммой элементов этого столбца, за вычетом текущего элемента. Определить значение и местоположение максимального элемента матрицы до и после ее преобразования. Определить значение и местоположение минимального элемента матрицы до и после ее преобразования. Для каждого столбца прямоугольной целочисленной матрицы подсчитать сумму входящих в него элементов и определить, имеются ли столбцы с одинаковой суммой. Подсчитать количество пар таких столбцов. каждый нулевой элемент заменить средним арифметическим значением ненулевых элементов той строки, в которой расположен этот элемент. Выполнить текущее сглаживание каждой строки прямоугольной матрицы и определить максимальное отклонение ее элементов от среднего арифметического значения данной строки до и после сглаживания. Примечание. При текущем сглаживании массива x1,x2,...,xn каждый j-ый элемент массива (j=2,3,...,n-1) заменяется средним арифметическим значением элементов с индексами j-1,j,j+1. В каждой строке прямоугольной матрицы перенести максимальный элемент в последнюю позицию строки, сдвинув при этом влево расположенные после него элементы. Учесть частный случай, когда максимальный элемент уже находится в последней позиции строки. Пример. Строка 5 18 21 12 10 24 13 17 8 10 после преобразования будет иметь вид 5 18 21 12 10 13 17 8 10 24 . В каждом столбце прямоугольной матрицы найти минимальный по модулю элемент и, если он не является диагональным, поменять его местами с диагональным элементом. Подсчитать количество таких перестановок. Дана квадратная матрица. Если в ее треугольной части, расположенной выше диагонали, имеются нулевые элементы, то заменить каждый из них минимальным, но отличающимся от нуля, значением элементов столбца, в котором расположен нулевой элемент. Для каждой строки матрицы определить сумму ее положительных элементов, а затем сгруппировать строки в порядке убывания этих сумм. В каждой строке прямоугольной матрицы подсчитать количество изолированных положительных элементов, т.е. элементов, окруженных слева и справа хотя бы одним неположительным элементом. Первый и последний элементы строки не учитывать. Определить номер строки, имеющей максимальное количество таких элементов. В каждом столбце матрицы найти минимальный элемент и определить номер столбца, имеющего максимальное по мо дулю значение такого элемента. 21 № 8. 9. 10. 11. 12. 13. 14. Задание В прямоугольной матрице определить значение и местоположение элемента, имеющего максимальное превышение по отношению к среднему значению соседних элементов. Элементы, расположенные на периметре матрицы, не анализировать. Указание. Для элемента с индексами (i,j), i=2..(m-1),j=2..(n-1) соседними элементами считать элементы, смежные с ним по вертикали и по горизонтали. определить номер столбца, сумма элементов которого максимальна. Элементы каждой строки прямоугольной матрицы заменить их дополнениями до максимального элемента этой же строки. Определить, насколько при этом изменится общая сумма элементов матрицы. Определить, имеются ли в прямоугольной матрице линейно зависимые строки и подсчитать количество пар таких строк. Примечание. Две строки матрицы линейно зависимы, если одну из них можно получить из другой умножением на постоянный коэффициент. каждый нулевой элемент заменить средним арифметическим значением ненулевых элементов того столбца, в котором расположен этот элемент. В прямоугольной матрице найти два элемента, которые в наименьшей и в наибольшей степени отличаются от среднего арифметического значения элементов данной матрицы, после чего обменять их местами. определить количество строк, элементы которых полностью упорядочены по убыванию определить, имеются ли в прямоугольной матрице линейно зависимые строки Элементами квадратной матрицы могут быть только числа -1, 0 или 1. Для каждого из столбцов матрицы выполнить следующее: если сумма элементов столбца не равна нулю, то заменить часть нулевых элементов значением +1 или -1 таким образом, чтобы указанная сумма как можно меньше отличалась от нуля. проверить, имеет ли место совпадение k-ой строки и k-го столбца (k=1..n). Отпечатать номера совпадающих строк и столбцов. К каждому элементу главной диагонали квадратной матрицы прибавить такое значение, чтобы сумма элементов в соответствующем столбце матрицы была нулевой. Сформировать вектор-строку, i-ая компонента которого равна приращению значения соответствующего элемента главной диагонали, после чего сгруппировать столбцы матрицы в порядке возрастания компонент этого вектора. Среди диагоналей квадратной матрицы, параллельных главной диагонали и расположенных ниже нее, найти такую, сумма модулей элементов которой максимальна по сравнению с другими диагоналями. каждый нулевой элемент заменить средним арифметическим значением ненулевых элементов того столбца, в котором расположен этот элемент. определить количество строк, элементы которых полностью упорядочены по возрастанию В прямоугольной матрице рассмотреть квадратные подматрицы размерностью 1,2,3,... , причем для всех подматриц левым верхним элементом является элемент исходной матрицы с индексами (1,1).Определить номер подматрицы, среднее арифметическое элементов которой имеет наибольшее значение. 22 № 15. 16. 17. 18. 19. Задание найти номера столбцов, содержащих соответственно максимальное и минимальное количество отрицательных элементов, после чего обменять их местами. Учесть частные случаи (в матрице нет отрицательных элементов; лишь один столбец матрицы содержит такие элементы; лишь два столбца содержат отрицательные элементы, но их количество одинаково). определить значение и местоположение максимального по модулю и минимального по модулю четных элементов, после чего обменять их местами. Обнулить элементы k-ой строки и l-го столбца прямоугольной матрицы, после чего присвоить этим элементам такие значения, что бы сумма элементов каждой строки и сумма элементов каждого столбца, за исключением l-го, была равна нулю. Значения k и l ввести с клавиатуры. Для прямоугольной матрицы найти минимальный из положительных элементов и максимальный из отрицательных элементов, после чего обменять их местами. Нулевые элементы не учитывать. найти максимальный по модулю элемент и заменить его средним арифметическим значением остальных элементов данной строки. В прямоугольной матрице определить количество столбцов, полностью состоящих из положительных элементов. Указание. Последовательный просмотр элементов столбца организовать таким образом, чтобы при обнаружении первого неположительного элемента остальные элементы столбца не проверялись. В прямоугольной матрице часть элементов имеют нулевое значение. Заменить каждый такой элемент полусуммой смежных ему элементов. Примечание. Угловые элементы матрицы имеют два смежных элемента; элементы, расположенные на периметре матрицы, но не являющиеся угловыми, имеют три смежных элемента; остальные элементы матрицы имеют четыре смежных элемента. найти минимальный из положительных элементов и максимальный из отрицательных элементов. В каждой строке прямоугольной матрицы, элементами которой являются целые положительные числа, определить сумму элементов, являющихся удвоенными нечетными числами, после чего сгруппировать строки в порядке убывания этих сумм. Сгруппировать элементы каждой строки, расположенные выше главной диагонали, в порядке убывания их абсолютных значений. В каждой строке прямоугольной матрицы определить разность d между средним арифметическим значением S1 элементов, стоящих на четных местах, и средним арифметическим значением S2 элементов, стоящих на нечетных местах. При этом учесть, что количество столбцов может быть как четное, так и нечетное. Сгруппировать столбцы в порядке убывания параметра d. найти минимальный из положительных элементов и максимальный из отрицательных элементов. Для каждой строки прямоугольной матрицы определить количество нарушений k условия упорядоченности ее элементов по возрастанию, т.е. условия a[i,j]<=a[i,j+1]. Сгруппировать строки в порядке возрастания параметра k. В каждой строке прямоугольной матрицы удалить максимальный элемент, сдвинув на одну позицию влево расположенные после него элементы данной строки. Последнему элементу строки присвоить нулевое значение. 23 № 20. 21. 22. 23. 24. Задание В каждой строке прямоугольной целочисленной матрицы определить процент четных положительных чисел по отношению к общему количеству положительных чисел в данной строке, после чего сгруппировать строки в порядке уменьшения указанного процента. В каждом столбце прямоугольной матрицы поменять местами минимальный элемент с элементом главной диагонали, если элемент главной диагонали, относящийся к данному столбцу, имеет отрицательное значение. Рассматривая в квадратной матрице диагональ, соединяющую левый нижний элемент с правым верхним элементом, определить минимальное по модулю число среди элементов, расположенных выше данной диагонали, и максимальное по модулю число среди элементов, расположенных ниже этой же диагонали, после чего обменять местами соответствующие этим числам элементы матрицы В каждой строке квадратной матрицы расположить элементы в порядке убывания их абсолютных значений, не затрагивая при этом положения элементов главной диагонали. Для прямоугольной матрицы A (m x n) сформировать одномерный массив B, i-му элементу которого присвоить значение 1, если в i-ой строке имеются по крайней мере три элемента, упорядоченных по убыванию, и значение 0 в противном случае. Переместить в начальную часть матрицы строки, для которых определено значение b[i] = 1, сохранив при этом относительное расположение этих строк. Рассматривая каждый столбец прямоугольной вещественной матрицы как вектор, определить, имеются ли в матрице ортогональные вектор-столбцы. Подсчитать количество таких пар векторов. Примечание. Для ортогональных векторов их скалярное произведение S равно нулю. Значение S считать равным нулю, если abs(S)<eps, где eps - малое число (например, 0.001) . Для каждой строки прямоугольной матрицы, кроме последней, определить количество k элементов, совпадающих по знаку с соответствующими элементами последней строки. В прямоугольной матрице определить значение и местоположение максимального по модулю и минимального по модулю четных элементов, после чего обменять их местами. В прямоугольной матрице определить количество столбцов, cодержащих только числа одного знака (положительные или отрицательные) и не содержащих нулевых элементов. Для каждой строки прямоугольной матрицы определить количество k элементов, значения которых расположены в заданном интервале [a,b]. Значения a и b ввести с клавиатуры. Если в j-ом столбце квадратной матрицы (j=1..n) максимальный элемент находится на главной диагонали, то разделить все элементы данного столбца на значение максимального элемента; в противном случае столбец матрицы оставить без изменений. Подсчитать общее количество преобразованных столбцов матрицы. Для прямоугольной матрицы определить значение и местоположение элемента, являющегося седловой точкой матрицы (если такая имеется). Указание. Седловой точкой матрицы считать не расположенный на ее периметре элемент c индексом (i,j), который является минимальным в i-ой строке и одновременно максимальным в j-ом столбце. 24 № 25. Задание найти максимальный по модулю элемент и заменить его средним арифметическим значением остальных элементов данной строки. Для каждого столбца прямоугольной матрицы, элементами которой являются целые положительные числа, определить сумму входящих в него элементов и, если она нечетная, добавить единицу к значению последнего элемента данного столбца, после чего сгруппировать элементы столбца в порядке убывания. найти номера векторов-строк, для которых модуль их скалярного произведения имеет максимальное значение. 4. Требования к отчету 1. 2. 3. 4. 5. Обоснование выбора структуры памяти класса и его интерфейса. Блок-схемы алгоритмов функций обработки (ПО3). Описание класса + схемы (ПО2, ПО4). Результаты тестирования класса. Заключение и рекомендации по совершенствованию класса и/или тестирующей программы. 25 ЛАБОРАТОРНАЯ РАБОТА 3 “Обработка строк” Цель. Освоение основ технологии объектно-ориентированного программирования на примере создания класса «строка». 1. Общие указания В С++ поддерживаются два типа строк – встроенный тип, доставшийся от С (С-строки у Страуструпа [11] или строковый литерал), и класс srting из стандартной библиотеки С++. В этой лабораторной Вы будете работать со строковым литералом. Строка – это массив символов. Один символ занимает один байт. Строка заканчивается нулевым байтом (символом ‘\0’). Таким образом, строка определяется тройкой: 4) Начальный адрес (логическое имя строки символов). 5) Тип символов 6) Кол-во элементов данного типа (длина строки). Память класса имеет вид [private: ] <тип> *<имя>; // строка int len; // длина строки Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть ввод, вывод элементов вектора; 3 функции обработки, в соответствии с вариантом задания; Описание строки Строка может быть описана 1) как массив с инициализацией: char message[ ] = “now is the time”; 2) как массив с фиксированным количеством символов: char st1[10], str[20]; 3) как динамический массив (через указатель): char *ps, *pt; Размещение в памяти Строка размещается в памяти следующим образом: I a m a s t r i Для следующего массива строк 26 n g ‘\0’ char* menu[] = {“Big Mac”,”Hamburger”, “Cheeseburger”, “Coca”, “Pepsi”}; Внутреннее представление имеет вид: Menu[0] B i g P e p M a c ‘\0’ Menu[1] Menu[2] ... menu[3] menu[4] s ‘\0’ i Использование строк в приложениях Все информационные системы используют строки или массивы строк. 2. Примеры работы со строками в C с использованием библиотеки <string.h> . Эта библиотека содержит ряд функций работы со строковыми литералами. Пример 1. //Ввести математическое выражение в виде строки. //Перед и после каждого знака арифметической операции //(+,-,*,/) вставить один пробел #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> void main() { char *s1; //исходная строка char *s2; //результирующая строка char *bl=" "; //пробел char s[1]; //одиночный символ в строке int i,j,k,l,n; clrscr();//очистка экрана printf("\n Input string - mathematical expression\n"); gets(s1); l=strlen(s1); //определение длины строки printf("\n Length of input string = %d",l); //копируем первый символ из s1 в s2 strncpy(s2,s1,1); printf("\n i=0 "); puts(s2); for (i=1; i<l;i++) { //копируем i-тый символ в s[0] s[0]=*(s1+i); 27 //если это арифметическая операция if ( (s[0]=='+')||(s[0]=='-')|| (s[0]=='/')||(s[0]=='*') ) { //то, копируем пробел,i-тый символ и пробел в s2 strncat(s2,bl,1); strncat(s2,s,1); strncat(s2,bl,1); } else // копируем только i-тый символ в s2 strncat(s2,s,1); printf("i=%d ",i); puts(s2); } printf("\n Length of new string = %d",strlen(s2)); printf("\n New string ="); puts(s2); getch(); } Пример 2. //Конвертирование теста программы на Паскале в C++ //Надо заменить символ '/*' на '{' и '*/' на '}' #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> void main() { char *s1; //input string char *s2; //result string char *sn1="*"; char *sn2="/"; char s[1]; //одиночный символ в строке int i,j,k,l,n; clrscr(); printf("\n Input string - part of pascal program\n"); gets(s1); l=strlen(s1); printf("\n Length of input string = %d\n",l); s2=""; //empty string for (i=0; i<l;i++) { //копируем i-тый символ в s[0] s[0]=*(s1+i); if (s[0]=='{') { //copy to s2 new symbol 1 strncat(s2,sn2,1); strncat(s2,sn1,1); } 28 else { if (s[0]=='}') { //copy to s2 new symbol 2 strncat(s2,sn1,1); strncat(s2,sn2,1); } else //copy only i-th character to s2 strncat(s2,s,1); } puts(s2); } printf("\n Length of new string = %d",strlen(s2)); printf("\n New string ="); puts(s2); getch(); } 2.1. Функции ввода/вывода строк 2.1.1 Работа со строками как с массивом символов getch() Функция Вводит символ с консоли без эхопечати (getch). Или с эхопечатью (getche) Синтаксис #include<conio.h> int getch(void); int getche(void); Файл, содержащий прототип conio.h Описание Функция getch читает один символ, непосредственно с консоли, без вывода его на экран. Возвращаемое значение getch возвращает введенный с клавиатуры символ. Переносимость Функция уникальна для DOS. Функция не совместима с Windows. 29 2.1.2 Строка как единое целое С другой стороны C++ включает специальные функции для ввода/вывода строк как единого целого. gets(str1) Вводит строку символов из потока Синтаксис #include<stdio.h> char *gets(char *s); Файл, содержащий прототип stdio.h Описание Функция gets читает строку символов, оканчивающуюся символом перевода строки в переменную *s из стандартного входного потока stdin. символьная строка оканчивается символом перехода на новую строку, который при записи в *s заменяется на нулевое окончание (\0). В отличие от scanf, gets позволяет вводить строки, содержащие символы пробела и табуляции. Все, что было введено до перевода каретки, помещается в s. Возвращаемое значение При успешном завершении, функция gets возвращает строку s; при достижении конца файла (EOF) или ошибке возвращается NULL. Переносимость Функции поддерживаются на системах UNIX и стандартом ANSI C. Функция совместима с Windows. 2.1.3.Строка как одно слово или строка фиксированной длины Scanf(“%s”,&s) Вводит все символы вплоть до пробела(не включая. Scanf(“%<n>s”,&s) Вводит n символов, где n указывает ширину поля ввода. 30 не Данная 2.2. Другие функции работы со строками Таблица 1 - Функции работы со строками Функция strcmp strcpy strdup strlen strncat strncpy strnset Прототип и краткое описание функции int strcmp(const char *str1, const char *str2); Сравнивает строки str1 и str2. Если str1< str2, то результат отрицательный, если str1 = str2, то результат равен 0, если str1> str2, то результат положительный. char* strcpy(char*s1, const char *s2); Копирует байты из строки s1 в строку s2 char *strdup (const char *str); Выделяет память и переносит в нее копию строки str. unsigned strlen (const char *str); Вычисляет длину строки str. char *strncat(char *s1, const char *s2, int kol); Приписывает kol символов строки s1 к строке s2. char *strncpy(char *s1, const char *s2, int kol); Копирует kol символов строки s1 в строку s2. char *strnset(char *str, int c, int kol); Заменяет первые kol символов строки s1 символом с. Строки, при передаче в функцию, в качестве фактических параметров могут быть определены либо как одномерные массивы типа char[], либо как указатели типа char*. В отличие от обычных массивов в этом случае нет необходимости явно указывать длину строки. Функции преобразования строки S в число: целое: int atoi(S); длинное целое: ошибке возвращает значение 0. long atol(S); действительное: double atof(S); при Функции преобразования числа V в строку S: целое: itoa(int V,char S,int kod); длинное целое: ltoa(long V,char S,int kod); 2<=kod<=36, для отрицательных чисел kod=10. 3. Пример реализации класса «строка» 3.1. Класс Слово (WORD )(строка в одно слово) Пример 3.1. #include <stdio.h> #include <conio.h> #include <string.h> class word { char *word; int l; public: word(); ~word(); 31 void inpw(); void outw(); void invword(); int getl(); }; word::word() { l=20; word=new char[20]; } word::~word() { delete word;} void word::inpw() //ввод с клавиатуры { printf("\nInput word:"); scanf("%s",word); l=strlen(word); } //end of function inpw() void word::outw() // output to screen { printf("%s",word); } //end of function void word::invword() { int i=0,k=l-1; char ch; while (i<k) { ch=word[i]; word[i]=word[k]; word[k]=ch; i++; k--; } } int word::getl() { return l;} void main() { clrscr(); word W1; W1.inpw(); printf("\n Word <"); W1.outw(); printf("> of %d characters ", W1.getl()); W1.invword(); printf(" is word <"); W1.outw(); printf("> in back order"); getch(); } Результат: Input word: abcdef Word <abcdef> of 6 character is word <fedcba> in back order 32 3.2. Класс Сообщение (Message )(строка длиной до 80 символов (одна экранная строка) Пример 3.2. (getch()) #include <stdio.h> #include <conio.h> class mes { char *mes; int l; public: mes(); ~mes(); void inpkey(); void outsc(); int c_words(); int getl(); }; mes::mes() { l=80; mes=new char[80]; } mes::~mes() { delete mes;} void mes::inpkey() //ввод с клавиатуры { char ch; int i; printf("\nInput string:"); i=0; while( ((ch=getch())!=13)&&(i<80) ) {putch(ch); mes[i]=ch; i++;} l=i; mes[i]='\0'; //end-of-string } //end of function inpkey() void mes::outsc() // output to screen { int i=0; char ch; while( (ch=mes[i])!='\0') { printf("%c",ch); i++; } } //end of function int mes::c_words() // подсчет слов в сообщении { int i,k=0; for (i=0; i<l; i++) if (mes[i]==' ') k++; return (k+1); } 33 int mes::getl() { return l;} void main() { clrscr(); mes M1; M1.inpkey(); printf("\n String <"); M1.outsc(); printf("> of %d characters consists of %d words", M1.getl(),M1.c_words()); getch(); } Пример 3.3 (gets) #include <stdio.h> #include <string.h> #include <conio.h> class mes { char *mes; int l; public: mes(); ~mes(); void inp1(); void out1(); int c_words(); int getl(); }; mes::mes() { l=80; mes=new char[80]; } mes::~mes() { delete mes;} void mes::inp1() //ввод с клавиатуры { printf("\nInput string:"); gets(mes); l=strlen(mes); } //end of function inp1() void mes::out1() // output to screen { puts(mes); } //end of function int mes::c_words()// подсчет слов в сообщении { int i,k=0; for (i=0; i<l; i++) 34 if (mes[i]==' ') k++; return (k+1); } int mes::getl() { return l;} void main() { clrscr(); mes M1; M1.inp1(); printf("\n String <"); M1.out1(); printf("> of %d caracters consists of %d words", M1.getl(),M1.c_words()); getch(); mes M2; M2.inp1(); printf("\n String <"); M2.out1(); printf("> of %d character consists of %d words", M2.getl(),M2.c_words()); getch(); } 3.3. Класс Предложение (SENTENCE) (Строка, Предложения, занимающего одну или более строк) состоящая из одного Простое повествовательное предложение заканчивается точкой ‘.’ (full stop). Восклицательное предложение заканчивается ‘!’ (exclamation mark). А вопросительное предложение заканчивается ‘?’ (question-mark). #include <stdio.h> #include <string.h> #include <conio.h> class sent { char *sent; int l; public: sent(); ~sent(); void inpf(FILE *f); void savef(FILE *f); int c_words(); int getl(); void edit(); }; sent::sent() { l=2000;//25*80 sent=new char[2000]; } 35 sent::~sent() { delete sent;} void sent::inpf(FILE *f) //input from file { int i; char ch; i=0; ch=fgetc(f); while( (ch!='.')&&(ch!='!')&&(ch!='?') ) { sent[i]=ch; i++; ch=fgetc(f); } sent[i]=ch; i++; l=i; sent[i]='\0'; } //end of function inpf() void sent::savef(FILE *f) // output to file { int i=0; char ch; while ((ch=sent[i])!='\0') { putc(ch,f); i++; } } //end of function int sent::c_words() // count of words in sentence { int i,k=0; for (i=0; i<l; i++) if (sent[i]==' ') k++; return (k+1); } int sent::getl() { return l;} void sent::edit() { //replace two or more blanks by one int i=0,k=0; char ch; while ((sent[i]!=' ')&&(sent[i]!='\0')) { i++; k++; } i++;k++; while (sent[i]==' ') i++; while (sent[i]!='\0') { while ((sent[i]!=' ')&&(sent[i]!='\0')) { sent[k]=sent[i]; i++; k++; } if (sent[i]!='\0') { sent[k]=' ';i++; k++; while (sent[i]==' ') i++; } } //delete blank befor the sentence-end-mark if (sent[k-2]==' ') {sent[k-2]=sent[k-1]; k--;} 36 l=k; sent[l]='\0'; } void main() { clrscr(); sent S1; FILE *f1; f1=fopen("Sent.dat","r"); FILE *f2; f2=fopen("Sent_res.dat","w+"); S1.inpf(f1); fprintf(f2,"\n Sentence <"); S1.savef(f2); fprintf(f2,"> of %d characters consists of %d words", S1.getl(),S1.c_words()); S1.edit(); fprintf(f2,"\n After edition sentence <"); S1.savef(f2); fprintf(f2,"> of %d characters consists of %d words", S1.getl(),S1.c_words()); fclose(f1); fclose(f2); getch(); } Проверим эту программу для различных предложений. 1) Например, для файла “sent.dat” This is a first sentence. Результирующий файл ( “sent_res.dat”) содержит: Sentence <This is a first sentence.> of 35 characters consists of 15 words After edition sentence <This is a first sentence.> of 25 characters consists of 5 words 2) Для файла “sent.dat” , содержащего Is this a first sentence ? Результирующий файл ( “sent_res.dat”) будет содержать: Sentence <Is this a first sentence ?> of 38 characters consists of 18 words After edition sentence <Is this a first sentence?> of 25 characters consists of 5 words 3) Для файла “sent.dat” , содержащего 37 Is this a first sentence of our very long text or this is a second sentence of small file ? Результирующий файл ( “sent_res.dat”) будет содержать: Sentence <Is this a first sentence of our very long text or this is a second sentence of small file ?> of 144 characters consists of 70 words After edition sentence <Is this a first sentence of our very long text or this is a second sentence of small file?> of 92 characters consists of 18 words 4. Варианты задания Создать класс «строка» для описания массива символов и произвести с ним следующие операции, согласно варианту: № Задание 1. В заданном тексте найти самое длинное слово и самую длинную фразу. Длина L нечетная, то удаляется символ, стоящий посередине строки; 2. В заданном тексте подсчитать частоту вхождения одинаковых слов. Длина L четная, то удаляются 2 первых и 2 последних символа; 3. Написать программу, которая считывает текст из файла и выводит на экран только строки, содержащие двузначные числа. Длина L кратна 2-м, то удаляются все числа, которые делятся на 2 4. Написать программу, которая считывает английский текст из файла и выводит на экран слова, начинающиеся с гласных букв. Длина L кратна 3-м, то удаляются все числа, делящиеся на 3; 5. Написать программу, которая считывает текст из файла и выводит его на экран, меняя местами каждые два соседних слова. Длина L >10, то удаляются все цифры; 6. Написать программу, которая считывает текст из файла и выводит на экран только предложения, не содержащие запятых. Длина L >15, то удаляются все a..z; 7. Написать программу, которая считывает текст из файла и определяет, сколько в нем слов, состоящих из не более чем четырех букв. Длина L=10, то удаляются все A..Z; 8. Написать программу, которая считывает текст из файла и выводит на экран только цитаты, то есть предложения, заключенные в кавычки. Длина строки L кратна 4-м, то первая часть строки меняется местами со второй; 9. Написать программу, которая считывает текст из файла и выводит на экран только предложения, состоящие из заданного количества слов. Длина строки L кратна 5-и, то подсчитывается количество скобок всех видов; 10. Написать программу, которая считывает английский текст из файла и выводит на экран слова текста, начинающиеся и оканчивающиеся на гласные буквы. Длина L кратна 2-м, то удаляются все числа, которые делятся на 6 38 № 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. Задание Написать программу, которая считывает текст из файла и выводит на экран только строки, не содержащие двузначных чисел. Длина строки L >5-и, то выделяется подстрока до первого пробела; Написать программу, которая считывает текст из файла и выводит на экран только предложения, начинающиеся с тире, перед которым могут находиться только пробельные символы. Длина строки L >6-и, то выделяется подстрока { } скобках; Написать программу, которая удаляет из введенной с клавиатуры строки начальные пробелы. Длина строки L >10-и, то удаляется подстрока в [] скобках; Часто встречающаяся ошибка начинающих наборщиков — дважды записанное слово. Обнаружить и исправить такие ошибки в тексте, записанном 80-символьными строками; Длина строки L >12-и, то удаляется подстрока до первой ( скобки; Упорядочить строки в алфавитном порядке Длина строки L кратна 4-м, то выделяется подстрока после последнего пробела; Определить, является ли введенное слово палиндромом (Шалаш, казак – слова читаются одинаково слева- направо и справа- налево) Длина строки L >5, то удаляются все точки. Каждую 80-байтовую строку заданного текста (например, стихотворного произведения) отцентрировать, то есть обеспечить осевую симметрию текста на экране добавлением пробелов слева. Длина строки L четная, то выделяется подстрока до первого пробела Написать программу, которая считывает текст из файла и выводит на экран все его предложения в обратном порядке. Длина строки L четная, то удаляется подстрока до первого пробела Написать программу, которая считывает текст из файла и выводит на экран сначала предложения, начинающиеся с однобуквенных слов, а затем все остальные. Длина строки L нечетная и <12, произвести инверсию (abcdef->fedcba) Написать программу, которая считывает текст из файла и выводит его на экран, заменив цифры от 0 до 9 на слова «ноль», «один»,.... «девять», начиная каждое предложение с новой строки. Длина строки L четная, то выделяется подстрока со второго пробела Написать программу, которая считывает текст из файла, находит самое длинное слово и определяет, сколько раз оно встретилось в тексте. Длина строки L нечетная, то выделяется подстрока после первого пробела Написать программу, которая считывает текст из файла и выводит его на экран, после каждого предложения добавляя, сколько раз встретилось в нем введенное с клавиатуры слово. Длина строки L нечетная, то удаляется подстрока со второго пробела Написать программу, которая считывает текст из файла и выводит на экран предложения, содержащие максимальное количество знаков пунктуации. Длина строки L кратна 3, то удаляется каждый 3-й символ 39 № 24. 25. Задание Написать программу, которая определяет, встречается ли в заданном текстовом файле заданная последовательность символов. Длина строки L четная, то удаляется каждый 2-й символ Написать программу, которая определяет, сколько раз встретилось заданное слово в текстовом файле. Длина строки L четная, то выделяется подстрока до последнего пробела 5. Требования к отчету 1. 2. 3. 4. 5. Обоснование выбора структуры памяти класса и его интерфейса. Блок-схемы алгоритмов функций обработки (ПО3). Описание класса + схемы (ПО2, ПО4). Результаты тестирования класса. Заключение и рекомендации по совершенствованию класса и/или тестирующей программы. 6. Контрольные вопросы 1. Как объявить динамическую строку в С++? 2. Какие вы знаете функции работы со строками? 3. Как определяются строки при передаче в функцию, в качестве фактических параметров? 40 Лабораторная работа №4 ПЕРЕГРУЗКА ОПЕРАЦИЙ Цель: Получить практические навыки работы с механизмом перегрузки операций для различных динамических структур данных. 1. Общие указания 1.1. Перегрузка операций Для перегрузки операции для класса в С++ используется следующий синтаксис: <Тип> operator <операция>(<входные параметры>) { <операторы>; } где < Тип > - тип, возвращаемый функцией; operator - ключевое слово; < операция > - перегружаемая операция. В языке С++ имеются следующие ограничения на перегрузку операций: С++ не различает префиксную и постфиксную формы ++ и - -; переопределяемая операция должна присутствовать в языке (например, нельзя определить операцию с символом #); нельзя переопределить операторы, заданные следующими символами . * :: ? ; переопределённые операции сохраняют свой изначальный приоритет. 1.2. Динамические структуры данных: стек, очередь список Поскольку с динамическими структурами данных вы уже знакомы из предыдущего курса по программированию, приведем только требования к памяти и интерфейсу классов этих структур. СТЕК: обычно реализуется с помощью массива. Реализует концепцию LIFO (Last In First Out), то есть элемент который добавили в стек последним выйдет из него первым. Память класса: [private: ] enum {EMPTY = -1, FULL = 999};// Нижняя и верхняя границы стека <тип> <имя>[FULL + 1]; // Массив для хранения данных (стек) int top; // Указатель на вершину стека Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть: добавление элемента void Push(char c); выталкивание элемента char Pop(); очистка стека; проверка существования элементов в стеке; проверка на переполнение стека; определение количества элементов в стеке; 3 функции перегрузки, в соответствии с вариантом задания. 41 ОЧЕРЕДЬ: представляет концепцию FIFO (First In First Out), то есть элемент, который добавили в очередь первым, первым ее и покинет. Реализуется с помощью динамического массива или списка. Память класса: [private: ] <тип> *<имя>; // Очередь unsigned MaxLength; // Максимальный размер очереди unsigned QueueLength// Текущий размер очереди Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть добавление элемента в очередь, удаление элемента из очереди, получение текущей длины очереди, получение максимальной длины очереди, установка максимальной длины очереди, путая/заполненная, очистка очереди. 3 функции обработки, в соответствии с вариантом задания. СПИСОК: это линейный набор ссылающихся на себя структур, называемых узлами, и объединенных указателем-связкой. Доступ к связующим узлам производится через связывающий указатель, хранящийся в каждом узле. Список не имеет фиксированного размера - он может расти или сокращаться по мере необходимости. Эта его особенность становится существенной, если число объектов в списке неизвестно до времени выполнения. Элемент(узел) данных списка может быть представлен, например, структурой struct <имя_struct> { <тип> <имя>;[<тип> <имя>;] // Данные <имя_struct>* Next; // Адрес следующего элемента списка }; Память класса: [private: ] <имя_struct>*<имя>; // Адрес головного элемента списка (в нашем примере типа структура) <тип> <имя>; // Количество элементов списка Методы класса Обязательными есть конструктор[ы], деструктор; Необходимыми есть добавление элемента в список, удаление элемента списка, распечатка содержимого списка, получение количества элементов, находящихся в списке, 3 функции обработки, в соответствии с вариантом задания. 2. Пример реализации ЗАДАНИЕ 1: Данный пример демонстрирует создание и реализацию функций двусвязного списка. Код функции main лишь описывает пример применения функций класса. 42 list.h #ifndef _LIST_ #define _LIST_ struct Elem // элемент списка { int data; // данные Elem * next, * prev; }; class List { Elem * Head, * Tail; // Голова, хвост int Count; // Количество элементов public: List();// Конструктор List(const List&);// Конструктор копирования ~List();// Деструктор int GetCount(); // Получить количество Elem* GetElem(int); // Получить элемент списка по номеру void DelAll(); // Удалить весь список void Del(int pos = 0); // Удаление элемента, если параметр не указывается, // то функция его запрашивает void Insert(int pos = 0); // Вставка элемента, если параметр не указывается, // то функция его запрашивает void AddTail(int n); // Добавление в конец списка void AddHead(int n); // Добавление в начало списка void Print();// Печать списка void Print(int pos); // Печать заданного элемента //************ Перегрузка**************// List& operator = (const List&); List operator + (const List&);// сложение двух списков (дописывание) // сравнение по элементам bool operator == (const List&); bool operator != (const List&); bool operator <= (const List&); bool operator >= (const List&); bool operator < (const List&); bool operator > (const List&); List operator - (); // переворачивание списка }; #endif list.cpp #include <iostream.h> #include "List.h" List::List() {// Изначально список пуст Head = Tail = NULL; Count = 0; 43 } List::List(const List & L) { Head = Tail = NULL; Count = 0; Elem *temp = L.Head; // Голова списка, из которого копируем while(temp != 0) // Пока не конец списка { AddTail(temp->data); // Копируем данные temp = temp->next; } } List::~List() { DelAll();// Удаляем все элементы } void List::AddHead(int n) { Elem * temp = new Elem; // новый элемент temp->prev = 0; // Предыдущего нет temp->data = n; // Заполняем данные temp->next = Head; // Следующий - бывшая голова if(Head != 0) // Если элементы есть Head->prev = temp; if(Count == 0) // Если элемент первый, то он одновременно и голова и хвост Head = Tail = temp; else // иначе новый элемент - головной Head = temp; Count++; } void List::AddTail(int n) { Elem * temp = new Elem; // Создаем новый элемент temp->next = 0; // Следующего нет temp->data = n; // Заполняем данные temp->prev = Tail; // Предыдущий - бывший хвост if(Tail != 0) // Если элементы есть Tail->next = temp; // Если элемент первый, то он одновременно и голова и хвост if(Count == 0) Head = Tail = temp; else Tail = temp; // иначе новый элемент - хвостовой Count++; } void List::Insert(int pos) { 44 // если параметр отсутствует или равен 0, то запрашиваем его if(pos == 0) { cout << "Input position: "; cin >> pos; } if(pos < 1 || pos > Count + 1) // Позиция от 1 до Count { cout << "Incorrect position !!!\n"; // Неверная позиция return; } if(pos == Count + 1) // Если вставка в конец списка { int data; // Вставляемые данные cout << "Input new number: "; cin >> data; // Добавление в конец списка AddTail(data); return; } else if(pos == 1) { int data; // Вставляемые данные cout << "Input new number: "; cin >> data; // Добавление в начало списка AddHead(data); return; } int i = 1; // Счетчик Elem * Ins = Head; // Отсчитываем от головы n - 1 элементов while(i < pos) { // Доходим до элемента, перед которым вставляемся Ins = Ins->next; i++; } // Доходим до элемента, который предшествует Elem * PrevIns = Ins->prev; Elem * temp = new Elem; // Создаем новый элемент cout << "Input new number: "; // Вводим данные cin >> temp->data; if(PrevIns != 0 && Count != 1) // настройка связей PrevIns->next = temp; temp->next = Ins; temp->prev = PrevIns; Ins->prev = temp; Count++; } void List::Del(int pos) { if(pos == 0) // если параметр отсутствует или равен 0, то запрашиваем его 45 { cout << "Input position: "; cin >> pos; } if(pos < 1 || pos > Count) // Позиция от 1 до Count { cout << "Incorrect position !!!\n"; return; // Неверная позиция } int i = 1; // Счетчик Elem *Del = Head; while(i < pos) { // Доходим до элемента, который удаляется Del = Del->next; i++; } // Доходим до элемента, который предшествует удаляемому Elem * PrevDel = Del->prev; // Доходим до элемента, который следует за удаляемым Elem * AfterDel = Del->next; if(PrevDel != 0 && Count != 1) // Если удаляем не голову PrevDel->next = AfterDel; if(AfterDel != 0 && Count != 1) // Если удаляем не хвост AfterDel->prev = PrevDel; if(pos == 1) // Удаляются крайние? Head = AfterDel; if(pos == Count) Tail = PrevDel; delete Del; // Удаление элемента Count--; } void List::Print(int pos) { if(pos < 1 || pos > Count) // Позиция от 1 до Count { cout << "Incorrect position !!!\n"; // Неверная позиция return; } Elem * temp; if(pos <= Count / 2) // Определяем с какой стороны быстрее двигаться { temp = Head; // Отсчет с головы int i = 1; while(i < pos) { temp = temp->next; // Двигаемся до нужного элемента i++; } } 46 else { temp = Tail; // Отсчет с хвоста int i = 1; while(i <= Count - pos) { temp = temp->prev; // Двигаемся до нужного элемента i++; } } cout << pos << " element: ";// Вывод элемента cout << temp->data << endl; } void List::Print() { // Если в списке присутствуют элементы, то пробегаем по нему // и печатаем элементы, начиная с головного if(Count != 0) { Elem * temp = Head; cout << "( "; while(temp->next != 0) { cout << temp->data << ", "; temp = temp->next; } cout << temp->data << " )\n"; } } void List::DelAll() { while(Count != 0) // Пока остаются элементы, удаляем по одному с головы Del(1); } int List::GetCount() { return Count; } Elem * List::GetElem(int pos) { Elem *temp = Head; if(pos < 1 || pos > Count) // Позиция от 1 до Count? { cout << "Incorrect position !!!\n"; return; // Неверная позиция } int i = 1; 47 while(i < pos && temp != 0) // Ищем нужный нам элемент { temp = temp->next; i++; } if(temp == 0) return 0; else return temp; } List& List::operator = (const List & L) { if(this == &L) // Проверка присваивания элемента "самому себе" return *this; // удаление старого списка this->~List(); // деструктор вызывает DelAll(); Elem * temp = L.Head; while(temp != 0) // Копируем элементы { AddTail(temp->data); temp = temp->next; } return *this; } List List::operator + (const List& L) // сложение двух списков { // Заносим во временный список элементы первого списка List Result(*this); // List Result = *this; Elem * temp = L.Head; while(temp != 0) // Добавляем во временный список элементы второго списка { Result.AddTail(temp->data); temp = temp->next; } return Result; } bool List::operator == (const List& L) { if(Count != L.Count) // Сравнение по количеству return false; Elem *t1, *t2; t1 = Head; t2 = L.Head; while(t1 != 0) // Сравнение по содержанию { // Сверяем данные, которые находятся на одинаковых позициях 48 if(t1->data != t2->data) return false; t1 = t1->next; t2 = t2->next; } return true; } bool List::operator != (const List& L) { return !(*this == L); // Используем предыдущую функцию сравнения } bool List::operator >= (const List& L) { if(Count > L.Count) // Сравнение по количеству return true; if(*this == L) // Сравнение по содержанию return true; return false; } bool List::operator <= (const List& L) { if(Count < L.Count) // Сравнение по количеству return true; if(*this == L) // Сравнение по содержанию return true; return false; } bool List::operator > (const List& L) { if(Count > L.Count) return true; return false; } bool List::operator < (const List& L) { if(Count < L.Count) return true; return false; } List List::operator - ()// переворот { List Result; Elem * temp = Head; // Копируем элементы списка, начиная с головного, // в свой путем добавления элементов в голову, 49 // таким образом, временный список Result будет содержать // элементы в обратном порядке while(temp != 0) { Result.AddHead(temp->data); temp = temp->next; } return Result; } main_li.cpp #include <iostream.h> #include "list.cpp" void main()// Тестовый пример { List L; const n = 10; int a[n] = {0,1,2,3,4,5,6,7,8,9}; // Добавляем элементы, стоящие на четных индексах, в голову, // на нечетных - в хвост for(int i = 0; i < n; i++) if(i % 2 == 0) L.AddHead(a[i]); else L.AddTail(a[i]); cout << "List L:\n"; // Распечатка списка L.Print(); cout << endl; L.Insert();// Вставка элемента в список cout << "List L:\n";// Распечатка списка L.Print(); // Распечатка 2-го и 8-го элементов списка L.Print(2); L.Print(8); List T; T = L; // Копируем список cout << "List T:\n";// Распечатка копии T.Print(); // Складываем два списка (первый в перевернутом состоянии) cout << "List Sum:\n"; List Sum = -L + T; // Распечатка списка Sum.Print();} 50 4.Варианты заданий Определить и реализовать класс. Определить и реализовать операции над данными этого класса. Написать и выполнить программу полного тестирования этого класса. Порядок выполнения работы. 1. Выбрать класс в соответствии с вариантом. 2. Определить и реализовать в классе необходимые конструкторы, деструктор, функции Input (ввод с клавиатуры) и Print (вывод на экран), перегрузить операцию присваивания. 3. Написать программу тестирования класса и выполнить тестирование. 4. Дополнить определение класса заданными перегруженными операциями (в соответствие с вариантом). 5. Реализовать эти операции с обоснованием выбранного способа (функция – член класса, внешняя функция, внешняя дружественная функция). Выполнить тестирование. № Реализуемый класс 1. Множество с элементами типа char + == + Добавить элемент в множество (char+set) проверка множеств на равенство объединение множеств 2. Множество с элементами типа char * < удалить элемент из множества (set-char) пересечение множеств сравнение множеств 3. Множество с элементами типа char + != > Добавить элемент в множество (char+set) проверка множеств на неравенство проверка на подмножество 4. Множество с элементами типа char + * int() Добавить элемент в множество (char+set) пересечение множеств Мощность множества 5. Множество с элементами типа char > + <= проверка на принадлежность множеству объединение множеств сравнение множеств 6. Однонаправленный список элементами типа char с + -== объединить списки (list+list) удалить элемент из начала (--list) проверка на равенство 7. Однонаправленный список элементами типа char с + == добавить элемент в начало (char+ list) удалить элемент из начала (-list) проверка на равенство 8. Однонаправленный список элементами типа char с + -!= Добавить элемент в конец (list + char) удалить элемент из конца (list--) проверка на неравенство 9. Однонаправленный с [] доступ к элементу в заданной позиции список Дополнительно перегружаемые операции 51 № Реализуемый класс Дополнительно перегружаемые операции элементами типа char == + проверка на равенство объединение списков с [] + != доступ к элементу в заданной позиции объединить два списка проверка на неравенство 11. Множество с элементами типа char () () == удалить элемент в заданной позиции добавить элемент в заданную позицию проверка на равенство 10. Однонаправленный список элементами типа char 12. Стек + -bool( ) Добавить элемент в стек извлечь элемент из стека проверка на пустоту 13. Очередь + -bool( ) Добавить элемент извлечь элемент проверка очереди на пустоту 14. одномерный массив (вектор) вещественных чисел + [] + сложение векторов доступ по индексу добавить число к вектору (double+vector) 15. одномерный массив (вектор) вещественных чисел [] - вычитание векторов доступ по индексу вычесть из вектора число (vector-double) 16. одномерный массив (вектор) вещественных чисел * [] * умножение векторов доступ по индексу умножить вектор на число 17. одномерный массив (вектор) вещественных чисел int ( ) [] () - 18. одномерный массив (вектор) вещественных чисел [] == != = доступ по индексу проверка на равенство проверка на неравенство присвоить всем элементам вектора значение 19. двумерный массив (матрица) вещественных чисел () * * доступ по индексу умножение матриц умножение матрицы на число 20. двумерный массив (матрица) вещественных чисел () == доступ по индексу разность матриц проверка матриц на равенство 21. двумерный массив (матрица) вещественных чисел () = доступ по индексу присвоить всем элементам матрицы значение размер вектора доступ по индексу установить новый размер вектора вычесть из вектора число (vector-double) 52 № Реализуемый класс Дополнительно перегружаемые операции + сложение матриц 22. двумерный массив (матрица) вещественных чисел () ++ != доступ по индексу транспонирование матрицы проверка матриц на неравенство 23. двумерный массив (матрица) вещественных чисел () () + доступ по индексу возведение в степень сложение матриц 24. Комплексное число + << == сложение комплексных чисел помещение в выводной поток комплексного числа (вывод) сравнение комплексных чисел 25. Комплексное число * >> умножение комплексных чисел вычитание комплексных чисел извлечение из потока комплексного числа (ввод) 53 ЛАБОРАТОРНАЯ РАБОТА 5 “Механизм создания порожденных классов. Наследование” Цель. Освоение механизма наследования на примере создания библиотеки графических примитивов для реализации заставки или элементов презентации. 1. Общие указания Для создания иерархии классов геометрических фигур необходимо определить их общие свойства: - все фигуры имеют центр (или точку привязки), т.е. переменные X,Y целого типа; - установка значений центра выполняется при помощи SetCenter(); - все фигуры «хотят быть нарисованными», т.е. иметь функцию-метод Draw(). class Shape { protected: int X,Y; // the center public: Shape(); ~Shape(); void SetCenter(int x, int y); virtual void Draw() {}; }; Реализация класса достаточна проста. Shape::Shape() { X=150; Y=100; } Shape::~Shape() { } void Shape::SetCenter(int x, int y) { X=x; Y=y; } Однако, отрисовка различных фигур выполняется по-разному, поэтому функция Draw() объявлена, но не реализована. Эта функция объявлена «виртуальной» в соответствии с правилами С++, которые предоставляют возможность реализовать ее в порожденных классах. 54 Пусть класс Shape будет базовым классом для двух порожденных классов: Circle и Rectangle. Порожденные классы содержат более детальную инфомацию и, конечно, известно, как можно нарисовать окражность и прямоугольник.. //синтаксис определения class Circle : public Shape { //порожденного класса int Radius; public: Circle(); virtual ~Circle(); void SetRadius(int r) {Radius = r;}; virtual void Draw(); }; Circle::Circle() :Shape() { Radius = 100; } Circle::~Circle() {} void Circle::Draw() { circle(X,Y,Radius); } class Rectangle : public Shape { int Height, Width; public: Rectangle(); void SetSides(int w, int h); virtual void Draw(); }; Rectangle::Rectangle() : Shape() { Width = 100; Height =100; } void Rectangle::SetSides(int w, int h) { Width = w; Height= h; } void Rectangle::Draw() { rectangle(X-Width/2,YHeight/2,X+Width/2,Y+Height/2); } 55 Таким образом, мы создали следующую иерархию: Заметим, что класс Circle имеет дополнительное свойство – радиус, поэтому память класса содержит переменную Radius, которая может быть задана функцией-методом SetRadius(). Подобным образом класс Rectangle имеет ширину и высоту (переменные Width и Height), которые устанавливаются функцией SetSides(). От класса Shape оба эти класса наследуют координаты центра X,Y и функцию SetCenter(). В классе Shape мы определили virtual void Draw(){}; с пустым телом функции {}, потому что неизвестно, как рисовать фигуру. В классах Circle и Rectangle функции Draw() переопределены, т.е. функция Shape::Draw() будет заменена функцией Circle::Draw() и функцией Rectangle::Draw(). Это можно отразить в следующей таблице отношений классов: класс Circle класс Rectangle int X; int Y; SetCenter(); int Radius; SetRadius(); наследовал Draw(); переопределил int X; int Y; SetCenter(); int Width; int Height; SetSides(); Draw(); добавил Заметим, что, если глубина иерархии более 2, то целесообразно указывать, от какого класса данный класс является порожденным и, соответственно, наследует данные и функции. Ниже приведен пример главной программы для тестирования классов Shape, Circle и Rectangle: 56 #include <graphics.h> #include <conio.h> Описание и реализация классов Shape, Circle и Rectangle main() { int graphdriver = DETECT; int graphmode; initgraph(&graphdriver,&graphmode,””); Circle c1; Rectangle r1; for (int i=50; i<150; i+=10) { c1.SetCenter(i,i); c1.SetRadius(50); c1.Draw(); r1.SetCenter(i,i); r1.SetSides(100,100); r1.Draw(); } getch(); closegraph(); } 2. Варианты задания Создать иерархию классов для цепочки, заданной в таблице. Разработать главную программу для реализации заставки или презентации (придумывается студентом). № 1 2 3 4 5 6 7 8 9 0 Цепочка наследования Shape -> Circle -> Ellipse -> EllipseArc Shape -> Circle -> Ellipse -> FillEllipse Shape -> Circle -> CircleArc -> FillCircleArc Shape -> Circle -> FillCircle -> FillEllipse Shape -> Sqr -> Rectangle -> FillRect Shape -> Sqr -> FillSqr -> FillRect Shape -> Circle -> FillCircle -> FillArc Shape -> Circle -> Arc -> EllipseArc Shape -> Circle -> Arc -> FillArc Shape -> Circle -> FillCircle -> FillArc 3. Содержание отчета 1. Рисунок иерархии классов. 2. Описание и реализация библиотеки классов. 3. Макет размещения графических фигур на экране дисплея. 4. Текст главной программы. 5. Результат работы программы (примеры приведены на рис.5.1). 57 Рисунок 5.1. Примеры заставок 58 ЛАБОРАТОРНАЯ РАБОТА 6 “Множественное наследование” Цель. Освоение механизма множественного наследования на примере создания графических «кнопок» для реализации интерфейса пользователя. 1. Общие указания Технология ООП включает механизам наследования от более одного класса. Этот механизм множественного наследования добавлен в C++ версии 22. Рассморим практический пример для отображения текста внутри окружности. Может показаться очень простым добавление строки в класс Circle и затем отображения ее функцией Circle::Show таким образом, чтобы текст отображался с окружностью вокруг него. Но, в действительности, текст и окружность – совершенно разные вещи. Для текста нам необходимы шрифт, размер символа и другие атрибуты. Но и текст, и окружность должны иметь одинаковый центр для размещения. В этом случае удобно применить базовый класс Location и создать от него две ветви: графический объект и текст, а затем соединить их в одном порожденном классе. Отношения предложенных классов показаны на рисунке ниже. Рисунок 6.1 – Структура классов 59 // MCIRCLE.CPP - пример реализации #include <graphics.h> #include "point.h" // реализация класса #include <string.h> #include <conio.h> // Иерархия классов Location->Point->Circle, (Circle and GMessage}->MCircle class Circle : public Point { protected: int Radius; public: Circle(int InitX, int InitY, int InitRadius); void Show(void); }; class GMessage : public Location { char *msg; // сообщение int Font; // BGI шрифт int Field; // размер поля для масштабирования текста public: GMessage(int msgX, int msgY, int MsgFont, int FieldSize, char *text); void Show(void); // отобразить сообщение }; class MCircle : Circle, GMessage // наследование от двух классов { public: MCircle(int mcircX, int mcircY, int mcircRadius, int Font, char *msg); void Show(void); // отобразить окружность с текстом }; // реализация Circle Circle::Circle(int InitX, int InitY, int InitRadius) : Point (InitX, InitY) { Radius = InitRadius; }; void Circle::Show(void) { Visible = true; circle(X, Y, Radius); } 60 // реализация для GMessage GMessage::GMessage(int msgX, int msgY, int MsgFont, int FieldSize, char *text) : Location(msgX, msgY) // X и Y устанавливаются конструктором класса Location равными msgX, msgY { Font = MsgFont; Field = FieldSize; msg = text; }; void GMessage::Show(void) { int size = Field / (8 * strlen(msg)); // 8 пикселов на символ settextjustify(CENTER_TEXT, CENTER_TEXT); // центрированный вывод settextstyle(Font, HORIZ_DIR, size); // установить стиль outtextxy(X, Y, msg); // отобразить текст } // реализация дляr MCircle MCircle::MCircle(int mcircX, int mcircY, int mcircRadius, int Font, char *msg) : Circle(mcircX, mcircY, mcircRadius), GMessage(mcircX,mcircY,Font,2*mcircRadius,msg) { } void MCircle::Show(void) { Circle::Show(); GMessage::Show(); } main() // пример использования { int graphdriver = DETECT, graphmode; initgraph(&graphdriver, &graphmode, "c:..\\bgi"); MCircle Small(250, 100, 25, SANS_SERIF_FONT, “You”); Small.Show(); MCircle Medium(250, 150, 100, TRIPLEX_FONT, “World”); Medium.Show(); MCircle Large(250, 250, 225, GOTHIC_FONT, “Universe”); Large.Show(); getch(); closegraph(); return 0; } Рассмотрим некоторые особенности. В реализации MCircle::Show вы видите два вызова функции Circle::Show(); и GMessage::Show();. Этот синтаксис показывает другое общее использование (::). Когда необходимо наследовать функции, такую как Show, компилятор нуждается в подсказке: какая Show требуется? Без дополнительного указания Show() булет ссылаться на Show() в текущем описании, названную MCircle::Show(). Для 61 вызова Show() других классов (естественно, если это разрешено), перед ее именем необходимо указывать имя класса, затем :: и имя функции (с аргументами, если они необходимы). Теперь немного пояснений к конструктору MCircle. Конструктор Point получает координаты расположения от конструктора Location. Так как MCircle наследует от обоих Circle и GMessage, конструктор MCircle может быть инициализирован путем вызова конструкторов этих классов: MCircle::MCircle(int mcircX, int mcircY, int mcircRadius, int font, char *msg) : Circle(mcircX, mcircY, mcircRadius), GMessage(mcircX, mcircY, 2*mcircRadius,msg) { } Тело конструктора пустое, потому что все необходимые данные он получает через инициализацию конструкторов-родителей в списке елементов инициаоизации (после символа : вводится список инициализирующих выражений, разделенных запятыми). Когда конструктур MCircle активизируется (например, при объявлении объекта типа МCircle), автоматически происходит установка начальных параметров. Первым вызывается конструктор Circle. Этот конструктор вызывает конструктор Point, который, в свою очередь, вызывает конструктор Location. Наконец, вызывается конструктор GMessage, который опять вызывает, теперь уже свою, копию конструктора Location для установки своих параметров X и Y. Аргументы, которые даны в конструкторе MCircle, передаются для инициализации соответствующих данных памяти базовых классов. Следует обратить внимание, что в качестве размера поля для масштабирования сообщения передается удвоенное значение радиуса окружности (2*mcircRadius ), тем самым осуществляется «вписывание» сообщение в окружность. В результате работы программы получаются окружности разного размера с сообщениями, отображенными различными по величине символами. You World Univers e Рисунок 6.3 – Окружность с вписанным в нее сообщением 62 4.Варианты заданий Определить и реализовать класс MCircle. Определить и реализовать операции над данными этого класса. Написать и выполнить программу использования этого класса для реализации интерфейса пользователя. Порядок выполнения работы. 1. Определить и реализовать класс MCircle. 2. Разработать программу, которая выводит на экран меню пользователя и выполняет вызов предыдущих 6-ти лабораторных работ. 3. Выполнить тестирование программы. 63 Рекомендуемая литература 1. Гради Буч. Объектно-ориентированный анализ и проектирование с примерами приложений на С++. 2-е изд. Rational Санта-Клара, Калифорния / Перевод с английского под редакцией И. Романовского и Ф. Андреева [электронный ресурс]. 2. Страуструп Б. Язык программирования С++. Специальное издание, М.:ООО БиномПресс, 2005г. 3. Электронный конспект лекций по курсу «ООП». 64 Приложение A СИСТЕМА ОБОЗНАЧЕНИЙ (приведена из книги Гради Буча [1]) Каждый класс должен иметь имя; если имя слишком длинно, его можно сократить или увеличить сам значок на диаграмме. Имя каждого класса должно быть уникально в содержащей его категории. Для некоторых языков, в особенности - для C++ и Smalltalk, мы должны требовать, чтобы каждый класс имел имя, уникальное в системе. Рис. А-2. Значок класса. На некоторых значках классов полезно перечислять несколько атрибутов и операций класса. "На некоторых", потому что для большинства тривиальных классов это хлопотно и не нужно. Атрибуты и операции на диаграмме представляют прообраз полной спецификации класса, в которой объявляются все его элементы. Если мы хотим увидеть на диаграмме больше атрибутов класса, мы можем увеличить значок; если мы совсем не хотим их видеть мы удаляем разделяющую черту и пишем только имя класса. Рис. А-3. Значок абстрактного класса. Рис. А-4. Значки отношений между классами. 0..1 - Ноль или одна 3..7 - Указанный интервал 1..3, 7 - Указанный интервал или точное число Обозначение мощности пишется у конца линии ассоциации и означает число связей между каждым экземпляром класса в начале линии с экземплярами класса в ее конце. Если мощность явно не указана, то подразумевается, что она не определена. Обозначения оставшихся трех типов связи уточняют 65 Рис. А-5 показывает, как описывается в этих обозначениях задача обслуживания тепличной гидропонной системы. Эта диаграмма представляет только малую часть структуры классов системы. Мы видим здесь класс GardeningPlan (план выращивания), который имеет атрибут, названный crop (посев), одну операцию-модификатор execute (выполнить) и одну операцию-селектор canHarvest (можно собирать?). Имеется ассоциация между этим классом и классом EnvironmentalController (контроллер среды выращивания): экземпляры плана задают климат, который должны поддерживать экземпляры контроллера. Рис. А-5. Диаграмма классов гидропонной системы. Эта диаграмма также показывает, что класс EnvironmentalController является агрегатом: его экземпляры содержат в точности по одному экземпляру классов Heater (нагреватель) и Cooler (охлаждающее устройство), и любое число экземпляров класса Light (лампочка). Оба класса Heater и Cooler являются подклассами абстрактного запускающего процесс класса Actuator, который предоставляет протоколы startUp и shutDown (начать и прекратить соответственно), и который использует класс Temperature. Рис. А-10. Значок утилиты классов. Как показано на Рис. А-10, утилита классов обозначается обычным значком класса с украшением в виде тени. В этом примере утилита классов PlanMetrics (параметры плана) предоставляет две важные операции: expectedYield (ожидаемый урожай) и timeToHarvest (время сбора урожая). Утилита обеспечивает эти две операции на основе услуг, предоставляемых классами нижнего уровня - GardeningPlan (план) и CropDatabase (база данных об урожае). Как показывает диаграмма, PlanMetrics зависит от CropDatabase: 66 получает от нее информацию об истории посевов. В свою очередь, класс PlanAnalyst использует услуги PlanHetrics. Рис. А-10 иллюстрирует Вложенность. Классы могут быть физически вложены в другие классы, а категории классов - в другие категории и т.д. Обычно это нужно для того, чтобы ограничить видимость имен. Вложение соответствует объявлению вложенной сущности в окружающем ее контексте. Мы изображаем вложенность физически вложенным значком; на Рис. А-11 полное имя вложенного класса - Nutritionist::NutrientProfile. Рис. А-11. Значок вложенности. В соответствии с правилами выбранного языка реализации, классы могут содержать экземпляры вложенного класса или использовать его. Языки обычно не допускают наследования от вложенного класса. Мы изображаем способ доступа следующими украшениями связи: <нет украшения> - открытый (по умолчанию) | - защищенный || - закрытый ||| - реализация Мы ставим их как "засечки" на линии связи у источника. Например, на Рис. А-12 показано, что класс GrainCrop множественно наследует от классов Crop (посев) (открытый суперкласс) и FoodItem (пища) (защищенный суперкласс). 67 Рис. А-12. Значок управления доступом. Типы отношении. В некоторых языках встречаются настолько всепроникающие типы отношений, с настолько фундаментальной семантикой, что было бы оправдано введение новых символов. В C++, например, имеется три таких конструкции: static - переменная (или функция) класса; virtual - совместно используемый базовый класс в ромбовидной структуре наследования; friend - класс, которому даны права доступа к закрытым и защищенным элементам другого класса. Рис. А-13. Значки отношений. Логично использовать для них такое же украшение в виде треугольного значка, как и для абстрактного класса, но с символами S, V или F соответственно. 68 Приложение B Терминологический словарь CRC-карточки, CRC cards. CRC - Class/Responsibilities/Collaborators, Класс/Ответственности/Сотрудники; простое, но достаточно эффективное средство мозгового штурма при выявлении ключевых абстракций и механизмов. абстрактная операция, abstract operation. Объявленная, но не реализованная операция в абстрактном классе. В C++ абстрактные операции объявляются как чисто виртуальные функции-члены. абстрактный класс, abstract class. Класс, который не может иметь экземпляров. Абстрактный класс пишется в предположении, что его конкретные подклассы дополнят его структуру и поведение, скорее всего, реализовав абстрактные операции. абстракция, abstraction. Существенные характеристики объекта, которые отличают его от всех других объектов и четко определяют его концептуальные границы для наблюдателя. Абстрагирование - процесс выявления абстракций. Один из основных элементов объектной модели. агент, agent. Объект, который подвергается воздействию со стороны и сам воздействует на другие объекты. Обычно агенты создаются для выполнения некоторой работы по поручению актеров или других агентов. актер, actor. Объект, воздействующий на другие объекты, но сам не подвергающийся воздействию с их стороны. В некоторых контекстах то же самое, что активный объект. активный объект, active object. Объект, которому выделен свой поток управления. алгоритмическая декомпозиция, algorithmic decomposition. Процесс разделения системы на части, каждая из которых отражает этап общего процесса. Применение структурного подхода к проектированию приводит к алгоритмической декомпозиции, которая фокусируется на потоке управления в системеархитектура модулей, module architecture. Граф, вершины которого соответствуют модулям, а ребра - отношениям модулей между собой. Архитектура модулей системы представляется совокупностью диаграмм модулей. архитектура процессов, process architecture. Граф, вершины которого соответствуют процессорам и устройствам, а ребра - соединениям между ними. Для описания архитектуры процессов системы используются диаграммы процессов. архитектура, architecture. Логическая и физическая структура системы, сформированная всеми стратегическими и тактическими проектными решениями. ассоциация, association. Отношение, означающее некоторую смысловую связь между классами. атрибут, attribute. Часть составного объекта (агрегата). 69 базовый класс, base class. Наиболее общий класс в какой-либо структуре классов. В большинстве приложений есть несколько таких корневых классов. В некоторых языках программирования определяется всеобщий базовый класс, который является суперклассом для всех остальных классов. блокирующий объект, blocking object. Пассивный объект, способный работать в многопоточном окружении. Вызов операции блокирующего объекта блокирует клиента на все время операции. видимость, visibility. Способность одной абстракции видеть другую и, таким образом, ссылаться на ее ресурсы извне. Абстракции видимы друг другу, только если они находятся в одном пространстве имен. Контроль экспорта может еще более ограничить доступ к видимым абстракциям. виртуальная функция, virtual function. Какая-либо операция над объектом. Виртуальная функция может быть переопределена в подклассах, следовательно, ее реализация определяется всем множеством методов, объявленных во всех классах дерева наследования. Термины "обобщенная функция" и "виртуальная функция" взаимозаменяемы. временная сложность, time complexity. Относительное или абсолютное время, за которое выполняется операция. действие, action. Некое происшествие в системе, требующее, с практической точки зрения, нулевого времени для своего завершения. Действием может быть вызов операции, запуск другого события, начало или остановка деятельности. делегирование, delegation. При делегировании один объект, ответственный за операцию, передает выполнение этой операции другому объекту. деструктор, destructor. Операция класса, которая освобождает состояние объекта и/или уничтожает сам объект. деятельность, activity. Операция, выполнение которой требует некоторого времени. диаграмма взаимодействий, interaction diagram. Часть системы обозначении объектноориентированного проектирования; используется для демонстрации выполнения какого-либо сценария в контексте диаграммы объектов. диаграмма классов, class diagram. Часть системы обозначений объектно-ориентированного проектирования; используется, чтобы наглядно показать классы и их взаимоотношения в логическом проекте системы. Может представлять всю структуру классов или ее часть. диаграмма модулей, module diagram. Часть системы обозначений объектноориентированного проектирования; используется для демонстрации разбиения классов и объектов по модулям в физическом проекте системы. Диаграмма модулей отображает архитектуру модулей системы. диаграмма объектов, object diagram. Часть системы обозначений объектноориентированного проектирования; используется, чтобы наглядно показать объекты и отношения между ними в логическом проекте системы. Может отражать всю объектную структуру или часть ее; обычно иллюстрирует смысл механизмов в логическом проекте. Отдельная диаграмма объектов - моментальный снимок из жизни системы. 70 диаграмма переходов и состояний, state transition diagram. Часть обозначений объектноориентированного проектирования; используется для отображения пространства состояний данного класса, событий, которые вызывают переход из одного состояния в другое, и действий, возникающих в результате смены состояния. диаграмма процессов, process diagram. Часть системы обозначений объектноориентированного проектирования; используется, чтобы наглядно показать, как процессы размешены по процессорам в физическом проекте системы. Диаграмма процессов отражает архитектуру процессов. динамическое связывание, dynamic binding. Связывание означает установление соответствия имени (например, объявленной переменной) с классом. Динамическое связывание происходит при выполнении программы в тот момент, когда создается объект, обозначенный именем. друг, friend. Класс или операция, имеющие доступ к закрытым операциям или данным некоторого класса. Только сам класс может называть своих друзей. закрытая часть, private. Часть интерфейса какого-либо класса, объекта или модуля, закрытая (невидимая) для других классов, объектов и модулей. защищенная часть, protected. Часть интерфейса какого-либо класса, объекта или модуля, невидимая для всех других классов, объектов и модулей за исключением подклассов. идентичность, identity. Природа объекта; то, что отличает его от других объектов. идиома, idiom. Выражение, общепринятое в каком-либо языке программирования или культуре какого-либо приложения, отражающее общепринятый способ использования данного языка. иерархия, hierarchy. Подчинение или упорядочение абстракций. Две типичных иерархии в сложной системе - структура классов (включая иерархию "обшее/частное") и структура объектов (включая иерархию "целое/часть"); иерархии можно также обнаружить в архитектурах модулей и процессов. инвариант, invariant. Логическое выражение некоторого условия, истинность которого необходимо соблюдать. инкапсуляция, encapsulation. Процесс разделения элементов абстракции, которые образуют ее структуру и поведение. Служит для отделения внешних обязательств объекта от его реализации. инстанцирование, instantiation. Подстановка параметров шаблона обобщенного или параметризованного класса; в результате создается конкретный класс, который может иметь экземпляры. интерфейс, interface. Внешний вид класса, объекта или модуля, выделяющий его существенные черты и не показывающий внутреннего устройства и секретов поведения. исключение, exception. Возбуждение исключения показывает, что некоторый логический инвариант не соблюдается. В C++ мы возбуждаем исключение, чтобы избежать 71 неправомочное исполнение операций и дать знать о возникшей проблеме другим объектам, которые могут перехватить исключение и принять меры. использовать, use. Ссылаться на абстракцию извне. итератор, iterator. Операция, позволяющая навещать части некоторого объекта. категория классов, class category. Логически полный набор классов, одни из которых видимы для других категорий классов, а другие - нет. Классы в категории сотрудничают для предоставления некоторого набора услуг. класс, class. Множество объектов с общей структурой и поведением. Термины "класс" и "тип" в большинстве случаев (но не всегда) взаимозаменяемы. Понятие класса отличается от понятия типа тем, что концентрируется на классификации по структуре и поведению. класс-контейнер, container class. Класс, экземпляры которого представляют собой коллекции других объектов. Контейнер может быть однородным (коллекции включают экземпляры только одного класса) либо неоднородным (коллекции включают экземпляры разных классов, имеющих обычно общий суперкласс). В C++ контейнеры обычно определяются как параметризованные классы с параметром, обозначающим класс объектов коллекции. клиент, client. Объект, который пользуется услугами другого объекта либо выполняя операции над последним, либо через доступ к его состоянию. ключ, key. Атрибут, значение которого однозначно идентифицирует объект. ключевая абстракция, key abstraction. Класс или объект, являющийся частью словаря предметной области. конкретный класс, concrete class. Класс, реализация которого завершена и который, поэтому, может иметь экземпляры. конструктор, constructor. Операция, создающая объект и/или инициализирующая его состояние. метакласс, metaclass. Класс класса; класс, экземпляры которого сами являются классами. метод, method. Операция над объектом, определенная как часть описания класса. Не любая операция является методом, но все методы - операции. Термины "метод", "сообщение" и "операция" обычно взаимозаменяемы. В некоторых языках методы существуют сами по себе и могут переопределяться подклассами; в других языках метод не может быть переопределен, - он служит как часть реализации обобщенных или виртуальных функций, которые можно переопределять в подклассах. механизм, mechanism. Структура, посредством которой объекты сотрудничают друг с другом, осуществляя поведение, которое соответствует требованиям системы. модификатор, modifier. Операция, изменяющая состояние объекта. модуль, module. Единица кода, служащая строительным блоком физической структуры системы; программный блок, который содержит объявления, выраженные в соответствии с 72 требованиями языка и образующие физическую реализацию части или всех классов и объектов логического проекта системы. Как правило, модуль состоит из интерфейсной части и реализации. модульность, modularity. Свойство системы, которая была разделена на связные и слабо зацепленные между собой модули. мономорфизм, monomorphism. Положение теории типов, согласно которому имена (например, переменных) могут обозначать только объекты одного и того же класса. мощность, cardinality. Число экземпляров класса: число экземпляров, участвующих в связи классов. наследование, inheritance. Отношение между классами, при котором класс использует структуру или поведение другого (одиночное наследование) или других (множественное наследование) классов. Наследование вводит иерархию "общее/частное" в которой подкласс наследует от одного или нескольких более общих суперклассов. Подклассы обычно дополняют или переопределяют унаследованную структуру и поведение. обобщенная функция, generic function. Какая-либо операция над объектом. Обобщенная функция класса может быть переопределена в подклассах; следовательно, ее реализация определяется всем множеством методов, объявленных во всех классах дерева наследования. Термины "обобщенная функция" и "виртуальная функция" взаимозаменяемы. обобщенный класс, generic class. Класс, служащий шаблоном для создания других классов: шаблон параметризуется другими классами, объектами и/или операциями. Обобщенный класс до создания объектов должен быть инстанцирован. Обобщенные классы используются как контейнерные классы. Термины "обобщенный класс" и "параметризованный класс" взаимозаменяемы. обратный инжиниринг, reverse-engineering. Восстановление логической или физической модели системы по коду. Противопоставляется прямому инжинирингу. объект, object. Нечто, чем можно оперировать. Объект имеет состояние, поведение и идентичность. Структура и поведение сходных объектов определены в общем для них классе. Термины "экземпляр" и "объект" взаимозаменяемы. объектная модель, object model. Совокупность основополагающих принципов, лежащих в основе объектно-ориентированного проектирования; парадигма программирования, основанная на принципах абстрагирования, инкапсуляции, модульности, иерархичности, типизации, параллелизма и устойчивости. объектное программирование, object-based programming. Метод программирования, основанный на представлении программы как совокупности объектов, каждый из которых является экземпляром некоторого типа. Типы образуют иерархию, но не наследственную. В таких программах типы рассматриваются как статические, а объекты имеют более динамическую природу, которую ограничивают статическое связывание и мономорфизм. объектно-ориентированная декомпозиция, object-oriented decomposition. Процесс разбиения системы на части, соответствующие классам и объектам предметной области. Практическое применение методов объектно-ориентированного проектирования приводит к 73 объектно-ориентированной декомпозиции, при которой мы рассматриваем мир как совокупность объектов, согласованно действующих для обеспечения требуемого поведения. объектно-ориентированное программирование, object-oriented programming (OOP). Методология реализации, при которой программа организуется, как совокупность сотрудничающих объектов, каждый из которых является экземпляром какого-либо класса, а классы образуют иерархию наследования. При этом классы обычно статичны, а объекты очень динамичны, что поощряется динамическим связыванием и полиморфизмом. объектно-ориентированное проектирование, object-oriented design (OOD). Методология проектирования, соединяющая процесс объектно-ориентированной декомпозиции и систему обозначений для представления логической и физической, статической и динамической моделей проектируемой системы. Система обозначений состоит из диаграмм классов, объектов, модулей и процессов. объектно-ориентированный анализ, object-oriented analysis. Метод анализа, согласно которому требования рассматриваются с точки зрения классов и объектов, составляющих словарь предметной области. объект-член, member object. Часть состояния объекта. В совокупности объекты-члены полностью определяют структуру объекта. Термины "переменная экземпляра", "поле". "объект-член" и "слот" взаимозаменяемы. ограничение, constraint. Выражение некоторого смыслового условия, которое должно выполняться. операция класса, class operation. Операция, например, конструктор или деструктор, общая для всего класса и не принадлежащая конкретному объекту. операция, operation. Нечто, проделываемое одним объектом над другим, чтобы вызвать реакцию. Все операции, которые можно выполнить над каким-либо объектом, сосредоточены в свободных подпрограммах и функциях-членах (методах). Термины "операция", "метод" и "сообщение" взаимозаменяемы. ответственность, responsibility. Поведение, за которое ответственен объект. открытая часть, public. Часть интерфейса какого-либо класса, объекта или модуля, открытая (видимая) для всех классов, объектов и модулей. параллелизм, concurrency. Свойство, отличающее активные объекты от неактивных. параллельный объект, concurrent object. Активный объект, способный работать в многопоточной среде. параметризованный класс, parameterized class. Класс, служащий шаблоном для других классов; шаблон параметризуется другими классами, объектами и/или операциями. Параметризованный класс должен быть инстацирован до создания объектов. Параметризованные классы используются как контейнеры. Термины "обобщенный класс" и "параметризованный класс" взаимозаменяемы. пассивный объект, passive object. Объект, не имеющий собственного потока управления. 74 переменная класса, class variable. Часть состояния класса. Совокупность всех переменных класса образует его структуру. Переменные класса совместно используются всеми его экземплярами. В C++ переменная класса объявляется как статический член. переменная экземпляра, instance variable. Часть состояния объекта. В совокупности переменные экземпляра полностью определяют структуру объекта. Термины "переменная экземпляра", "поле", "объект-член" и "слот" взаимозаменяемы. переход, transition. Переход из одного состояния в другое. поведение, behavior. Действия и реакции объекта, выраженные в терминах передачи сообщений и изменения состояния; видимая извне и воспроизводимая активность объекта. подкласс, subclass. Класс, наследующий от одного или нескольких классов (которые называются его непосредственными суперклассами). подсистема, subsystem. Совокупность модулей, часть которых видима для других подсистем, а часть - скрыта. поле, field. Часть состояния объекта; совокупность полей объекта образуют его структуру. Термины "поле", "переменная экземпляра", "объект-член" и "слот" означают одно и то же. полиморфизм, polymorphism. Положение теории типов, согласно которому имена (например, переменных) могут обозначать объекты разных (но имеющих общего родителя) классов. Следовательно, любой объект, обозначаемый полиморфным именем, может посвоему реагировать на некий общий набор операций. последовательное проектирование, round-trip gestalt design. Стиль проектирования, который подчеркивает последовательность и итеративность в развитии системы: посредством уточнения различных, хотя и согласованных логических и физических представлений системы в целом; объектно-ориентированное проектирование основывается на последовательном проектировании, что является выражением взаимозависимости общей картины проекта и его деталей. последовательный объект, sequential object. Пассивный объект, рассчитанный на работу в однопоточном окружении. постусловие, postcondition. Инвариант, соблюдаемый на выходе из операции. поток управления, thread of control. Отдельный процесс. Запуск потока управления приводит к возникновению независимой динамической деятельности в системе; данная система может иметь несколько одновременно выполняемых потоков, некоторые из которых могут динамически возникать и уничтожаться. Многопроцессорные системы допускают истинную многопоточность. в то время как на однопроцессорных компьютерах возможна только иллюзия многопоточности. (Термин "thread of control" переводится также "нить управления". В данном издании принят перевод "поток управления" как более распространенный. Отметим, что в некоторых случаях автор использует термин "flow of control", который переведен также. - Примеч. ред.) предусловие, precondition. Инвариант, предполагаемый на входе в операцию. 75 примесь, mixin. Класс, реализующий какое-либо четко выделенное поведение; используется для уточнения поведения других классов посредством наследования; поведение примеси обычно ортогонально поведению класса, с которым она смешивается. пространственная сложность, space complexity. Относительный или абсолютный объем памяти, занимаемый объектом. пространство состояний, state space. Перечислимое множество всех возможных состояний объекта. Пространство состояний программы содержит неопределенное, но конечное число состояний (не обязательно желаемых или ожидаемых). протокол, protocol. Способы, которыми объекты могут действовать и реагировать; полное статическое и динамическое представление объекта; протокол объекта определяет допустимое поведение объекта. процесс, process. Запуск одного потока управления. процессор, processor. Часть аппаратного обеспечения, имеющая вычислительные ресурсы. прямой инжиниринг, forward-engineering. Создание исполнимого кода по логической или физической модели. Противопоставляется обратному инжинирингу. раздел, partition. Категории классов или подсистемы, составляющие часть данного уровня абстракции. реактивная система, reactive system. Система, движимая событиями. Поведение такой системы не определяется простым отображением "вход-выход". реализация, implementation. Внутреннее представление класса, объекта или модуля, включая секреты его поведения. роль, role. Способность или цель, с которой класс или объект участвует в отношениях с другими; некоторая четко выделяемая черта поведения объекта в определенный момент времени; роль - это лицо, которое объект являет миру в данный момент. свободная подпрограмма, free subprogram. Процедура или функция, которая выполняется как непримитивная операция над объектом или объектами одного и тоже или различных классов. Свободная подпрограмма - это любая подпрограмма, которая не является методом какого-либо класса. связь, link. Связь между объектами, экземпляр ассоциации. селектор, selector. Операция, имеющая доступ к состоянию объекта, но не изменяющая его. сервер, server. Объект, который никогда не воздействует на другие объекты, но используется ими; объект, предоставляющий некоторые услуги. сигнатура, signature. Полная спецификация операции с указанием типов аргументов и возвращаемого значения. сильно типизированный, strongly typed. Свойство языка программирования, в соответствии с которым во всех выражениях гарантируется согласованность типов. 76 синхронизация, synchronization. Семантика параллельности операции. Операция может быть простой (присутствует только один поток управления); синхронной (рандеву двух потоков); односторонняя (рандеву, при котором одному из потоков приходится ждать);по истечении времени (рандеву, в котором один процесс ждет другого определенное время); асинхронной (два процесса независимы друг от друга). система реального времени, real-time system. Система, в которой некоторые существенные процессы должны укладываться в отведенное время. Система "жесткого" реального времени должна быть детерминированной; запаздывание с реакцией грозит катастрофой. скрытие информации, information hiding. Процесс скрытия всех секретов объекта, которые ничего не добавляют к его существенным характеристикам; обычно скрывают структуру объекта и реализацию его методов. словарь данных, data dictionary. Полный перечень всех классов в системе. слой, layer. Совокупность категорий классов или подсистем одного уровня абстракции. слот, slot. Часть состояния объекта; совокупность слотов образуют структуру объекта. Термины "поле", "переменная экземпляра", "объект-член" и "слот" означают одно и то же. событие, event. Что-то, что может изменить состояние системы. сообщение, message. Операция, которую один объект может выполнять над другим. Термины "сообщение", "метод" и "операция" обычно взаимозаменяемы. составной объект (агрегат), aggregate object. Объект, состоящий из других объектов (его частей). состояние, state. Совокупный результат поведения объекта: одно из стабильных условий, в которых объект может существовать, охарактеризованных количественно; в любой конкретный момент времени состояние объекта включает в себя перечень (обычно, статический) свойств объекта и текущие значения (обычно, динамические) этих свойств. сотрудничество, collaboration. Процесс, в котором несколько объектов сотрудничают для обеспечения требуемого поведения верхнего уровня. сохраняемость, persistence. Способность объекта существовать во времени, переживая породивший его процесс, и (или) в пространстве, перемещаясь из одного адресного пространства в другое. среда разработки, framework. Набор классов, предоставляющих некоторые базовые услуги в определенной области. Таким образом, среда разработки экспортирует классы и механизмы, которые клиенты могут использовать или адаптировать. статическое связывание, static binding. Связывание означает установление соответствия имени (например, объявленной переменной) классу. Статическое связывание происходит при объявлении имени (во время компиляции), до того, как объект будет создан. страж, guard. Логическое выражение, применяемое к событию; если выражение истинно, то событие происходит и система изменяет состояние. 77 стратегическое проектное решение, strategic design decision. Проектные решения, которые имеют решающее влияние на архитектуру. структура классов, class structure. Граф, вершины которого соответствуют классам, а ребра - отношениям классов. Структура классов для конкретной системы представляется в виде совокупности диаграмм классов. структура объектов, object structure. Граф, вершины которого соответствуют объектам, а ребра - отношениям объектов. Для отражения структуры объектов или ее части используются диаграммы объектов. структура, structure. Конкретное представление состояния объекта. Каждый объект имеет собственное состояние, независимое от других объектов, хотя все объекты одного класса имеют одинаковое представление состояния. структурное проектирование, structured design. Метод проектирования, основанный на алгоритмической декомпозиции. суперкласс, superclass. Класс, которому наследуют другие классы (называемые непосредственными подклассами). сценарий, scenario. Последовательность событий, выражающая некий аспект поведения системы. тактическое проектное решение, tactical design decision. Проектное решение, имеющее ограниченное значение для архитектуры. тип, type. Определение области допустимых значений, которые может принимать объект, и множества операций, которые могут выполняться над объектом. Термины "класс" и "тип" обычно (но не всегда) взаимозаменяемы; тип отличается от класса тем, что фокусируется на поддержке общего протокола. типизация, typing. Механизмы, препятствующие замене объектов одного типа на другой или, в крайнем случае, жестко ограничивающие такую замену. трансформационная система, transformational system. Система, поведение которой определяется в терминах отображения "вход-выход". управление доступом, access control. Механизм доступа к данным и операциям класса. В C++ открытые элементы доступны всем, защищенные элементы доступны подклассам, так называемым друзьям класса и файлам реализации, закрытые элементы доступны реализации и друзьям класса. Наконец, элементы с доступом на уровне реализации доступны только в файле реализации класса. уровень абстракции, level of abstraction. Относительное упорядочение абстракций по структурам классов, объектов, модулей или процессов. В терминах иерархии "часть/целое" объект находится на более высоком уровне абстракции, чем другие, если он строится на основе этих объектов: в терминах иерархии "общее/частное", высокоуровневые абстракции носят более обобщенный характер, чем низкоуровневые. услуга, service. Поведение, обеспечиваемое некоторой частью системы. 78 устройство, device. Часть аппаратуры, не имеющая собственных вычислительных ресурсов. утверждение, assertion. Логическое выражение некоторого условия, истинность которого необходимо обеспечить. утилита класса, class utility. Совокупность свободных подпрограмм. На C++ - класс, который состоит только из статических членов и/или функций-членов. функциональная точка, function point. В контексте анализа требований к системе отдельное поведение, видимое извне и поддающееся проверке. функция, function. Некоторое преобразование "вход-выход", вытекающее из поведения объекта. функция-член, member function. Операция над объектом, определенная как часть описания класса. Все функции-члены - операции, но не все операции - функции-члены. Термины "функции-члены" и "методы" взаимозаменяемы. В некоторых языках функции-члены существуют сами по себе и могут переопределяться подклассами; в других языках функциячлен не может быть переопределена, - она служит как часть реализации обобщенных или виртуальных функций, которые можно переопределять в подклассах. экземпляр, instance. Нечто, чем можно оперировать. Экземпляр имеет состояние, поведение и идентичность. Структура и поведение всех экземпляров класса определяются этим классом. Термины "объект" и "экземпляр" взаимозаменяемы. 79 Навчальне видання Методичні вказівки й завдання щодо виконання лабораторних робот з дисципліни «Об’єктно – орієнтоване програмування» (для студентів очної, заочної, заочної прискореної з наданням денних освітніх послуг форм навчання з напрямку «Комп’ютерна інженерія») Укладачі: Мальчева Раїса Вікторівна Приходько Тетяна Олександрівна 80