Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Выполнение чистых виртуальных функций



 

Обычно чистые виртуальные функции объявляются в абстрактном базовом классе и не выполняются. Поскольку невозможно создать объект абстрактного базового класса, как правило, нет необходимости и 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: {

74: public:

75: Dog(int age, COLOR color ):

76: Mammal(age), itsColor(color)

77: { cout < < " Dog constructor...\n"; }

78: virtual ~Dog() { cout < < " Dog destructor...\n"; }

79: virtual void Speak()const { cout < < " Woof!... \n"; }

80: virtual void 51eep() const { cout < < " Dog snoring...\n"; }

81: virtual void Eat() const { cout < < " 0og eating...\n"; }

82: virtual void Move() const { cout < < " Dog running...\n"; }

83: virtual void Reproduce() const

84: { cout < < " Dogs reproducing...\n"; }

85:

86: protected:

87: COLOR itsColor;

88: };

89:

90: int main()

91: {

92: Animal *pAnimal=0;

93: int choice;

94: bool fQuit = false;

95:

96: while (1)

97: {

98: cout < < " (1)Dog (2)Horse (3)Fish(0)Quit: ";

99: cin > > choice; 100:

101: switch (choice)

102: {

103: case 1: pAnimal = new Dog(5, Brown);

104: break;

105: case 2: pAnimal = new Horse(4, Black);

106: break;

107: case 3: pAnimal = new

108: break;

109: default: fQuit = true

110: break;

111: }

112: if (fQuit)

113: break;

114:

115: pAnimal-> Speak();

116: pAnimal-> Eat();

117: pAnimal-> Reproduce();

118: pAnimal-> Move();

119: pAnimal-> Sleep();

120: delete pAnimal;

121: cout < < " \n";

122: }

123: return 0;

124: }

 

Результат:

(1)Dog (2)Horse (3)Bird (0)Quit: 1

Animal constructor...

Mammal constructor...

Dog constructor...

Woof!...

Dog eating...

Dog reproducing....

Dog running...

Dog snoring...

Dog destructor...

Mammal destructor...

Animal destructor...

(1)Dog (2)Horse (3)Bird (0)Quit: 0

 

Анализ: В строках 7—21 объявляется абстрактный тип данных Animal. Единственный метод этого класса, не являющийся чистой виртуальной функцией, это общий для объектов всех производных классов метод itsAge. Остальные пять методов — Sleep(), Eat(), Reproduce(), Move() и Speak() — объявлены как чистые виртуальные функции.

Класс Mammal производится от Animal в строках 29—37 и не содержит никаких данных. В нем замещается функция Reproduce(), чтобы задать способ размножения, общий для всех млекопитающих. Класс Fish производится непосредственно от класса Animal, поэтому функция Reproduce() в нем замещается иначе, чем в классе Mammal (и это соответствует реальности).

Во всех других классах, производимых от класса Mammal, теперь нет необходимости замещать общий для всех метод Reproduce(), хотя при желании это можно сделать для определенного класса, как, например, в нашей программе это было сделано в строке 83 для класса Dog. Все остальные чистые виртуальные функции были замещены в классах Fish, Horse и Dog, поэтому для каждого из них можно создавать соответствующие объекты.

В теле программы используется указатель класса Animal, с помощью которого делаются ссылки на все объекты производных классов. В зависимости от того, с каким объектом связан указатель в текущий момент, вызываются соответствующие виртуальные функции.

При попытке создать объекты для классов абстрактных типов данных Animal или Mammal компилятор покажет сообщение об ошибке.

 


Поделиться:



Популярное:

Последнее изменение этой страницы: 2017-03-08; Просмотров: 452; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.08 с.)
Главная | Случайная страница | Обратная связь