Ранее было сказано, что динамический полиморфизм влечет за собой некоторые накладные расходы. Как вы помните, обычные методы места в классе не занимают. Однако с виртуальными методами ситуация другая. Следующий пример показывает, что при наличии хотя бы одной виртуальной функции размер класса без полей равен четырем (листинг 9.4).
Листинг 9.4. Размеры классов с виртуальными функциями
class OneVirtual
{ virtual void f(void) {}
};
class TwoVirtual { virtual void f(void) {} virtual void g(void) {}
}:
int main()
{ cout << sizeof(OneVirtual) << endl; // размер = 4
cout << sizeof(TwoVirtual) << endl; // размер = 4
return 0;
В то же время количество виртуальных функций, очевидно, роли не играет — размер класса остается равен четырем. В данном случае 4 — это размер указателя.
ПРИМЕЧАНИЕ
Мы работаем на платформе intel под Windows. На других платформах размер указателя может быть другим.
Если в классе есть «родные» или унаследованные виртуальные функции, компилятор создает таблицу виртуальных функций — обычно ее называют VMT (Virtual Method Table). В этой таблице для каждой виртуальной функции содержится указатель на нее. Адрес этой таблицы и помещается в класс.
Таким образом, при наличии виртуальных функций расходуется дополнительная память. Выполняется программа тоже несколько медленнее, чем при статическом связывании. Во-первых, при создании объекта с виртуальными методами таблицу VMT требуется инициализировать — это должно быть сделано в конструкторе. Во-вторых, функции вызываются косвенно — через указатель таблицы. Тем не менее динамический полиморфизм — это настолько важный механизм, что разработчики языка Java решили сделать все методы виртуальными по умолчанию1. В С++ виртуальность управляема, и такая плата2 считается вполне приемлемой. |