什麼是多態
父類引用或者指針指向子類對象。
編譯時多態(靜態連編):函數和運算符重載。編譯階段就確定了地址。
運行時多態(動態連編):通常說的多態。
多態的使用
使用條件:
- 父類中函數爲虛函數
- 子類繼承父類並重寫該虛函數,子類virtual可寫可不寫
- 父類指針或引用指向子類對象,然後調用方法。
靜態連編
#include <iostream>
using namespace std;
class Animal
{
public:
void speak()
{
cout << "動物" << endl;
}
};
class Cat : public Animal
{
public:
//子類重寫父類方法
void speak() //子類中virtual可寫可不寫
{
cout << "小貓" << endl;
}
};
void doSpeak(Animal &animal){
animal.speak();
}
int main()
{
Cat cat;//創建cat對象並傳入方法,cat是animal的子類,所以可以認爲cat是animal的一種類型
//所以可以把cat傳進去
doSpeak(cat);//動物 但是這裏調用的還是父類的方法,因爲沒有產生多態,是靜態編譯,只有用virtual才能產生多態
Animal &a1 = cat1;
a1.speak();//動物
Animal *a2 = new Cat;
a2->speak();//動物
}
動態連編
#include <iostream>
using namespace std;
class Animal
{
public:
void virtual speak()
{
cout << "動物" << endl;
}
};
class Cat : public Animal
{
public:
//子類重寫父類方法
void speak()
{
cout << "小貓" << endl;
}
};
void doSpeak(Animal &animal){
animal.speak();
}
int main()
{
Cat cat1;
doSpeak(cat1);//小貓 使用了virtual所以爲動態連編,爲多態
Animal &a1 = cat1;
a1.speak();//小貓
Animal *a2 = new Cat;
a2->speak();//小貓
}
多態的好處
Animal *a2 = new Cat;
a2->speak();//小貓
比如這句話,我們可以根據我們的需要來創建不同對象,我們可以創建小貓,小狗,小豬,然後把它給animal。然後animal調用speak就可以實現多態,也就是根據創建的不同對象調用不同對象中的speak方法。“一個接口,多種方法”,程序在運行時才決定調用的函數。接口重用。
代碼的原則:修改關閉,擴展開放。
計算器例子
#include <iostream>
using namespace std;
class Calculator
{
public:
int v1;
int v2;
void setv1(int v)
{
v1 = v;
}
void setv2(int v)
{
v2 = v;
}
int virtual getResult()
{
return v1 + v2;
}
};
class PlusCalculator : public Calculator
{
int virtual getResult()
{
return v1 + v2;
}
};
class SubCalculator : public Calculator
{
int virtual getResult()
{
return v1 - v2;
}
};
int main()
{
Calculator *calculator;
//使用加法計算器
calculator = new PlusCalculator;
calculator->setv1(10);
calculator->setv2(20);
cout << calculator->getResult() << endl; //30
delete calculator; //清除
//使用減法計算器
calculator = new SubCalculator;
calculator->setv1(10);
calculator->setv2(20);
cout << calculator->getResult() << endl;
}
如果我們需要繼續擴展添加乘法計算器,就直接添加,使用的方式和上面一樣,擴展非常方便。這就是多態的好處。結構性好,可讀性高。
C++比C語言效率低,其實就是在多態上,因爲內部結構複雜了,就比c語言低一點點。
純虛函數和抽象類
接着上面的例子,我們可以把父類的函數作爲一個純虛函數。作用和上面一樣實現多態。
- 當一個類有純虛函數時,這個類就爲抽象類,不能實例化。
- 子類必須重寫實現所有純虛函數,不然子類也是一個抽象類,不能實例化。
class Calculator
{
public:
int v1;
int v2;
void setv1(int v)
{
v1 = v;
}
void setv2(int v)
{
v2 = v;
}
//純虛函數:子類必須重寫此函數
//這個類爲抽象類,該類無法實例化;
int virtual getResult() = 0;
};
虛析構和純虛析構
當我們繼承的時候,父類指針指向子類對象,然後釋放父類,則只創建了子類對象,而沒有釋放子類對象。這樣會導致釋放不乾淨內存問題。
#include <iostream>
using namespace std;
class Calculator
{
public:
Calculator()
{
cout << "計算器構造" << endl;
}
~Calculator()
{
cout << "計算器析構" << endl;
}
};
class PlusCalculator : public Calculator
{
public:
PlusCalculator()
{
cout << "加法計算器構造" << endl;
}
~PlusCalculator()
{
cout << "加法計算器析構" << endl;
}
};
int main()
{
Calculator *calculator;
//使用加法計算器
calculator = new PlusCalculator;
delete calculator;
}
/*
計算器構造
加法計算器構造
計算器析構
*/
解決問題:使用虛析構
#include <iostream>
using namespace std;
class Calculator
{
public:
Calculator()
{
cout << "計算器構造" << endl;
}
virtual ~Calculator()
{
cout << "計算器析構" << endl;
}
};
class PlusCalculator : public Calculator
{
public:
PlusCalculator()
{
cout << "加法計算器構造" << endl;
}
virtual ~PlusCalculator()
{
cout << "加法計算器析構" << endl;
}
};
int main()
{
Calculator *calculator;
//使用加法計算器
calculator = new PlusCalculator;
delete calculator;
}
/*
計算器構造
加法計算器構造
加法計算器析構
計算器析構
*/
虛析構也可以定義爲純虛析構,但是需要類內聲明,類外必須實現,如果析構函數爲純虛析構,則該類爲抽象類,不能實例化。
也就是說如果析構函數是純虛析構則該類肯定爲抽象類。
向上轉型和向下轉型
向下轉型
父類轉爲子類,叫做向下轉型,是不安全的。
因爲子類東西多,父類東西少,父類轉爲子類是不安全的。
Animal *animal = new Animal;
Cat *cat = (Cat*) animal; // 把父類轉爲子類
向上轉型
子類轉爲父類,是安全的,向上轉型,父類東西少,是安全的。
Cat *cat = new Cat;
Animal *animal = (Animal*) cat; // 把父類轉爲子類
發生了多態
如果發生類多態,則向下轉換也是安全的。
Animal *animal = new Cat;
Cat *cat = (Cat*) animal;