C++多態性是通過虛函數來實現的,虛函數允許子類重新定義成員函數,而子類重新定義父類的做法稱爲覆蓋(override),或者稱爲重寫。
重寫:子類重定義基類中的virtual函數,通過子類指針或引用指向子類對象時,調用的是子類中的的函數。
重載:一個類內部的具有不同形參的函數構成重載,不涉及基類和子類。
隱藏:這裏“隱藏”是指派生類的函數屏蔽了與其同名的基類函數或成員變量,
對於基類子類同名函數來說,只要不是virtual重寫,其他的子類同名函數都將隱藏基類的同名函數,即:
(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual
關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
(2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual
關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。
下面貼一段代碼:
#include <iostream>
#include <stdio.h>
using namespace std;
class TestA{
public:
TestA():mCount(0){}
virtual ~TestA() {printf("release A\n");}
public:
virtual void init() {printf("this is A init\n");mCount = 5/10;}
virtual void increment() {printf("this is A++\n"); mCount++;}
virtual void print() {printf("this is A, count = %d\n",mCount);}
private:
int mCount;
};
class TestB : public TestA{
public:
TestB():mCount(0){}
virtual ~TestB() {printf("release B\n");}
public:
virtual void init() {printf("this is B init\n");mCount = 10;}
virtual void print() {printf("this is B, count = %d\n",mCount);}
private:
int mCount;
};
int main(void)
{
TestB *pB = new TestB();
pB->init(); // mCount = 10
pB->increment();//重點在這裏,TestB中並沒重寫虛函數increment ,調用了TestA中
//函數,並且使TestA::mCount數據成員自增+1, 然後再調用TestB中的打印
// TestB::mCount 爲10,並不是11, 這裏要分清楚是哪一個數據成員
pB->print(); //打印的是TestB中的mCount 10
TestA *pA = (TestA*)pB;// 這裏涉及到多態,當用基類的指針指向派生類對象時,派生類中有虛函數,則會有一個虛指針,
//在分配的TestB內存塊的最開頭的前四個字節,指向TestB中存在的虛函數首地址,
//可以在內存中用看pB的值,然後看內存,前四個字節爲虛指針,
//所以pA指向了pB的虛指針所在內存塊的地址,因此這裏和上面情況其實是一樣的
pA->init();//TestB::mCount爲10
pA->increment();//TestA::mCount + 1
pA->print();//打印的是TestB中的mCount 10
TestA aA = *pB;//此處將*pB中已經對TeseA中的數據成員初始化了,並且將該值給了aA中的數據成員
//下面都是調用TestA中的函數
aA.print();//這裏TestA::mCount爲2
aA.init(); //TestA::mCount又被重置0
aA.increment();//TestA::mCount + 1
aA.print();//TestA::mCount 爲 1
delete pB;// pB爲派生類對象指針,且TestA和TestB的析構均爲虛析構,
// 會先調用B的析構,然後A的析構, 最後應用程序結束時,調用aA的析構函數
// 即Release B ReleaseA ReleaseA
}
結果:
如果還不懂,可以參考以下兩篇文章: