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

1 ... 203 204 205 [ 206 ] 207 208 209 ... 342


Строка

Ь.print О; Статическое связывание

явным образом вызывает функцию-элемент print класса Boss, используя операцию доступа к элементу точка из заданного объекта b класса Boss. Это является примером статического связывания, поскольку тип объекта, для которого вызывается функция, известен во время компиляции. Этот вызов включен с целью сравнения, чтобы показать, правильная ли функция print вызывается при использовании динамического связывания. Строка

cout заработал $ << Ь.earnings О; Статическое связывание

явно вызывает функцию-элемент earnings класса Boss с помощью операции доступа к элементу точка из заданного объекта b класса Boss. Это также пример статического связывания. Этот вызов включается с целью сравнения, чтобы показать, правильная ли функция earnings вызывается при использовании динамического связывания.

10.7. Новые классы и динамическое связывание

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

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

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

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

Ниже показано, каким образом компилятор и программа (во время выполнения) управляют полиморфизмом с незначительными затратами.

Динамическое связывание требует, чтобы во время выполнения программы вызов виртуальной функции-элемента был бы направлен варианту вир-



туальной функции соответствующего класса. Для этого служит таблица виртуальных методов или viable, которая реализуется в виде массива, содержащего указатели на функции. У каждого класса, который содержит виртуальные функции, имеется таблица виртуальных методов. Для каждой виртуальной функции в классе таблица имеет элемент, содержащий указатель на вариант виртуальной функции, используемый в объектах данного класса. Виртуальная функция, используемая в некотором классе, может быть определена в этом классе или прямо или косвенно наследоваться из базового класса, стоящего выше в иерархии.

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

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

Совет по повышению эффективности 10.1

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

Совет по повышению эффективности 10.2

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

10.8. Виртуальные деструкторы

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

Существует простое решение этой проблемы: объявление деструктора базового класса виртуальным. Это автоматически приведет к тому, что все деструкторы производных классов станут виртуальными, даже если они



Рис. 10.2. Определение абстрактного базового класса Shape (часть 1 из 9)

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

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

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

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

Объявление конструктора виртуальной функцией. Конструкторы не могут быть виртуальными.

10.9. Учебный пример : интерфейс наследования

и его реализация

в нашем следующем примере (рис. 10.2) повторно рассматривается иерархия форм точка, круг и цилиндр из предыдущей главе. Но мы дополнительно создаем для этих форм головной элемент этой иерархии в виде абстрактного базового класса Shape. У класса Shape имеются две чистых виртуальных функции printShapeName и print, так что Shape является абстрактным базовым классом. Класс Shape включает также еще две другие виртуальные функции - area и volume, каждая из которых имеет в классе реализацию, возвращающую нулевое значение. Класс Point наследует эти реализации от класса Shape. Это имеет смысл, поскольку и площадь, и объем точки равны нулю. Класс Circle наследует функцию volume класса Point, но имеет собственную реализацию функции area. Класс Cylinder имеет собственные реализации функций area и volume.

SHAPE.Н

Определение абстрактного базового класса Shape #ifndef SHAPE H ttdefine SHAPE H

class Shape { public:

virtual float areaO const { return 0.0; } virtual float volume{) const { return 0.0; }

virtual void printShapeName{) const = 0; Чистая виртуальная

функция

virtual void print() const = 0; Чистая виртуальная функция



1 ... 203 204 205 [ 206 ] 207 208 209 ... 342

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