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

Implementing visitor pattern for a vector of objects in C++

这是 的后续。

我们可以针对上一个问题中的问题实现访问者模式,如中所建议的:

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& 传递给另一个 class、state。但是如果向量中的所有对象都是 Child_A 类型它调用 state.method_A,如果向量中所有对象都是 Child_B 类型它调用 state.method_B 否则调用 state.method_C。只有 state 直接与 parent classes.

一起使用

我希望这能让事情更清楚一些。

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

据我了解,您希望代码中包含以下内容:

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 类型以用于此循环。

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


您可以做两件事,都需要在运行时确定 "real" 子类型。

选项 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 将 return 一个 nullptr,否则它将 return 一个有效的指针。

简而言之:

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.
};