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

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

Жанры

Программирование. Принципы и практика использования 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. Замещение

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

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

Убийца

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

Блуждающие огни 3

Панченко Андрей Алексеевич
3. Блуждающие огни
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
Блуждающие огни 3

Камень Книга одиннадцатая

Минин Станислав
11. Камень
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Камень Книга одиннадцатая

Инкарнатор

Прокофьев Роман Юрьевич
1. Стеллар
Фантастика:
боевая фантастика
рпг
7.30
рейтинг книги
Инкарнатор

Его наследник

Безрукова Елена
1. Наследники Сильных
Любовные романы:
современные любовные романы
эро литература
5.87
рейтинг книги
Его наследник

Real-Rpg. Город гоблинов

Жгулёв Пётр Николаевич
1. Real-Rpg
Фантастика:
фэнтези
7.81
рейтинг книги
Real-Rpg. Город гоблинов

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

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

Любимая учительница

Зайцева Мария
1. совершенная любовь
Любовные романы:
современные любовные романы
эро литература
8.73
рейтинг книги
Любимая учительница

Калибр Личности

Голд Джон
2. Калибр Личности
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Калибр Личности

Возвращение

Кораблев Родион
5. Другая сторона
Фантастика:
боевая фантастика
6.23
рейтинг книги
Возвращение

Бандит

Щепетнов Евгений Владимирович
1. Петр Синельников
Фантастика:
фэнтези
7.92
рейтинг книги
Бандит

Везунчик. Дилогия

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

Новый Рал 10

Северный Лис
10. Рал!
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Новый Рал 10

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

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