C++多态讲解以及常见面试题
多態(tài)的概念
什么是多態(tài)
? 多態(tài)就是在不同繼承關(guān)系的類對(duì)象,去調(diào)用同一函數(shù),產(chǎn)生了不同的行為。
實(shí)現(xiàn)多態(tài)的條件
調(diào)用函數(shù)的對(duì)象是指針或引用。
被調(diào)用函數(shù)必須是虛函數(shù),并且完成了虛函數(shù)的重寫。
函數(shù)重載
多態(tài)的作用
??封裝是為了代碼模塊化,繼承是擴(kuò)展已存在的代碼,他們的目的都是為了 實(shí)現(xiàn)代碼的復(fù)用,但是多態(tài)的目的是為了實(shí)現(xiàn)接口的重用,也就是多不管傳遞過來的是哪個(gè)類對(duì)象,函數(shù)都能狗狗通過這個(gè)接口調(diào)用到適應(yīng)自己對(duì)象的實(shí)現(xiàn)方法。
??虛函數(shù)重寫:子類重寫父類的函數(shù)(協(xié)變:子類中的返回值和父類中的返回值是父子關(guān)系,并且是引用或指針)。如果只在父類中添加virtual關(guān)鍵字,子類中的重寫函數(shù)也會(huì)稱為虛函數(shù),童謠可以構(gòu)成多態(tài),否則為重定義。
函數(shù)重載、重定義、重寫的區(qū)別:
虛函數(shù)和純虛函數(shù)
??虛函數(shù)就是在類的成員函數(shù)錢錢添加了virtual關(guān)鍵字。主要是為了實(shí)現(xiàn)多態(tài),通過一張?zhí)摵瘮?shù)表來實(shí)現(xiàn)。它允許子類重寫來自父類的成員函數(shù)。
??純虛函數(shù)就是在基類中只對(duì)對(duì)應(yīng)的虛函數(shù)進(jìn)行聲明,在最后加=0,定義純虛函數(shù)的類是一個(gè)抽象類,抽象類不能被實(shí)例化,它體現(xiàn)的是接口繼承,子類只是繼承了這個(gè)接口的形式,不需要使用他里面的功能,要實(shí)現(xiàn)自己的功能。在子類中必須實(shí)現(xiàn)父類的純虛函數(shù),如果不實(shí)現(xiàn)父類的純虛函數(shù),這個(gè)子類也是一個(gè)抽象類,同樣不能區(qū)實(shí)例化對(duì)象。
??那么什么是抽象類呢?抽象類就是包含純虛函數(shù)的類,這個(gè)類不能被實(shí)例化,因?yàn)轭惗x的不完整,成員函數(shù)都沒有實(shí)現(xiàn),這種類只是為了接口繼承的實(shí)現(xiàn),繼承他的子類并不想去用它,子類要實(shí)現(xiàn)自己的功能。
??無論是虛函數(shù)還是純虛函數(shù),都是在基類中為派生類提供編程接口,面向?qū)ο笞詈诵牡乃枷刖褪菍?duì)接口編程,而不是對(duì)實(shí)現(xiàn)編程,在C++中,就是使用繼承和多態(tài)來事項(xiàng)這種思想。如果想讓基類為派生類提供缺省的處理方法,那么就將這個(gè)函數(shù)設(shè)為虛函數(shù),如果是想讓派生類必須重寫該虛函數(shù),就將這個(gè)函數(shù)設(shè)為純虛函數(shù)。
1、虛函數(shù)和純虛函數(shù)可以定義在同一個(gè)類中,一旦某個(gè)類包含了純虛函數(shù),這個(gè)類就是一個(gè)抽象類。
2、虛函數(shù)可以直接被使用,但是純虛函數(shù)必須要在派生類中實(shí)現(xiàn)之后才可以使用,一i那位純虛函數(shù)在積累中只聲明沒有定義,所以無法直接使用。
3、虛函數(shù)和純虛函數(shù)都可以在子了中被重寫,一墮胎的形式被調(diào)用。
4、虛函數(shù)和純虛函數(shù)都是為了實(shí)現(xiàn)接口繼承而出現(xiàn)。
5、虛函數(shù)的定義:virtual + 函數(shù)純虛函數(shù)定義:virtual + 函數(shù) + =0;
6、虛函數(shù)和純虛函數(shù)都不能設(shè)為static,因?yàn)閟tatic在編譯的時(shí)候就要被綁定,但是虛函數(shù)和純虛函數(shù)實(shí)在運(yùn)行時(shí)在確定的。
虛函數(shù)表
??虛函數(shù)表是一個(gè)函數(shù)指針數(shù)組,在末尾存放的時(shí)一個(gè)空指針nullptr,在VS中存放在代碼段,在虛函數(shù)表中只存放虛函數(shù)執(zhí)政,他不存放普通函數(shù)的指針,將新創(chuàng)建的虛函數(shù)指針存放在虛函數(shù)表末尾。
??虛標(biāo)指針:存放在對(duì)象模型中,在32位機(jī)上,存放在對(duì)象模型的頭4個(gè)字節(jié)中。
單繼承中的虛表
??在單繼承中所有的虛函數(shù)都存放在虛表中,如果有被重寫的,直接將虛表中的虛函數(shù)指針換成重寫后的虛函數(shù)指針即可,如果在子類中的添加了新的虛函數(shù),按照新的虛函數(shù)的聲明順序?qū)⑵涮砑拥教摫碇小T谔摫碇?#xff0c;先添加父類的虛函數(shù)指針,再添加子類的虛函數(shù)指針。
多繼承中的虛表
#include <iostream> using namespace std; class base1 { public:virtual void func1(){cout << "base1::func1" << endl;}virtual void func2(){cout << "base1::func2" << endl;} private:int b1; }; class base2 { public:virtual void func1(){cout << "base2::func1" << endl;}virtual void func2(){cout << "base2::func2" << endl;} private:int b2; }; class derive:public base1, public base2 { public:virtual void func1(){cout << "derive::func1" << endl;}virtual void func3(){cout << "derive::func3" << endl;} private:int d1; }; typedef void(*VFPTR)(); //函數(shù)指針 void printFunc(VFPTR* vftable) {cout << "虛表地址" << vftable << endl;for (int i = 0; (*vftable) != nullptr; ++i){cout << "第" << i << "個(gè)函數(shù)地址" << *vftable << "--->";(*vftable)();//調(diào)這個(gè)函數(shù)++vftable;//指針向后走,打印下一個(gè)函數(shù)地址} }int main() {derive d;//取虛標(biāo)地址的方法://先取到d的地址,由于虛標(biāo)指針存放在對(duì)象的頭四個(gè)字節(jié)中,所以要將他轉(zhuǎn)為int*類型(int* 位四個(gè)字節(jié)),這樣就可以得到頭四個(gè)字節(jié)的內(nèi)容,對(duì)這個(gè)已經(jīng)得到的四個(gè)字節(jié)解引用就可以得到它的值,但現(xiàn)在他是整型的,我們要的是函數(shù)指針,所以要將他強(qiáng)轉(zhuǎn)位函數(shù)指針類型的指針就可以得到虛表的地址(VFPTR*)*((int*)&b);//如果要調(diào)用虛表中的第一個(gè)函數(shù),對(duì)他向后偏移1個(gè)單位再解引用就可以調(diào)用虛表中的第一個(gè)函數(shù)(*(((VFPTR*)*((int*)&b))+1))();//打印第二個(gè)虛標(biāo)地址//方法一://給出一個(gè)base2的指針,讓他存放d的地址,這時(shí)候就發(fā)生了一個(gè)天然的轉(zhuǎn)換,這個(gè)指針中存放的就是第二個(gè)虛表的地址base2* pd = &d;//方法二//讓第一個(gè)虛表的地址向后偏移base1的大小(VFPTR*)*((int*)((char*)&b+sizeof(base1)))return 0; }接口繼承和實(shí)現(xiàn)繼承
??普通函數(shù)的繼承就是一種實(shí)現(xiàn)繼承,派生類繼承了基類函數(shù),繼承的是函數(shù)的實(shí)現(xiàn)。
??虛函數(shù)的繼承是接口繼承,派生類繼承的是基類函數(shù)的接口,目的是為了重寫,從而達(dá)成多態(tài),繼承的是接口。如果不是為了實(shí)現(xiàn)多態(tài),不要把函數(shù)設(shè)為虛函數(shù)。
—>常見面試題<—
總結(jié)
以上是生活随笔為你收集整理的C++多态讲解以及常见面试题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 检测系列--YOLO系列
- 下一篇: s3c2440移植MQTT