在 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&
作为其参数。
那么我们如何针对这个问题实现访问者模式呢?可能吗?
编辑
foo
将 std::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.
};
这是
我们可以针对上一个问题中的问题实现访问者模式,如
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&
传递给另一个 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.
};