draw_lines()
из класса Circle
, выполняется именно функция draw_lines()
из класса Circle
, а не функция draw_lines()
из класса Shape
. Это свойство часто называют Наследование, динамический полиморфизм и инкапсуляция — наиболее распространенные характеристики
Довольно много технической терминологии! Но что все это значит? И как на самом деле эти механизмы работают? Давайте сначала нарисуем простую диаграмму наших классов графического интерфейса, показав их отношения наследования.
Стрелки направлены от производного класса к базовому. Такие диаграммы помогают визуализировать отношения между классами и часто украшают доски программистов. По сравнению с коммерческими пакетами эта иерархия классов невелика и содержит всего шестнадцать элементов. Причем в этой иерархии только класс Open_polyline
Shape
), несмотря на то, что он представляет абстрактное понятие о фигуре и никогда не используется для ее непосредственного воплощения.14.3.1. Схема объекта
Как объекты размещаются в памяти? Как было показано в разделе 9.4.1, схема объекта определяется членами класса: данные-члены хранятся в памяти один за другим. Если используется наследование, то данные-члены производного класса просто добавляются после членов базового класса. Рассмотрим пример.
Класс Circle
Shape
(в конце концов, он является разновидностью класса Shape
) и может быть использован вместо класса Shape
. Кроме того, класс Circle
имеет свой собственный член r
, который размещается в памяти после унаследованных данных-членов. Shape
draw_lines()
из класса Shape
. Для этого обычно в таблицу функций заносится ее адрес. Эта таблица обычно называется vtbl
(таблица виртуальных функций), а ее адрес часто имеет имя vptr
(виртуальный указатель). Указатели обсуждаются в главах 17-18; здесь они действуют как ссылки. В конкретных реализациях языка таблица виртуальных функций и виртуальный показатель могут называться иначе. Добавив таблицу vptr
и указатели vtbl
к нашему рисунку, получим следующую диаграмму.Поскольку функция draw_lines()
vtbl
, за ней следует функция move()
, вторая виртуальная функция. Класс может иметь сколько угодно виртуальных функций; его таблица vtbl
может быть сколь угодно большой (по одной ячейке на каждую виртуальную функцию). Теперь, когда мы вызовем функцию x.draw_lines()
, компилятор сгенерирует вызов функции, найденной в ячейке draw_lines()
таблицы vtbl
, соответствующей объекту x
. В принципе код просто следует по стрелкам на диаграмме.