Основы информацинных технологий
.pdfcout<<"Результат"<<endl;
//p=&ob1; // указатель на объект базового класса //p->print(); // вызов функции базового класса р=&оb2;
p->print(); // вызов функции производного класса
}
6.5.4 Абстрактные классы
Базовый класс обычно содержит ряд виртуальных функций, которые час то фиктивны и имеют пустое тело. Эти функции существуют как некоторая аб стракция, конкретное значение им придается в производных классах. Такие функции называются чисто виртуальными (pure virtual function), т.е. такими, тело которых не определено. Общая форма записи чисто виртуальной функции имеет вид: virtual прототип функции = 0;
Если класс является производным класса с чисто виртуальной функцией и эта функция в нем не описана, тогда функция остается чисто виртуальной и в этом производном классе. Следовательно, такой производный класс является абстрактным. Хотя иерархия классов не требует обязательного включения в нее каких-либо абстрактных классов, однако программы, использующие объектноориентированное программирование, все же имеют иерархию, порожденную абстрактным базовым классом. Абстрактные классы могут составлять несколь ко уровней иерархии. В качестве примера можно привести иерархию форм. Ие рархия может порождаться абстрактным базовым классом Shape. На уровень ниже можно получить два абстрактных класса TwoDShape и ThreeDShape. При переходе еще на один уровень ниже можно определить конкретные классы для двухмерных форм (Circle, Rectangle) и трехмерных (Cube, Sphere) (рисунок 6.11). Согласно языку UML имя абстрактного класса пишется курсивом.
Рисунок 6.11 - Иерархия наследования классов
Рассмотрим пример программы приведенной иерархии классов, в кото рой будет выводиться название фигуры, площадь двухмерных и объем трех мерных фигур:
#include<iostream>
#include<iomanip>
210
using namespace std; |
|
#include<string.h> |
|
const float pi=3.14159; |
|
class Shape |
// абстрактный базовый класс |
{ |
|
public: |
|
virtual void print()=0; |
// печать названия фигуры |
};
class TwoDShape: public Shape
{
protected: float r;
public:
virtual void area() = 0; // вычисление площади фигуры
};
class ThreeDShape: public Shape
{
protected: float h;
public:
virtual void volume(); // вычисление объема фигуры
};
class Circle: public TwoDShape |
// класс "Окружность" |
{ |
|
public: |
|
void print() |
|
{ |
|
cout<<"Окружность"<<endl;
}
void area()
{ cout<<"Введите радиус окружности"<<endl; cin>>r;
cout<<"Площадь окружности"<<setw(10)<<pi*r*r<<endl;
}
};
class Rectangle: public TwoDShape // класс "Квадрат"
{
public:
void print()
{cout<<"Квадрат"<<endl; } void area()
{cout<<"Введите сторону квадрата"<<endl; cin>>r;
}cout<<"Площадь квадрата"<<setw(7)<<r*r<<endl;
};
211
class Sphere: public ThreeDShape |
// класс "Сфера" |
{ |
|
public: |
|
void print() |
|
{ |
|
c o u t « "Сфера" « |
endl; |
}
void volume()
{
cout<<"Введите радиус сферы"<<endl; cin>>h;
cout<<"Объем сферы"<<setw(10)<<(4.0*pi*h*h*h)/3.0<<endl;
}
}:
class Cube: public ThreeDShape // класс "Куб"
{
public:
void print()
{
cout<<"Куб"<<endl;
}
void volume()
{ cout<<"Введите сторону куба"<<endl; cin>>h;
cout<<"Объем куба"<<h*h*h<<endl;
}
};
int main()
{
Shape *ptr; // указатель на абстрактный базовый класс TwoDShape *ptr1;
ThreeDShape *ptr2; Circle okr, Rectangle pr; Sphere sf;
Cube kb;
//вызов функций класса "Окружность" ptr1=&okr;
ptr1->print(); ptr1->area();
//вызов функций класса "Квадрат" ptr1=≺
ptr1->print(); ptr1->area();
//вызов функций класса "Сфера" ptr2=&sf;
212
ptr2->print(); ptr2->volume();
// вызов функций класса "Куб" ptr2=&kb;
ptr2->print(); ptr2->volume();
}
6.5.5 Виртуальное наследование
При множественном наследовании возможно возникновение нескольких ситуаций, которые приводят к ошибке. Одна из таких ситуаций - конфликтимен методов или атрибутов нескольких базовых классов, например:
class Base_1 // 1-й базовый класс
{
public:
void prog()
{
cout<<"Базовый класс Base_1"<<endl;
}
};
class Base_2 // 2-й базовый класс
{public:
void prog()
{ cout<<"Базовый класс Base_2"<<endl; }
}; |
|
|
class Derive: public Base_1, public Base_2 |
|
|
{ |
// выполнение каких-то действий |
}; |
int main()
{Derive *obj=new Derive; // указатель на производный класс
//obj->prog(); |
// ошибка |
obj->Base_1::prog(); |
|
obj->Base_2::prog(); |
|
return 0; |
|
}
Если функция prog() вызывается таким образом, то компилятор не может определить, функцию какого класса (Base_l или Base_2) вызвать на выполне ние. Ошибку можно устранить, если явно указать, какому из базовых классов принадлежит вызываемая функция:
obj->Base_1::prog(); или obj->Base_2::prog();
Иная конфликтная ситуация возникает, если иерархия наследования клас сов следующая (рисунок 6.12):
213
Рисунок 6.12 - Виртуальное наследование классов Используя такую иерархию, классы В1, В2 и С можно объявить так:
class В1: public А
{};
class В2: public А
{};
class С: public B1, public В2
{};
В этом случае производный класс С может содержать два одинаковых базовых класса А (А <- В1 <- С и А <- В2 <- С) и для каждого базового класса А стро ится свой объект (т.е. объекты-дубликаты). Такая ситуация будет неоднознач ной и вызовет ошибку. Проблема объектов-дубликатов решается с помощью виртуального наследования. Если базовый класс наследуется как virtual, то только один объект базового класса А появляется в производном классе С. Этот процесс называется наследованием виртуального базового класса.
|
Пример программы виртуального наследования: |
|
#include<iostream> |
|
|
#include<iomanip> |
|
|
using namespace std; |
|
|
#include<string.h> |
|
|
class A |
|
|
{ |
char naz[20]; |
// название фирмы |
|
public: |
|
|
A(char *NAZ) |
// конструктор |
{
strcpy(naz,NAZ);
}
~A()
{ cout<<"Деструктор класса A"<<endl; } void a_prnt()
{
cout<<"Фирма"<<setw(8)<<naz<<endl;
}
};
214
class B1: virtual public A |
// базовый класс А является виртуальным |
|
{ |
|
|
protected: |
|
|
long n_otd; |
// номер сотрудника |
|
int |
nom; |
// номер отдела |
public: |
|
|
В1 (char *NAZ, long TN, int NOM): A(NAZ) |
||
{ |
n_otd=TN; |
|
|
nom=NOM; |
|
}
~B1()
{
cout<<"Деструктор класса В1"<<endl;
}
void b1_prnt()
{
A::a_prnt();
cout<<setw(8)<<"Номер сотрудника"<<setw(4)<<n_otd<<endl
<<setw(10)<<"Номер отдела"<<nom<<endl;
}
};
class B2: virtual public A // базовый класс А является виртуальным
{
protected:
double zp; // заработная плата public:
B2(char *NAZ, double ZP): A(NAZ) // конструктор
{zp=ZP; }
~B2()
{cout<<"Деструктор класса B2"<<endl; } void b2_pmt()
{
cout<<"Заработная плата"<<setw(8)<<zp<<endl;
}
};
class C: public B1, public B2
{ |
|
char *fam; |
// фамилия сотрудника |
public: |
|
//конструктор производного класса
C(char *FAM, char *NAZ, long TN, int NOM, double ZP): B1(NAZ,TN,NOM),B2(NAZ,ZP),A(NAZ)
{
fam=new char[strlen(FAM)+1];
}strcpy (fam, FAM);
215
~C()
{
cout<<"Деструктор производного класса"<<endl;
}
void c_prnt()
{ B1::b1_prnt(); B2::b2_prnt();
cout<<"фамилия"<<setw(8)<<fam<<endl;
}
};
void main()
{
С obj("Иванов","ИПНК", 10,5,555.5);
С *pt; // указатель на объект производного класса pt=&obj;
pt->c_prnt(); // вызов функции производного класса
}
В приведенном примере при создании объекта obj конструктор класса А вызывается из конструктора класса С первым и только один раз. затем конст рукторы классов В1 и В2 в том порядке, в котором они описаны в строке насле дования классов: class С: public B1, public В2 .
6.5.6Лабораторная работа №4
Наследование
Цель: изучить принципы наследования.
Варианты заданий
1 Реализовать базовый класс Shape. Создать производные TwoDShape и ThreeDShape, от которых унаследовать всевозможные конкретные формы (круг, прямоугольник, куб, цилиндр). Реализовать функции print (для вывода типа и размера объектов каждого класса), area (вычисление площади), volume (вычис ление объема). Использовать конструктор с параметрами.
2 Создать базовый класс А, в котором есть переменные а и b, производ ный класс В, в этом классе есть переменная с, производный класс С (наследует
ся от класса В), в нем есть переменная/. Вычислить значение выражения:
х=а2 + b2 + c/f.
Использовать конструктор с параметрами.
3 В базовом классе А задать переменную а. От класса А получить произ водный класс В, в котором задать переменную b. От класса В получить произ водный класс С и в нем вычислить площадь прямоугольника. Использовать конструктор с параметрами.
4 Создать базовый класс А, в нем есть переменная а. От класса А полу чить производный класс В, в нем есть переменная b. От класса В получить про изводный класс С. В классе С вычислить корни квадратного уравнения. Ис пользовать конструктор с параметрами.
216