Чтение онлайн

на главную - закладки

Жанры

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

Для того чтобы обработать вызов виртуальной функции, нам нужна еще одна порция данных в объекте класса
Shape
: информация о том, какая функция будет на самом деле вызываться при обращении к функции
draw_lines
из класса
Shape
. Для этого обычно в таблицу функций заносится ее адрес. Эта таблица обычно называется
vtbl
(таблица виртуальных функций), а ее адрес часто имеет имя
vptr
(виртуальный указатель). Указатели обсуждаются в главах 17-18; здесь они действуют как ссылки. В конкретных реализациях
языка таблица виртуальных функций и виртуальный показатель могут называться иначе. Добавив таблицу
vptr
и указатели
vtbl
к нашему рисунку, получим следующую диаграмму.

Поскольку функция

draw_lines
— первая виртуальная функция, она занимает первую ячейку в таблице
vtbl
, за ней следует функция
move
, вторая виртуальная функция. Класс может иметь сколько угодно виртуальных функций; его таблица
vtbl
может быть сколь угодно большой (по одной ячейке на каждую виртуальную функцию). Теперь, когда мы вызовем функцию
x.draw_lines
, компилятор сгенерирует вызов функции, найденной в ячейке
draw_lines
таблицы
vtbl
, соответствующей объекту
x
. В принципе код просто следует по стрелкам на диаграмме.

Итак, если объект

x
относится к классу
Circle
, будет вызвана функция
Circle::draw_lines
. Если объект
x
относится к типу, скажем,
Open_polyline
, который использует таблицу
vtbl
точно в том виде, в каком ее определил класс
Shape
, то будет вызвана функция
Shape::draw_lines
. Аналогично, поскольку в классе
Circle
не определена его собственная функция
move
, при вызове
x.move
будет выполнена функция
Shape::move
, если объект
x
относится к классу
Circle
. В принципе код, сгенерированный для вызова виртуальной функции, может просто найти указатель
vptr
и использовать его для поиска соответствующей таблицы
vtbl
и вызова нужной функции оттуда. Для этого понадобятся два обращения к памяти и обычный вызов функции, — быстро и просто.

Класс

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

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

Circle::draw_lines
), при котором функция из производного класса записывается в таблицу
vtbl
вместо соответствующей функции из базового класса, называется замещением (overriding). Например, функция
Circle::draw_lines
замещает функцию
Shape::draw_lines
.

Почему

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

14.3.2. Вывод классов и определение виртуальных функций

Мы указываем, что класс является производным, упоминая базовый класс перед его именем. Рассмотрим пример.

struct Circle:Shape { /* ... */ };

По умолчанию члены структуры, объявляемой с помощью ключевого слова
struct
, являются открытыми (см. раздел 9.3) и наследуют открытые члены класса. Можно было бы написать эквивалентный код следующим образом:

class Circle : public Shape { public: /* ... */ };

Эти два объявления класса

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

Не забудьте указать слово

public
, когда захотите объявить открытые члены класса. Рассмотрим пример.

class Circle : Shape { public: /* ... */ }; // возможно, ошибка

В этом случае класс

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

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

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

struct Shape {

// ...

virtual void draw_lines const;

virtual void move;

// ...

};

virtual void Shape::draw_lines const { /* ... */ } // ошибка

void Shape::move { /* ... */ } // OK

14.3.3. Замещение

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

Поделиться:
Популярные книги

Физрук 2: назад в СССР

Гуров Валерий Александрович
2. Физрук
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Физрук 2: назад в СССР

На границе империй. Том 2

INDIGO
2. Фортуна дама переменчивая
Фантастика:
космическая фантастика
7.35
рейтинг книги
На границе империй. Том 2

Последний Паладин

Саваровский Роман
1. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин

Маленькая хозяйка большого герцогства

Вера Виктория
2. Герцогиня
Любовные романы:
любовно-фантастические романы
7.80
рейтинг книги
Маленькая хозяйка большого герцогства

Под маской моего мужа

Рам Янка
Любовные романы:
современные любовные романы
5.67
рейтинг книги
Под маской моего мужа

Главная роль 3

Смолин Павел
3. Главная роль
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Главная роль 3

Дракон

Бубела Олег Николаевич
5. Совсем не герой
Фантастика:
фэнтези
попаданцы
9.31
рейтинг книги
Дракон

Мне нужна жена

Юнина Наталья
Любовные романы:
современные любовные романы
6.88
рейтинг книги
Мне нужна жена

Кротовский, сколько можно?

Парсиев Дмитрий
5. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кротовский, сколько можно?

Черный дембель. Часть 4

Федин Андрей Анатольевич
4. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 4

Идеальный мир для Лекаря 22

Сапфир Олег
22. Лекарь
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Идеальный мир для Лекаря 22

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

Довлатов. Сонный лекарь

Голд Джон
1. Не вывожу
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Довлатов. Сонный лекарь

Газлайтер. Том 16

Володин Григорий Григорьевич
16. История Телепата
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Газлайтер. Том 16