【问题标题】:Implementing visitor pattern for a vector of objects in C++在 C++ 中为对象向量实现访问者模式
【发布时间】:2015-06-25 09:51:21
【问题描述】:

这是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,否则调用state.method_C。只有 state 直接与 parent 类一起使用。

我希望这能澄清一些事情。

【问题讨论】:

  • 我不明白你在问什么。你想让foo做什么?
  • 另外,不确定您是否可以创建std::vector<Base&>
  • 你想要什么行为?您可以依次访问每个元素或将容器视为一个整体。也就是说,Base 类中的 foo() 是什么?课堂上的treat_same() 是什么Derived_A?我还想指出,调用 BaseDerivedParentChild 不会使代码更清晰,为什么没有例如Visitor 基类代替?
  • 我认为使用std::vector<> 可以获得的最接近的东西是std::vector<unique_ptr<Base>>,但请注意所有权已转移。
  • @Barry,我更正了。

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


【解决方案1】:

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

据我了解,您的代码中需要类似以下内容:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-05
    • 2023-03-28
    • 2013-04-25
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多