101 13. More on Inheritance in C++ 13.1 Virtual Bases and Diamond Hierarchies 13.1.1 Virtual Bases and Constructor Diamond hierarchies 為 C++ 多 重 繼 承 之 一 大 問 題 程 式 範 例 : oop_ex116.cpp 對 於 此,C++ 提 供 了 virtual base 之 方 式 來 解 決, 例 如 以 下 之 繼 承 架 構,Y 與 T 繼 承 X 時, 最 好 將 X 宣 告 為 virtual base, 避 免 重 複 繼 承 造 成 ambiguous, 其 作 法 為 於 宣 告 繼 承 時 使 用 virtual 關 鍵 字 : X Y Z T W U class Y : virtual public X. 如 Y 與 T 均 將 X 宣 告 為 virtual base, 則 同 時 繼 承 了 Y 與 T 的 類 別 其 中 只 有 一 份 X 物 件, 由 Y 與 T 共 享, 例 如 U 之 物 件 中 具 有 Y 與 T 物 件,Y 與 T 共 有 一 份 X 物 件
102 但 如 將 X 宣 告 為 virtual base, 則 X 下 的 所 有 衍 生 類 別, 無 論 是 direct 或 indirect 繼 承 它, 其 建 構 函 式 均 需 要 明 確 地 呼 叫 X 的 建 構 函 式 如 非 virtual base class, 衍 生 類 別 是 不 能 呼 叫 其 indirect base class 的 建 構 函 式, 只 能 透 過 呼 叫 immediate base class 的 建 構 函 式 來 間 接 引 用 (invoke) 以 初 始 化 indirect base class 的 資 料 成 員, 故 如 X 非 virtual base, 則 Z W 與 U 的 建 構 函 式 不 能 呼 叫 X 的 建 構 函 式, 但 如 X 為 virtual base, 則 Z W 與 U 的 建 構 函 式 均 必 須 明 確 地 呼 叫 X 的 建 構 函 式 程 式 範 例 : oop_ex117.cpp,oop_ex118.cpp 注 意 要 點 : 1. 架 構 中 每 個 類 別 的 建 構 函 式 均 必 須 明 確 地 呼 叫 X 的 建 構 函 式 2. 當 Y 類 別 宣 告 其 基 本 類 別 X 為 virtual, 則 繼 承 Y 之 衍 生 類 別 Z 的 建 構 函 式 對 Y 建 構 函 式 之 引 用, 將 忽 略 其 中 對 X 建 構 函 式 之 引 用 3. U 只 包 含 了 一 個 X 物 件, 由 Y 與 T 共 享, 故 X::print( ) 不 會 造 成 ambiguous call 4. TA 只 包 含 了 一 個 Person 物 件, 故 原 oop_ex116.cpp 中 ambiguous calls 均 不 會 發 生 13.1.2 Virtual Bases and Copy Constructor 由 於 virtual base 的 產 生 將 使 的 其 下 各 層 類 別 的 建 構 函 式 均 需 要 明 確 地 呼 叫 virtual base 的 建 構 函 式, 故 此 形 成 的 規 則, 是 virtual base 下 各 層 類 別 的 拷 貝 建 構 函 式 均 需 要 明 確 地 呼 叫 virtual base 的 拷 貝 建 構 函 式 程 式 範 例 : oop_ex119.cpp 13.1.3 Virtual Bases and Copy Assignment Operators 我 們 可 看 到 將 基 本 類 別 宣 告 為 virtual base 將 影 響 到 其 下 direct 或 indirect 衍 生 類 別 的 建 構 函 式 的 定 義 方 式, 包 括 拷 貝 建 構 函 那 拷 貝 指 定 運 算 子 應 為 何? 程 式 範 例 : oop_ex120.cpp 注 意 要 點 :
103 1. 拷 貝 指 定 運 算 子 非 建 構 函 式 2. 拷 貝 指 定 運 算 子 不 受 基 本 類 別 是 否 為 virtual base 所 影 響 3. 執 行 U 之 拷 貝 指 定 運 算 子,U 物 件 中 之 x 資 料 成 員 將 分 別 被 W 與 T 之 拷 貝 指 定 運 算 子 設 定 其 值 兩 次 4. 如 U 之 拷 貝 指 定 運 算 子 中 沒 有 呼 叫 W 之 拷 貝 指 定 運 算 子, 觀 察 執 行 之 結 果 5. Y 與 T 之 拷 貝 指 定 運 算 子 寫 法 不 同, 但 功 能 完 全 相 同 13.1.4 Summary for Diamond Hierarchies 先 前 的 Diamond Hierarchies 繼 承 架 構 雖 於 設 計 上 較 直 觀, 但 卻 有 需 將 基 本 類 別 宣 告 為 virtual base 的 必 要, 此 使 得 程 式 難 以 延 伸 發 展 (extend) Main Disadvantages of OO Design That Involves Diamond Hierarchies: 1. Difficult to extend existing code. 2. Excessively rigid categorization. 3. Type conversion inefficiencies. 13.2 Mixin Classes Mixin class 為 只 有 純 虛 擬 函 式 (pure virtual function) 所 構 成 的 類 別, 其 有 如 Java 的 Interface, 其 作 用 是 規 定 / 賦 與 繼 承 其 之 衍 生 類 別 需 有 的 能 力 ( 之 函 式 原 型 ), 繼 承 其 之 衍 生 類 別 必 須 對 其 所 定 義 之 純 虛 擬 函 式 提 出 實 作, 才 能 實 體 化 程 式 範 例 : oop_ex121.cpp Project 2 問 題 二 的 Mixin class 參 考 : oop_ex122.cpp 使 用 Mixin class 之 設 計 雖 比 先 前 的 Diamond Hierarchies 繼 承 架 構 要 有 彈 性, 但 仍 有 以 下 缺 點 Main Disadvantages of OO Design Based on Mixin Classes: 1. Excessively rigid categorization.
104 2. Type conversion inefficiencies. 13.3 Type Conversion Inefficiencies 假 設 某 大 學 部 學 生 完 成 了 其 大 學 學 業, 並 準 備 進 入 研 究 所, 成 為 研 究 生, 究 程 式 的 觀 點, 我 們 想 要 達 成 的 是, 將 原 來 為 Student 之 物 件, 轉 換 成 為 GraduateStudent 之 物 件, 並 為 其 新 加 入 屬 於 GraduateStudent 之 資 料 首 先 就 將 Student 之 物 件 轉 換 成 為 GraduateStudent 之 物 件 而 言, 雖 GraduateStudent 為 Student 之 衍 生 類 別, 但 就 物 件 的 轉 換 上, 並 沒 有 記 憶 體 操 作 上 有 效 的 方 法, 以 下 為 可 能 的 方 式 之 一 GraduateStudent* CreateGraduateStudentFromStruent( Student* donestudent, Department newdepartment, Teacher& theadvisor ) GraduateStudent * gp = new GraduateStudent( donestudent->getname( ), donestudent->getidentification( ), donestudent->getbirthdate( ), donestudent->getaddress( ), donestudent->getstudentstatus( ), newdepartment, theadvisor ); delete donestudent; return gp; 以 下 為 另 一 個 方 法, 但 與 上 面 的 方 法 一 樣 沒 效 率 GraduateStudent* CreateGraduateStudentFromStruent( Student* donestudent, Department newdepartment, Teacher& theadvisor )
105 //first create an empty GraduateStudent Object GraduateStudent * gp = new GraduateStudent( 0, // name 0, // ssn 0, // birthdate 0, // address eunknown, // studentstatus newdepartment, theadvisor ); Student* tsp = gp; *tsp = *donestudent; delete donestudent; return gp; 13.4 OO Design Using Role Playing Classes 使 用 Role Playing Classes, 即 是 以 has a 取 代 is a 關 係 來 設 計, 某 個 Person 可 以 有 一 個 以 上 的 角 色 物 件, 角 色 物 件 則 定 義 了 此 類 角 色 應 ( 可 ) 有 的 能 力, 擁 有 某 角 色 物 件 即 具 有 扮 演 此 類 角 色 應 有 的 能 力, 例 如 某 人 為 TA, 則 其 為 同 時 擁 有 Student 與 Teacher 此 兩 個 Role Playing Classes 的 Person 物 件, 具 有 Student 物 件 則 有 enrollforclass 等 能 力, 具 有 Teacher 物 件 則 有 teachforclass 等 能 力 假 設 某 大 學 部 學 生 完 成 了 其 大 學 學 業, 並 準 備 進 入 研 究 所, 成 為 研 究 生, 我 們 只 要 將 此 Person 物 件 其 角 色 (UniversityMember* 成 員 所 指 物 件 ) 由 原 為 Student 之 物 件, 改 成 GraduateStudent 之 物 件 即 可, 不 需 改 變 原 為 Person 型 態 物 件 之 型 別, 亦 不 需 拷 貝 Person 物 件 其 他 非 角 色 之 資 料 成 員 使 用 Role Playing Classes 之 設 計, 所 有 Role Playing Classes 的 物 件 在 擁 有 者 物 件 中 均 是 以 基 本 類 別 的 指 標 來 參 照, 例 如 Student Teacher 等 物 件, 於 Person 中 均 是 以 UniversityMember 的 指 標 來 定 位 問 題 在 於, 對 於 一 個 物 件, 我 們 如 何
106 得 知 其 是 否 具 有 某 種 角 色, 而 有 此 角 色 之 能 力 例 如 對 於 某 Person 物 件, 我 們 須 能 知 道 其 是 否 具 有 Student 之 角 色, 有 的 話 才 能 修 課 13.4.1 dynamic_cast Operation of RTTI C++ 之 Run Time Type Identification (RTTI) 可 用 於 此 一 判 斷, 假 設 我 們 想 知 道 某 個 UniversityMember 指 標 所 指 的 物 件 是 否 為 Student 之 物 件, 我 們 可 使 用 C++ 之 RTTI 所 提 供 之 dynamic_cast 運 算 如 下 Student* st = dynamic_cast<student*>(um); 其 中 um 為 UniversityMember* 之 型 態, 如 um 所 指 的 物 件 非 Student, 則 dynamic_cast 傳 回 0 例 : void enrollstudentincourse(const Courses& newcourse, Person* astudent) UniversityMember* role = astudent.getactiverole( ); Student* studentptr = dynamic_cast<student*>(role); if(studentptr!= 0) studentptr->enrollforcourse( newcourse); else cout<< The person is not a student. Cannot enroll in a course. <<endl; 程 式 範 例 : oop_ex123.cpp 注 意 要 點 : 1. 欲 使 用 RTTI, 有 些 編 譯 器 需 先 啟 動 RTTI 功 能, 例 如 Visual C++, 其 設 定 為 : Project->Settings->C/C++->Category 選 擇 C++ Language-> 勾 選 Enable RTTI 2. Dynamic casts 只 可 作 用 於 polymorphic types( 具 有 至 少 一 個 virtual function 的 類 別 ) 此 並 非 真 正 的 問 題, 因 為 如 沒 有 任 何 虛 擬 函 式, 至 少 可 將 其 解 構 函
107 式 宣 告 為 virtual 3. Dynamic casts 只 能 用 於 某 繼 承 架 構 中 之 類 別 間 的 轉 換 4. 除 指 標 型 態 外,Dynamic casts 也 可 作 用 於 物 件 之 參 考 (object references), 但 此 轉 換 只 能 於 try-catch 區 塊 中 使 用, 例 如 : void f(universitymember& roleref) try Student& studentref = dynamic_cast<student&>(roleref); // if flow of control gets here, that means yes, the true identity of // the object refered by roleref is indeed a Student. // So it can be enrolled for a course. catch(bad_cast& b) // means the object refered by roleref is not a Student. // so do something here. 5. C++ 之 RTTI 機 制 同 時 提 供 了 static_cast 運 算 子 以 供 型 別 之 轉 換, 其 為 強 制 的 靜 態 轉 換, 此 常 會 有 問 題, 例 如 我 們 自 然 可 用 其 將 Derived* 轉 成 Base*, 但 其 亦 可 做 強 制 的 downcasting 將 Base* 轉 成 Derived*, 不 像 dynamic_cast 會 根 據 物 件 的 實 際 型 態 判 斷 此 轉 換 是 否 可 行 6. C++ 之 RTTI 機 制 的 另 一 個 casting operator 為 reinterpret_cast, 其 主 要 作 用 是 將 某 int 轉 換 成 記 憶 體 位 址 13.4.2 typeid of RTTI RTTI 之 typeid 函 式, 可 得 到 某 物 件 所 屬 類 別 之 type_info, 以 用 於 判 斷 某 物 件 實 際 為 何 種 類 別, 或 兩 物 件 是 否 實 際 為 同 類 別 等 比 較 程 式 範 例 : oop_ex124.cpp 注 意 要 點 : 1. 欲 使 用 typeid, 需 引 用 <typeinfo.h> 2. 其 輸 入 參 數 可 以 是 物 件 ( 例 如 *(ab)), 或 者 類 別 名 稱 ( 例 如 printable), 傳 回 值 是 一 個 指 向 type_info 物 件 的 參 考,type_info 物 件 是 一 個 系 統 支 援 的 物 件,
108 代 表 一 種 類 別 3. 由 type_info 物 件 之 name 函 式 成 員 所 傳 回 的 字 串 是 由 系 統 所 保 有, 無 法 由 delete 所 釋 放 4. 其 對 物 件 所 屬 類 別 之 判 斷 不 具 多 型 之 行 為, 例 如 printandscaleable 之 物 件 非 printable 之 物 件,typeid 對 輸 入 物 件 之 傳 回 值 只 有 一 個, 即 其 實 際 所 屬 類 別 之 type_info 物 件 5. typeid 對 輸 入 類 別 名 稱 之 傳 回 值 即 此 類 別 之 type_info 物 件 Project 2 問 題 三 的 參 考 範 例 : oop_ex125.cpp