多態——概念
多態按字面的意思就是多種形態。當類之間存在層次結構,並且類之間是通過繼承關聯時,就會用到多態。
C++ 多態意味着調用成員函數時,會根據調用函數的對象的類型來執行不同的函數。
下面的實例中,基類 Shape 被派生爲兩個類,如下所示:
#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,如下所示:
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
修改後,當編譯和執行前面的實例代碼時,它會產生以下結果:
Rectangle class area
Triangle class area
此時,編譯器看的是指針的內容,而不是它的類型。因此,由於 tri 和 rec 類的對象的地址存儲在 *shape 中,所以會調用各自的 area() 函數。
正如您所看到的,每個子類都有一個函數 area() 的獨立實現。這就是多態的一般使用方式。有了多態,您可以有多個不同的類,都帶有同一個名稱但具有不同實現的函數,函數的參數甚至可以是相同的。
例子二
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "動物在說話" << endl;
}
virtual void eat()
{
cout << "動物在喫飯" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小貓在說話" << endl;
}
virtual void eat()
{
cout << "小貓在喫魚" << endl;
}
};
//調用doSpeak ,speak函數的地址早就綁定好了,早綁定,靜態聯編,編譯階段就確定好了地址
//如果想調用貓的speak,不能提前綁定好函數的地址了,所以需要在運行時候再去確定函數地址
//動態聯編,寫法 doSpeak方法改爲虛函數,在父類上聲明虛函數,發生了多態
// 父類的引用或者指針 指向 子類對象
void doSpeak(Animal & animal) //Animal & animal = cat
{
animal.speak();
}
//如果發生了繼承的關係,編譯器允許進行類型轉換
void test01()
{
Cat cat;
doSpeak(cat);
}
void test02()
{
//cout << sizeof(Animal) << endl;
//父類指針指向子類對象 多態
Animal * animal = new Cat;
//animal->speak();
// *(int*)*(int*)animal 函數地址
((void(*)()) (*(int*)*(int*)animal))();
// *((int*)*(int*)animal+1)貓喫魚的地址
((void(*)()) (*((int*)*(int*)animal + 1)))();
}
int main(){
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
測試
test1
小貓在說話
test2
test2說明了多態的原理
小貓在說話
小貓在喫魚