Программирование >>  Оптимизация возвращаемого значения 

1 ... 35 36 37 [ 38 ] 39 40 41 ... 96


С1 vtbl

реализация С1::-С1

- реализация C1::f1

- реализация C1::f2 . реализация C1;:f3

Рис. 4.1

Обратите внимание, что в таблице отсутствуют невиртуальная функция f 4 и конструктор С1. Невиртуальные функции, включая конструкторы, которые по умолчанию являются невиртуальными, реализуются точно так же, как обычные функции С, и имеют поэтому ту же производительность.

Если класс С2 наследует от класса С1, переопределяет некоторые из наследуемых виртуальных функций и добавляет еще несколько своих:

class С2: public С1 { public: С2 () ;

virtual ~С2 О;

virtual void f1();

virtual void f5(char *str);

Невиртуальная функция. Переопределенная функция. Переопределенная функция. Новая виртуальная функция.

то элементы в виртуальной таблице указывают на функции, соответствующие типу его объектов. Эти элементы включают указатели на виртуальные функции класса С1, которые не переопределяются в классе С2 (см. рис. 4.2).

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

class С1 { public: С1 {) ;

virtual ~С1{) ;

virtual void f1{);

virtual int f2(char c) const;

virtual void f3(const strings s)

void f4() const;

TO массив виртуальной таблицы класса CI будет выглядеть примерно так, как показано на рис. 4.1.



С2 vtbl

реализация С2::-С2

реализация C2::f1

реализация C1;;f2 реализация C1::f3 реализация C2;:f5

Рис. 4.2

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

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

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

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



Данные

vptr

Рис. 4.3

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

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

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

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

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



1 ... 35 36 37 [ 38 ] 39 40 41 ... 96

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