Наведемо програму, яка ілюструє принцип наслідування класів.
Приклад 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 і здійснено звернення до його функцій.
програмування