Ну, это интересный вопрос, но позвольте мне попытаться ответить на него очень методично !!!
скажем, компилятор должен разрешить вызов следующим образом: *
a-> SomeFunc ();
*.
Теперь компилятор будет методично выполнять следующие шаги.
1.) Во-первых, компилятор знает объявленный тип переменной a, поэтому он проверит, есть ли у объявленного типа объекта a (lets call this, class A for time being
), метод с именем someFunc () и что это должно быть публично. Этот метод может быть объявлен в class A
, или он может быть производным от одного из базовых классов класса A, но это не имеет значения для компилятора, и он просто проверяет его существование с помощью спецификатора доступа существо public
.
- Нет необходимости говорить, что любая ошибка на этом шаге вызовет ошибку компилятора.
2.) Во-вторых, как только метод проверен на принадлежность к классу A, компилятор должен разрешить вызов правильного метода, поскольку многие методы могут быть там с одинаковыми именами (благодаря перегрузке функции). Этот процесс разрешения правильного метода называется overloading resolution
. Компилятор достигает этого, сопоставляя сигнатуры вызываемого метода со всеми перегруженными методами, которые являются частью класса. Таким образом, из всех someFunc() s
будет найден и рассмотрен только правильный someFunc () (соответствующий сигнатурам с вызываемым методом).
3.) Теперь наступает трудная часть. Может случиться так, что someFunc () может быть переопределен в одном из подклассов класса A (lets call this class AA and needless to say it is some subclass of A
), и эта переменная a (объявлена как тип) A) может фактически ссылаться на объект класса AA (это допустимо в C ++ для включения полиморфизма). Теперь, если объявлено, что метод someFunc () имеет тип virtual
, в базовом классе (т.е. в классе A) и someFunc () был переопределен подклассом (ами) A (либо в AA, либо в классах между A и AA ), правильная версия someFunc () должна быть найдена компилятором.
Теперь представьте, что вы компилятор, и у вас есть задача выяснить, есть ли у класса AA
этот метод. Очевидно, что класс AA будет иметь этот метод, поскольку он является подклассом A, и открытый доступ A в классе A уже был проверен на шаге 1 компилятором !!! , Но в качестве альтернативы, как упомянуто в предыдущем абзаце, someFunc () может быть переопределен классом AA (или любым другим классом между A и AA), что и нужно отлавливать компилятору. Следовательно, вы (поскольку вы играете в компиляторе) могли бы сделать систематическую проверку, чтобы найти самый нижний (самый низкий в дереве наследования) переопределенный метод someFunc (), начиная с класса A и заканчивая в классе AA. В этом поиске вы будете искать те же сигнатуры методов, которые были проверены при разрешении перегрузки. Этот метод будет методом, который будет вызван.
Теперь вы можете задаться вопросом: «Какого чёрта», этот поиск выполняется каждый раз? ... Ну не совсем. Компилятор знает, что каждый раз обнаруживает это, и поэтому поддерживает структуру данных с именем Virtual Table
для каждого типа класса. Представьте себе виртуальную таблицу как отображение из сигнатур методов (которые являются общедоступными) в указатели функций. Эта виртуальная таблица создается компилятором во время процесса компиляции и сохраняется в памяти во время выполнения программы. В нашем примере класс A и класс AA будут иметь свои собственные виртуальные таблицы. И когда компилятор должен найти someFunc () в классе AA (поскольку фактический объект, на который указывает переменная a, имеет тип AA), он просто найдет указатель на функцию через виртуальную таблицу класса AA. Это так же просто, как хэширование в таблице и операция с постоянным временем.
Привет
Avid