В некоторых объектно-ориентированных языках, которые поддерживают позднее связывание, проверка ошибочных сообщений осуществляется на этапе выполнения программы. С++, посредством виртуальных функций, поддерживает и статический контроль сообщений, и позднее связывание.
class P {
public:
P(void){}
virtual void hello(void) {}
};
class C: public P {
public:
C(void) {}
virtual void hello(void) { cout << “Hello world”; }
};
void main() {
P *p; C c;
p=&c;
p->hello(“Hello”); // ошибка
}
Компилятор выдаст ошибку на строке p->hello(“hello”). Компилятор может определить, что виртуальная функция hello не имеет параметров, даже если сообщение связывается с методом hello класса C во время выполнения программы. Виртуальные функции, определенные в родительском или потомственных классе имеют одинаковые списки параметров, так как позднее связывание не оказывает влияния на контроль типов параметров.
Техническая реализация виртуальных функций
Объект С++ представляет собой непрерывный участок памяти. Указатель на такой объект содержит начальный адрес этого участка. Когда вызывается функция-член (объекту посылается сообщение), этот вызов транслируется в обычный вызов функции с дополнительным аргументом, который содержит указатель на объект.
Например,
ClassName *object;
object->message(10);
преобразуется в
ClassName_message(object,10);
При создании объектов производных классов их поля сцепляются с полями родительских классов. Эту функцию выполняет компилятор.
Виртуальные функции реализованы с использованием таблиц функций. Рассмотрим следующие классы.
class P {
int value;
public:
virtual int method1(float r):
virtual void method2(void);
virtual float method3(char *s);
};
class C1:public P {
public:
void method2(void);
};
class C2: public C1 {
public:
float method3(char *s);
};
Таблица виртуальных функций, virtualTabl, содержит функции-члены каждого класса в полиморфическом кластере. Указатель на эту таблицу имеют все объекты классов и подклассов полиморфического кластера.
Типичный объект, приведенных выше классов, выглядит, примерно, так:
int value;
virtual_table->P::method1
C1::method2
C2::method3
Компилятор преобразует вызов виртуальной функции в косвенный вызов через virtualTable. Например,
С2 *с;
c->method3(“Hello”);
преобразуется в
(*(c->virtual_table[2]))(c,”Hello”);