Виртуальные функции. Что это такое? Часть 2 (исходники)Источник: NET Code Сергей Малышев (aka Михалыч)
Часть 2. Абстрактные классы и пример использованияДавайте продолжим начатое в первой части статьи рассмотрение использования виртуальных функций. На этот раз я предлагаю вам конкретный пример. Пусть очень простой, но зато он полностью работоспособен, и позволяет продемонстрировать все тонкости, о которых вы уже прочитали.
Чистые виртуальные функции Можно подумать, что все остальные функции грязные! Нет, конечно. Чистая в данном случае означает буквально пустая функция. Давайте посмотрим, что такое чистая виртуальная функция. Код: class A Как видите, все отличие только в том, что появилась конструкция =0, которая называется чистый спецификатор. Чистая виртуальная функция абсолютно ничего не делает и недоступна для вызовов. Ее назначение служить основой (если хотите, шаблоном) для замещающих функций в производных классах. Класс, который содержит хотя бы одну чистую виртуальную функцию, называется абстрактным классом. Почему абстрактным? Потому, что создавать самостоятельные объекты такого класса нельзя. Это всего лишь заготовка для других классов. Механизм абстрактных классов разработан для представления общих понятий, которые в дальнейшем предполагается конкретизировать. Эти общие понятия обычно невозможно использовать непосредственно, но на их основе можно, как на базе, построить производные частные классы, пригодные для описания конкретных объектов. Все животные в своем поведении имеют такие функции, как есть, пить, спать, издавать звук. Имеет смысл определить базовый класс, в котором сразу объявить все эти функции и сделать их чистыми виртуальными. А потом из этого класса выводить классы, описывающие конкретных животных (или виды), со своим специфичным поведением. А базовый класс при этом действительно получается абстрактным. Ведь он не описывает никакое более-менее конкретное животное (даже вид животных). Это может быть и рыба и птица разные вещи!
Простейшая программаВозвращаясь к воинственному примеру из первой части, надо сказать, что в этом случае это абсолютно миролюбивый и даже детский пример. Кстати, не смотря на всю его простоту, он вполне может быть основой для создания простейшей развивающей игры для детей очень младшего возраста. Код: #include <iostream.h> В качестве базового класса мы соорудили абстрактный класс Animal. Он имеет единственный член-данные Title, описывающий кличку животного. В нем есть явно определенный конструктор, который присваивает животному его имя. И единственная чистая виртуальная функция speak(), которая описывает, какие звуки издает животное. Код: Бобик говорит гав-гав Все работает. Каждый объект сам выводит свою запись. Виртуальные функции действуют! Код: Error: animals.cpp(42,25):Virtual function 'Lion::speak()' conflicts with base class 'Cat' А система помощи из Borland C++ 5 выдаст следующий хелп: Код: A virtual function has the same argument types as one in a base class, but a different return type. This is illegal. Дальше еще интереснее. Попробуйте раскомментировать третью функцию, а первые две закомментируйте. Компилятор на сей раз просто выдаст предупреждение (но не ошибку, и программа будет работать!): Код: Warn : animals.cpp(44,3):'Lion::speak(int)' hides virtual function 'Cat::speak()' Это тот самый случай, когда объявляется замещающая виртуальная функция с тем же самым типом возвращаемого значения, но с другим набором параметров. Что в случае со звуками, которые издает лев может быть передано функции speak(), как параметр? Предположение типа каким местом издается звук я отмел сразу же и бесповоротно. Предположим, что это зависимость от времени суток, то есть когда. Ну, например, ближе к ночи лев захотел спать, и стал зевать. Поэтому в данном случае функции speak(int When)передан параметр When, который правда в ней нигде не используется, но это не важно. Функция-то все равно работать будет. Код: Бобик говорит гав-гав Оба-на! Кажется что-то не так! Лев-то у вас уже не рычит и не зевает, а мило мяукает. С чего бы это? А ведь компилятор предупреждал функция 'Lion::speak(int)' скрывает (переопределяет) виртуальную функцию 'Cat::speak()'. Это уже совсем другая функция! Поэтому, раз в данном классе нет правильно определенной виртуальной функции, то по указателю вызывается виртуальная функция speak()из базового класса. А в нашем случае базовым для класса Lion является класс Cat. Вот лев у вас и замяукал! Вот, пока что и все. Рассмотрение теории виртуальных функций в общем случае мы закончили. Правда мы не рассмотрели еще много разных моментов. Но это уже очень тонкие тонкости. Об этом можно целую книгу написать. |