Абстрактные типы данных
Чтобы объявить класс как абстрактный тип данных.достаточно добавить в него одну или несколько чистых виртуальных функций. Для этогопосле объявления функции необходимо добавить - 0, например:
сlass Shape
{
virtual void Draw() = 0; // чистая виртуальная функция
}
Выполнение чистых виртуальных функций
Обычно чистые виртуальные функции объявляются в абстрактном базовом классе и не выполняются. Поскольку невозможно создать объект абстрактного базового класса, как правило, нет необходимости и ff выполнении чистой виртуальной функции. Класс ADT существует только как объявление интерфейса объектов, создаваемых в производных классах.
Тем не менее все же иногда возникает необходимость выполнения чистой виртуальной функции. Она может быть вызвана из объекта, произведенного от ADT, например чтобы обеспечить общую функциональность для всех замещенных функций. В листинге 13.9 представлен видоизмененный листинг 13.7, в котором класс Shape объявлен как ADT и в программе выполняется чистая виртуальная функция Draw(). Функция замещается в классе Circle, что необходимо для создания объекта этого класса, но в объявлении замещенной функции делается вызов чистой виртуальной функции из базового класса. Это средство используется для достижения дополнительной функциональности методов класса.
В данном примере дополнительная функциональность состоит в выведении на экран простого сообщения. В реальной программе чистая виртуальная функция может содержать достаточно сложный программный код, например создание окна, в котором рисуются все фигуры, выбираемые пользователем.
Листинг 13.9. Выполнение чистых виртуальных функций
1: // Выполнение чистых виртуальных функций
2:
3: #include <iostream.h>
4:
5: class Shape
6: {
7: public:
8: Shape(){ }
9: virtual ~Shape(){ }
10: virtual long GetArea() = 0;
11: virtual long GetPerim()= 0;
12: virtual void Draw() = 0;
13: private:
14: };
15:
16: void Shape::Draw()
17: {
18: cout << "Abstract drawing mechanism!n";
19: }
20:
21: class Circle : public Shape
22: {
23: public:
24: Circle(int radius):itsRadius(radius) { }
25: virtual ~Circle() { }
26: long GetArea() { return 3 * itsRadius * itsRadius; }
27: long GetPerim() { return 9 * itsRadius; }
28: void Draw();
29: private:
30: int itsRadius;
31: int itsCircumference;
32: };
33:
34: voidCircle::Draw()
35: {
36: cout << "Circle drawing routine here!n";
37: Shape::Draw();
38: }
39:
40:
41: class Rectangle : public Shape
42: {
43: public:
44: Rectangle(int len, int width):
45: itsLength(len), itsWidth(width){ }
46: virtual ~Rectangle(){ }
47: long GetArea() { return itsLength * itsWidth; }
48: long GetPerim() { return 2*itsLength + 2*itsWidth;
49: virtual int GetLength() { return itsLength; >
50: virtual int GetWidth() { return itsWidth; }
51: void Draw();
52: private:
53: int itsWidth;
54: int itsLength;
55: };
56:
57: void Rectangle::Draw()
58: {
59: for (int i = 0; i<itsLength; i++)
60: {
61: for (int j = 0; j<itsWidth; j++)
62: cout << "x ";
63:
64: cout << "n";
65: }
66: Shape::Draw();
67: }
68:
69:
70: class Square : public Rectangle
71: {
72: public:
73: Square(int len);
74: Square(int len, int width);
75: virtual ~Square(){ }
76: long GetPerim() { return 4 * GetLength();}
77: };
78:
79: Square::Square(int len):
80: Rectangle(len,len)
81: { }
82:
83: Square::Square(int len, int width):
84: Rectangle(len,width)
85:
86: {
87: if (GetLength() != GetWidth())
88: cout << "Error, not a square... a Rectangle??n";
89: }
90:
91: int main()
92: {
93: int choice;
94: bool fQuit = false;
95: Shape * sp;
96:
97: while (1)
98: {
99: cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
100: cin >> choice;
101:
102: switch (choice)
103: {
104: case 1: sp = new Circle(5);
105: break;
106: case 2: sp = new Rectangle(4,6);
107: break;
108: case 3; sp = new Square (5);
109: break;
110: default: fQuit = true;
111: break;
112: }
113: if (fQuit)
114: break;
115:
116: sp->Draw();
117: delete sp;
118: cout << "n";
119: }
120: return 0;
121: }
Результат:
(1)Circle (2)Rectangle (3)Square (0)Quit: 2
x x x x x x
x x x x x x
x x x x x x
X X X Х X X
Abstract drawing mechanism!
(1)Circle (2)Rectangle (3)Square (0)Quit: 3
x x x x x
X X X X X
X X X X X
X X X X X
X X X X X
Abstract drawing mechanism!
(1)Circle (2)Rectangle (3)Square (0)Quit: 0
Анализ: В строках 5—14 объявляется класс абстрактного типа данных Shape с тремя чистыми виртуальными функциями. Впрочем, для того чтобы класс стал ADT, достаточно было объявить в нем хотя бы один из методов как чистую виртуальную функцию.
Далее в программе все три функции базового класса замешаются в производных классах Circle и Rectangle, но одна из них — функция Draw() — выполняется как чистая виртуальная функция, поскольку в объявлении замещенного варианта функции в производных классах есть вызов исходной функции из базового класса. В результате выполнение этой функции в обоих производных классах приводит к выведению на экран одного и того же сообщения.
Сложная иерархия абстракций
Иногда бывает необходимо произвести один класс ADT от другого класса ADT, например для того, чтобы в производном классе ADT преобразовать в обычные методы часть функций, объявленных в базовом классе как чистые виртуальные, оставив при этом другие функции чистыми.
Так, в классе Animal можно объявить методы Eat(), Sleep(), Move() и Reproduce() как чистые виртуальные функции. Затем от класса Animal производятся классы Mammal и Fish.
Исходя из соображения, что все млекопитающие размножаются практически одинаково, имеет смысл в классе Mammal преобразовать метод Reproduce() в обычный, оставив при этом методы Eat(), Sleep() и Move() чистыми виртуальными функциями.
Затем от класса Mammal производится класс Dog, в котором необходимо заместить все три оставшиеся чистые виртуальные функции, чтобы получить возможность создавать объекты класса Dog.
Таким образом, наследование одного класса ADT от другого класса ADT позволяет объявлять общие методы для всех следующих производных классов, чтобы не замещать потом эти функции по отдельности в каждом производном классе.
В листинге 13.10 показан базовый костяк программы, в котором используется объявленный выше подход.
Листинг 13.10. Наследование класса ADT от другого класса ADT
1: // Листинг 13.10.
2: // Deriving ADTs from other ADTs
3: #include <iostream.h>
4:
5: enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown };
6:
7: class Animal // Общий базовый класс для классов Mammal и Fish
8: {
9: public:
10: Animal(int);
11: virtual ~Animal() { cout << "Animal destructor...n"; }
12: virtual int GetAge() const { return itsAge; }
13: virtual void SetAge(int age) { itsAge = age; }
14: virtual void Sleep() const = 0;
15: virtual void Eat() const = 0;
16: virtual void Reproduce() const = 0;
17: virtual void Move() const = 0;
18: virtual void Speak() const = 0;
19: private:
20: int itsAge;
21: };
22:
23: Animal::Animal(int age):
24: itsAge(age)
25: {
26: cout << "Animal constructor...n";
27: }
28:
29: class Mammal : public Animal
30: {
31: public:
32: Mammal(int age):Animal(age)
33: { cout << "Mammal constructor...n";}
34: virtual ~Mammal() { cout << "Mammal destructor...n";}
35: virtual void Reproduce() const
36: { cout << "Mammal reproduction depicted...n"; }
37: };
38:
39: class Fish : public Animal
40: {
41: public:
42: Fish(int age):Animal(age)
43: { cout << "Fish constructor...n";}
44: virtual ~Fish() { cout << "Fish destructor...n"; }
45: virtual void Sleep() const { cout << "fish snoring...n"; }
46: virtual void Eat() const { cout << "fish feeding...n"; }
47: virtual void Reproduce() const
48: { cout << "fish laying eggs...n"; }
49: virtual void Move() const
50: { cout << "fish swimming...n"; }
51: virtual void Speak() const { }
52: };
53:
54: class Horse : public Mammal
55: {
56: public:
57: Horse(int age, COLOR color ):
58: Mamrnal(age), itsColor(color)
59: { cout << "Horse constructor...n"; }
60: virtual ~Horse() { cout << "Horse destructor...n"; }
61: virtual void Speak()const { cout << "Whinny!... n"; }
62: virtual COLOR GetItsColor() const { return itsColor; }
63: virtual void Sleep() const
64: { cout << "Horse snoring.,.n"; }
65: virtual void Eat() const { cout << "Horse feeding...n"; }
66: virtual void Move() const { cout << "Horse running...n";} 67:
68: protected:
69: COLOR itsColor;
70: };
71:
72: class Dog : public Mammal
73: {