C++中的重載、覆蓋、隱藏介紹
前幾天面試時(shí)被問(wèn)及C++中的覆蓋、隱藏,概念基本答不上來(lái),只答了怎么用指針實(shí)現(xiàn)多態(tài),也還有遺漏。最終不歡而散。回來(lái)后在網(wǎng)上查找學(xué)習(xí)了一番,做了這個(gè)總結(jié)。其中部分文字借用了別人的博客,望不要見(jiàn)怪。
概念
一、重載(overload)
指函數(shù)名相同,但是它的參數(shù)表列個(gè)數(shù)或順序,類(lèi)型不同。但是不能靠返回類(lèi)型來(lái)判斷。
(1)相同的范圍(在同一個(gè)作用域中) ;
(2)函數(shù)名字相同;
(3)參數(shù)不同;
(4)virtual 關(guān)鍵字可有可無(wú)。
(5)返回值可以不同;
二、重寫(xiě)(也稱(chēng)為覆蓋 override)
是指派生類(lèi)重新定義基類(lèi)的虛函數(shù),特征是:
(1)不在同一個(gè)作用域(分別位于派生類(lèi)與基類(lèi)) ;
(2)函數(shù)名字相同;
(3)參數(shù)相同;
(4)基類(lèi)函數(shù)必須有 virtual 關(guān)鍵字,不能有 static 。
(5)返回值相同(或是協(xié)變),否則報(bào)錯(cuò);<—-協(xié)變這個(gè)概念我也是第一次才知道…
(6)重寫(xiě)函數(shù)的訪(fǎng)問(wèn)修飾符可以不同。盡管 virtual 是 private 的,派生類(lèi)中重寫(xiě)改寫(xiě)為 public,protected 也是可以的
三、重定義(也成隱藏)
(1)不在同一個(gè)作用域(分別位于派生類(lèi)與基類(lèi)) ;
(2)函數(shù)名字相同;
(3)返回值可以不同;
(4)參數(shù)不同。此時(shí),不論有無(wú) virtual 關(guān)鍵字,基類(lèi)的函數(shù)將被隱藏(注意別與重載以及覆蓋混淆) 。
(5)參數(shù)相同,但是基類(lèi)函數(shù)沒(méi)有 virtual關(guān)鍵字。此時(shí),基類(lèi)的函數(shù)被隱藏(注意別與覆蓋混淆) 。
例子
#include <iostream> using namespace std; class SParent { public: SParent( ){}; SParent( const SParent &p ) { cout << "parent copy construct" << endl; } int add( int a,int b ) { cout << "parent int add" << endl; return a + b; } double add( double a,double b ) { cout << "parent double add" << endl; return a + b; } virtual int dec( int a,int b ) { cout << "parent int dec" << endl; return a - b; } }; class SChild : public SParent { public: //using SParent::add; float add( float a,float b ) { cout << "child float add" << endl; return a + b; } int dec(int a, int b) { cout << "child int dec" << endl; return a - b; } }; int main() { /* 測(cè)試重載 */ SParent parent; parent.add( 3,5 ); parent.add( (double)3,(double)5 ); cout << endl; /* 測(cè)試覆蓋 */ SChild *pchild = (SChild *)new SParent();/* 基類(lèi)強(qiáng)轉(zhuǎn)為子類(lèi)...危險(xiǎn)...,用dynamic_cast轉(zhuǎn)換也不行 */ pchild->dec( 10,3 ); SParent *pparent = new SChild(); pparent->dec( 11,3 ); cout << endl; /* 測(cè)試隱藏 */ SChild child; child.add( (int)3,(int)5 ); cout << endl; /* 測(cè)試函數(shù)表 */ ((SParent *)NULL)->add( 4,6 ); ((SChild *)NULL)->add( 4,6 ); int a = 0; ((SChild *)&a)->add( 4,6 ); cout << endl; /* 測(cè)試函數(shù)地址 */ ((SParent)child).add( (int)4,(int)8 ); child.SParent::add( 3,5 ); return 0; }
輸出結(jié)果:
parent int add parent double add parent int dec child int dec child float add parent int add child float add child float add parent copy construct parent int add parent int add 按 <RETURN> 來(lái)關(guān)閉窗口...
理解
int SParent::add(int a,int b)與double SParent::add( double a,double b )是重載
int SParent::add(int a,int b)與double SParent::add( double a,double b )都被子類(lèi)SChild中的float SChild::add( float a,float b )隱藏
int SParent::dec( int a,int b )被子類(lèi)SChild中的int SChild::dec( int a,int b )覆蓋
測(cè)試
1.重載測(cè)試,簡(jiǎn)單易懂,略過(guò)。
2.覆蓋測(cè)試。dec函數(shù)在基類(lèi)、子類(lèi)中同名同參,為虛函數(shù),故稱(chēng)覆蓋。
SChild *pchild = (SChild *)new SParent()創(chuàng)建的是一個(gè)基類(lèi)對(duì)象,其函數(shù)表應(yīng)該為
SParent *pparent = new SChild();創(chuàng)建一個(gè)子類(lèi)對(duì)象,其函數(shù)表應(yīng)該為
由上面的函數(shù)表可見(jiàn),當(dāng)發(fā)生覆蓋時(shí),子類(lèi)的函數(shù)名會(huì)把基類(lèi)的同名函數(shù)覆蓋(這也就是為什么叫覆蓋的原因吧)。這樣我們可以利用一個(gè)指向子類(lèi)的基類(lèi)指針實(shí)現(xiàn)多態(tài)。但重點(diǎn)只有一
個(gè),就是函數(shù)表里到底指向誰(shuí)(不管這個(gè)指針經(jīng)過(guò)轉(zhuǎn)換后是什么類(lèi)型的).故輸出分別為父類(lèi)、子類(lèi)。這是一個(gè)運(yùn)行時(shí)多態(tài)。
3.隱藏測(cè)試
int SParent::add(int a,int b)與double SParent::add( double a,double b )都被子類(lèi)SChild中的float SChild::add( float a,float b )覆蓋,是因?yàn)樗麄兺?,而且在不同的作用域?基類(lèi)、子類(lèi)作用域是不同的)。child.add( (int)3,(int)5 );這行代碼中,編譯器在子類(lèi)中查找add函數(shù),只找到了一個(gè)(基類(lèi)的add(int a,int b)會(huì)被編譯根據(jù)隱藏規(guī)則略過(guò)),再根據(jù)隱式類(lèi)型轉(zhuǎn)換發(fā)現(xiàn)該函數(shù)適用。如果無(wú)法隱式轉(zhuǎn)換,則編譯不過(guò)。隱藏的原因:防止隱式類(lèi)型轉(zhuǎn)換造成錯(cuò)誤。比如int也是可以轉(zhuǎn)換成char的,假如基類(lèi)有一函數(shù)add(char a,char b),子類(lèi)也有一函數(shù)add(double a,double b)。程序員想著在子類(lèi)隱式把int轉(zhuǎn)換為double,但編譯器可能調(diào)的是基類(lèi)的。這也防止了一些庫(kù)或封裝好的基類(lèi)對(duì)程序員造成困擾。
像上面的代碼,如果你確實(shí)需要基類(lèi)的函數(shù),可以用using SParent:add。則把基類(lèi)的add函數(shù)域擴(kuò)大到了子類(lèi),構(gòu)成重載。
4.函數(shù)表測(cè)試
上面我們說(shuō)到函數(shù)表,這個(gè)是在編譯時(shí)定好的,程序運(yùn)行時(shí)加載到內(nèi)存中。這意味著我們可以直接通過(guò)地址去調(diào)用函數(shù)。所以((SChild *)NULL)->add( 4,6 );這種代碼也是能運(yùn)行通過(guò)的。網(wǎng)上還有人通過(guò)計(jì)算直接取到了函數(shù)表的地址直接調(diào)用了。但這種代碼不安全不規(guī)范不說(shuō),還有個(gè)更大的問(wèn)題。當(dāng)成員函數(shù)里需要調(diào)用成員變量時(shí),通過(guò)這種假的對(duì)象指針肯定找不到成員變量表,直接訪(fǎng)問(wèn)了非法內(nèi)存。
5.函數(shù)地址測(cè)試
有了隱藏、覆蓋,哪么我們要怎么調(diào)用被隱藏、覆蓋的函數(shù)呢。下面有兩種方法:
((SParent)child).add( (int)4,(int)8 );
child.SParent::add( 3,5 );
第一種是比較低效的方法。事實(shí)上它是通過(guò)拷貝構(gòu)造函數(shù)生成一個(gè)臨時(shí)的基類(lèi)變量去調(diào)用基類(lèi)的add函數(shù)。
第二種通過(guò)::指定域去訪(fǎng)問(wèn)。這種方法是編譯器根據(jù)域直接找到了基類(lèi)的函數(shù)地址,跟函數(shù)表沒(méi)有多大關(guān)系。
相關(guān)文章
c++連接mysql5.6的出錯(cuò)問(wèn)題總結(jié)
下面小編就為大家?guī)?lái)一篇c++連接mysql5.6的出錯(cuò)問(wèn)題總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,祝大家游戲愉快哦2016-12-12QT獲取顯示當(dāng)前時(shí)間和日期的方法(用QTime,QDate和QDateTime)
獲取當(dāng)期日期時(shí)間在我們?nèi)粘i_(kāi)發(fā)中經(jīng)常會(huì)遇到,下面這篇文章主要給大家介紹了關(guān)于QT獲取顯示當(dāng)前時(shí)間和日期的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08C++實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03c++中的4種類(lèi)型轉(zhuǎn)化方式詳細(xì)解析
const_cast該函數(shù)用于去除指針變量的常量屬性,將它轉(zhuǎn)換為一個(gè)對(duì)應(yīng)指針類(lèi)型的普通變量。反過(guò)來(lái),也可以將一個(gè)非常量的指針變量轉(zhuǎn)換為一個(gè)常指針變量2013-10-10深入淺出理解C語(yǔ)言初識(shí)結(jié)構(gòu)體
C?數(shù)組允許定義可存儲(chǔ)相同類(lèi)型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是?C?編程中另一種用戶(hù)自定義的可用的數(shù)據(jù)類(lèi)型,它允許你存儲(chǔ)不同類(lèi)型的數(shù)據(jù)項(xiàng),本篇讓我們來(lái)了解C?的結(jié)構(gòu)體2022-02-02