在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關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。