-
虛函數(shù)定義:
查看全部 -
多態(tài):
靜態(tài)多態(tài)(早綁定):重載
動態(tài)多態(tài)(晚綁定):以封裝繼承為基礎(chǔ)
查看全部 -
在我們還沒有學(xué)習(xí)多態(tài)的時候,父類和子類出現(xiàn)了同名函數(shù),這個時候就稱之為函數(shù)的隱藏(因為沒有虛函數(shù)?)
如果我們沒有在子類當(dāng)中定義同名的虛函數(shù),那么在子類虛函數(shù)表中就會寫上父類的虛函數(shù)的函數(shù)入口地址;如果我們在子類當(dāng)中也定義了虛函數(shù),那么在子類的虛函數(shù)表中我們就會把原來的父類的虛函數(shù)的函數(shù)地址覆蓋一下,覆蓋成子類的虛函數(shù)的函數(shù)地址,這種情況就稱之為函數(shù)的覆蓋。
查看全部 -
含有純虛函數(shù)的類叫抽象類,除了純虛函數(shù)還可以有其他成員函數(shù)。接口類就是只有純虛函數(shù)的類,不包含其他非純虛函數(shù)。
用接口類指針作為函數(shù)參數(shù)才能體現(xiàn)多態(tài)的優(yōu)勢
查看全部 -
首先flymatch函數(shù)不僅可以傳入指針,還可以傳入實例化對象。
1、課程中為什么要傳入flyable指針呢,是基于基類的引用和指針可以引用子類的對象這一原則,也就是說flyable *p? 這個指針既可以引用bird也可以引用plan,這樣做的好處后面會說到。
2、那為什么不用flyable p 呢,是因為flyable是純虛函數(shù),flyable p就是實例化一個flyable的對象p。顯而易見,純虛函數(shù)是不能被實例化的,所以flyable p 是非法的。
3、那可以傳入什么樣的實例化對象呢,bird和plan的實例化對象都可以。但是一旦這樣做了就會有一個限制,如果函數(shù)中聲明的是一個bird的對象,那么這個函數(shù)就不能再調(diào)用plan的對象了,因為bird和plan是兩個完全不同的類,所以函數(shù)只能接受bird的對象而不能接受plan的對象,就像一個人如果是男人,那么他肯定不可能是女人。如果想處理plan的對象,那么只能再創(chuàng)建另外一個函數(shù)來處理了。
4、事實都不是絕對的,人妖的出現(xiàn)證明了一個人即可以是男人也可以是女人,而flayable *p的出現(xiàn)呢,就是為了讓函數(shù)即可以調(diào)用bird的對象又可以調(diào)用plan的對象以及其它flyable的子類對象,這種編程靈活性是所有編程人員所崇尚的。
講解完畢,如果幫到你請給個好評吧!媳婦非得要那個慕課君抱枕,拼命賺積分ing。。。。。。
總結(jié):基類指針可以調(diào)用子類函數(shù)? 父類有多個子類,那他就可以幾個都調(diào)用了。
接口類:只有純虛函數(shù),無其他函數(shù)、數(shù)據(jù)成員,子類繼承父類純需,還需再定義為虛函數(shù),--因為在父類中沒有實現(xiàn),需要在子類中實現(xiàn)。
查看全部 -
純虛函數(shù):比如說定義一個人類,不知道他具體干嘛,這時候就可以定義一個抽象類的work--》純虛函數(shù)
只在類中聲明,不定義;
純虛函數(shù)不能實例化對象,但可以指向子類地址;
查看全部 -
異常處理
拋出異常:throw?
捕獲異常:try{}catch(){}
void?test1() { ????if() ????{ ????????throw?new?IndexException(); ????} }
int?main() { ????try ????{ ????????test1(); ????????test2(); ????} ????catch(int){cout<<"error:int"<<endl;} ????catch(double?&e){cout<<"e="<<e<<endl;} ????catch(Exception?&e){e.printException;} ????catch(...){cout<<"error..."<<endl;} ???? ????system("pause"); ????return?0; }
(1)throw 時可以定義:throw string("blablabla"); throw 時可以動態(tài)申請內(nèi)存:throw new IndexException,或者throw IndexException()
注意:throw IndexException() 可以類比new IndexException() 實際上前者是拋出一個類,后者是申請一個類。 從老師的視頻中可以看到,該類是使用默認(rèn)構(gòu)造函數(shù), 即沒有初始化參數(shù)。所以IndexException后加(),內(nèi)無參數(shù),如果有參數(shù),那么()內(nèi)應(yīng)該填寫相應(yīng)初始化參數(shù)值。?
(2) catch(異常父類 &e)比較常用,這樣就可以調(diào)用子類的相關(guān)函數(shù),如打印對應(yīng)異常信息。也可以catch(類型),catch(類型 &e)。catch(...)是最后的處理。
(3) 如果test1()拋出異常,直接跳到外部函數(shù)的catch(){}捕獲異常,結(jié)束調(diào)用函數(shù),也不再執(zhí)行test2(),并結(jié)束程序。
(4) 常見的異常:數(shù)據(jù)下標(biāo)越界,除數(shù)為零,內(nèi)存不足
查看全部 -
RTTI 運行時類型識別(Run-Time Type Identification )
有的編譯器需要手動包含頭文件#include <typeinfo>
在父類指針指向子類對象時,為了能夠調(diào)用子類對象的非虛函數(shù),引入RTTI,進行對象類型識別與對象指針的轉(zhuǎn)換,從而。。。
1、typeid注意事項:
(1)type_id返回一個type_info對象的引用
(2)如果想通過基類的指針獲得派生類的數(shù)據(jù)類型,基類必須帶有虛函數(shù)
(3)只能獲取對象的實際類型
Flyable *p=new Bird();
?cout<<type(p).name()<<endl; ? ? 打印出變量p的類型:class Flyable * cout<<tupe(*p).name()<<endl; ? ?打印出*p的類型:class Bird
2、dynamic_cast注意事項:
(1)只能用于指針和引用的轉(zhuǎn)換
(2)要轉(zhuǎn)換的類型必須有虛函數(shù),父類和子類都有虛函數(shù)表明是多態(tài)類
(3)轉(zhuǎn)換成功返回子類地址,失敗返回NULL
查看全部 -
重載,隱藏,覆蓋的區(qū)別:
(1)重載是指同一類中兩個同名函數(shù),但是參數(shù)的類型和數(shù)量不同(靜態(tài)多態(tài))
(2)隱藏是指子類對象調(diào)用函數(shù)時,對父類同名函數(shù)的隱藏。具有同名函數(shù),即使參數(shù)不同。 如果要調(diào)用父類的該函數(shù)需要:子類對象.父類::函數(shù)
?(3)覆蓋是指父類指針指向子類對象的情況下,通過父類指針調(diào)用虛函數(shù)時,子類同名虛函數(shù)對父類同名虛函數(shù)的覆蓋。具有同名的虛函數(shù),且參數(shù)與返回類型也相同。(動態(tài)多態(tài))
注意:父類指針指向子類對象的情況下,如果父類與子類同名的成員函數(shù)不是虛函數(shù),則只能調(diào)用父類的該函數(shù),子類該函數(shù)和子類其他數(shù)據(jù)成員一樣不同被父類指針調(diào)用。
查看全部 -
對象的大小
(1)類實例化對象時,對象的大小就是類中數(shù)據(jù)成員所占的內(nèi)存大小(數(shù)據(jù)成員,不包含成員函數(shù));
(2)若類中存在一個或多個虛函數(shù)或虛析構(gòu)函數(shù),則會在實例對象的前4個(字節(jié)Byte)內(nèi)存單元存放一個虛函數(shù)表指針;
注意1:每個類只有一個虛函數(shù)表,所有該類的對象共用一張?zhí)摵瘮?shù)表。
注意2:父類和子類的虛函數(shù)表不同,但虛函數(shù)表中的函數(shù)地址可能相同,指向同一個虛函數(shù)地址(這種情況父類虛函數(shù)未被子類的覆蓋, 沒有形成多態(tài))
(3)如果類中沒有數(shù)據(jù)成員或虛函數(shù),則對象大小為1字節(jié),用來標(biāo)識對象的存在。
查看全部 -
虛函數(shù)實現(xiàn)多態(tài)的原理:
虛函數(shù)表指針(指向了虛函數(shù)表的首地址)->虛函數(shù)表(地址的偏移找到對應(yīng)的虛函數(shù)的地址)->虛函數(shù)。
在父類指針指向子類實例時:
(1)在子類中定義了同名的虛函數(shù),就會在子類的虛函數(shù)列表中將父類中定義的虛函數(shù)的函數(shù)地址覆蓋掉,從而實現(xiàn)多態(tài)。
(2) 虛析構(gòu)函數(shù)的原理:通過父類類指針指向子類實例的內(nèi)存空間,找到子類的虛函數(shù)表指針?biāo)赶虻奶摵瘮?shù)表,然后在表中找到虛析構(gòu)函數(shù)地址,從而找到虛析構(gòu)函數(shù)執(zhí)行子類的虛析構(gòu)函數(shù),再自動執(zhí)行父類的虛析構(gòu)函數(shù)。
補充1:理論前提,執(zhí)行完子類的析構(gòu)函數(shù)后,會執(zhí)行父類的析構(gòu)函數(shù).
補充2:C++中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機制。關(guān)于多態(tài),簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調(diào)用實際子類的成員函數(shù)。
?這種技術(shù)可以讓父類的指針有“多種形態(tài)”,這是一種泛型技術(shù)。所謂泛型技術(shù),說白了就是試圖使用不變的代碼來實現(xiàn)可變的算法。
?比如:模板技術(shù),RTTI技術(shù),虛函數(shù)技術(shù),要么是試圖做到在編譯時決議,要么試圖做到運行時決議
查看全部 -
virtual的適用情形與使用限制
virtual適用的以下情形:
虛繼承,class B:virtual public A (菱形繼承A-B,C-D)避免重復(fù)繼承數(shù)據(jù)
虛函數(shù),virtual void fun() ?實現(xiàn)多態(tài),不同子類實例化的父類調(diào)用相同方法而結(jié)果不同。子類與父類的虛函數(shù)名和參數(shù)完全一致才能實現(xiàn)多態(tài),只是函數(shù)內(nèi)部實現(xiàn)方法不同。虛函數(shù)具有繼承性,父類對應(yīng)函數(shù)是虛函數(shù),子類對應(yīng)函數(shù)必然也是虛函數(shù)。
虛析構(gòu)函數(shù),virtual ~A( ) ?防止釋放子類實例化的父類中子類內(nèi)存泄漏的問題(子類構(gòu)造函數(shù)中在堆中用new申請了內(nèi)存時引起的內(nèi)存泄漏),最好析構(gòu)前都加virtual?
virtual的使用限制
1、普通函數(shù)不能是虛函數(shù),也就是virtual只能修飾類的成員函數(shù),不能修飾全局函數(shù)
2、靜態(tài)成員函數(shù)不能是虛函數(shù),因為靜態(tài)成員函數(shù)是整個類的,而不是和某個對象共同存在的
3、內(nèi)聯(lián)函數(shù)不能是虛函數(shù),如果修飾了內(nèi)聯(lián)函數(shù),那么會忽略掉inline關(guān)鍵字
4、構(gòu)造函數(shù)不能是虛函數(shù)
查看全部 -
當(dāng)父類與子類之間有重名函數(shù)時:
(1)通過子類對象訪問父類該重名函數(shù)(于是有了隱藏):
? ? ? ? 父類與子類有重名函數(shù)時,這時出現(xiàn)了隱藏,即子類是繼承了父類的該重名函數(shù)的,但是將其隱藏了。通過子類對象訪問該函數(shù)時,訪問的是子類自己的該函數(shù),如果想要訪問父類的該函數(shù),則要在函數(shù)前面加上父類的名稱空間限定;代碼示例如下:父類Person和子類Worker,都有一個同名函數(shù)name();通過子類對象worker調(diào)用父類該函數(shù)時,worker.Person::name();
(2)通過父類指針指向子類對象,并訪問子類重名函數(shù)(于是有了虛函數(shù)):
? ? ? ? 父類指針指向子類對象時,父類指針只能訪問子類對象的數(shù)據(jù)成員(部分,繼承自父類的),和訪問父類在代碼區(qū)的自己的成員函數(shù);此時,如果想要通過父類指針訪問到子類對象的成員函數(shù)(即通過父類指針調(diào)用子類的析構(gòu)函數(shù)或者普通重名函數(shù)),就要將該重名函數(shù)設(shè)定為虛函數(shù),然后將虛函數(shù)地址放進該類的虛函數(shù)表中(于是有了覆蓋,即在子類虛函數(shù)表中用子類虛函數(shù)地址覆蓋掉父類虛函數(shù)地址),而對象又多了個數(shù)據(jù)成員(虛函數(shù)表指針),且在對象內(nèi)存塊首位;這樣當(dāng)我們用父類指針指向子類對象,且調(diào)用子類重名虛函數(shù)時,就要先在虛函數(shù)表中查找,如果找到,就執(zhí)行;即調(diào)用成員函數(shù)時,有虛函數(shù)表先在虛函數(shù)表中查找,然后再在代碼區(qū)查找;
? ? ? ? 這里我只解釋了父類指針訪問子類重名函數(shù)的情況,沒有解釋虛析構(gòu)函數(shù)不重名也能訪問的情況。我想可能是析構(gòu)函數(shù)有自己特殊的用法吧,可能每個類的析構(gòu)函數(shù)都是同一個名字,而在代碼中名字是不同的吧。這里如果有同學(xué)想明白了,希望能不吝賜教;
(3)通過子類對象初始化父類對象,通過父類對象不能訪問子類重名函數(shù)(虛):
? ? ? ? 按照上面的理論,子類對象初始化父類對象后,子類對象的數(shù)據(jù)成員會覆蓋掉父類對象的數(shù)據(jù)成員,但是這里,父類原有的虛函數(shù)表是沒有被覆蓋掉的,父類對象的虛函數(shù)表指針還是自己的表指針;表指針里的虛函數(shù)地址還是父類自己的虛函數(shù)地址,所以此時通過父類對象只能訪問到子類對象的數(shù)據(jù)成員(繼承自父類的),訪問自己代碼區(qū)的成員函數(shù)和虛函數(shù)列表中的自己的虛函數(shù),不能訪問到子類虛函數(shù)列表中的虛函數(shù);
查看全部 -
看到這了查看全部
-
用父類Exception就可以捕獲到try中的子類對像,然后通過RTT處理
查看全部
舉報