使用取消引用的指针的多态性会产生意外的结果.为什么?

Polymorphism with a dereferenced pointer yields unexpected results...why?

本文关键字:意外 结果 为什么 多态性 取消 引用 指针      更新时间:2023-10-16

我遇到了一个C++难题,可能需要一些帮助! 请考虑以下代码:

#include <iostream>
struct Node
{
virtual void print() 
{
std::cout << "Node::print" << std::endl;
}
};
struct Drawable : public Node
{
virtual void print() 
{
std::cout << "Drawable::print" << std::endl;
}
};
struct Visitor
{
virtual void apply(Node& node)
{
std::cout << "apply(Node&)" << std::endl;
node.print();
}
virtual void apply(Drawable& drawable) 
{
std::cout << "apply(Drawable&)" << std::endl;
drawable.print();
}
};
struct Renderer
{
virtual void accept(Node* node, Visitor* visitor)
{
visitor->apply(*node);
}
};
int main(int argc, char** argv)
{
Renderer r;
Visitor v;
Drawable* drawable = new Drawable();
r.accept(drawable, &v);
return 0;
}

输出为:

apply(Node&)
Drawable::print

我期待调用 Visitor::apply(Drawable&),但 apply(Node&) 被调用。为什么?

看看这行代码:

virtual void accept(Node* node, Visitor* visitor)
{
visitor->apply(*node); // <--- Here
}

当编译器看到此对apply的调用时,它需要确定您是要调用接受Node&apply版本还是接收Drawable&apply版本。请注意,这是关于选取哪个重载而不是选取哪个覆盖的决定。有关重载的决定是在编译时做出的。在这里,编译器查看表达式*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的类型在编译时将与相关对象的类型相同。这意味着重载选择将选择最特定于此类型的apply版本。

要使用此accept函数,您可以编写如下内容:

virtual void accept(Node* node, Visitor* visitor)
{
node->accept(visitor);
}

现在,想想会发生什么。accept成员函数标记为virtual,因此要调用的accept的具体版本取决于node所指向的事物的类型(即它使用动态类型而不是静态类型)。这是使用覆盖解析而不是重载解析。然后,这将调用适当的覆盖,如上所示,该覆盖选择适当的重载

希望这有帮助!