一.多态
1.多态的定义
指相同对象收到不同消息或不同对象收到相同消息时产生不同的动作
2.静态多态(早绑定)VS动态多态(晚绑定)
静态多态:以下图为例子,程序在编译前就确定了使用的是哪个calcArea函数,这就是早绑定也就是静态多态。
动态多态:目前所指的对象类型在运行时才确定的。
前面我们定义了三个类,在使用的时候:我们调用的calcArea()都是我们的父类Shape的成员函数,那么我们怎么调用我们Rect和Circle的的成员函数呢!那就是虚函数!
改进的方法:将我们要实现多态的成员函数变为虚函数calcArea,这样就可以实现我们动态多态了!
3.代码实例
main函数文件:
#include<iostream>
#include<stdlib.h>
#include<string>
#include "Circle.h"
#include "Rect.h"
using namespace std;
/****************************************************************/
/*动态多态、虚函数
要求:
1.定义Shape类,成员函数:calcArea(),构造函数,析构函数
2.定义Rect类,成员函数:calcArea(),构造函数,析构函数
成员数据:m_dWidth,m_dHeight
3.定义Circle类,成员函数:calcArea(),构造函数,析构函数
成员数据:m_dR
*/
/****************************************************************/
int main(void)
{
Shape *shape1 = new Rect(3, 6);
Shape *shape2 = new Circle(5);
shape1->calcArea();
shape2->calcArea();
delete shape1;
shape1 = NULL;
delete shape2;
shape2 = NULL;
system("pause");
return 0;
}
Shape.h和Shape.cpp文件
#ifndef SHAPE_H
#define SHAPE_H
#include <iostream>
using namespace std;
class Shape
{
public:
Shape();
~Shape();
virtual double calcArea();
};
#endif
#include<stdio.h>
#include<iostream>
#include"Shape.h"
using namespace std;
Shape::Shape()
{
cout << "Shape()" << endl;
}
Shape::~Shape()
{
cout << "~Shape()" << endl;
}
double Shape::calcArea()
{
cout << "Shape->calcArea()" << endl;
return 0;
}
Cicle.h和Cicle.cpp文件
#ifndef CIRCLE_H
#define CIRCLE_H
#include"Shape.h"
class Circle :public Shape
{
public:
Circle(double r);
~Circle();
double calcArea();
protected:
double m_dR;
};
#endif
#include"Circle.h"
Circle::Circle(double r)
{
cout << "Circle()" << endl;
m_dR = r;
}
Circle::~Circle()
{
cout << "~Circle()" << endl;
}
double Circle::calcArea()
{
cout << "Circle->calcArea()" << endl;
return 3.14*m_dR*m_dR;
}
Rect.h和Rect.cpp文件
#ifndef RECT_H
#define RECT_H
#include"Shape.h"
class Rect :public Shape
{
public:
Rect(double width, double height);
~Rect();
double calcArea();
protected:
double m_dWidth;
double m_dHeight;
};
#endif
#include"Rect.h"
Rect::Rect(double width, double height)
{
cout << "Rect()" << endl;
m_dHeight = height;
m_dWidth = width;
}
Rect::~Rect()
{
cout << "~Rect()" << endl;
}
double Rect::calcArea()
{
cout << "Rect->calcArea()" << endl;
return m_dHeight*m_dWidth;
}
运行结果:我们可以发现调用了Rect和Circle的成员函数。去掉virtual会发现只调用父类的成员函数!
二.动态遇到的问题以及实现原理
1.内存泄漏
从下面的例子我们可以看出,我们用父类的指针shape1来指向子类的Circle,问题在于当我们开辟了一个空间之后,delete shape1是否将我们开辟的空间归还呢!显然没有delete shape1调用的是父类的析构函数!这样就造成了内存泄漏!
解决的方法——虚析构函数(Circle里面的virtual可写可不写,不写系统会自动加上去)
注意:执行完子类的析构函数就会执行父类的析构函数
2.虚函数的使用局限:
1.虚函数不能修饰普通函数(也就是这个函数必须是某个类的成员函数)
2.静态成员函数不能是虚函数
3.内联函数不能是虚函数(负责计算机会忽略掉inline使其变成单纯的虚函数)
4.构造函数不能是虚函数
3.虚函数的实现原理
第一种情况:子类中没有定义该同名函数。我们可以看到Shape和Circle的虚函数表中,虽然虚函数表的地址不同,但是最后指向的函数时相同的。
第二种情况:子类中定义有同名函数。由于Circle自己定义了自己该函数,所以虚函数表中该函数指针的值被覆盖为指向自己定义的函数。这也是多态的原理了
4.函数的覆盖与隐藏:
隐藏:父类和子类中出现了同名函数,此时子类就隐藏了父类的函数。
子类的函数与父类的名称相同,但是参数不同,父类函数被隐藏
子类函数与父类函数的名称相同,参数也相同,但是父类函数没有virtual,父类函数被隐藏
覆盖:父类和子类中出现同名函数并且参数也相同,父类的函数时虚函数,那么子类覆盖父类函数。