2015年6月30日 星期二

繼承



在前兩篇的主程式中,透過 class myData object1 物件的 object1.add(d) 來加入資料,再呼叫 common_divisor(object1.data,object1.len) 來找出答案,由於 common_divisor 並不包含在 class myData 中,所以開發人員需要了解 class myData 的資料結構及 common_divisor 的參數型態才能把兩者搭配在一起,這樣會增加程式開發的複雜度,雖然把 common_divisor 放進 class myData 中可以改善這情況,但 class myData 偏向基礎資料類別,應該要讓很多不同的開發需求都能使用,如果把 common_divisor 函式加進去,那對於不需要"最大公因數"的應用來說這個函式就多餘了。

以繼承 class myData 來建立一個包含 common_divisor 的衍生類別 gcd,是可以不用重頭開發,又可避免在 class myData 添加多餘函式的方法。


C++的繼承機制有 private, protected, public 三種,不同的繼承方式會限制衍生類別對外公開父類別成員的機制,以下程式以 public 繼承來建立衍生類別 gcd:


#include <iostream>

class myData
{
//私有區段,只有class內部程式可存取
private:
   int _boundary; //陣列空間上限

//保護區段,只有class內部程式和繼承的class可存取
protected: 
   int len;  //資料總數 
   int *data; //用來存放所有輸入資料的記憶空間指標

//公用區段,class內外部程式均可存取
public:
   myData() //建構函式,當物件產生時會自動執行
   {
     len=0; //資料總數歸0
     _boundary=10; //邊界設為10 
     data=new int[_boundary]; //配置10個int記憶空間 
   }

   ~myData() //解構函式,當物件消滅時會自動執行
   {
     delete [] data; //釋放data記憶空間
   }

   void add(int d)
   {
     if (len>=_boundary-1) //資料滿了,增加記憶空間
     {
       int *newptr=new int[_boundary+10]; //動態配置比原來多10個int記憶空間
       for(int i=0; i<len; i++)
         newptr[i]=data[i]; //把舊資料複製到新的位置
       delete [] data; //釋放舊的記憶空間
       data=newptr; //資料指標指向新的位置
       _boundary+=10;
     }
     data[len++]=d; //把參數d放在最後的位置後再把資料長度+1
   }
};

class gcd : public myData //衍生類別 gcd 以 public 方式繼承 myData 
{
private:
   int common_divisor(int a, int b) //遞迴函式1
   {
     return (a%b==0) ? b : common_divisor(b,a%b); //交換位置輾轉相除
   }

   int common_divisor(int d[], int n) //遞迴函式2
   {
     if(n==1)
       return d[0];
     if(n==2)
       return common_divisor(d[0],d[1]); //呼叫遞迴函式1
     if(n>2)
       return common_divisor(common_divisor(d,n/2),
                         common_divisor(&d[n/2],n-n/2) ); //呼叫遞迴函式2 
     return 0; //n小於1才會發生
   }

public:
   int result() { return common_divisor(data, len); }
};

int main()
{
   int d;
   gcd mygcd;

   std::cout << "請輸入n個正整數,輸入完成請按 Ctrl-Z" << std::endl;
   while ( std::cin >> d )
      mygcd.add(d);
   
   std::cout << "最大公因數=" << mygcd.result();
   return 0;
}

衍生類別 class gcd 對於父類別 class myData 是以 public 的方式繼承,有權存取父類別的 protected: 和 public: 區段,但無權存取父類別的 private: 區段;而在父類別 public: 區段中的資料或函式,以 public 方式繼承的衍生類別也會將其對外界公開,所以主程式只要宣告一個 gcd 物件 mygcd,即可以 mygcd.add() 叫用父類別的函式,最後再以 mygcd.result() 叫用 class gcd 的函式 result 得到結果。

由於外界已不必直接操作 class myData 中的 len 和 data,所以把它們搬到 
protected 區段以提高資料的安全性,同時也保留讓衍生類別可以存取的彈性。而置於 private 區段的資料或函式,仍然只有 class 本身有存取權限,繼承者或外部程式均無法存取。

public繼承
父類別衍生類別
publicpublic
protectedprotected
private無法存取

protected繼承
父類別衍生類別
publicprotected
protectedprotected
private無法存取

private繼承
父類別衍生類別
publicprivate
protectedprivate
private無法存取



沒有留言:

張貼留言