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
}
结果:
如果还不懂,可以参考以下两篇文章: