重載與多態
重載與多態與重寫的區別:
重載 :
函數名相同,但是函數的參數不同,調用時根據參數的不同決定調用哪一個函數。
多態 :
函數名相同,函數形參相同。調用時根據函數類型時虛函數還是普通函數決定調用哪一個。
重寫 :
若子類和父類的某個函數具有相同的函數名,相同的形參列表,且父類中的函數被定義為虛函數,則子類對該函數的實現被稱為函數的重寫。
caution!
若函數不聲明為虛函數,只能通過類名限定名的方式訪問父類或者子類的方法。這叫做隱藏。
若函數聲明為虛函數,則可以通過指針訪問每一個方法,這叫做覆蓋。
接下來,讓我們分別對以上的概念進行實現:
一.重載
1.函數重載
在同一個作用域內,可以聲明幾個功能類似的同名函數,但是這些同名函數的形式參數(指參數的個數、類型或者順序)必須不同。您不能僅通過返回類型的不同來重載函數。
如下例子:
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整數為: " << i << endl;
}
void print(double f) {
cout << "浮點數為: " << f << endl;
}
void print(char c[]) {
cout << "字符串為: " << c << endl;
}
};
int main()
{
printData pd;
// 輸出整數
pd.print(5);
// 輸出浮點數
pd.print(500.263);
// 輸出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
運行結果為:
整數為: 5
浮點數為: 500.263
字符串為: Hello C++
2.運算符重載
重載的運算符是帶有特殊名稱的函數,函數名是由關鍵字 operator 和其后要重載的運算符符號構成的。與其他函數一樣,重載運算符有一個返回類型和一個參數列表。
#include<iostream>
using namespace std;
class Complex
{
public:
Complex(int real1=0,int xu1=0):real(real1),xu(xu1){};
void show()const;
friend Complex &operator++(Complex &a);// 返回一個引用即代表當前對象本身,使用引用類型,可以減少內存使用,比如使用返回引用類型,那么它依然還是當前對象,並沒有重新構造一個對象。
friend Complex operator++(Complex &b,int);
private:
int real,xu;
};
Complex &operator++(Complex &a)
{
a.real++;
a.xu++;
return a;// 返回當前對象(立即可體現出來前置++的值)
}
Complex operator++(Complex &b,int)
{
Complex old=b;
++b;
return old;
}
void Complex::show()const
{
cout<<"real=="<<real<<endl;
cout<<"xu="<<xu<<endl;
}
void main()
{
Complex c1,c2,c3;
++c1;
c3=c2++;
c1.show();
c3.show();
c2.show();
}
如果定義為類中的函數
#include<iostream>
using namespace std;
class Complex
{
public:
Complex(int real1=0,int xu1=0):real(real1),xu(xu1){};
void show()const;
Complex &operator++();//后置++,無參數
Complex operator++(int);//前置++,只有一個參數
private:
int real,xu;
};
Complex & Complex::operator++()
{
this->real++;
this->xu++;
return *this;
}
Complex Complex::operator++(int)
{
Complex old=*this;
++(*this);
return old;
}
void Complex::show()const
{
cout<<"real=="<<real<<endl;
cout<<"xu="<<xu<<endl;
}
void main()
{
Complex c1,c2,c3;
++c1;
c3=c2++;
c1.show();
c3.show();
c2.show();
}
運行結果均為:
real==1
xu=1
real==0
xu=0
real==1
xu=1
caution!
《C專家編程》中有如下描述(P276,人民郵電出版社):
++a表示取a的地址,增加它的內容,然后把值放在寄存器中;
a++表示取a的地址,把它的值裝入寄存器,然后增加內存中的a的值;
二.多態
1.靜態多態
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函數
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存儲矩形的地址
shape = &rec;
// 調用矩形的求面積函數 area
shape->area();
// 存儲三角形的地址
shape = &tri;
// 調用三角形的求面積函數 area
shape->area();
return 0;
}
運行結果為:
Parent class area
Parent class area
我們沒有看到我們想要的結果。
導致錯誤輸出的原因是,調用函數 area() 被編譯器設置為基類中的版本,這就是所謂的靜態多態,或靜態鏈接 - 函數調用在程序執行前就准備好了。有時候這也被稱為早綁定,因為 area() 函數在程序編譯期間就已經設置好了。
現在我們在Shape函數area前加virtual。此時,編譯器看的是指針的內容,而不是它的類型。因此,由於 tri 和 rec 類的對象的地址存儲在 *shape 中,所以會調用各自的 area() 函數。
正如所看到的,每個子類都有一個函數 area() 的獨立實現。這就是多態的一般使用方式。有了多態,可以有多個不同的類,都帶有同一個名稱但具有不同實現的函數,函數的參數甚至可以是相同的。這類函數就叫虛函數。
虛函數 是在基類中使用關鍵字 virtual 聲明的函數。在派生類中重新定義基類中定義的虛函數時,會告訴編譯器不要靜態鏈接到該函數。
我們想要的是在程序中任意點可以根據所調用的對象類型來選擇調用的函數,這種操作被稱為動態鏈接,或后期綁定。