1. 程式人生 > >C++進階--多繼承

C++進階--多繼承

//###########################################################################
/*
 * 多繼承
 *
 *  -- 一個類直接派生自不止一個基類
 *
 *  -- 利弊?
 */


//###########################################################################
/*
 * 多繼承
 */


class InputFile {
   public:
   void read();
   private:
   void open();
};

class OutputFile {
   public:
   void write();
   void open();
};

class IOFile : public InputFile, public OutputFile {
};

int main() {
   IOFile f;
}


// Notes:
//   void open();
//   f.open();    //這裡編譯不過,即使其中一個是私有函式。因為函式匹配性檢查在許可權檢查之前
//正確的呼叫方式:
//f.Output::open();
//兩者類都存在open函式,改進成如下方式


class File {                //         File
   public:                  //         /  \        -
   string name;        // InputFile  OutputFile
   void open();           //         \  /
};                            //        IOFile

class InputFile : virtual public File {
};                              
                                
class OutputFile : virtual public File {
};                              

class IOFile : public InputFile, public OutputFile {
};  // 菱形繼承

int main() {
   IOFile f;
   f.open();    //但是這樣仍然編譯不過,open()二義性
   //f.InputFile::name = "File1";  //不僅open有兩個,name也有兩個
   //f.OutputFile::name = "File2";
}


//解決方式: 虛繼承
//但是引入了一個新問題,基類的初始化用哪個?
//C++提供了在最終派生指定的一個解決辦法
class File {     
   public:      
   File(string fname);
};             

class InputFile : virtual public File {
   InputFile(string fname) : File(fname) {}     //這邊的File(fname)會被忽略
};                              
                                
class OutputFile : virtual public File {
   OutputFile(string fname) : File(fname) {}    //這邊的File(fname)會被忽略
};                              

class IOFile : public InputFile, public OutputFile {
   IOFile(string fname) : OutputFile(fname), InputFile(fname), File(fname) {} //不管派生類有多遠,都要負責初始化虛基類
};  

int main() {
   IOFile f;
}


// 既然有這些問題,為什麼要用多繼承?
/*
 * 介面隔離原則
 * 
 * 將大的介面分割成更小且更專用的介面。從而使使用者只需要知道他們感興趣的方法
 */

//例如,Andy可能總共有500個API,但是如果你只關心他作為工程師的特性,你只需要知道工程師的40個API
class Engineer {
   public:
   ...; // 40 APIs
};

class Son {
   public:
   ...; // 50 APIs
};

...

class Andy : public Engineer, Son {
   public:
   ...; // 500 APIs
   };


/*
 * ISP的好處:
 * 1. 介面易於使用
 * 2. 靜態型別安全
 */
/*

// 那麼怎麼樣在享受多繼承的好處的同時,避免前面提到的問題
 *  【純虛類】
 *
 *  虛類: 有一個或多個純虛擬函式的類
 *
 *  純虛類: 
 *  一個類只包含純虛擬函式
 *    - 沒有資料
 *    - 沒有實體函式
 *    - 沒有私有和保護的函式
 */

class OutputFile {
   public:
   void write() = 0; //沒有初始化及二義性的問題
   void open() = 0;
};



/*
 * 總結:
 * 1. 多繼承是一個重要的技術, 即 介面隔離原則
 * 2. 在使用多繼承時只從純虛類派生
 */