多態的基本概念
多態性是一個接口多種實現,分為類的多態性和函數多態性。
函數的多態性(重載)是指一個函數被定義成多個不同參數的函數。
類的多態性用一句話概括就是:
在基類的函數前加上virtual關鍵字(即虛函數),在派生類中重寫該函數,
運行時將會根據對象的實際類型來調用相應的函數。如果對象類型
是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數。
以下是針對函數的多態的一個簡單的實現:
#include<iostream>
using namespace std;
float Sum(float a, float b)
{
return a + b;
}
int Sum(int a, int b)
{
return a + b;
}
int main()
{
int var1, var2;
float var3, var4;
cin >> var1 >> var2;
cin >> var3 >> var4;
cout << Sum(var1, var2) << endl << Sum(var3, var4) << endl;
system("pause");
return 0;
}
下面是程序的運行結果:

從程序代碼中可以看出,定義了兩個同名的函數Sum,他們的不同之處在於函數參數類型以及返回值類型的不同。
在對程序進行編譯時,編譯器並沒有報錯,並且程序的運行結果也是我們所期待的。因為在此程序中使用了函數
多態(重載)這一特性,這樣編譯器在對代碼進行編譯的過程中就會通過函數參數的不同來進行同名函數的選擇調用,
選擇出最合適的函數類型。這就是函數多態的方便之處。
我們來看一下下面這段代碼:
#include<iostream>
using namespace std;
class Mammal {
public:
void speak() {
cout << " Mammal::speak " << endl;
}
};
class Dog :public Mammal {
public:
void speak()
{
cout << " Dog::speak " << endl;
}
};
int main()
{
Dog a;
a.speak();
Mammal *b = &a;
b->speak();
system("pause");
return 0;
}
此程序運行結果:

以上代碼中,定義了一個基類Mammal,在由其派生出了類Dog,二者都有函數speak(),我們
在main函數里面首先調用了Dog類的speak函數,然后通過將Dog類的a的地址賦給Mammal類b,
我們並想以這種方式來通過基類來調用派生類的函數,但是從程序運行的結果來看,這種方式
並不可取。
那若想得到我們所期待的結果,此時就需要用到類的多態性。我們只需將
基類中的speak函數聲明為虛函數,即加上
virtual
關鍵字:class Mammal {
public:
virtual void speak() {
cout << " Mammal::speak " << endl;
}
};
此時再將程序運行:

這樣就得到了我們所想要的結果。
在類的多態性里面還有純虛函數,含有純虛函數的類也稱之為抽象類:
純虛函數的使用:
1,當想在基類中抽象出一個方法,且該基類只做能被繼承,而不能被實例化;
2,這個方法必須在派生類(derived class)中被實現;
構造純虛函數只需要在虛函數定義是在右邊加上
“= 0”
的格式就可以。下面是利用類的繼承來計算矩形和圓的面積與周長的程序(純虛函數):
#include<iostream>
using namespace std;
#define PI 3.1415926
class Shape {
public:
virtual double getArea() = 0;
virtual double getPerim() = 0;
};
class Rectangle :public Shape {
public:
virtual double getArea();
virtual double getPerim();
Rectangle(double a = 1, double b = 1) :r1(a), r2(b) {
}
private:
double r1;
double r2;
};
double Rectangle::getArea() {
return r1 * r2;
}
double Rectangle::getPerim() {
return 2 * (r1 + r2);
}
class Circle : public Shape {
public:
virtual double getArea();
virtual double getPerim();
Circle(double a) :r(a) {
}
private:
double r;
};
double Circle::getArea()
{
return PI * r*r;
}
double Circle::getPerim()
{
return 2 * PI*r;
}
int main()
{
Rectangle a(1.2, 3.4);
Circle b(3);
cout << "Rectangle的面積:" << a.getArea() << endl;
cout << "Rectangle的周長:" << a.getPerim() << endl;
cout << "Circle的面積:" << b.getArea() << endl;
cout << "Circle的周長:" << b.getPerim() << endl;
system("pause");
return 0;
}
因為矩形與圓的周長與面積的計算方式不同,所以在這里可以將基類定義為抽象類。在此
程序中定義了一個抽象類Shape,在此基礎上派生出類Retangle和Circle。這兩個派生類
都繼承了抽象類里面的純虛函數getArea(),getPerim()二者分別為計算面積以及周長的函數。
因為在派生類中純虛函數不能被直接繼承,所以在兩個類中,純虛函數都被重新定義。
下面是程序的運行結果:

在這個程序中純虛函數的優越性就直接體現出來了。
函數運算符的重載:
運算符的重載可以通過兩種方式來實現:
1、將函數定義為類的成員函數
2、將函數定義為非成員函數(類的友元函數)
運算符重載規則:
1、如果一個運算符函數是成員函數,則它的第一個(左側)運算對象綁定到隱式的this指針上,
因此,成員運算符函數的(顯式)參數數量比運算符的運算對象總少一個。
2、當運算符函數是非成員函數時,函數的參數與該運算符作用的運算對象數量一樣多。
為了區分運算符到底是前置還是后置運算符,后置版本接受一個額外的(不被使用)int類型的形參。 使用后置運算符時,編譯器為這個形參提供一個值為0的實參。這個形參的唯一作用就是區分前置 版本和后置版本的函數。
“++”運算符通過友元函數來實現:
#include<iostream>
using namespace std;
class Point {
public:
friend Point operator ++(Point &a);//前++
friend Point operator ++(Point &a, int);//后++
Point(int a = 0) :x(a) {
}
void DisPlay()
{
cout << "私有成員 x = " << x << endl;
}
private:
int x;
};
Point operator++(Point &a)
{
a.x = a.x + 1;
return a;
}
Point operator ++(Point &a, int)
{
Point b = a;
a.x = a.x + 1;
return b;
}
int main()
{
Point a(10);
a.DisPlay();
Point b = a++;
b.DisPlay();
a.DisPlay();
++a;
a.DisPlay();
system("pause");
return 0;
}
下面是程序的運行結果:

從程序運行結果來看,根據函數參數的不同,編譯器將重載函數“++”分為了前加加和后加加兩個不同的函數。
再看下面這段代碼(虛析構函數):
#include<iostream>
using namespace std;
class BaseClass {
public:
BaseClass()
{
cout << "--BaseClass的構造函數被調用--" << endl;
}
virtual ~BaseClass()
{
cout << "--BaseClass的虛析構函數被調用--" << endl;
}
};
class DerivedClass:public BaseClass {
public:
DerivedClass()
{
cout << "--DerivedClass的構造函數被調用--" << endl;
}
~DerivedClass()
{
cout << "--DerivedClass的析構函數被調用--" << endl;
}
};
void Delete(BaseClass *a)
{
delete a;
}
int main()
{
BaseClass *a = new DerivedClass;
Delete(a);
system("pause");
return 0;
}
在此程序中定義了一個基類BaseClass,從它派生出了類DerivedClass。基類中的析構函數被聲明為了
虛析構函數,在主函數中將一個動態分配的DerivedClass的對象地址賦給一個BaseClass的指針,然后
通過指針釋放對象空間。
下面是代碼的運行結果:

