1. 程式人生 > >C++ 過載、重寫(覆蓋)、重定義(隱藏) 與 Java 過載、重寫(覆蓋)、隱藏的區別

C++ 過載、重寫(覆蓋)、重定義(隱藏) 與 Java 過載、重寫(覆蓋)、隱藏的區別

C++:
一、過載(overload)
指函式名相同,但是它的引數表列個數或順序,型別不同。但是不能靠返回型別來判斷。
(1)相同的範圍(在同一個作用域中) ;
(2)函式名字相同;
(3)引數不同;
(4)virtual 關鍵字可有可無。
(5)返回值可以不同;

二、重寫(也稱為覆蓋 override)
是指派生類重新定義基類的虛擬函式,特徵是:
(1)不在同一個作用域(分別位於派生類與基類) ;
(2)函式名字相同;
(3)引數相同;
(4)基類函式必須有 virtual 關鍵字,不能有 static 。
(5)返回值相同(或是協變),否則報錯;<—只要原來的返回型別是指向基類的指標或引用,新的返回型別是指向派生類的指標或引用,覆蓋的方法就可以改變返回型別。這樣的型別稱為協變返回型別。


(6)重寫函式的訪問修飾符可以不同。儘管 virtual 是 public 的,派生類中重寫改寫為private,protected 也是可以的

例:

#include <iostream>
using namespace std;
class A {
public:
       virtual void fun3(int i) {
              cout << "A::fun3() : " << i << endl;
       }
};
class B : public A {
private: // 重寫函式的訪問修飾符可以不同 
// 重寫/覆蓋 virtual void fun3(int i) { cout << "B::fun3() : " << i << endl; } }; int main() { A a; B b; A * pa = &a; pa->fun3(3); pa = &b; pa->fun3(5); return 0; }

輸出:
在這裡插入圖片描述

三、重定義(也成隱藏)


(1)不在同一個作用域(分別位於派生類與基類) ;
(2)函式名字相同;
(3)返回值可以不同;
(4)**引數不同。**此時,不論有無 virtual 關鍵字,基類的函式將被隱藏(注意別與過載以及覆蓋混淆) 。
(5)**引數相同,但是基類函式沒有 virtual關鍵字。**此時,基類的函式被隱藏(注意別與覆蓋混淆) 。
例:

#include <iostream>
using namespace std;
class A {
public:
       virtual void fun3(double i) {
              cout << "A::fun3() : " << i << endl;
       }
};
class B : public A {
public:
       // 隱藏/重定義
       virtual void fun3(int i) {
              cout << "B::fun3() : " << i << endl;
       }
};
int main()
{
       A a;
       B b;
       b.fun3(1.68);
       
       A * pa = &a;
       pa->fun3(3);
       pa = &b;
       pa->fun3(5);
       
       return 0;
}

輸出:
在這裡插入圖片描述

Java:
過載: 相同方法名,不同簽名,可以在同一個類中,也可以發生在由於繼承而相關的不同類中
例:

public class Main {
    public static void main(String[] args) {
        // write your code here

        A a = new A();
        a.p(10);
        a.p(10.0);
    }
}

class B {
    public void p(double i) {
        System.out.println(i * 2);
    }
}

class A extends B {
    // 過載
    public void p(int i) {
        System.out.println(i);
    }
}

輸出:

10
20.0

重寫/覆蓋: 相同的簽名,相同的返回值型別或者返回值型別協變(Java5.0以後才支援返回值協變),不同類中(即派生類和基類)中。
例:

public class Main {
    public static void main(String[] args) {
        // write your code here

        A a = new A();
        a.p(10);
        a.p(10.0);
    }
}

class B {
    public void p(double i) {
        System.out.println(i * 2);
    }
}

class A extends B {
    // 重寫/覆蓋
    public void p(double i) { // 如public int p(double i),因為返回值不同又不是協變,所以將報錯!
        System.out.println(i);
    }
}

輸出:

10.0
10.0

重寫/覆蓋: 子類重寫父類的方法,要求方法名和引數型別完全一樣(引數不能是子類),返回值和異常比父類小(也叫協變,即為父類的子類)或者相同,訪問修飾符比父類大或者相同。

覆蓋是對於例項方法而言的

方法不能交叉覆蓋:子類例項方法不能覆蓋父類的靜態方法

              子類的靜態方法也不能覆蓋父類的例項方法(編譯時報錯)

隱藏: 父類和子類擁有相同名字的屬性(成員變數)或者方法( 方法隱藏只有一種形式,就是父類和子類存在相同的靜態方法)時,父類的同名的屬性或者方法形式上不見了,實際是還是存在的。

隱藏是對於靜態方法和成員變數(包括靜態變數和例項變數) 而言的

(1)當發生隱藏的時候,宣告型別是什麼類,就呼叫對應類的屬性或者方法,而不會發生動態繫結

(2) 屬性只能被隱藏,不能被覆蓋

(3)變數可以交叉隱藏:子類例項變數/靜態變數可以隱藏父類的例項/靜態變數

隱藏和覆蓋的區別

(1)被隱藏的屬性,在子類被強制轉換成父類後,訪問的是父類中的屬性

在無強制轉換時子類要訪問父類的屬性使用super關鍵字

(2)被覆蓋的方法,在子類被強制轉換成父類後,呼叫的還是子類自身的方法

 子類要是想訪問父類的方法,可以使用super關鍵字

RTTI(run time type identification,執行時型別檢查)

RTTI只針對覆蓋,不針對隱藏:因為覆蓋是動態繫結,是受RTTI約束的,隱藏不受RTTI約束

執行時型別為引用變數所指向的物件的型別,編譯時型別是引用變數自身的型別。
例:

public class Main {
    public static void main(String[] args) {
        // write your code here

        Circle circle = new Circle();//本類引用指向本類物件
        Shape shape = new Circle();//父類引用指向子類物件(會有隱藏和覆蓋)

        System.out.println(circle.name);
        circle.printType();
        circle.printName();
        //以上都是呼叫Circle類的方法和引用

        System.out.println(shape.name);//呼叫父類被隱藏的name屬性
        shape.printType();//呼叫子類printType的方法
        shape.printName();//呼叫父類隱藏的printName方法
    }

}

class Shape {
    public String name = "shape";

    public Shape(){
        System.out.println("shape constructor");
    }

    public void printType() {
        System.out.println("this is shape");
    }

    public static void printName() {
        System.out.println("shape");
    }
}

class Circle extends Shape {
    public String name = "circle"; //父類屬性被隱藏

    public Circle() {
        System.out.println("circle constructor");
    }

    //對父類例項方法的覆蓋
    public void printType() {
        System.out.println("this is circle");
    }

    //對父類靜態方法的隱藏
    public static void printName() {
        System.out.println("circle");
    }

}

輸出:
在這裡插入圖片描述

參考文章:
https://www.cnblogs.com/tanky_woo/archive/2012/02/08/2343203.html
https://blog.csdn.net/snow_7/article/details/51579278