1. 程式人生 > >C++---過載、重寫(覆蓋)、重定義(隱藏)

C++---過載、重寫(覆蓋)、重定義(隱藏)

我們經常會被過載、重寫、重定義經常被我搞混,今天就專門總結區別一下。

1.過載

1.過載概念: 過載指的都是函式過載,函式過載就是函式的一種特殊情況,C++允許在同一作用域中宣告幾個功能類似的同名函式,這些同名函式的形參列 表(引數個數、型別、順序)必須不同,常用來處理實現功能類似資料型別不同的問題。
2.構成過載的條件: 函式名相同,引數列表必須不同(引數個數,型別,順序),返回型別可以相同,也可以不相同。
3.用法: 通過傳入不同的引數(個數,型別,順序),來呼叫不同的函式。
4. C++中函式過載是一種靜態繫結(早繫結),也就是在程式編譯期間確定了程式的行為,在這期間,編譯器會根據所傳遞的引數型別,個數,順序來確定具體應該呼叫哪個函式,如果有對應的函式就呼叫,否則會發生隱式型別轉換或報錯。
我們拿個例子來介紹;

int Add(int left, int right)
{
	return left + right;
}
double Add(double left, double right)
{
	return left + right;
}
long Add(long left, long right)
{
	return left + right;
}
int main()
{
	Add(10, 20);
	Add(10.0, 20.0);
	Add(10L, 20L);

	return 0;
}

程式碼中函式的名字相同引數列表不同,就構成了函式過載,在呼叫時,編譯器會相應的呼叫對應的函式。

重寫(覆蓋)

1.重寫概念: 重寫是針對基類中虛擬函式的。在派生類中實現一個與基類虛擬函式原型(返回值型別、函式名字、引數列表)相同的虛擬函式,即派生類與基類中虛擬函式的原型完全相同,這樣就構成了對基類虛擬函式的重寫。
2.構成重寫的條件: 父類函式必須為虛擬函式,並且子類的虛擬函式和父類虛擬函式原型完全相同(返回值是型別,函式名,引數列表)。如果子類的虛擬函式和父類的虛擬函式原型完全相同,此時子類的虛擬函式前面的virtual關鍵字是可以省略的,這樣的話編譯器會預設加上,但是最好加上。
3.特例: 我們上面說的只是普通的重寫,重寫還有兩個特例,這兩種情況也會構成重寫。
a.協變: 基類中虛擬函式返回基類物件的指標或引用,派生類與基類同名虛擬函式返回派生類物件的指標或引用,此種情況也構成重寫,但是此時派生類與基類虛擬函式返回值型別不同。
b.解構函式 :

基類中的解構函式如果是虛擬函式,只要派生類的解構函式顯式提供,就構成重寫,此種情況派生類與基類虛擬函式函式名字不同。

舉個例子:

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "買票全價" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "買票半價" << endl;
	}
};

void Func(Person & p)
{
	p.BuyTicket();
}
void Test()
{
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
}

在這裡插入圖片描述
傳入不同的物件,會呼叫相應的購票函式。函式重寫就是構成多型的一個條件。
構成多型有兩個條件:

  1. 基類中必須包含虛擬函式,並且派生類一定要對基類中的虛擬函式進行重寫
  2. 通過基類物件的指標或者引用呼叫虛擬函式
    上面的程式碼中重寫函式就構成了多型,此時只需要在Func函式傳入不同的物件,就會呼叫不同的買票函式,從而實現了多型。

關於為什麼傳入不同的物件會呼叫不同的函式?
其原因在於多型呼叫是一種動態繫結,也就是說在程式執行期間,根據具體拿到的型別確定程式的行為,從而呼叫相應的函式。多型呼叫是和物件有關的。而上面所說的靜態繫結則是和型別有關。
關於多型呼叫的底層實現原理在我之前部落格講過:—>多型底層呼叫實現原理

重定義(隱藏)

1.概念: 子類和父類中有同名成員(成員變數,成員函式),子類成員將遮蔽父類對同名成員的直接訪問。也就是說隱藏唯一 一個即針對於變數又針對於函式。
2.訪問規則: 在子類成員函式中,可以使用 (基類::基類成員) 來訪問,也就是要加作用域限定符( :: )。
3.隱藏的結果: 因為子類繼承了父類,所以此時子類就擁有了兩個同名函式,這兩個函式不一定具有相同的功能,此時不管是在子類內部還是外部通過子類的物件呼叫該同名成員時,呼叫的都是子類本來具有的同名成員,若要通過子類的物件呼叫父類的同名成員,則需要在同名成員前加上作用域限定符。隱藏會導致呼叫成員時呼叫不到自己想要的成員,所以在實際中在繼承體系裡面最好不要定義同名的成員。
4.注意: 在父類和子類這兩個作用域中,不能構成重寫的都是重定義(隱藏)。