多种状态,多种形态,同一个东西,在不同的环境下,会呈现出不同的状态。具体点就是去完成某个行为,当不同的对象完成时会产生不同的形状。
多态的实现
实现多条的3个条件
1、继承,多个子类一个父类。
2、父类有虚函数,子类重写父类的虚函数。
3、父类指针指向子类对象(一个函数,函数的形参是父类的指针,实参是子类对象的地址)。
虚函数:就是函数名前加virtual
多态实现的原理
虚函数:就是函数名前加virtual
多态实现的原理
现在我们想知道:为什么父类指针指向子类对象(只能访问到子类从父类继承过来的成员)但是最终调用的是子类重写后的虚函数而不是从父类那里继承的?
理解以上问题,首先需要知道虚函数表和虚函数指针的概念。
虚函数表(函数(虚函数)指针数组):只要一个类有虚函数,那么这个类就会有一个虚函数表,存放这个类的所有的虚函数的入口地址。
虚函数表指针:有虚函数的类创建的对象的前四个字节(32位)是虚函数表的首地址
#include <iostream>
using namespace std;
class Base
{
public:
virtual void Func1() {
cout << "Func1()" << endl;
}
private:
int _b = 1;
};
int main()
{
Base base;
cout << sizeof(base) << endl;
}
上图可以看出来,基类中存在虚函数,计算基类的对象的大小为8,这是为什么呢?就是因为除了成员_b意外,还有一个_vfptr放在对象前面,_vfptr是一个指针,叫做虚函数表指针(虚表的地址),虚函数表里面存的是虚函数的地址,表是一个函数指针数组,里面存的是函数指针。
基类的类对象的虚函数指针指向虚函数表,虚函数表再去调用不同的函数。
子类重写父类的虚函数,子类的对象的虚函数指针指向虚函数表中的函数的入口地址,所以只要将子类的虚函数表的入口地址变为需要调用的子类的虚函数,而不是从父类继承的虚函数,知道了该调用那个虚函数后,就能够实现多态了。
我们可以通过通过调用父类的指针指向子类的对象来实现,就可以将子类的虚函数表的入口地址变为需要调用的子类的虚函数,就可以实现多态了。
代码演示
#include <iostream>
using namespace std;
//形状类:求面积 属性:面积
//圆类 求面积 属性:半径 面积
//矩形类 求面积 属性:宽 高 面积
class Shape
{
public:
Shape()
{
s = 0;
cout << "Shape()" << endl;
}
~Shape()
{
cout << "~Shape()" << endl;
}
virtual float getArea()
{
cout << "Shape getArea" << endl;
return s;
}
protected:
float s; //面积
};
class Circle :public Shape
{
public:
Circle(float _r = 10)
{
r = _r;
cout << "Circle(float)" << endl;
}
~Circle()
{
cout << "~Circle()" << endl;
}
virtual float getArea()
{
cout << "Circle getArea" << endl;
s = 3.14 * r * r;
return s;
}
private:
float r; //半径
};
class Rectangle :public Shape
{
public:
Rectangle(float _w = 5, float _h = 5)
{
w = _w;
h = _h;
cout << "Rectangle(float)" << endl;
}
~Rectangle()
{
cout << "~Rectangle()" << endl;
}
virtual float getArea()
{
cout << "Rectangle getArea" << endl;
s = w * h;
return s;
}
private:
float w; //宽
float h; //高
};
void calArea(Circle *shape)
{
cout << "面积是:" << shape->getArea() << endl;
}
void calArea(Rectangle *shape)
{
cout << "面积是:" << shape->getArea() << endl;
}
int main()
{
//调用同一个函数,传入不同的形状对象,执行不同的计算面积的函数
Circle circle;
calArea(&circle);
Rectangle rect;
calArea(&rect);
return 0;
}