重載(overload)與重寫(override)

提綱.png

一.C++中的重寫、重載、重定義

1.重載(overload)

概念

同一個(gè)類中的函數(shù)具有相同的名稱,但是參數(shù)的列表不相同的情形,這樣的同名不同參數(shù)的函數(shù)之間,互成為重載函數(shù)。

基本條件
  • 同一個(gè)類中(相同的作用域中)
  • 函數(shù)名稱必須相同
  • 函數(shù)參數(shù)必須不相同,可以是參數(shù)類型或者參數(shù)個(gè)數(shù)不同
  • 函數(shù)的返回值可以不同

2.重寫(override)

概念

也稱為覆蓋,子類重新定義的父類中有相同的名稱或者參數(shù)的虛函數(shù),主要在繼承關(guān)系中出現(xiàn)。

基本條件
  • 子類重寫父類中的virtual函數(shù)
  • 重寫函數(shù)和被重寫函數(shù)的函數(shù)名和參數(shù)必須一致(具體實(shí)現(xiàn)一般不同)
  • 重寫函數(shù)和被重寫函數(shù)的返回值相同,要么都返回指針,要么都返回引用
  • 重寫函數(shù)和被重寫函數(shù)都是virtual函數(shù)(其中重載函數(shù)可以帶virtual,也可以不帶)
注意
  • 靜態(tài)方法不能被重寫,也就是static和virtual不能同時(shí)使用
  • C++ 11中新增了final關(guān)鍵字,final修飾的虛函數(shù)不能被被重寫

3.重定義(redefining)

注意

也叫隱藏,子類重定義父類中的非虛函數(shù),屏蔽了父類的同名函數(shù)(相當(dāng)于創(chuàng)建了一個(gè)新的函數(shù),跟父類無關(guān))

基本條件
  • 子類和父類函數(shù)的名稱相同,參數(shù)也相同,父類中的函數(shù)不是virtual,父類的函數(shù)將被隱藏
  • 子類和父類的函數(shù)名稱相同,但參數(shù)不同,此時(shí)不管父類函數(shù)是不是virtual函數(shù),都將被隱藏。

4.深入理解C++中的重寫(override)與重定義(redefining)

上面我們對C++中的重寫(override)與重定義(redefining)做了概念上的區(qū)分,只要記住上面的概念特征就嗯呢該區(qū)分這兩種操作,下面我們來從原理上說明下這兩種操作有什么區(qū)別:

重寫(override)

上面我們已經(jīng)說了,C++中重寫的時(shí)候重寫的是父類中的虛函數(shù)(virtual),因此我們需要從虛函數(shù)的工作原理說起。
??由于對于沒有使用virtual的函數(shù),程序?qū)⒏鶕?jù)引用類型或指針類型選擇方法;如果使用了virtual,程序?qū)⒏鶕?jù)引用或指針指向的對象類型來選擇方法,因此對于虛函數(shù),到底使用哪個(gè)函數(shù)(父類或者子類中的函數(shù))是不能再編譯器確定的,因?yàn)榫幾g器不知道指針或者引用指向的是哪種對象,故編譯器需要在程序運(yùn)行時(shí)選擇正確的虛方法的代碼,這被稱為動態(tài)聯(lián)編。為了是動態(tài)聯(lián)編能夠在運(yùn)行時(shí)正確的進(jìn)行決策,必須采用一些跟蹤基類的指針或者引用指向的對象類型。

通常,編譯器處理虛函數(shù)的方法是:給每個(gè)對象添加一個(gè)隱藏成員。隱藏成員中保存了一個(gè)指向虛函數(shù)表的指針。虛函數(shù)表是一個(gè)保存函數(shù)地址的數(shù)組,每一個(gè)地址都對應(yīng)該類中的一個(gè)虛函數(shù)?;悓ο蟀粋€(gè)指針,該指針指向基類中所有虛函數(shù)的地址表。派生類對象將包含一個(gè)指向自己(獨(dú)立)虛函數(shù)表的指針。
??如果派生類重寫(override)了虛函數(shù),則派生類的虛函數(shù)表將保存新函數(shù)的地址。

虛函數(shù)的工作原理.jpg

因此,當(dāng)用指針調(diào)用一個(gè)函數(shù)時(shí),回去指針?biāo)笇ο蟮奶摵瘮?shù)表中去查找被重寫后的虛函數(shù)。

重定義(redefining)

參照隱藏規(guī)則,派生類的成員函數(shù)隱藏了基類的同名函數(shù)。所謂隱藏就是指派生類類型的對象、引用、指針訪問基類和派生類都有的同名函數(shù)的時(shí)候,訪問的是派生類的函數(shù),隱藏了基類同名函數(shù)。派生類既然自動繼承了基類的成員,那么基類成員就可以被派生類直接訪問,那么為什么訪問的是派生類的成員函數(shù)呢?所以隱藏 規(guī)則實(shí)際上就是默認(rèn)的c++名字解析過程。
??在繼承機(jī)制下,派生類的類域被嵌套在基類的類域中,派生類的名字解析過程如下:

  • 1)首先在派生類中查找改名字
  • 2)如果第一步未查找到,及派生類的類域?qū)Ω拿譄o法進(jìn)行解析,則編譯器在外圍基類類域查找改名字的定義

所以準(zhǔn)確來說,當(dāng)派生類和基類有同一名字的成員時(shí),派生類成員是隱藏了對基類成員的直接訪問。那么如果要訪問基類同名成員呢?加上類作用域限定例如:Base::g(float)就可以了

二.Java 方法重載和重寫

1.重載(overload)

Java中的重載和C++中的基本一致,這里再強(qiáng)調(diào)一下:

  • 重載的方法必須具有不同的參數(shù)列表
  • 重載的方法可以有不同的返回值類型
  • 重載的方法可以有不同的訪問修飾符

2.重寫(override)

若子類中聲明的方法與父類中的某一方法具有相同的方法名、返回值類型和參數(shù)列表,則子類中的方法將覆蓋父類中的方法。 如需要調(diào)用父類中原有的方法,可以使用super關(guān)鍵字,該關(guān)鍵字引用了當(dāng)前類的父類

方法重寫的規(guī)則如下:

  • 重寫方法的參數(shù)列表必須與被重寫方法的參數(shù)列表完全相同
  • 重寫方法的返回值類型必須與被重寫方法的返回值類型完全相同
  • 重寫方法的訪問權(quán)限不能比被重寫方法的訪問權(quán)限更嚴(yán)格。比如:如果父類的一個(gè)方法被聲明為public,那么在子類中重寫該方法就不能聲明為protected
  • 聲明為final的方法不能被重寫
  • 聲明為static的方法不能被重寫,但是能夠在子類中再次聲明,父類中的static方法會被隱藏
  • 子類和父類在同一個(gè)包中,那么子類可以重寫父類所有方法,除了聲明為private和final的方法
  • 子類和父類不在同一個(gè)包中,那么子類只能夠重寫父類的聲明為public和protected的非final方法
  • 構(gòu)造方法不能被重寫

3.Java沒有“重定義”這個(gè)概念

三.重寫的意義

重寫伴隨著繼承,他的的意義主要在于實(shí)現(xiàn)多態(tài),用父類的引用來操作子類對象,但在實(shí)際的運(yùn)行中對象將運(yùn)行自己重寫的方法。
??實(shí)際應(yīng)用中,用得最多的一種運(yùn)行時(shí)多態(tài),就是用只知道父類型(可能是類,更多的可能是接口)的定義,這樣只能調(diào)用父類型的方法,繼承該父類并重寫該方法的子類,就會自動執(zhí)行重寫了的方法(這在大型軟件里很常用,父類型和子類型可能是不同的人編寫的,以利于協(xié)作編程)。

public class OverrideTest {
    public static void main (String[] args) {
        Animal h = new Horse();
        h.eat();

        Opertion o = new Horse();
        o.run(30);

    }
}

class Animal {
    public void eat(){
        System.out.println ("Animal is eating.");
    }
}

class Horse extends Animal implements Opertion{
    public void eat(){
        System.out.println ("Horse is eating.");
    }
    public void buck(){
    }
    @Override
    public void run(int time) {
        System.out.println ("Horse is run: "+ time +"s");
    }
}
public interface Opertion {
    void run(int time);
}

輸出為:

Horse is eating.
Horse is run: 30s

在Java中, Animal h = new Horse(); 中引用h指向子類Horse對象,因此其調(diào)用的也是子類Horse的eat方法。但是,如果調(diào)用子類特有的方法,如上例的h.buck(); 會出現(xiàn)編譯錯(cuò)誤,除非強(qiáng)制轉(zhuǎn)換為子類對象((Horse) h).buck();。
??同時(shí)需要注意的是向上轉(zhuǎn)形是自動的,即Animal h = new Horse();父類的引用指向子類對象是可以的,但是子類的引用指向父類對象就是錯(cuò)的,必須顯示的進(jìn)行強(qiáng)制轉(zhuǎn)換。

C++中由于動態(tài)聯(lián)編,指針或者引用指向的虛函數(shù)可能會與上述行為略有不同。

四.Java和C++的一些比較

1.Java和C++中的重寫

可以看到,Java中重寫只要寫一個(gè)和父類函數(shù)同名同參數(shù)的同返回值類型的函數(shù)即可(這在C++中就是“重定義”),不需要抽象函數(shù)(即C艸中的虛函數(shù)),因此Java中的普通函數(shù)就能起到C艸中虛函數(shù)的作用。

2.Java中為什么沒有“重定義”?

上面我們說了,只要是一個(gè)和父類函數(shù)同名同參數(shù)的同返回值類型的函數(shù),不管加沒加“@override”注解,他就是重寫的函數(shù)。此時(shí)這個(gè)重載的函數(shù)就相當(dāng)于C艸中“重定義”的函數(shù)。只不過Java中需要用super關(guān)鍵字來調(diào)用父類中的函數(shù)(需要參數(shù)相同),而C++中需要用作用域解析運(yùn)算符“::”來調(diào)用父類中的方法。

3. Java抽象函數(shù)與C++純虛函數(shù)

Java中沒有虛函數(shù)的概念,因?yàn)镃++的虛函數(shù)是用來讓子類重寫的,然而Java中普通方法就可以被子類重寫,因此沒有必要用虛函數(shù)。
??Java中的抽象函數(shù)相當(dāng)于C艸中的純虛函數(shù),兩者換湯不換藥:

  • C++中純虛函數(shù)形式為:virtual void print() = 0;
  • Java中純虛函數(shù)形式為:abstract void print();

4.Java抽象類與C++抽象類

C++類中只要含有一個(gè)純虛函數(shù),該類就是抽象類
??Java抽象類是用abstract修飾聲明的類,當(dāng)然類中至少要含有一個(gè)抽象方法,不然聲明的抽象類沒有意義

5.Java接口和C++虛基類

接口的存在是為了形成一種規(guī)約,他更多是對“行為”的一種抽象(抽象類是對“屬性”的一種約束)。在三種的例子中我們也定義了一個(gè)接口
??接口中的方法會被隱式地指定為public abstract,接口中的變量會被隱式地指定為public static final變量,并且接口中所有的方法不能有具體的實(shí)現(xiàn),也就是說,接口中的方法必須都是抽象方法。從這里可以隱約看出接口和抽象類的區(qū)別,接口是一種極度抽象的類型,它比抽象類更加“抽象”,并且一般情況下不在接口中定義變量。

  • C++中接口其實(shí)就是全虛基類。
  • Java中接口是用interface修飾的類。

6. 小結(jié)

  • C++虛函數(shù) == Java普通函數(shù)
  • C++純虛函數(shù) == Java抽象函數(shù)
  • C++虛基類 == Java抽象類
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 重載與重寫是Java面向?qū)ο缶幊痰闹匾拍睢?重寫(Override)重寫是子類繼承父類后,對父類允許訪問(子類有...
    冷月的帥哥閱讀 357評論 0 0
  • 1.C和C++的區(qū)別?C++的特性?面向?qū)ο缶幊痰暮锰帲?答:c++在c的基礎(chǔ)上增添類,C是一個(gè)結(jié)構(gòu)化語言,它的重...
    杰倫哎呦哎呦閱讀 9,984評論 0 45
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,619評論 1 32
  • (一)Java部分 1、列舉出JAVA中6個(gè)比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,241評論 0 62
  • 小編費(fèi)力收集:給你想要的面試集合 1.C++或Java中的異常處理機(jī)制的簡單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 5,148評論 1 114

友情鏈接更多精彩內(nèi)容