Посмотрите на эту строку кода:
virtual void accept(Node* node, Visitor* visitor)
{
visitor->apply(*node); // <--- Here
}
Когда компилятор видит этот вызов apply
, он должен определить, хотите ли вы вызвать версию apply
, которая принимает Node&
, или версию apply
, которая принимает Drawable&
, Обратите внимание, что это решение о том, какую перегрузку выбрать, а какую переопределить . Решения о перегрузке принимаются во время компиляции. Здесь компилятор смотрит на выражение *node
и говорит: «Ну, node
- это Node *
, поэтому *node
- это Node&
», потому что он не может знать во время компиляции, какой тип во время выполнения вещь, на которую указывает node
, есть. В результате это всегда будет вызывать apply(Node &)
и никогда не будет вызывать apply(Drawable&)
.
Это объясняет, что происходит, но как это исправить? Обычный способ настроить шаблон посетителя - поместить функцию, подобную этой, в основу иерархии классов:
struct Node {
virtual void accept(Visitor* visitor);
};
Тогда бы каждый подкласс Node
переопределил эту функцию Каждое переопределение будет выглядеть примерно так:
struct Pizkwat: Node {
virtual void accept(Visitor* visitor) override {
visitor->apply(*this);
// do more things, optionally
}
};
В строке кода в этом переопределении тип *this
во время компиляции будет того же типа, что и рассматриваемый объект. Это означает, что выбор overload выберет версию apply
, наиболее специфичную для этого типа.
Чтобы использовать эту accept
функцию, вы можете написать что-то вроде этого:
virtual void accept(Node* node, Visitor* visitor)
{
node->accept(visitor);
}
Теперь подумайте о том, что происходит. Функция-член accept
помечена virtual
, поэтому конкретная версия accept
для вызова зависит от типа объекта, на который указывает node
(то есть используется динамический тип а не статический тип ). При этом используется переопределение разрешение вместо перегрузка разрешение. Затем это вызовет правильное переопределение, которое, как вы видели выше, затем выберет правильную перегрузку .
Надеюсь, это поможет!