我有一个抽象类型A
,以及两个派生类型A1
和A2
。
我想在类A中添加一个方法M,它采用类型A的参数。但是,我需要ad hoc多态。
确实,我需要3个实现:A1::M(A1 a)
,A1::M(A2 a)
和A2::(A1 a)
,A2::M(A2 a)
。
但是我想用一种抽象的方法用类型为A的指针调用方法M.
我可以在课程A
中放置所有签名声明,但它很糟糕。
答案 0 :(得分:2)
class A {
public:
virtual void M(A &) = 0;
virtual void M(A1 &) = 0;
virtual void M(A2 &) = 0;
};
class A1 : public A {
public:
virtual void M(A &a) { a.M(*this); }
virtual void M(A1 &a) { std::cout << "A1 <- A1\n"; }
virtual void M(A2 &a) { std::cout << "A2 <- A1\n"; }
};
class A2 : public A {
public:
virtual void M(A &a) { a.M(*this); }
virtual void M(A1 &a) { std::cout << "A1 <- A2\n"; }
virtual void M(A2 &a) { std::cout << "A2 <- A2\n"; }
};
(参见例如http://ideone.com/nycls。)
我认为没有办法避免基类中的多次重载。
答案 1 :(得分:0)
如果你想要多态行为 - 那么你需要的只是基类A中的一个方法。然后你可以在A1,A2中重新实现该方法。
之后你可以写:
A *a1 = new A1();
A *a2 = new A2();
a1->M(a2); //polymorphic behavior
如果你做这样的事情:
struct A
{
virtual void M(A *a) {}
};
struct A1 : public A
{
virtual void M(A1 *a) {cout << "A1" << endl;}
virtual void M(A *a) {cout << "A" << endl;}
};
然后:
A1 * a1 = new A1();
a1->M(a1); //prints "A1"
A * a = a1;
a->M(a1); //prints "A"
我认为这不是你想要的行为
答案 2 :(得分:0)
为什么不做那样的事?
void A1::M( A a )
{
if( dynamic_cast< A1* >( &a ) )
{
// do your A1::M1(A1 a) stuff
}
else
if( dynamic_cast< A2* >( &a ) )
{
// do your A1::M2(A2 a) stuff
}
else
{
throw std::logic_error( "Unsupported A type." );
}
}
为A2 :: M做等效的事情吗?
答案 3 :(得分:0)
这是双重调度。当你写:
A* p1;
A* p2;
p1->M(*p2);
应根据*p1
的类型和*p2
的类型进行调度。
在开始之前,您必须意识到这意味着n^2
的功能
n
个不同的派生类型。而在某个地方,有人必须意识到
所有派生类型(除非你可以定义某种类型
“默认”实现一对未知类型。)
有两种实现方法。最简单的,如果是层次结构 是关闭的(即客户端代码不能引入新的派生类) 通常在基类中使用大量虚函数 受到保护,因为它们不是为了在外面被调用而设计的 层次结构:
// Forward references needed for all derived classes...
class A1;
class A2;
// ...
class A
{
protectd:
virtual void doM(A1* arg) = 0;
virtual void doM(A2* arg) = 0;
// ...
public:
virtual void M(A& arg) = 0;
};
在派生类中,M
的实现始终相同:
void A1::M(A& arg)
{
arg.doM( this );
}
这很简单,而且相对有效,但需要改变 抽象基础和所有派生类(必须实现) 每次添加新的派生类时,新的虚函数。它的 但对于封闭的层次结构非常有用;我在课堂上使用它 部分行为的战略模式,各种各样 策略都在源文件中定义,而不是暴露给 客户(战略的抽象基础只是前进的 在标题中声明,因此如果我添加了一个,则不需要更改标题 策略)。
更通用的解决方案是std::map
,有一对
typeid
作为索引。您不能直接使用typeid
,因为它不是
可复制。 C ++ 11提供了type_index
来包装它;如果你正在使用
较旧的编译器,自己实现它是相当简单的。该
基本原则是(可能在A本身)的行为:
typedef std::pair<std::type_index, std::type_index> TypePairKey;
typedef void (*FuncPtr)( M* arg1, M* arg2 );
typedef std::unordered_map<TypePairKey, FuncPtr> DispatchMap;
static DispatchMap ourDispatchMap;
使用:
void M( A& arg ) // NOT virtual !!!
{
DispatchMap::iterator entry
= ourDispatchMap.find(
DispatchMap::value_type( typeid( *this ), typeid( arg ) ) );
assert( entry != ourDispatchMap.end() );
// Or some default handling, maybe throw NotYetImplemented()
(*entry->second)( this, &arg );
}
真正的问题在于编写每个单独的函数和
在地图中插入地址(首次使用前)。功能
当然,他们自己可以使用dynamic_cast
,甚至是static_cast
你可以肯定他们只能从这里打电话,他们可以
所涉及的班级的朋友,但仍有
n 2 他们。 (一个常见的解决方案是制作它们
其中一个类的静态成员,并拥有每个派生类
定义一个类型的静态成员,它执行所有的注册
它负责的职能。)