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

时间:2018-11-01 17:46:52

标签: c++ polymorphism visitor

我遇到了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&)。为什么?

1 个答案:

答案 0 :(得分:7)

看看这一行代码:

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

当编译器看到对apply的调用时,它需要确定您是要调用包含apply的{​​{1}}版本还是{{1}的版本}包含Node&。请注意,这是决定选择哪个超载,而不是选择哪个超载。有关重载的决定是在编译时进行的。在这里,编译器查看表达式apply并说“好吧,Drawable&*node,所以nodeNode *”,因为它不能在编译时知道*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所指向的事物的类型(即,它使用动态类型,而不是静态类型)。这是使用 override 分辨率,而不是 overload 分辨率。然后,将调用适当的替代,如您在上面看到的,然后选择适当的超载

希望这会有所帮助!