|
Программирование >> Дополнительные возможности наследования
тель vptr для объекта класса Dog инициализируется при создании части объекта, принадлежащей базовому классу Mammal, как показано на рис. 11.3. После вызова конструктора класса Dog указатель vptr настраивается таким образом, чтобы указывать на замещенный вариант виртуальной функции (если такой есть), существующий для класса Dog (рис. 11.4). В результате при использовании указателя на класс Mammal указатель vptr по-прежнему ссылается на тот вариант виртуальной функции, который соответствует реальному типу объекта. Поэтому при обращении к методу SpeakO в предыдущем примере выполнялась та функция, которая была задана в соответствующем производном классе. Нельзя Орать там, находясь здесь Если для объекта класса Dog объявлен метод WagTailO, который не принадлежит классу Mammal, то невозможно получить доступ к этому методу, используя указатель класса Mamma] (если только этот указатель не будет явно преобразован в указатель класса Dog). Поскольку функция WagTaiJO не является виртуальной и не принадлежит классу Mammal, то доступ к ней можно получить только из объекта класса Dog или с помощью указателя этого класса. Поскольку любые преобразования чреваты ошибками, создатели С++ допустили только явные преобразования типов. Всегда можно преобразовать любой указатель класса Mammal в указатель класса Dog, но есть более надежный и безопасный способ вызова метода WagTailO. ЧтобЬ( разобраться в тонкостях упомянутого метода, необходимо освоить множественное наследование, о котором речь пойдет на следующем занятии, или научиться работе с шаблонами, что будет темой занятия 20. ДроОленое оОъекта Следует обратить внимание, что вся магия виртуальных функций проявляется только при обращении к ним с помощью указателей и ссылок. Если передать объект как значение, то виртуальную функцию вызвать не удастся. Эта проблема показана в листинге 11.10. AucmuHZ 11.10. ДроОквние оОъвхта при передаче его как значения 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Листинг 11.10. Дробление объекта при передачи его как .значения #include <iostream.h> class Mammal public; Mammal():itsAge(1) { } virtual MammalO { } virtual void Speak() const { cout Mammal speak!\ n ; } protected: int itsAge; } ; class Dog : public Mammal { 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public: void SpeakOconst { cout Woof!\ n ; } } ; class Cat ; public Mammal { public; void SpeakOconst { cout Meow!\ n ; } 17 18 19 20 21 22 23 24 25 26 27 void ValueFunction (Mammal); void PtrFunction (Mammal*); void RefFunction (Mammal&); int mainO { Mammal* ptr=0; int choice; while (1) { bool fOuit = false; cout (1)dog (2)cat (O)Quit cin >> choice; switch (choice) { case 0: fOuit = true; break; case 1: ptr = new Dog; break; case 2: ptr = new Cat; break; default: ptr = new Mammal; break; if (fOuit) break; PtrFunction(ptr); RefFunction(ptr); ValueFunction(*ptr); return 0; void ValueFunction (Mammal MammalValue) { MammalValue. SpeakO; void PtrFunction (Mammal pMammal) { pMammal->Speak(); 67 68 69 70 71 72 void RefFunction (Mammal & rMammal) { rMammal.Speak(); (l)dog (2)cat (O)Quit: 1 Woof Woof Mammal Speak! (1)dog (2)cat (O)Quit: 2 Meow! Meow! Mammal Speak! (1)dog (2)cat (O)Quit: 0 В строках 5-25 определяются классы Mammal, Dog и Cat. Затем объявляются три функции - PtrFunction(), RefFunction() и ValueFunction(). Они принимают соответственно указатель класса Mammal, ссылку класса Mammal и объект класса Mammal. После чего выполняют одну и ту же операцию - вызывают метод Speak(). Пользователю предлагается выбрать объект класса Dog или класса Cat, после чего в строках 43-46 создается указатель соответствующего типа. Судя по информации, выведенной программой на экран, пользователь первый раз выбрал объект класса Dog, который был создан в свободной области памяти 43-й строкой программы. Затем объект класса Dog передается в три функции с помощью указателя, с помощью ссылки и как значение. В том случае, когда в функцию передавался адрес объекта с помощью указателя или ссылки, успешно выполнялась функция-член Oog->Speak(). На экране компьютера дважды появилось сообщение, соответствующее выбранному пользователем объекту. Разыменованный указатель передает объект как значение. В этом случае функция распознает принадлежность переданного объекта классу Mammal, компилятор разбивает объект класса Dog пополам и использует только ту часть, которая была создана конструктором класса Mammal. В таком случае вызывается версия метода Speak(), которая была объявлена для класса Mammal, что и отобразилось в информации, выведенной программой на экран. Те же действия и с тем же результатом были выполнены затем и для объекта класса Cat. Виртуальные уеструкторы в том случае, когда ожидается указатель на объект базового класса, вполне допустима и часто используется на практике передача указателя на объект производного класса. Что произойдет при удалении указателя, ссылающегося на объект производного класса? Если деструктор будет объявлен как виртуальный, то все пройдет отлично - будет вызван деструктор соответствующего производного класса. Затем деструктор производного класса автоматически вызовет деструктор базового класса, и указанный объект будет удален целиком. Отсюда следует правило: если в классе объявлены виртуальные функции, то и деструктор должен быть виртуальным.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |