初識C++之函式過載、重寫、重定義的區別
在C++的學習中,慢慢接觸了一些很容易混淆的名詞,今天就來剖析幾個容易混淆的名詞。
1、函式過載
過載函式是函式的一種特殊情況,為方便使用,C++允許在同一範圍中宣告幾個功能類似的同名函式,但是這些同名函式的形式引數(指引數的個數、型別或者順序)必須不同,也就是說用同一個運算子完成不同的運算功能。這就是過載函式。過載函式常用來實現功能類似而所處理的資料型別不同的問題。
想要構成過載函式必須要滿足以下幾個條件:
①作用域相同;
②函式名相同;
③引數列表不同(引數個數、引數型別不同或者引數順序不同);
下面給出具體的例項:
①引數個數不同
class A
{
public :
void Fun()
{
cout << "Fun()" << endl;
}
void Fun(int i)
{
cout << "Fun(int i)" << endl;
}
private:
int data;
};
②引數型別不同
class A
{
public:
void Fun(int i)
{
cout << "Fun(int i)" << endl;
}
void Fun(double d)
{
cout << "Fun(double d)" << endl;
}
private:
int data;
};
③引數順序不同
class A
{
public:
void Fun(int i, double d)
{
cout << "Fun(int i, double d)" << endl;
}
void Fun(double d, int i)
{
cout << "Fun(double d, int i)" << endl;
}
private:
int data;
};
注意:僅僅返回值不同是無法構成過載的
class A
{
public:
void Fun(int i)
{
cout << "no return" << endl;
}
int Fun(int i)
{
cout << "return i;" << endl;
return i;
}
private:
int data;
};
上面的程式碼編譯會報錯:
上面的例子都放在類中是因為想要表明它們是在同一作用域,當然這個不是必須的,只要滿足在同一作用域即可。
關於函式過載的呼叫機制可以參考:
http://blog.csdn.net/ljx_5489464/article/details/50962363
PS:運算子過載也屬於函式過載。
2、函式重寫
函式重寫其實就是函式覆蓋,當你在派生類中聲明瞭一個與基類函式的函式名、返回值、引數列表完全相同,函式體相同或不同的成員函式時,你就已經將基類函式重寫了,當你在用基類物件或基類物件的指標呼叫該函式時,就是呼叫的派生類的函數了。
想要構成重寫函式必須要滿足以下幾個條件:
①作用域不同(分別處於基類和派生類,也就是它是在繼承關係中才會存在的);
②函式名相同、引數列表、返回值必須相同(協變除外,這個後面會講到);
③基類必須有virtual關鍵字,也就是基類要被重寫的函式必須是虛擬函式;
④訪問修飾符可以不同(也就是是否構成重寫與訪問修飾符無關);
下面給出例項:
①簡單的重寫關係
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Base
{
private:
virtual void Fun(int i)
{
cout << "Base::Fun()" << endl;
}
private:
int data;
};
class Derived : public Base
{
public:
void Fun(int i)
{
cout << "Derived::Fun()" << endl;
}
private:
int data;
};
int main()
{
Derived d;
d.Fun(1);
return 0;
}
可以看到在派生類中,對基類的Fun函式進行了重寫,所以這兒會呼叫派生類的Fun函式。
②特殊的重寫關係–>協變
在基類中有個虛擬函式返回基類的指標,在派生類中有個和基類同名虛擬函式返回派生類的指標,這種情況稱為協變
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Base
{
public:
virtual Base* Fun()
{
cout << "Base:" << this << endl ;
return this;
}
private:
int data;
};
class Derived: public Base
{
public:
virtual Derived* Fun()
{
cout << "Derived:" << this << endl;
return this;
}
private:
int data;
};
int main()
{
Base b;
Derived d;
Base *pb = b.Fun();
Derived *pd = d.Fun();
cout << "pb = " << pb << endl;
cout << "pd = " << pd << endl;
return 0;
}
3、函式重定義
這裡的重定義跟我們平時遇到的錯誤列表中的重定義不一樣,平時我們的我們遇到的錯誤列表裡的重定義是指在同一作用域裡定義了函式名相同、返回值相同、引數列表相同的函式(這樣肯定會報錯啊,因為我們在呼叫函式時,編譯器不知道該為我們呼叫哪一個行函式,會產生二義性),我們今天要講的函式重定義,是一種合法的重定義,它是在不同的作用域裡的函式因為某種機制產生的原因。
想要構成重寫函式必須要滿足以下幾個條件:
①作用域不同(分別處於基類和派生類,也就是它是在繼承關係中才會存在的);
②函式名相同
③沒有構成重寫
其實簡單講,只要滿足前兩條,並且沒有構成重寫,就構成了函式重定義(感覺有點說廢話的感覺)。
還是上例項:
①只是函式體不同
class Base
{
private:
void Fun(int i)
{
cout << "Base::Fun()" << endl;
}
private:
int data;
};
class Derived : public Base
{
public:
void Fun(int i)
{
cout << "Derived::Fun()" << endl;
}
private:
int data;
};
②函式引數列表不同
⑴
class Base
{
private:
void Fun()
{
cout << "Base::Fun()" << endl;
}
private:
int data;
};
class Derived : public Base
{
public:
void Fun(int i)
{
cout << "Derived::Fun()" << endl;
}
private:
int data;
};
⑵
class Base
{
private:
virtual void Fun() //即使有virtual關鍵字,也不能構成重寫,因為另個函式的引數列表不同,所以同樣是重定義
{
cout << "Base::Fun()" << endl;
}
private:
int data;
};
class Derived : public Base
{
public:
void Fun(int i)
{
cout << "Derived::Fun()" << endl;
}
private:
int data;
};
這裡都只是列舉了一些簡單的常見的例子,可以舉一反三。
PS:過載與覆蓋
這裡的過載與覆蓋是說派生類的函式與繼承於基類的同名函式的關係。本來區分過載與覆蓋並不算困難,但是C++的隱藏規則使問題複雜性陡然增加。這裡“隱藏”是指派生類的函式遮蔽了與其同名的基類函式,規則如下:
(1)如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual 關鍵字,基類的函式將被隱藏(注意別與過載混淆)。
(2)如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)。