首先要理解 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;
}