文章目录
6.1 多态概述
当发出一条命令时,不同对象接收到相同的命令后,所作出的动作不同。
严谨说:相同对象收到不同消息 or 不同对象收到相同消息时产生不同的动作。
6.2 虚函数机器实现原理
6.2.1 静态多态(早绑定)
class Rect{
public:
int calcArea(int width);
int calcArea(int width,int height);
};
定义的一个矩形类Rect中,定义2个成员函数(名字相同),但是2个函数的参数不同,他们是互为重载的函数。
静态绑定:根据传参的个数,计算机在编译时自动地调用相应的函数,即在运行前的编译阶段,函数的程序(调用哪个)已经确定下来了。
6.2.2 动态多态(晚绑定)
1.小栗子
圆形类和矩形类(不同对象)分别由自己的计算机面积的方法,但方法不同,即下达相同命令给不同对象,却做着不同的操作。
动态多态的前提:以封装和继承为基础。
在一个形状类Shape类中,定义了一个成员函数(计算面积):
class Shape{
public:
double calcArea(){
cout<<"calcArea()"<<endl;
return 0;
}
}
再定义2个类,这两个类都是以public方式继承shape类。
先是圆形类:
class Circle:public Shape{
public:
Circle(double r){//构造函数
m_dR=r;
}
double calcArea();
private:
double m_dR;
};
double Circle::calcArea(){
return 3.14*m_dR*m_dR;
}
再是矩形类:
class Rect:public Shape{
public:
Rect(double width,double height){//构造函数
m_dWidth=width;
m_dHeigth=height;
}
double calcArea();
private:
double m_dWidth;
double m_dHeigth;
};
double Rect::calcArea(){
return m_dWidth*m_dHeight;
}
在main函数中使用时,
可以使用父类的指针shape1指向其中的一个子类对象Circle,
并且用另一个父类指针shape2指向一个矩形的对象。
这两个子类对象都被它的父类指针所指向。
int main(){
Shape *shape1=new Circle(4.0);
Shape *shape2=new Rect(3.0,5.0);
shape1->calcArea();
shape2->calcArea();
return 0;
}
输出的结果是2个calcArea().
2.虚函数:
采用virtual
修饰类的成员函数。
在父类shape中定义成员函数时,就把我们想要实现多态的成员函数前加virtual关键字(使其成为成员虚函数):
虚函数——“虚假”的函数,父类引用子类对象,子类成员函数重写父类方法(函数)。
写法:即将基类的calcArea
函数前加virtual,和子类Circle和子类Rect的calcArea
函数声明前面加上virtual。
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
using namespace std;
class Shape{
public:
virtual double calcArea(){//虚函数
cout<<"calcArea()"<<endl;
return 0;
}
};
class Circle:public Shape{
public:
//Circle(double r);
virtual double calcArea();
Circle(double r){//构造函数
m_dR=r;
}
private:
double m_dR;
};
double Circle::calcArea(){//成员函数
return 3.14*m_dR*m_dR;
}
class Rect:public Shape{
public:
Rect(double width,double height){//构造函数
m_dWidth=width;
m_dHeigth=height;
}
virtual double calcArea();
private:
double m_dWidth;
double m_dHeigth;
};
double Rect::calcArea(){//成员函数的实现
return m_dWidth*m_dHeigth;
}
int main(){
Shape *shape1=new Circle(4.0);
Shape *shape2=new Rect(3.0,5.0);
cout<<shape1->calcArea();
cout<<shape2->calcArea();
system("pause");
return 0;
}
输出结果不再是2个calcArea(),而是调用了2个子类中重写基类的calcArea()
函数,分别计算出圆和矩形的面积。
虚函数——“虚假”的函数,父类引用子类对象,子类成员函数重写父类方法。
6.3 虚函数代码实践
在主调程序中定义2个Shape类的指针(一个指向子类Rect,一个指向子类Circle)。
用两个指针分别调用计算面积的函数——看调用的父类的还是子类的面积函数;
最后将2个指针对象销毁——看销毁父类指针时,能否销毁子类的对象。
1.项目结构
2.3个头文件
3.cpp文件
#include "Circle.h"
#include<iostream>
using namespace std;
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;
}
#include<iostream>
#include<stdlib.h>
#include"Circle.h"
#include"Rect.h"
using namespace std;
int main(){
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;
}
#include "Rect.h"
#include<iostream>
using namespace std;
Rect::Rect(double width,double height){
cout<<"Rect()"<<endl;
m_dWidth=width;
m_dHeight=height;
}
Rect::~Rect(){
cout<<"~Rect()"<<endl;
}
double Rect::calcArea(){
cout<<"Rect----calcArea"<<endl;
return m_dWidth*m_dHeight;
}
主函数
#include"Shape.h"
#include<iostream>
using namespace std;
Shape::Shape(){
cout<<"Shape()"<<endl;
}
Shape::~Shape(){
cout<<"~Shape()"<<endl;
}
double Shape::calcArea(){
cout<<"Shape---calcArea()"<<endl;
return 0;
}
程序分析
(1)前四行结果:
实例化一个Rect对象
——会先执行父类的构造函数,再执行本身的构造函数。Circle实例化同理。
(2)中间两行
没有做到我们所想的调用Rect和Circle中的calcArea()函数
——解决方案:在3个头文件中的calcArea()
函数前加上virtual
,
注意子类的calcArea函数前的virtual
不是一定要加上的,不加上系统也会自动加上,但是最好加上。
都加了virtual
后的结果:
(3)最后两行
销毁Shape1和Shape2时,只执行父类的析构函数,并没有执行2个子类的析构函数。