C+中的动态adhoc多态性+; 在我的C++代码中,我有一个类集合:代码> A1、A2、…、,所有这些都是从类 A派生的。我还有一个向量v包含指向所有类型a的对象的指针。我想实现一个函数foo(a*x,a*y),它在x和y类型中是动态的即席多态。为了使这更具体,设想A是Shape,A1,A2,…,是圆,Rect,…,和foo是相交(Shape*x,Shape*y)
对于我所关心的A的派生类型的组合,我可以使用类似于C+中的动态adhoc多态性+; 在我的C++代码中,我有一个类集合:代码> A1、A2、…、,所有这些都是从类 A派生的。我还有一个向量v包含指向所有类型a的对象的指针。我想实现一个函数foo(a*x,a*y),它在x和y类型中是动态的即席多态。为了使这更具体,设想A是Shape,A1,A2,…,是圆,Rect,…,和foo是相交(Shape*x,Shape*y),c++,design-patterns,polymorphism,overloading,C++,Design Patterns,Polymorphism,Overloading,对于我所关心的A的派生类型的组合,我可以使用类似于foo(A1*x,A2*y)的声明重载foo,但是这对向量v引用的对象不起作用,因为函数重载与虚拟方法不同,是静态处理的。我也不能使用形式为A1::foo(A2*y)的虚拟方法,因为这只动态解析方法类的类型(A1),而不是参数的类型(A2) 我想到的唯一解决方案是实现foo,如下所示: void foo(A *x, A*y) { if (A1* a1 = dynamic_cast<A1*>(x)) { if (A1* a1
foo(A1*x,A2*y)
的声明重载foo
,但是这对向量v
引用的对象不起作用,因为函数重载与虚拟方法不同,是静态处理的。我也不能使用形式为A1::foo(A2*y)
的虚拟方法,因为这只动态解析方法类的类型(A1
),而不是参数的类型(A2
)
我想到的唯一解决方案是实现foo
,如下所示:
void foo(A *x, A*y) {
if (A1* a1 = dynamic_cast<A1*>(x)) {
if (A1* a1 = dynamic_cast<A1*>(y)) {
...
}
...
}
if (A2* a2 = dynamic_cast<A2*>(x)) {
if (A1* a1 = dynamic_cast<A1*>(y)) {
...
}
...
}
...
}
void foo(A*x,A*y){
如果(A1*A1=动态_-cast(x)){
如果(A1*A1=动态_铸造(y)){
...
}
...
}
如果(A2*A2=动态_投射(x)){
如果(A1*A1=动态_铸造(y)){
...
}
...
}
...
}
然而,我总是被告知,诉诸动态强制转换很少是一个好主意。有没有更惯用的方法来实现这一点?这是双重分派。访问者模式可以实现这一点。您需要一个虚拟函数,使第一个类型具体化以应用访问者。然后,您需要另一个虚拟函数使第二种类型具体化并返回访问者:
struct Shape
{
// Each derived class simply calls visit with the concrete type:
// return visitor.visit(*this);
virtual bool accept(const IntersectionVisitor& visitor) const = 0;
// Each derived class return a visitor which knows how to calculate
// the intersection of this particular class type with all types of
// shapes. The visit() overrides of this visitor have access to both
// concrete shape types.
virtual IntersectionVisitor intersection_visitor() const = 0;
};
struct IntersectionVisitor
{
// Calculate the intersection of this concrete shape with a Circle
virtual bool visit(const Circle&) = 0;
// Calculate the intersection of this concrete shape with a Rect
virtual bool visit(const Rect&) = 0;
};
bool intersects(const Shape& shape1, const Shape& shape2)
{
return shape2.accept(shape1.intersection_visitor());
}
仅仅因为你能,并不意味着你应该。您可以通过变体更简单地实现这一点:
using Shape = std::variant<Circle, Rect, ...>;
bool intersects(const Circle&, const Circle&) { ... }
bool intersects(const Circle&, const Rect&) { ... }
// all shape combinations, like before
// Visitation is just std::visit:
bool intersects(const Shape& shape1, const Shape& shape2)
{
return std::visit([](const auto& s1, const auto& s2) {
return intersects(s1, s2);
}, shape1, shape2);
}
使用Shape=std::variant;
布尔相交(常数圆&,常数圆&){…}
布尔相交(常数圆&,常数矩形&){…}
//所有形状组合,像以前一样
//探视只是性病::探视:
布尔相交(常数形状和形状1、常数形状和形状2)
{
返回标准::访问([](常数自动&s1,常数自动&s2){
返回交点(s1、s2);
},形状1,形状2);
}
这是双重分派。访问者模式可以实现这一点。您需要一个虚拟函数,使第一个类型具体化以应用访问者。然后,您需要另一个虚拟函数使第二种类型具体化并返回访问者:
struct Shape
{
// Each derived class simply calls visit with the concrete type:
// return visitor.visit(*this);
virtual bool accept(const IntersectionVisitor& visitor) const = 0;
// Each derived class return a visitor which knows how to calculate
// the intersection of this particular class type with all types of
// shapes. The visit() overrides of this visitor have access to both
// concrete shape types.
virtual IntersectionVisitor intersection_visitor() const = 0;
};
struct IntersectionVisitor
{
// Calculate the intersection of this concrete shape with a Circle
virtual bool visit(const Circle&) = 0;
// Calculate the intersection of this concrete shape with a Rect
virtual bool visit(const Rect&) = 0;
};
bool intersects(const Shape& shape1, const Shape& shape2)
{
return shape2.accept(shape1.intersection_visitor());
}
仅仅因为你能,并不意味着你应该。您可以通过变体更简单地实现这一点:
using Shape = std::variant<Circle, Rect, ...>;
bool intersects(const Circle&, const Circle&) { ... }
bool intersects(const Circle&, const Rect&) { ... }
// all shape combinations, like before
// Visitation is just std::visit:
bool intersects(const Shape& shape1, const Shape& shape2)
{
return std::visit([](const auto& s1, const auto& s2) {
return intersects(s1, s2);
}, shape1, shape2);
}
使用Shape=std::variant;
布尔相交(常数圆&,常数圆&){…}
布尔相交(常数圆&,常数矩形&){…}
//所有形状组合,像以前一样
//探视只是性病::探视:
布尔相交(常数形状和形状1、常数形状和形状2)
{
返回标准::访问([](常数自动&s1,常数自动&s2){
返回交点(s1、s2);
},形状1,形状2);
}
我同意别人告诉你的话。使用dynamic_cast是一种非常糟糕的代码味道。是的,它被称为“访问者模式”。一个好的C++教材将有一个完整的解释和例子。@ SavaVaveKik我知道访问者模式是什么,但是没有看到它如何应用于这个场景。你能详细说明一下吗?在两个相互作用的对象之间,这种类型的动态调度称为双重调度。它有一个关于如何在C++中使用访问者模式来完成它的例子。使用dynamic_cast是一种非常糟糕的代码味道。是的,它被称为“访问者模式”。一个好的C++教材将有一个完整的解释和例子。@ SavaVaveKik我知道访问者模式是什么,但是没有看到它如何应用于这个场景。你能详细说明一下吗?在两个相互作用的对象之间,这种类型的动态调度称为双重调度。它有一个例子,说明如何在C++中使用访问者模式来完成它。