2010年9月17日 星期五

多重繼承 multiple inheritance 的注意事項

簡易的多重繼承範例如下


class CWatch
{
  // 錶
};

class CMechanicalWatch : public CWatch
{
  // 機械錶
};

class CQuartzWatch : public CWatch
{
  // 石英錶
};

class CHybridWatch : public CMechanicalWatch, public CQuartzWatch
{
  // 利用石英操控更精準的混合式機械錶
};


這時會產生一個尷尬的情況,CHybridWatch 內含有兩份 CWatch,所以我們若要用基底類別指標指向最未層的衍生類別時,必須轉型指定


CHybridWatch hw;
CWatch *whA = (CMechanicalWatch*)&hw;
CWatch *whB = (CQuartzWatch*)&hw;


雖然可以這麼做,但一支普通的錶不太需要兩支時針、兩支分針、兩個殼,我們希望只有一份 CWatch 存在,這時可以加入 virtual 關鍵字


class CWatch
{
  // 錶
};

class CMechanicalWatch : virtual public CWatch
{
  // 機械錶
};

class CQuartzWatch : public virtual CWatch
{
  // 石英錶
};

class CHybridWatch : public CMechanicalWatch, public CQuartzWatch
{
  // 利用石英操控更精準的混合式機械錶
};


可以發現,virtual 的位置可以放在前面或後面都沒關係,如此在 CHybridWatch 中就只會有一份 CWatch,但以下的程式碼會發現一個問題


class CWatch
{
  // 錶
  CWatch(int w);
};

class CMechanicalWatch : virtual public CWatch
{
  // 機械錶
  CMechanicalWatch(int m, int w) : CWatch(w);
};

class CQuartzWatch : public virtual CWatch
{
  // 石英錶
  CQuartzWatch(int q, int w) : CWatch(w);
};

class CHybridWatch : public CMechanicalWatch, public CQuartzWatch
{
  // 利用石英操控更精準的混合式機械錶
  CHybridWatch(int h, int m, int q, int w) :
    CMechanicalWatch(m, w), CQuartzWatch(q, w)
};


一般情況下,由於建構 CWatch 有透過機械、石英兩條路,為免衝突,同時也為了在機械、石英兩個衍生類別建構前先完成 CWatch 的建構,所以 CWatch 只能使用預設的建構式來建構。

但在虛擬繼承的情況下,我們可以明確的指定基底建構式如


class CHybridWatch : public CMechanicalWatch, public CQuartzWatch
{
  // 利用石英操控更精準的混合式機械錶
  CHybridWatch(int h, int m, int q, int w) :
    CMechanicalWatch(m, w), CQuartzWatch(q, w), CWatch(w);
};


這種明確指定建構式的語法只限定虛擬繼承,其餘情況是會產生語法錯誤的。

此外若機械錶、石英錶有 polymorphism 的情況存在,例如 Stop();
CHybridWatch 若要使用 Stop() 就必須指定明確的基底類別


void CHybridWatch::Stop()
{
  CMechanicalWatch::Stop();
  CQuartzWatch::Stop();
}


這是合法的,但我們不需要停止兩次指針,這時解決方法是產生更細緻動作的 function


class CWatch
{
  // 錶
  virtual void Stop() = FALSE;
};

class CMechanicalWatch : virtual public CWatch
{
  // 機械錶
protected:
  StopHandSet(); // 停止指針
public:
  Stop() {StopHandSet(); /*後續動作*/};
};

class CQuartzWatch : public virtual CWatch
{
  // 石英錶
protected:
  StopPower(); // 停止電源
public:
  Stop() {StopPower(); /*後續動作*/};
};

class CHybridWatch : public CMechanicalWatch, public CQuartzWatch
{
  // 利用石英操控更精準的混合式機械錶
public:
  Stop()
  {
    CMechanicalWatch::StopHandSet();
    CQuartzWatch::StopPower();
    /*後續動作*/
  };
};


比較特別的技巧大概就是特別製作的 function 我們製作為 protected 成員,這可以讓此函式只允許第一層的衍生類別使用。

沒有留言:

張貼留言