C++学习之路—继承与派生(四)拓展与总结
(根據(jù)《C++程序設(shè)計》(譚浩強(qiáng))整理,整理者:華科小濤,@http://www.cnblogs.com/hust-ghtao轉(zhuǎn)載請注明)
??? 1??? 拓展部分
本節(jié)主要由兩部分內(nèi)容組成,分別是(1)基類與派生類的轉(zhuǎn)換和(2)繼承與組合
1.1??? 基類與派生類的轉(zhuǎn)換
??? 在前幾篇博客中,可以看到在3種繼承方式中,只有公有繼承能較好的保留基類的特征,它保留了除構(gòu)造函數(shù)和析構(gòu)函數(shù)以外所有的基類成員,基類的公有或保護(hù)成員的訪問權(quán)限在派生類中全部保留了下來,在派生類外可以調(diào)用基類的公有成員函數(shù)以訪問基類的私有成員。而非公用派生類不能實現(xiàn)基類的全部功能(例如在派生類外不能調(diào)用基類的公有成員函數(shù)訪問基類的私有成員)。因此,只有公有派生類才是基類真正的子類型,它完整的繼承了基類的功能。
??? 如果不同類型數(shù)據(jù)之間可以自動轉(zhuǎn)換和賦值,成為賦值兼容?;惻c公用派生類之間具有賦值兼容關(guān)系,由于派生類中包含從基類繼承的成員,因此可以將派生類的值賦給基類對象,在用到基類對象的時候可以用其子對象代替。基類與(公用)派生類之間的賦值兼容表現(xiàn)在以下4個方面:
(1)派生類對象可以向基類對象賦值。若類B是類A的公用派生類,則可進(jìn)行以下操作:
1: A a1 ; //定義基類A對象a1 2: B b1 ; //定義公用派生類B的對象b1 3: a1 = b1 ; //用派生類B的對象b1對基類對象a1賦值在進(jìn)行賦值時舍棄派生類自己新增加的成員,所謂賦值只是對數(shù)據(jù)成員賦值,對成員函數(shù)不存在賦值問題。賦值后不能試圖通過對象a1去訪問派生類對象b1新增的成員,假設(shè)age是派生類B中新增加的成員,則:
1: a1.age = 23 ; //錯誤,a1中不包含派生類中增加的成員 2: b1.age = 21 ; //正確,b1中包含派生類中增加的成員?
總結(jié):只能用子類對象對基類對象賦值,而不能用基類對象對其子類對象賦值。同一基類的不同派生類對象之間也不能賦值。
(2)派生類對象可以代替基類對象向基類對象的引用進(jìn)行賦值或初始化。
如果已經(jīng)定義了基類A對象a1,可以定義a1的引用變量:
1: A a1 ; //定義基類A對象a1 2: B b1 ; //定義公用派生類B對象b1 3: A& r = a1 ; //定義基類A對象的引用r,并用a1對其初始化 4: //也可以將上面最后一行改為 5: A& r = b1 ; //定義基類A對象的引用r,并用派生類B對象b1對其初始化此時r并不是b1的別名,也不是與b1共享同一段存儲單元。它只是b1中基類部分的別名,r與b1中基類部分共享同一段存儲單元,r與b1具有相同的起始地址。
(3)如果函數(shù)的參數(shù)是基類對象或基類對象的引用,相應(yīng)的實參可以用子類對象。例如:
1: void fun ( A& r ) //形參是A類對象的引用 2: { 3: cout << r.num << endl ; //輸出該引用中的數(shù)據(jù)成員num 4: } 5: B b1 ; //定義公用派生類的對象b1 6: fun( b1 ) ; //輸出B對象b1的基類的數(shù)據(jù)成員num的值在fun函數(shù)中只能輸出派生類中基類成員的值。
(4)派生類對象的地址可以賦給指向基類對象的指針變量,即指向基類對象的指針變量可以指向派生類對象。示例程序如下:
1: #include <iostream> 2: #include <string> 3: using namespace std ; 4:? 5: class Student //聲明Student類 6: { 7: public: 8: Student ( int , string , float ) ; //聲明構(gòu)造函數(shù) 9: void display(); //聲明輸出函數(shù) 10: private: 11: int num ; 12: string name ; 13: float score ; 14: }; 15: Student::Student( int n , string nam , float s ) //定義構(gòu)造函數(shù) 16: { 17: num = n ; 18: name = nam ; 19: score = s ; 20: } 21: void Student::display() //定義輸出函數(shù) 22: { 23: cout << endl << "num:" << num << endl ; 24: cout << "name:" << name << endl ; 25: cout << "score:" << score << endl ; 26: } 27:? 28: class Graduate : public Student //聲明公用派生類Graduate 29: { 30: public: 31: Graduate( int , string , float , float ) ; //聲明構(gòu)造函數(shù) 32: void display() ; //聲明輸出函數(shù) 33: private: 34: float wage ; 35: }; 36: Graduate::Graduate( int n , string nam , float s , float w ) : Stident( n , nam , s ) 37: , wage( w ) {} //定義構(gòu)造函數(shù) 38: void Graduate::display() //定義輸出函數(shù) 39: { 40: Student::display() ; 41: cout << "wage:" << wage << endl ; 42: } 43:? 44: int main() 45: { 46: Student stud1( 1001 , "Li" , 87.5 ) ; //定義Student類對象stud1 47: Graduate grad1( 2001 , "wang" , 98.5 , 1000 ) ; //定義Graduate類對象grad1 48: Student *pt = &stud1 ; //定義指向基類的指針并指向stud1 49: pt->display() ; //調(diào)用stud1.display函數(shù) 50: pt = &grad1 ; //指針指向grad1 51: pt->display() ; //調(diào)用grad1.display函數(shù) 52:? 53: return 0 ; 54: }先看一下程序運行結(jié)果,再進(jìn)行具體的分析:
分析:有很多讀者會認(rèn)為,在派生類中有兩個同名的display成員函數(shù),根據(jù)同名覆蓋的規(guī)則,第二次被調(diào)用的應(yīng)當(dāng)是派生類Graduate對象的display函數(shù),在執(zhí)行Graduate::display函數(shù)過程中調(diào)用Student::display函數(shù),輸出num,name,score,然后再輸出wage的值。很明顯與上述結(jié)果不符,why?問題在于pt是指向Student類對象的指針變量,即使讓它指向了grad1,但實際上pt指向的是從grad1從基類繼承的部分。通過指向基類對象的指針,只能訪問派生類中的基類成員,而不能訪問派生類增加的成員。
??? 通過本例可以看到,用指向基類對象的指針變量指向子類對象是合法的、安全的,不會出現(xiàn)編譯上的錯誤。但人們更希望通過使用基類指針能夠調(diào)用基類和子類對象的成員,要解決這個問題,就要用到以后會講到的多態(tài)性和虛函數(shù)。
1.2?? 繼承與組合
在一個類中以另一個類的對象作為數(shù)據(jù)成員的,稱為類的組合。例如,聲明Professor類是Teacher類的派生類,另有一個類BirthDate,包含year,month,day等數(shù)據(jù)成員。我們可以將教授生日的信息加入到Professor類的聲明中。如:
1: class Teacher //聲明教師類 2: { 3: public: 4: ... 5: private: 6: int num ; 7: string name ; 8: char sex ; 9: }; 10:? 11: class Birthdate //聲明生日類 12: { 13: public: 14: ... 15: private: 16: int year ; 17: int month ; 18: int day ; 19: }; 20:? 21: class Professor : public Teacher //聲明教授類 22: { 23: public: 24: ... 25: private: 26: Birthdate birthday ; //Birthdate類的對象作為數(shù)據(jù)成員 27: };?
?
???? 類的組合和類的繼承一樣,都是有效地利用已有類的資源。但二者的概念和用法不同。通過繼承建立了派生類與基類的關(guān)系,這是一種“is-a”的關(guān)系,如“白貓是貓”,派生類是基類的具體化的實現(xiàn),是基類的一種。通過組合則建立了成員類和組合類的關(guān)系,它們之間是“has-a”的關(guān)系。不能說Professor是一個Birthdate,只能說教授有一個Birthdate的屬性。
?
?? 2?? 繼承在軟件開發(fā)中的重要意義
??? 縮短軟件開發(fā)過程的關(guān)鍵是鼓勵軟件的重用。繼承機(jī)制在很大一部分上解決了這個問題。編寫面向?qū)ο蟮某绦驎r要把注意力放在實現(xiàn)對自己有用的類上面,對已有的類加以整理和分類,進(jìn)行剪裁和修改,并在此基礎(chǔ)上集中精力編寫派生類新增加的部分。
??? 人們?yōu)槭裁催@么看重繼承,要求在軟件開發(fā)中使用繼承機(jī)制,盡可能的通過繼承建立一批新的類,有以下幾個原因:
(1)有許多基類是被程序的其他部分或其他程序使用的,這些程序要求保持原有的基類不受破壞。
(2)用戶往往得不到基類的源代碼。如果使用的類庫,用戶無法知道成員函數(shù)的代碼,因此也就無法對基類進(jìn)行修改,保證了基類的安全。
(3)在類庫中,一個基類可能已被指定與用戶所需的多種組建有聯(lián)系,因此類庫不允許被修改。
(4)許多類是專門被設(shè)計為基類的,并沒有什么獨立的功能,只是一個框架,或者說是抽象類。設(shè)計這些通用的類目的是建立通用的數(shù)據(jù)結(jié)構(gòu),以便用戶在此基礎(chǔ)上添加各種功能建立派生類。
轉(zhuǎn)載于:https://www.cnblogs.com/hust-ghtao/p/3500928.html
總結(jié)
以上是生活随笔為你收集整理的C++学习之路—继承与派生(四)拓展与总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 到底什么是极简主义?
- 下一篇: 再谈原型和原型链