Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 67 68 69 70 71 72 73 74 75 ... 170

Нельзя брать там, находясь здесь

Если для объекта класса Dog объявлен метод WagTail(), который не принадлежит классу Mammal, то невозможно получить доступ к этому методу, используя указатель класса Mammal (если только этот указатель не будет явно преобразован в указатель класса Dog). Поскольку функция WagTail() не является виртуальной и не принадлежит классу Mammal, то доступ к ней можно получить только из объекта класса Dog или с помощью указателя этого класса.

Поскольку любые преобразования чреваты ошибками, создатели C++ допустили только явные преобразования типов. Всегда можно преобразовать любой указатель класса Mammal в указатель класса Dog, но есть более надежный и безопасный способ вызова метода WagTail(). Чтобы разобраться в тонкостях упомянутого метода, необходимо освоить множественное наследование, о котором речь пойдет на следующем занятии, или научиться работе с шаблонами, что будет темой занятия 20.

Дробление объекта

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

Листинг 11.10. Дробление объекта при передаче его как значения

1: //Листинг 11.10. Дробление объекта при передачи его как значения

2:

3: #include <iostream.h>

4:

5: class Mammal

6: {

7:    public:

8:       Mammal():itsAge(1) { }

9:       virtual ~Mammal() { }

10:      virtual void Speak() const { cout << "Mammal speak!n"; }

11:   protected:

12:      int itsAge;

13: };

14:

15: class Dog : public Mammal

16: {

17:    public:

18:       void Speak()const { cout << "Woof!n"; }

19: };

20:

21: class Cat : public Mammal

22: {

23:    public:

24:       void Speak()const { cout << "Meow!ri"; >

25: };

26:

27: void ValueFunction (Mammal);

28: void PtrFunction (Mammal*);

29: void RefFunction (Mammal&);

30: int main()

31: {

32:    Mammal* ptr=0;

33:    int choice;

34:    while (1)

35:    {

36:       bool fQuit = false;

37:       cout << "(1)dog (2)cat (0)Quit: ";

38:       cin >> choice;

39:       switch (choice)

40:       {

41:          case 0: fQuit = true;

42:          break;

43:          case 1: ptr = new Dog;

44:          break;

45:          case 2: ptr = new Cat;

46:          break;

47:          default: ptr = new Mammal;

48:          break;

49:       }

50:       if (fQuit)

51:       break;

52:       PtrFunction(ptr);

53:       RefFunction(*ptr);

54:       ValueFunction(*ptr);

55:    }

56:    return 0;

57: }

58:

59: void ValueFunction (Mammal MammalValue)

60: {

61:    MammalValue.Speak();

62: }

63:

64: void PtrFunction (Mammal * pMammal)

65: {

66:    pMammal->Speak();

67: }

68:

69: void RefFunction (Mammal & rMammal)

70: {

71:    rMammal.Speak();

72: }

Результат:

(1)dog (2)cat (0)Quit: 1

Woof

Woof

Mammal Speak!

(1)dog (2)cat (0)Quit: 2

Meow!

Meow!

Mammal Speak!

(1)dog (2)cat (0)Quit: 0

Анализ: В строках 5—25 определяются классы Mammal, Dog и Cat. Затем объявляются три функции — PtrFunction(), RefFunction() и ValueFunction(). Они принимают соответственно указатель класса Mammal, ссылку класса Mammal и объект класса Mammal. После чего выполняют одну и ту же операцию — вызывают метод Speak().

Пользователю предлагается выбрать объект класса Dog или класса Cat, после чего в строках 43—46 создается указатель соответствующего типа.

Судя по информации, выведенной программой на экран, пользователь первый раз выбрал объект класса Dog, который был создан в свободной области памяти 43-й строкой программы. Затем объект класса Dog передается в три функции с помощью указателя, с помощью ссылки и как значение.

В том случае, когда в функцию передавался адрес объекта с помощью указателя или ссылки, успешно выполнялась функция-член Dog->Speak(). На экране компьютера дважды появилось сообщение, соответствующее выбранному пользователем объекту.

Разыменованный указатель передает объект как значение. В этом случае функция распознает принадлежность переданного объекта классу Mammal, компилятор разбивает объект класса Dog пополам и использует только ту часть, которая была создана конструктором класса Mammal. В таком случае вызывается версия метода Speak(), которая была объявлена для класса Mammal, что и отобразилось в информации, выведенной программой на экран.

Те же действия и с тем же результатом были выполнены затем и для объекта класса Cat.

Виртуальные деструкторы

В том случае, когда ожидается указатель на объект базового класса, вполне допустима и часто используется на практике передача указателя на объект производного класса. Что произойдет при удалении указателя, ссылающегося на объект производного класса? Если деструктор будет объявлен как виртуальный, то все пройдет отлично — будет вызван деструктор соответствующего производного класса. Затем деструктор производного класса автоматически вызовет деструктор базового класса, и указанный объект будет удален целиком.

Отсюда следует правило: если в классе объявлены виртуальные функции, то и деструктор должен быть виртуальным.

Виртуальный конструктор-копировщик

Конструкторы не могут быть виртуальными, из чего можно сделать вывод, что не может быть также виртуального конструктора-копировщика. Но иногда требуется, чтобы программа могла передать указатель на объект базового класса и правильно скопировать его в объект производного класса. Чтобы добиться этого, необходимо в базовом классе создать виртуальный метод Clone(). Метод Clone() должен создавать и возвращать копию объекта текущего класса.

Поскольку в производных классах метод Clone() замещается, при вызове его создаются копии объектов, соответствующие выбранному классу. Программа, использующая этот метод, показана в листинге 11.11.

Листинг 11.11. Виртуальный конструктор-копировщик

1: //Листинг 11.11. Виртуальный конструктор-копировщик

2:

3: #include <iostream.h>

4:

5: class Mammal

6: {

7:    public:

8:       Mammal():itsAge(1) { cout << "Mammal constructor...n"; }

9:       virtual ^Mammal() { cout << "Mammal destructor...n"; }

10:      Mammal (const Mammal & rhs);

11:      virtual void Speak() const { cout << "Mammal speak!n"; }

12:      virtual Mammal* Clone() { return new Mammal(*this); }

13:      int GetAge()const { return itsAge; }

14:   protected:

15:      int itsAge;

16: };

17:

18: Mammal::Mammal (const Mammal & rhs):itsAge(rhs.GetAge())

19: {

20:    cout << "Mammal Copy Constructor...n";

21: }

22:

23: class Dog : public Mammal

24: {

25:    public:

26:       Dog() { cout << "Dog constructor...n"; }

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

28:       Dog (const Dog & rhs);

29:       void Speak()const { cout << "Woof!n"; }

30:       virtual Mammal* Clone() { return new Dog(*this); }

31: };

32:

33: Dog::Dog(const Dog & rhs):

34: Mammal(rhs)

35: {

36:    cout << "Dog copy constructor...n";

37: }

38:

39: class Cat : public Mammal

40: {

41:    public:

42:       Cat() { cout << "Cat constructor,,,n"; }

43:       ~Cat() { cout << "Cat destructor...n"; }

44:       Cat (const Cat &);

45:       void Speak()const { cout << "Meow!n"; }

46:       virtual Mammal* Clone() { return new Cat(*this); }

47: };

48:

49: Cat::Cat(const Cat & rhs):

50: Mammal(rhs)

51: {

52:    cout << "Cat copy constructor..,n";

53: }

54:

55: enum ANIMALS { MAMMAL, D0G, CAT };

56: const int NumAnimalTypes = 3;

57: int main()

58: {

59:    Mammal *theArray[NumAnimalTypes];

60:    Mammal* ptr;

61:    int choice, i;

62:    for ( i = 0; i<NumAnimalTypes; i++)

63:    {

64:       cout << "(1)dog (2)cat (3)Mammal: ";

65:       cin >> choice;

66:       switch (choice)

67:       {

68:          case DOG: ptr = new Dog;

69:          break;

70:          case CAT: ptr = new Cat;

71:          break;

72:          default: ptr = new Mammal;

73:          break;

74:       }

75:       theArray[i] = ptr;

76:    }

77:    Mammal *OtherArray[NumAnimalTypes];

78:    for (i=0;i<NumAnimalTypes;i++)

79:    {

80:       theArray[i]->Speak();

81:       OtherArray[i] = theArray[i]->Clone();

82:    }

83:    for (i=0;i<NumAnimalTypes;i++)

84:       OtherArray[i]->Speak();

85:    return 0;

86: }

Результат: 

1: (1)dog (2)cat (3)Mammal: 1

2: Mammal constructor...

3: Dog constructor...

4: (1)dog (2)cat (3)Mammal: 2

5: Mammal constructor...

6: Cat constructor...

7: (1)dog (2)cat (3)Mammal: 3

8: Mammal constructor...

9: Woof!

10: Mammal Copy Constructor...

11: Dog copy constructor...

12: Meow!

13: Mammal Copy Constructor...

14: Cat copy constructor...

15: Mammal speak!

16: Mammal Copy Constructor...

17: Woof!

18: Meow!

19: Mammal speak!

Анализ: Листинг 11.11 похож на два предыдущих листинга, однако в данной программе в классе Mammal добавлен один новый виртуальный метод — Clone(). Этот метод возвращает указатель на новый объект класса Mammal, используя конструктор-копировщик, параметр которого представлен указателем <<this.

1 ... 67 68 69 70 71 72 73 74 75 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий