这是这个问题的后续内容。
我们可以为上一个问题中的问题实现访问者模式,如以下答案所示:
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&
作为其自变量。那么我们如何实现访问者模式来解决这个问题呢?有可能吗?
编辑
foo
将std::vector<std::shared_ptr<Parent>> const&
传递给另一个类state
。但是,如果矢量中的所有对象都是Child_A
类型,则它调用state.method_A
,如果矢量上的所有对象是Child_B
类型,则调用state.method_B
,否则调用state.method_C
。只有state
可以直接与parent
类一起使用。
我希望这能澄清一些问题。
我不太确定我能不能得到你想要的,但我会试试的。
据我所知,您希望在代码中包含以下内容:
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.
};