4

好的:这是我的问题:我有一个基础合成类,它接受一个访问者,然后迭代它的节点。奇迹般有效。但是,我必须使用从这个合成派生的,并认识到我必须重写派生类中的“accept()”方法才能进行正确的双重调度(我不明白)。

这带来了两个缺陷:第一,我必须打破基础的隐藏结构,第二,我必须重复代码。为了清楚起见,这是我的伪代码:

struct Visitor
{
    void visit( BaseComposit*)    { throw( "not expected"); };
    void visit( DerivedComposit*) { throw( "ok"); };
};

class BaseComposit 
{
private:  

    std::vector< BaseComposit*> nodes;

public:

    virtual void accept( Visitor* v)
    {
        v->visit( this);

        for( int i = 0; i < nodes.size(); i++)
            nodes[ i]->accept( v);
    }
};

class DerivedComposit : public BaseComposit
{
public:
};

有什么优雅的解决方案吗?谢谢 !

编辑:将“virtual”添加到“accept()”以使其更精确......

4

2 回答 2

1

如果要强制始终执行某些代码,请使用非虚拟接口 (NVI) 模式:

class BaseComposit 
{
private:  
    std::vector<BaseComposit*> nodes;

    virtual void call_visit(Visitor* v) { v->visit(this); }

public:

    void accept(Visitor* v)
    {
        call_visit(v);

        for(int i = 0; i < nodes.size(); i++)
            nodes[i]->accept(v);
    }
};

class DerivedComposit : public BaseComposit
{
    void call_visit(Visitor* v) override { v->visit(this); }
public:
};
于 2017-12-07T09:25:46.773 回答
1

有什么优雅的解决方案吗?

并不真地。这就是让访客模式有点痛苦的原因。尽管您可以在模板的帮助下稍微减轻重复:

class BaseComposit 
{
private:  

    std::vector<BaseComposit*> nodes;

protected:

    template<class T>
    void accept_impl( Visitor* v, T* this_ )
    {
        v->visit( this_ );

        for(int i = 0; i < nodes.size(); i++)
            nodes[i]->accept(v);
    }

public:

    virtual void accept( Visitor* v ) { accept_impl( v, this ); }
};

现在accept必须产生的重复更小了。

另外,正如@Oliv 指出的那样,您的示例确实应该accept是一个虚函数。否则整件事都行不通。


如果你真的很喜欢冒险,你可以引入一个宏来简化accept每个类的“注入”。像这样:

#define INJECT_ACCEPT() void accept( Visitor* v ) override { accept_impl( v, this ); }

但是您仍然需要在每个派生类中使用它来使定义出现。

class DerivedComposit : public BaseComposit
{
public:
    INJECT_ACCEPT();
};

C++ 语法的一个奇怪特性允许使用分号。所以我想有人可能会争辩说上面看起来很“自然”。

于 2017-12-07T08:58:46.190 回答