Нельзя брать там, находясь здесь
Если для объекта класса 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.
Виртуальные деструкторы
В том случае, когда ожидается указатель на объект базового класса, вполне допустима и часто используется на практике передача указателя на объект производного класса. Что произойдет при удалении указателя, ссылающегося на объект производного класса? Если деструктор будет объявлен как виртуальный, то все пройдет отлично — будет вызван деструктор соответствующего производного класса. Затем деструктор производного класса автоматически вызовет деструктор базового класса, и указанный объект будет удален целиком.
|
|
Отсюда следует правило: если в классе объявлены виртуальные функции, то и деструктор должен быть виртуальным.
Дата добавления: 2019-02-12; просмотров: 215; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!