43項 多重継承は慎重に使おう

多重継承は慎重に使おう
多重継承を使うと、名前衝突により問題が起こることがあります。
あいまいな状態になるときは、
①同名の関数を複数継承したとき、関数を個別に再定義できない問題
②1つのクラスを複数の経路で継承してしまうとき、名前解決があいまいになる問題
があります。
本項には上記2つの場合の解決策が挙げられていますが、できる限り多重継承は避けるべきです。


①同名の関数を複数継承したとき、関数を個別に再定義できない問題

下のコードのような状態です。

class Lottery
{
public:
	virtual void draw();
};

class GraphicalObject
{
public:
	virtual void draw();
};

class LotterySimulation : public Lottery, public GraphicalObject
{
public:
	virtual void draw(); //呼び出したポインタの型によって、
               //Lottery::draw()とGraphicalObject::draw()を
               //使い分けたいが、無理
};



LotterySimulation *pls = new LotterySimulation;
Lottery *pl = new LotterySimulation;
GraphicalObject *pg = new LotterySimulation;

pls->draw(); //LotterySimulation::draw()
pl->draw(); //LotterySimulation::draw()
pg->draw(); //LotterySimulation::draw()

本書で挙げられている解決策は、新しいクラスを追加することです。

class Lottery
{
public:
	virtual void draw();//1,この関数が呼び出されると、
};
class AuxLottery : public Lottery
{
public:
	virtual void lotteryDraw() = 0;//3,2の関数で呼び出されたこの関数も、仮想関数テーブルをたどり・・・
	virtual void draw(){ lotteryDraw(); }//2,仮想関数テーブルをたどり、この関数が呼び出される
};

class GraphicalObject
{
public:
	virtual void draw();
};
class AuxGraphicalObject : public GraphicalObject
{
public:
	virtual void graphicalObjectDraw() = 0;
	virtual void draw(){ graphicalObjectDraw(); }
};

class LotterySimulation : public AuxLottery, public AuxGraphicalObject
{
public:
	virtual void lotteryDraw();//4,この関数が呼び出される。この関数には、Lottery::Draw()を上書きした処理をさせる。
	virtual void graphicalObjectDraw();
};



Lottery *pl = new LotterySimulation;
GraphicalObject *pg = new LotterySimulation;

pl->draw(); //LotterySimulation::lotteryDraw()
pg->draw(); //LotterySimulation::graphicalObjectDraw()

②1つのクラスを複数の経路で多重に継承してしまうとき、名前解決があいまいになる問題

下のような状態です

class A
{
public:
	virtual void func();
};

class B : public A
{
};
class C : public A
{
};

class D :public B, public C
{
};

D d;
d.func(); //あいまい

virtual継承によって解決します。

class A
{
public:
	virtual void func();
};

class B : virtual public A //virtual継承 Aをひとつにする
{
};
class C : virtual public A
{
};

class D :public B, public C
{
};

最後に

本書にも書かれていますが、できる限り多重継承は避けるべきです。