Освой самостоятельно С++ за 21 день.
Шрифт:
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
Анализ:
Пользователю предлагается выбрать объект класса 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 GetAgeconst { 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 Speakconst { 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 Speakconst { cout << "Meow!\n"; }
46: virtual Mammal* Clone { return new Cat(*this); }
47: };
48:
49: Cat::Cat(const Cat & rhs):