Программирование >>  Структурное программирование 

1 ... 198 199 200 [ 201 ] 202 203 204 ... 342


10.3. Виртуальные функции

Предположим, что ряд классов форм, таких, как Circle (круг). Triangle (треугольник), Rectangle (прямоугольник). Square (квадрат) и т.д., являются производными от базового класса Shape (форма). В объектно-ориентированном программировании каждый из этих классов может быть наделен способностью нарисовать свою форму. Хотя каждый класс имеет свою функцию рисования draw, для разных форм эти функции совершенно различны. При рисовании любой формы, какая бы она ни была, было бы прекрасно иметь возможность работать со всеми этими формами в целом как с объектами базового класса Shape. Тогда для рисования любой формы мы могли бы просто вызвать функцию draw базового класса Shape и предоставить программе динамически (т.е. во время выполнения программы) определять, какую из функций draw производного класса следует использовать.

Для того, чтобы предоставить такого рода возможность, объявим функцию draw виртуальной функцией и затем переопределим функцию draw в каждом производном классе, чтобы она рисовала соответствующую форму.

10.2. Поля типов и операторы switch

Одним из средств управления объектами различных типов является применение оператора switch, который может по-разному обрабатывать различные объекты в зависимости от их типов. Например, в иерархии форм, в которой каждая форма хранит свой тип в некотором поле - элементе данных, структура выбора switch способна определить по значению этого поля, какую из функций print надо вызвать для работы с объектом данного типом. Однако, при использовании логики оператора switch возникает множество проблем. Программист может забыть произвести проверку типа объекта, когда она необходима. Он также может забыть проверить все возможные случаи в операторе switch. Если программа, основанная на использовании оператора switch, модифицируется и в нее добавляются новые типы объектов, то программист может забыть вставить новые случаи в существующие операторы switch. Каждое добавление и удаление класса требует изменения каждого соответствующего оператора switch в системе. Отслеживание всего этого требует немалого времени и может являться источником ошибок.

Как мы увидим, виртуальные функции и полиморфное программирование может исключить потребность в применении логики оператора switch. Программист может использовать механизм виртуальных функций для автоматического выполнения той же логики, избегая при этом различных ошибок, типичных для применения оператора switch.

Замечание по технике программирования 10.1

Интересным следствием использования виртуальных функций и полиморфизма является то, что программы приобретают более простой вид. Они включают меньше логических ветвлений и больше простого последовательного кода. Это упрощение облегчает тестирование, отладку и сопровождение программ.



Функция объявляется виртуальной с помощью ключевого слова virtual, предшествующего прототипу функции в базовом классе. Например, в базовом классе Shape можно написать

virtual void draw{) const;

Этот прототип объявляет, что функция draw является константной функцией, которая не принимает никаких аргументов, ничего не возвращает и является виртуальной функцией.

Замечание по технике программирования 10.2

Если функция однажды объявлена виртуальной, то она остается виртуальной на любом более низком уровне иерархической структуры.

Хороший стиль программирования 10.1

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

Замечание по технике программирования 10.3

Если в производном классе решено не описывать виртуальную функцию, то производный класс непосредавенно наследует описание виртуальной функции из базового класса.

Если функция draw в базовом классе объявлена как virtual и если мы затем вызываем функцию draw через указатель базового класса, указывающий на объект производного класса (например, shapePtr->draw()), то программа будет динамически (т.е. во время выполнения программы) выбирать соответствующую функцию draw производного класса. Это называется динамическим связыванием и будет продемонстрировано в учебных примерах разделов 10.6 и 10.9.

Замечание по технике программирования 10.4

Переопределенная виртуальная функция должна иметь тот же самый тип возвращаемого значения и ту же сигнатуру, что и виртуальная функция базового класса.

Типичная ошибка программирования 10.1

Если в производном классе переопределяется виртуальная функция базового класса и данная функция не имеет тот же тип возвращаемого значения и ту же сигнатуру, что и соответствующая функция базового класса, то возникает синтаксическая ошибка.

Когда виртуальная функция вызывается путем обращения к заданному объекту по имени и при этом используется операция доступа к элементу точка (например, squareObject.draw( )), тогда эта ссылка разрешается (обрабатывается) во время компиляции (это называется статическим связыванием) и в качестве вызываемой определяется функция класса данного объекта (или наследуемая этим классом).



10.4. Абстрактные базовые классы и конкретные

классы

Когда мы думаем о классе как о типе, мы предполагаем, что будут создаваться объекты этого типа. Однако, имеются случаи, в которых полезно определять классы, для которых программист не намерен создавать какие-либо объекты. Такие классы называются абстрактными классами. Поскольку они применяются в качестве базовых классов в процессе наследования, мы обычно будем называть их абстрактными базовыми классами. Объекты абстрактного базового класса не могут быть реализованы.

Единственным назначением абстрактного класса является создание соответствующего базового класса, от которого другие классы могут унаследовать интерфейс и реализацию. Классы, объекты которых могут быть реализованы, называются конкретными классами.

У нас может быть абстрактный базовый класс TwoDimensionalObject (двумерный), из которого мы можем получить конкретные классы, такие, как Square, Circle, Triangle и т.д. У нас может быть также абстрактный базовый класс ThreeDimensionalObject (трехмерный), из которого можно получить конкретные классы, такие, как Cube, Sphere, Cylinder и т.д. Абстрактные базовые классы являются слишком общими для определения реальных объектов; нам требуется больше определенности, чтобы можно было думать о реализации объектов. Для этого предназначены конкретные классы; они обладают необходимой спецификой, делающей реальным создание объектов.

Класс делается абстрактным путем объявления одной или более его виртуальных функций чисто виртуальными. Чистой виртуальной функцией является такая функция, у которой в ее объявлении тело определено как О (инициализатор равен 0); например:

virtual float earnings( ) const = 0; чистая виртуальная

функция

Замечание по технике программирования 10.5

Если класс является производным от класса с чистой виртуальной функцией и если эта чистая виртуальная функция не описана в производном классе, тогда функция остается чистой виртуальной и в производном классе. Следовательно, такой производный класс также является абстрактным классом.

Типичная ошибка программирования 10.2

Попытка создать объект абстрактного класса (т.е. класса, который содержит хотя бы одну чистую виртуальную функцию) является синтаксической ошибкой.

Иерархия не требует обязательного включения каких-либо абстрактный классов. Но, как мы увидим далее, многие хорошие программы, использующие объектно-ориентированное программирование, имеют иерархию классов, порожденную абстрактным базовым классом. В некоторых случаях абстрактные классы составляют несколько верхних уровней иерархии. Наглядным примером этого является иерархия форм. Иерархия может порождаться абстрактным базовым классом Shape. При переходе на один уровень ниже мы может иметь на два абстрактных класса больше, например, абстрактные ба-



1 ... 198 199 200 [ 201 ] 202 203 204 ... 342

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика