Для того чтобы разобраться в ситуации, необходимо уяснить, что такое связывание. Связывание — это сопоставление вызова функции с телом. В приведенных ранее примерах связывание выполняется на этапе трансляции (до запуска) программы. Такое связывание обычно называют ранним, или статическим.
При трансляции класса Base (см. листинг 9.1) компилятор ничего не знает о классах-наследниках1, поэтому он не может предполагать, что метод f () будет переопределен в классе Derived. Его естественное поведение — «прочно» связать вызов f () с телом метода класса Base. Аналогично при трансляции функции settimeO компилятору ничего не известно о типе реально передаваемого объекта во время выполнения программы. Поэтому вызов метода print() связывается с телом метода базового класса Clock, как и определено в заголовке функции settimeO. Точно так же указатель на базовый класс «прочно» связывается с методом базового класса во время трансляции.
Конечно, при вызове метода по указателю в данном конкретном случае мы можем вызвать метод производного класса, задав явное преобразование указателя:
static_cast<Alarm*>(cl)->print();
Или так:
((Alarm *)cl)->print();
// "лишние" скобки нужны!
1 Эти классы могут быть определены в других файлах многомодульной программы (см. главу 11).
Однако для функции settimeO и метода CallFunction() это сделать невозможно — нам необходимо именно разное поведение в зависимости от типа объекта. Да и с указателем не все так просто: если такой вызов прописан внутри функции, которая принимает этот указатель как параметр (например, settime (Clock *cl)), то мы имеем те же проблемы. |