Наведемо програму, яка ілюструє принцип наслідування класів.
Приклад 11.5. Обчислити площу круга та поверхню циліндра, якщо задано радіус круга і висоту циліндра.
// Р11_5.СРР — наследование классов #include<iostream.h> #include<math.h> #include <conio.h> #define pi M_PI inline float sqr(float x) { return x*x; } class Circle //---------------- базовый класс { protected: float r; public: Circle(float rVal=0) { r=rVal; } void setRadius(float rVal) { r=rVal; } float Radius () { return r; } float Агеа() { return pi*sqr(r); } /* функція вычисления площади круга */ void ShowData(); }; class Cylinder: public Circle //--- производный класс (наследник) { protected: float h; public: Cylinder(float hVal=0, float rVaI=0) { h=hVal; r = rVal; } void set_h(float hVal) { h=hVal; } float Height() { return h; } float Area() /* функция вычисления поверхности цилиндра с использованием функции Агеа() базового класса */ { return 2*Circle::Area() +2*pi*r*h; } void ShowData(); }; void Circle::ShowData() { cout << "Circle radius = "<<Radius()<<endl; cout << "Circle area = "<<Area()<<"\n\n"; } void Cylinder::ShowData() { cout<<"Cylinder radius = "<<Radius()<<endl; cout<<"Cylinder area = "<<Area()<<"\n\n"; } int main() { Circle crcl(5); Cylinder cldr(10,7); crcl.ShowData(); cldr.ShowData(); getch (); return 0;
Результат виконання програми:
Circle radius = 5
Circle area = 78.5398
Cylinder radius = 7
Cylinder area = 747.699
У програмі описано базовий клас Circle, в якому реалізується функція визначення площі круга Агеа( ). У похідному класі Cilinder також є функція з ім’ям Агеа( ), що призначена для знаходження поверхні циліндра, причому в ній застосовується така ж функція базового класу, але вона записана як Circle:: Агеа( ), тобто з використанням операції розширення області свого класу.
Такі функції називаються поліморфними. Поліморфізм — це можливість для об’єктів різних класів, що пов’язані між собою завдяки успадкуванню, реагувати по-різному при зверненні до однієї і тієї ж функції-члену класу. Поліморфізм звичайно реалізується шляхом використання віртуальних функцій.
Якщо функція-член класу визначена у базовому класі, але перевизначена в похідному і викликається для виконання, то використовується версія цієї функції з базового класу. Але якщо ця функція оголошена віртуальною у базовому і похідному класах, то для її виконання застосовується форма, що відповідає своєму класу.
Приклад 11.6. Піднести до квадрата змінну х і отримане значення поділити на 2, далі змінну піднести до куба.
Проілюструємо цей приклад двома програмами. У першій з них (див. Р11_6_1.СРР) оголошено два класи: базовий mycl1 і похідний mycl2. У базовому класі описані функцїї-члени fun1() і fun2(), одна з яких підносить до квадрата змінну х, а друга ділить одержане значення на 2.
У похідному класі також описано функцію fun1(), але вона підносить до куба, тобто перевизначає цю функцію з базового класу.
Головна програма має оголошений об’єкт р1 похідного класу, який, здавалося б, при виконанні функції fun2() повинен використовувати функцію funl() похідного класу, однак він викликає таку функцію з базового класу.
// Р11__6_1.СРР — использование классов #include <iostream.h> #include <conio.h> class mycl1 { public: float fun1 (float x) { return x*x; } float fun2(float x) { return fun1(x)/2; } }; class mycl2:public mycl1 { public: float funl(float x) { return x*x*x; } }; main() { mycl2 p1; cout << "pl.fun2(10)= " << p1.fun2(10) << endl; getch (); return 0; }
Результати виконання програми:
p1.fun2(10)= 50
У наступній програмі (див. Р11_6_2.СРР) функції fun1() оголошено віртуальними, тому одержимо інший результат.
// Р11_6_2.СРР — использование виртуальных функций #include <iostream.h> #include <conio.h> class mycl1 { public: virtual float fun1(float x) { return x*x; } float fun2(float x) { return fun1(x)/2; } }; class mycl2:public mycl1 { public: virtual float funl (float x) { return x*x*x; } }; main() { mycl1 p1; mycl2 p2; cout << "p1.fun2(10)= "<<p1.fun2(10)<<endl; cout << "p2.fun2(10)= "<<p2.fun2(10)<<endl; getch (); return 0; }
Результат розв’язання програми:
p1.fun2(10)= 50
p2.fun2(10)= 500
Віртуальна функція описується як звичайна, тільки перед її ім’ям записується словоvirtual.
Класи зручно використовувати при створенні власної бібліотеки модулів, призначених для розв’язання різних задач. У такому випадку описання класу розміщують в окремому файлі, ім’я якого записують з розширенням .h. У головній програмі, яку записують з окремим ім’ям і розширенням .срр, оголошують потрібні об’єкти і викликають необхідні для роботи функції-члени класу.
Приклад 11.7. Створити програму обчислення площ прямокутника і квадрата з використанням віртуальних функцій.
У програмі при визначенні площі прямокутника для правильного використання ширини відповідні функції Width() базового і похідного класів оголошено віртуальними.
// Р11_7.СРР — использование виртуальных функций #include <iostream.h> #include <conio.h> class Square { protected: float length; public: Square (float len) { length=len; } float Length() { return length; } virtual float Width { return length; } float Area() { return Length()*Width(); } }; class Rectangl : public Square { protected: float width; public: Rectangl (float len, float wide): Square(len), width(wide) { } virtual float Width() { return width; } }; main() { Square sq(10); Rectangl rec(10,12); cout<<" For square len = "<<sq.Length() <<endl <<"Area= "<<sq.Area()<<endl; cout<<" For rectangle len = "<<rec.Length()<<endl<< "width= "<<rec.Width()<<endl <<"Area= "<<rec.Area()<<endl; getch (); return 0; }
Результат розв’язання програми:
For square len = 10
Area= 100
For rectangle len = 10
width = 12
Area= 120
Приклад 11.8. Створити файл Tmassiv.h, який має функцію введення масиву, та залежно від завдання відсортувати цей масив за спаданням або за зростанням елементів.
// Р11_8.СРР — создание файла ("Tmassiv.h"), содержащего функции #include <iostream.h> class Tmassiv { float *ptr; int sz;![]()
У програмі описано клас Tmassiv, в якому створюється масив, що потім обробляється і виводиться на екран. Клас Tmassiv містить:
- покажчик *ptr — для створення масиву;
- змінну sz — для фіксування розміру масиву;
- конструктор класу, в якому виділяється динамічна пам’ять для розміщення масиву та ініціюється його розмір;
- деструктор класу, який організує звільнення динамічної пам’яті;
- функції-члени класу:
fsize() — для повернення розміру масиву;
vvod() — для введення значень елементів масиву;
vivod() — для виведення елементів масиву на екран;
sort() — для сортування масиву за зростанням або за спаданням значень елементів.
Приклад 11.9. Створити програму з використанням масиву за умовою прикладу 11.8.
// Р11_9.СРР — использование файла "Tmassiv.h" #include <iostream.h> #include <conio.h> #include "Tmassiv.h" // подключение файла "Tmassiv.h" main() { Tmassiv mas(5); /* используется класс Tmassiv, описанный в файле "Tmassiv.h" */ int і; float m; for (і = 0; і < mas.fsize(); i++) { cout << "Enter "<< і << " element\n"; cin >> m; mas.vvod(m,i); } cout << "Enter prizn.\n"; cin >> mas.pr; mas.sort(); for (i = 0; і < mas.fsize(); i++) cout << "mas[" << і << "]=" << mas.vivod(i) << " "; сout << endl; getch (); return 0; }
Результати роботи програми:
Enter 0 element 5.2
Enter 1 element 4.8
Enter 2 element 10
Enter 3 element 0.5
Enter 4 element 4.8
Enter prizn. 1
mas[0]=10 mas[l]=5.2 mas[2]=4.9 mas[3]=4.8 mas[4J=0.5
Enter prizn. 2
mas[0]=0.5 mas[l]=4.8 mas[2]=4.9 mas[3]=5.2 mas[4]=10
Слід зауважити, що оголошення класу не залежить від головної програми. Створений у класі масив можна використовувати в ній по-різному, залежно від необхідності (визначати суму його елементів тощо). Але для цього краще використовувати функції члени класу.
Для розміщення елементів масиву використовується динамічна пам’ять, що виділяється в конструкторі за допомогою оператора new:
ptr = new float [sz];,
а звільняється деструктором за допомогою оператора delete:
delete[ ] ptr;.
Тому для обробки масивів використання конструкторів і деструкторів є обов’язковим.
Розглянемо, як треба обробляти матрицю, яка являє собою масив масивів. Її ім’я — це покажчик на масив покажчиків, кожен з яких є покажчиком на рядок матриці, тобто на звичайний масив.
В оголошенні динамічної матриці з ім’ям рт треба використовувати запис — **рm(форма pm[n][m] є помилковою) і виділяти динамічну пам’ять як для покажчика на масив покажчиків *рm, так і для кожного покажчика на рядок рm.
Приклад 11.10. Ввести матрицю та обчислити суми елементів, які розташовано вище і нижче елементів головної діагоналі.
// Р11_10.СРР — вычисление необходимых сумм элементов матрицы #include <iostream.h> #include <conio.h> class Tmatr { int n, m; public: int **pm; Tmatr(int a, int b); ~Tmatr(); void vvod(); int sum1(); int sum2(); }; Tmatr::Tmatr(int a, int b) //---------- конструктор класса // выделение динамич. памяти для матрицы из а строк и b столбцов { n = a; m = b; pm = new int *[m]; for (int і = 0; і < n; і++) pm[i] = new int[m]; } Tmatr:: ~Tmatr() //---------------- деструктор класса // освобождение памяти { for (int і = о; і < n; i++) delete [ ] pm[i]; delete[ ] pm; } void Tmatr::vvod() // функция ввода матрицы { for (int і = 0; і < n; i++) { cout << "\nEnter elementi " << і << " stroki\n"; for (int j = 0; j < m; j++) cin >> pm[i][j]; } } int Tmatr::sum1() /* функция подсчета суммы элементов, расположенных ниже главной диагонали матрицы */ { int s1 = 0; for (int і = 0; і < n; і++) for (int j = 0; j < m; j++) if (i>j) s1 += pm[i][j]; return s1; } int Tmatr::sum2() /* функция подсчета суммы элементов, расположенных выше главной диагонали матрицы */ { int і, j, s2 = 0; for (i = 0; i<n; i++) for (j = 0; j < m; j++) if(i<j) s2 += pm[i][j]; return s2; } main() { Tmatr matr(4,4); int k; matr.vvod(); cout«"\ns1 ="<<matr.sum1()<<" s2 = "<<matr.sum2()<<endl; getch (); }
Результати розв’язання програми:
Enter elementi 0 stroki
5 6 7 2
Enter elementi 1 stroki
3 1 4 0
Enter elementi 2 stroki
9 10 11 7
Enter elementi 3 stroki
3 5 5 8
S1 = 36
S2 = 26
У цій програмі створено клас Tmatr, що крім змінних, конструктора і деструктора містить функції-члени:
- vvod() — для введення елементів матриці;
- sum1() — для визначення суми елементів, розташованих нижче головної діагоналі;
- sum2() — для визначення суми елементів, розташованих вище головної діагоналі матриці.
У головній функції main() оголошено об’єкт matr(4,4) і здійснюються звернення до інших функцій програми. Це приводить до одержання значень вказаних сум.
У наступному прикладі розглянемо, як можна записувати до файла дані типу структура, користуючись класом.
Приклад 11.11. Записати дані типу структура у файл та вивести список робітників за заданим фахом.
/* Р11_11.СРР — запись данных типа структура в файл и их обработка */ #include <iostream.h> #include <string.h> #include <conio.h> #include <fstream.h> #include <stdio.h> const n=4; struct rab { char fam[20]; char spec[30]; float zarpl; }; class Trab { public: rab spis; void vvod() /* функция ввода данных типа структура и записи их в файл */ { int і; ofstream fout("rab_spis.dat"); if (!fout) cout<<"Cannot open file\n"; for (і = 0; і < n; i++) { cont << "Enter " << (i+1) << " fam, spec, zarpl\n"; gets(spis.fam); gets(spis.spec); cin >> spis.zarpl; cout << endl; fout.write((char*) & spis, sizeof(spis)); } fout.close(); } void vivod() /* функция чтения данных типа структура из файла и вывода их на экран*/ { char fp[30]; int і; ifstream fin("rab_spis.dat"); if (!fin) cout << "Cannot open file for reading\n"; cout << "Enter specialiti\n"; gets(fp); while (fin.read((char*) &spis, sizeof(spis))) if (strcmp(fp,spis.spec)==0) cout << spis.fam << " "<< spis.spec <<" "<< spis.zarpl << endl; } }; main() { Trab st; cout << "\nWorking program\n"; st.wod(); st.vivod(); getch (); }
Результати виконання програми:
Working program
Enter і fam, spec, zarpl
Геращенко И. С.
инженер 950
Enter 2 fam, spec, zarpl
Даниленко Т. К.
слесарь
650
Enter 3 fam, spec, zarpl
Ящук П. H.
повар
450
Enter 5 fam, spec, zarpl
Яковченко С. С.
слесарь
700
Enter specialiti
слесарь
Даниленко Т. К. слесарь 650
Яковченко С. С. слесарь 700
У прикладі 11.11 створено клас Trab, що містить дві функції: одна з ім’ям vvod()— призначена для запису в файл відомостей про робітників, тобто їх прізвищ (fam),спеціальностей (spec) і розміру платні (zarpl), оформлених у вигляді структури (rab), друга з ім’ям vivod() — для виведення на екран даних робітників заданої спеціальності. На запитання «Enter specialiti» було введено слово «слесарь». Програма виводить дані про всіх слюсарів, які були записані у списку. В головній функції main() оголошено об’єкт класу st і здійснено звернення до його функцій.