在C ++中实现对象向量的访问者模式

时间:2015-04-18 13:20:23

标签: c++ class c++11 vector visitor-pattern

这是this question的后续行动。

我们可以按照this answer中的建议,在上一个问题中实现问题的访问者模式:

class Base {
    foo(Parent& p) {
        p.accept(*this);
    }
    virtual void visit(Child_A&) = 0;
    virtual void visit(Child_B&) = 0;
};

class Parent {
    virtual void accept(Base&) = 0;
};

class Child_A: Parent {
    void accept(Base& v) {
        v.visit(*this);
    }
};

class Child_B: Parent {
    void accept(Base& v) {
        v.visit(*this);
    }
};

class Derived_A: Base { 
    void treat_same(Parent&) {
        // ...
    }
    void visit(Child_A& a) {
        treat_same(a);
    }
    void visit(Child_B& b) {
        treat_same(b);
    }
};
class Derived_B: Base { 
    void visit(Child_A&) {
        // ...
    }
    void visit(Child_B&) {
        // ...
    }
};

但现在考虑foo是否期望std::vector<std::shared_ptr<Parent>> const&作为其参数。 那么我们如何为问题实现访问者模式呢?有可能吗?

修改

foostd::vector<std::shared_ptr<Parent>> const&传递给另一个班级state。但是,如果向量中的所有对象都是Child_A类型,则调用state.method_A,如果向量中的所有对象都是Child_B类型,则调用state.method_B,否则调用{{} 1}}。只有state.method_C可以直接使用state类。

我希望这会清除一些事情。

1 个答案:

答案 0 :(得分:0)

我不太确定我能得到你想要的东西,但我会试一试。

据我了解,您需要在代码中使用以下内容:

std::vector<std::shared_ptr<Parent>> children;
Base * handler = new Derived_A; // or new Derived_B.
for (child : children) {
    // You want this call to correctly distinguish 
    // between objects of Child_A and Child_B
    handler->visit(child); 

不幸的是,C ++并不允许你这样做。 (*继续阅读)因为函数调用是由类型决定的,所有子元素都是shared_ptr类型,用于此循环。

幸运的是,一切都没有丢失。


你可以做两件事,都要求在运行时确定“真实”类型的孩子。

选项1:在Parent中添加一个字段,标识应如何处理。

这需要遵循以下内容。

class Parent {
public:
    enum class SubType {
        A,
        B,
    };

    virtual void accept(Base &) = 0;

    // Subclasses must implement this to 
    // allow instances of base to correctly handle the objects.
    virtual SubType handle_as() const = 0;
};

Base的实现将包含以下内容:

class Base {
     void visit( shared_ptr<Parent> p ) {
         switch( p->handle_as() ) {
         case Parent::SubType::A:
             this->accept( *static_ptr_cast<Child_A>(p) );
             break;
         case Parent::SubType::B:
             this->accept( *static_ptr_cast<Child_B>(p) );
             break;
     }
     // In addition to your accept(Child_A &) accept(Child_B &) etc.
};

选项2:使用运行时类型标识。 (RTTI)。

使用动态强制转换的另一种选择。这将在您的整个可执行文件中启用RTTI,在这种情况下,这可能是您想要的,但请注意,它确实会产生较小的性能+可执行文件的大小成本。

如果强制转换是非法的,

dynamic_cast将返回nullptr,否则它将返回一个有效的指针。

简而言之:

class Base {
     void visit( shared_ptr<Parent> p ) {
         if ( dynamic_ptr_cast<Child_A>(p) ) {
             this->accept( *dynamic_ptr_cast<Child_A>(p) );
         } 
         else if ( dynamic_ptr_cast<Child_B>(p) ) {
             this->accept( *dynamic_ptr_cast<Child_B>(p) );
         } 
     }
     // In addition to your accept(Child_A &) accept(Child_B &) etc.
};