首先要理解 Dispatch 是什么。

多态的实现依赖虚表(vtable)完成运行时决策——真正调用哪个函数,由运行时的实际对象类型决定。这一过程称为一次 dispatch,即实现了一次动态多态。

Double dispatch 的典型应用场景是设计模式中的 Visitor 模式(参考:http://my.oschina.net/kkkkkkkkkkkkk/blog/670610)。

问题:重载在编译期绑定

先看下面这段代码存在的问题:

class Monster;
                class FlyMonster;
                class WalkMonster;
                
                
                
                class MonsterMgr
                {
                public:
                	void Add(Monster*monster)
                	{
                		cout << "Monster" << endl;
                	}
                	void Add(FlyMonster*fly)
                	{
                		cout << "fly" << endl;
                	}
                
                	void Add(WalkMonster*walk)
                	{
                
                		cout << "walk" << endl;
                	}
                };
                
                
                
                class Monster
                {
                public:
                
                };
                
                class FlyMonster:public Monster
                {
                public:
                };
                
                class WalkMonster :public Monster
                {
                public:
                
                };
                
                int main(int argc, char *argv[])
                {
                	Monster *monster = new FlyMonster;
                
                	MonsterMgr *mgr = new MonsterMgr;
                
                
                	mgr->Add(monster);
                	system("pause");
                	return 0;
                }

由于 Add 有多个重载版本,而重载决议在编译期就已确定——变量 monster 的声明类型是 Monster*,所以编译器绑定的是 Monster 版本的 Add,而不是我们期望的 FlyMonster 版本。

解决方案:引入第二次 Dispatch

为了达到目的,需要再引入一次 dispatch。原本的设计是由 MonsterMgr 主动添加怪物,现在改为由怪物自己把自身添加到 MonsterMgr 中,借助虚函数触发第二次 dispatch。这样,子类的 this 指针才是真正的运行时对象,Add 就能正确匹配到 FlyMonster 版本。

这种设计有一种"反转控制"的感觉。C++ 本身不直接支持 double dispatch,通过这种方式便可以优雅地实现:

class Monster;
                class FlyMonster;
                class WalkMonster;
                
                class MonsterMgr
                {
                public:
                	void Add(Monster*monster)
                	{
                		cout << "Monster" << endl;
                	}
                	void Add(FlyMonster*fly)
                	{
                		cout << "fly" << endl;
                	}
                
                	void Add(WalkMonster*walk)
                	{
                
                		cout << "walk" << endl;
                	}
                };
                
                
                
                class Monster
                {
                public:
                	virtual void AddToMgr(MonsterMgr*m)
                	{
                		m->Add(this);
                	}
                };
                
                class FlyMonster:public Monster
                {
                public:
                	virtual void AddToMgr(MonsterMgr*m)
                	{
                		m->Add(this);
                	}
                };
                
                class WalkMonster :public Monster
                {
                public:
                	virtual void AddToMgr(MonsterMgr*m)
                	{
                		m->Add(this);
                	}
                };
                
                
                
                
                
                int main(int argc, char *argv[])
                {
                	Monster *monster = new FlyMonster;
                
                	MonsterMgr *mgr = new MonsterMgr;
                
                
                	monster->AddToMgr(mgr);
                
                
                	system("pause");
                	return 0;
                }