天天看點

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;
}
           

輸出:

C++ 重載、重寫(覆寫)、重定義(隐藏) 與 Java 重載、重寫(覆寫)、隐藏的差別

三、重定義(也成隐藏)

(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;
}
           

輸出:

C++ 重載、重寫(覆寫)、重定義(隐藏) 與 Java 重載、重寫(覆寫)、隐藏的差別

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");
    }

}
           

輸出:

C++ 重載、重寫(覆寫)、重定義(隐藏) 與 Java 重載、重寫(覆寫)、隐藏的差別

參考文章:

https://www.cnblogs.com/tanky_woo/archive/2012/02/08/2343203.html

https://blog.csdn.net/snow_7/article/details/51579278