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