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

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

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

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

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

Конструкторы не могут быть виртуальными, из чего можно сделать вывод, что не может быть также виртуального конструктора-копировщика. Но иногда требуется, чтобы программа могла передать указатель на объект базового класса и правильно скопировать его в объект производного класса. Чтобы добиться этого, необходимо в базовом классе создать виртуальный метод 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.

Метод Clone() замещается в обоих производных классах — Dog и Cat — соответствующими версиями, после чего копии данных передаются на конструкторы- копировщики производных классов. Поскольку Clone() является виртуальной функцией, то в результате будут созданы виртуальные конструкторы-копировщики, как показано в строке 81.

Пользователю предлагается выбрать объект класса 0og, Cat или Mammal. Объект выбранного типа создается в строках 62-74. В строке 75 указатель на новый объект добавляется в массив данных.

Затем осуществляется цикл, в котором для каждого объекта массива вызываются методы Speak() и Clone() (см. строки 80 и 81). В результате выполнения функции возвращается указатель на копию объекта, которая сохраняется в строке 81 во втором массиве.

В строке 1 вывода на экран показан выбор пользователем опции 1 — создание объекта класса Dog. В создание этого объекта вовлекаются конструкторы базового и производного классов. Эта операция повторяется для объектов классов Cat и Mammal в строках вывода 4-8.

В строке 9 вывода показано выполнение метода Speak() для объекта класса Dog. Поскольку функция Speak() также объявлена как виртуальная, то при обращении к ней вызывается та ее версия, которая соответствует типу объекта. Затем следует обращение еще к одной виртуальной функции Clone(), виртуальность которой проявляется в том, что при вызове из объекта класса Dog запускаются конструктор класса Mammal и конструктор-копировщик класса Dog.

То же самое повторяется для объекта класса Cat (строки вывода с 12—14) и объекта класса Mammal (строки вывода 15 и 16). В результате создается массив объектов, для каждого из которых вызывается своя версия функции Speak().