一、基本概念 1、類的繼承,是新的類從已有類那里得到已有的特性?;驈囊延蓄惍a(chǎn)生新類的過(guò)程就是類的派生。原有的類稱為基類或父類,產(chǎn)生的新類稱為派生類或子類。 2、派生類的聲明: class 派生類名:繼承方式 基類名1, 繼承方式 基類名2,...,繼承方式 基類名n { 派生類成員聲明; }; 3、一個(gè)派生類可以同時(shí)有多個(gè)基類,這種情況稱為多重繼承,派生類只有一個(gè)基類,稱為單繼承。直接派生,間接派生。 4、繼承方式規(guī)定了如何訪問(wèn)基類繼承的成員。繼承方式有public, private, protected。如果不顯示給出繼承方式,默認(rèn)為private繼承。繼承方式指定了派生類成員以及類外對(duì)象對(duì)于從基類繼承來(lái)的成員的訪問(wèn)權(quán)限。 5、派生類繼承基類中除構(gòu)造和析構(gòu)函數(shù)以外的所有成員。 6、派生類生成: 吸收基類成員(除構(gòu)造析構(gòu)函數(shù)以外的所有成員); 改造基類成員(根據(jù)繼承方式調(diào)整基類成員的訪問(wèn),函數(shù)在子類中的覆蓋,以及虛函數(shù)在子類中的覆蓋); 添加新的成員; 7、公有繼承 當(dāng)類的繼承方式為公有繼承時(shí),基類的公有和保護(hù)成員的訪問(wèn)屬性在派生類中不變,而基類的私有成員不可訪問(wèn)。即基類的公有成員和保護(hù)成員被繼承到派生類中仍作為派生類的公有成員和保護(hù)成員。派生類的其他成員可以直接訪問(wèn)它們。無(wú)論派生類的成員還是派生類的對(duì)象都無(wú)法訪問(wèn)基類的私有成員。 8、私有繼承 當(dāng)類的繼承方式為私有繼承時(shí),基類中的公有成員和保護(hù)成員都以私有成員身份出現(xiàn)在派生類中,而基類的私有成員在派生類中不可訪問(wèn)?;惖墓谐蓡T和保護(hù)成員被繼承后作為派生類的私有成員,派生類的其他成員可以直接訪問(wèn)它們,但是在類外部通過(guò)派生類的對(duì)象無(wú)法訪問(wèn)。無(wú)論是派生類的成員還是通過(guò)派生類的對(duì)象,都無(wú)法訪問(wèn)從基類繼承的私有成員。通過(guò)多次私有繼承后,對(duì)于基類的成員都會(huì)成為不可訪問(wèn)。因此私有繼承比較少用。 9、保護(hù)繼承 保護(hù)繼承中,基類的公有成員和私有成員都以保護(hù)成員的身份出現(xiàn)在派生類中,而基類的私有成員不可訪問(wèn)。派生類的其他成員可以直接訪問(wèn)從基類繼承來(lái)的公有和保護(hù)成員,但是類外部通過(guò)派生類的對(duì)象無(wú)法訪問(wèn)它們,無(wú)論派生類的成員還是派生類的對(duì)象,都無(wú)法訪問(wèn)基類的私有成員。 二、派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù) 1、派生類中由基類繼承而來(lái)的成員的初始化工作還是由基類的構(gòu)造函數(shù)完成,然后派生類中新增的成員在派生類的構(gòu)造函數(shù)中初始化。 2、派生類構(gòu)造函數(shù)的語(yǔ)法: 派生類名::派生類名(參數(shù)總表):基類名1(參數(shù)表1),基類名(參數(shù)名2)....基類名n(參數(shù)名n),內(nèi)嵌子對(duì)象1(參數(shù)表1),內(nèi)嵌子對(duì)象2(參數(shù)表2)....內(nèi)嵌子對(duì)象n(參數(shù)表n) { 派生類新增成員的初始化語(yǔ)句; } 注:構(gòu)造函數(shù)的初始化順序并不以上面的順序進(jìn)行,而是根據(jù)聲明的順序初始化。 3、如果基類中沒(méi)有不帶參數(shù)的構(gòu)造函數(shù),那么在派生類的構(gòu)造函數(shù)中必須調(diào)用基類構(gòu)造函數(shù),以初始化基類成員。 4、派生類構(gòu)造函數(shù)執(zhí)行的次序: 調(diào)用基類構(gòu)造函數(shù),調(diào)用順序按照它們被繼承時(shí)聲明的順序(從左到右); 調(diào)用內(nèi)嵌成員對(duì)象的構(gòu)造函數(shù),調(diào)用順序按照它們?cè)陬愔新暶鞯捻樞颍?/div> 派生類的構(gòu)造函數(shù)體中的內(nèi)容。 例子: #include <iostream> #include <time.h> using namespace std; class B1 { public: B1(int i) { cout<<"constructing B1 "<<i<<endl; } }; class B2 { public: B2(int j) { cout<<"constructing B2 "<<j<<endl; } }; class B3 { public: B3() { cout<<"constructing B3"<<endl; } }; class C: public B2, public B1, public B3 { public: C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b) { } private: B1 memberB1; B2 memberB2; B3 memberB3; }; int main() { C obj(1,2,3,4); return 0; }
輸出結(jié)果為: constructing B2 2 constructing B1 1 constructing B3 constructing B1 3 constructing B2 4 constructing B3 5、析構(gòu)函數(shù) 派生類的析構(gòu)函數(shù)的功能是在該對(duì)象消亡之前進(jìn)行一些必要的清理工作,析構(gòu)函數(shù)沒(méi)有類型,也沒(méi)有參數(shù)。析構(gòu)函數(shù)的執(zhí)行順序與構(gòu)造函數(shù)相反。 例子: #include <iostream> #include <time.h> using namespace std; class B1 { public: B1(int i) { cout<<"constructing B1 "<<i<<endl; } ~B1() { cout<<"destructing B1"<<endl; } }; class B2 { public: B2(int j) { cout<<"constructing B2 "<<j<<endl; } ~B2() { cout<<"destructing B2"<<endl; } }; class B3 { public: B3() { cout<<"constructing B3"<<endl; } ~B3() { cout<<"destructing B3"<<endl; } }; class C: public B2, public B1, public B3 { public: C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b) { } private: B1 memberB1; B2 memberB2; B3 memberB3; }; int main() { C obj(1,2,3,4); return 0; }
輸出結(jié)果為: constructing B2 2 constructing B1 1 constructing B3 constructing B1 3 constructing B2 4 constructing B3 destructing B3 destructing B2 destructing B1 destructing B3 destructing B1 destructing B2 三、派生類成員的標(biāo)識(shí)和訪問(wèn) 1、派生類成員屬性劃分為四種: 不可訪問(wèn)的成員;私有成員;保護(hù)成員;公有成員; 2、作用域分辨 形式為:基類名::成員名;基類名::成員名(參數(shù)表); 如果某派生類的多個(gè)基類擁有同名的成員,同時(shí),派生類又新增這樣的同名成員,在這種情況下,派生類成員將覆蓋所有基類的同名成員。這就需要這樣的調(diào)用方式才能調(diào)用基類的同名成員。 例子:多繼承同名 #include <iostream> #include <time.h> using namespace std; class B1 { public: int nV; void fun() { cout<<"member of B1 "<<nV<<endl; } }; class B2 { public: int nV; void fun() { cout<<"member of B2 "<<nV<<endl; } }; class D1: public B1, public B2 { public: int nV; void fun() { cout<<"member of D1 "<<nV<<endl; } }; int main() { D1 d1; d1.nV = 1; d1.fun(); d1.B1::nV = 2; d1.B1::fun(); d1.B2::nV = 3; d1.B2::fun(); return 0; }
輸出結(jié)果為: member of D1 1 member of B1 2 member of B2 3 以上通過(guò)作用域分辨符,解決了訪問(wèn)基類中被屏蔽的同名成員。 3、如果某個(gè)派生類的部分或全部直接基類是從另一個(gè)共同的基類派生而來(lái),在這些直接基類中,從上一級(jí)基類繼承來(lái)的成員就擁有相同的名稱,因此派生類中也就會(huì)產(chǎn)生同名現(xiàn)象,對(duì)這種類型的同名成員也要使用作用域分辨符來(lái)唯一標(biāo)識(shí),而且必須用直接基類進(jìn)行限定。 例子: #include <iostream> #include <time.h> using namespace std; class B0 { public: int nV; void fun() { cout<<"member of B0 "<<nV<<endl; } }; class B1:public B0 { public: int nV1; }; class B2:public B0 { public: int nV2; }; class D1:public B1, public B2 { public: int nVd; void fund() { cout<<"member of D1"<<endl; } }; int main() { D1 d1; d1.B1::nV = 2; d1.B1::fun(); d1.B2::nV = 3; d1.B2::fun(); return 0; }
輸出結(jié)果為: member of B0 2 member of B0 3 在這種情況下,派生類對(duì)象在內(nèi)存中就同時(shí)擁有成員nV及fun的兩份拷貝。但是很多情況下,我們只需要這樣一個(gè)這樣的數(shù)據(jù)拷貝,同一成員的多份拷貝增加了內(nèi)存的開(kāi)銷。可以通過(guò)虛函數(shù)來(lái)解決這個(gè)問(wèn)題。 4、虛基類 為了解決前面提到的多重拷貝的問(wèn)題,可以將共同基類設(shè)置為虛基類,這時(shí)從不同的路徑繼承過(guò)來(lái)的同名數(shù)據(jù)成員在內(nèi)存中就只有一個(gè)拷貝,同一個(gè)函數(shù)也只有一個(gè)映射。 虛基類的聲明是在派生類的聲明過(guò)程,其語(yǔ)法形式為: class 派生類名::virtual 繼承方式 基類名; 例子: #include <iostream> #include <time.h> using namespace std; class B0 { public: int nV; void fun() { cout<<"member of B0 "<<nV<<endl; } }; class B1:virtual public B0 { public: int nV1; }; class B2:virtual public B0 { public: int nV2; }; class D1:public B1, public B2 { public: int nVd; void fund() { cout<<"member of D1"<<endl; } }; int main() { D1 d1; d1.nV = 2; d1.fun(); return 0; }
輸出結(jié)果為: member of B0 2 5、虛基類及其派生類的構(gòu)造函數(shù) 一般而言,派生類只對(duì)其直接基類的構(gòu)造函數(shù)傳遞參數(shù),但是在虛基類中,不管是直接或間接虛基類的所有派生類,都必須在構(gòu)造函數(shù)的成員初始化列表中列出對(duì)虛基類的初始化。 例子: #include <iostream> #include <time.h> using namespace std; class B0 { public: B0(int n) { nV = n; } int nV; void fun() { cout<<"member of B0 "<<nV<<endl; } }; class B1:virtual public B0 { public: B1(int a):B0(a) { } int nV1; }; class B2:virtual public B0 { public: B2(int a):B0(a) { } int nV2; }; class D1:public B1, public B2 { public: D1(int a):B0(a), B1(a), B2(a) { } int nVd; void fund() { cout<<"member of D1"<<endl; } }; int main() { D1 d1(1); d1.nV = 2; d1.fun(); return 0; }
以上例子看上去B0的構(gòu)造函數(shù)好像被調(diào)用了三次,但是實(shí)際上只有D1類中的D1(int a):B0(a), B1(a), B2(a) 才是真正的調(diào)用了B0構(gòu)造函數(shù)。 四、賦值兼容規(guī)則 1、賦值兼容規(guī)則是指在需要基類對(duì)象的任何地方都可以使用公有派生類的對(duì)象來(lái)替代。 2、賦值兼容規(guī)則中所指的替代包括: 派生類的對(duì)象可以賦值給基類對(duì)象; 派生類的對(duì)象可以初始化基類的引用; 派生類對(duì)象的地址可以賦給指向基類的指針。 在替代之后,派生類對(duì)象就可以作為基類的對(duì)象使用,但只能使用從基類繼承的成員。 例子: #include <iostream> #include <time.h> using namespace std; class B0 { public: void display() { cout<<"B0::display()"<<endl; } }; class B1:public B0 { public: void display() { cout<<"B1::display()"<<endl; } }; class B2:public B0 { public: void display() { cout<<"B2::display()"<<endl; } }; void fun(B0 *ptr) { ptr->display(); } int main() { B0 b0; B1 b1; B2 b2; fun(&b0); b0 = b1; fun(&b0); b0 = b2; fun(&b0); return 0; }
輸出結(jié)果為: B0::display() B0::display() B0::display() 通過(guò)這種賦值兼容后,每次調(diào)用的同名函數(shù)都是基類的同名函數(shù),如果想調(diào)用派生類的,則需要使用虛函數(shù)。 |
|