Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Либерти Джесс. Освой самостоятельно С++ за 21 день. - royallib.ru.rtf
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
2.55 Mб
Скачать

Чистые виртуальные функции

C++ поддерживает создание абстрактных типов данных с чистыми виртуальными функциями. Чистыми виртуальными функциями называются такие, которые инициализируются нулевым значением, например:

virtual void Draw() = 0;

Класс, содержащий чистые виртуальные функции, является ADT. Невозможно создать объект для класса, который является ADT. Попытка создания объекта для такого класса вызовет сообщение об ошибке во время компиляции. Помещение в класс чистой виртуальной функции будет означать следующее:

• невозможность создания объекта этого класса;

• необходимость замещения чистой виртуальной функции в производном классе.

Любой класс, произведенный от ADT, унаследует от него чистую виртуальную функцию, которую необходимо будет заместить, чтобы получить возможность создавать объекты этого класса. Так, если класс Rectangle наследуется от класса Shape, который содержит три чистые виртуальные функции, то в классе Rectangle должны быть замещены все эти три функции, иначе он тоже будет ADT. В листинге 13.8 изменено объявление классa Shape таким образом, чтобы он стал абстрактным типом данных. Остальная часть листинга 13.7 не изменилась, поэтому не приводится. Просто замените объявление класса в строках 7—16 листинга 13.7 листингом 13.8 и запустите программу.

Листинг 13.8. Абстрактные типы данных  

1: класс Shape

2: {

3:    public:

4:       Shape(){ }

5:       ~Shape(){ }.

6:       virtual long GetArea() = 0; // ошибка

7:       virtual long GetPerim()= 0;

8:       virtual void Draw() = 0;

9:    private:

10: };

Результат:

(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 x

(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

(1)Circle (2)Rectangle (3)Square (0)Quit: 0

Анализ: Как видите, выполнение программы не изменилось. Просто теперь в программе невозможно создать объект класса Shape.

Абстрактные типы данных

Чтобы объявить класс как абстрактный тип данных.достаточно добавить в него одну или несколько чистых виртуальных функций. Для этогопосле объявления функции необходимо добавить - 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() — выполняется как чистая виртуальная функция, поскольку в объявлении замещенного варианта функции в производных классах есть вызов исходной функции из базового класса. В результате выполнение этой функции в обоих производных классах приводит к выведению на экран одного и того же сообщения.