【C++】面向对象之多态篇

文章目录

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()函数,分别计算出圆和矩形的面积。
【C++】面向对象之多态篇
虚函数——“虚假”的函数,父类引用子类对象,子类成员函数重写父类方法。

6.3 虚函数代码实践

在主调程序中定义2个Shape类的指针(一个指向子类Rect,一个指向子类Circle)。

用两个指针分别调用计算面积的函数——看调用的父类的还是子类的面积函数;

最后将2个指针对象销毁——看销毁父类指针时,能否销毁子类的对象。

1.项目结构

【C++】面向对象之多态篇

2.3个头文件

【C++】面向对象之多态篇【C++】面向对象之多态篇【C++】面向对象之多态篇

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后的结果:
【C++】面向对象之多态篇
(3)最后两行
销毁Shape1和Shape2时,只执行父类的析构函数,并没有执行2个子类的析构函数。

6.4 虚析构函数

6.5 虚函数和虚析构函数实现原理

(1)函数指针

(2)函数的覆盖与隐藏

(3)虚析构函数的实现原理

(4)虚函数表的代码实践

6.6 纯虚函数和抽象类

(1)纯虚函数的实现原理

(2)纯虚函数和抽象类的代码实践

6.7 接口类

(1)接口类

(2)接口类代码实践

6.8 RTTI:运行时类型识别

(1)RTTI–运行时类型识别

(2)RTTI代码实践

6.9 异常处理

(1)异常处理

(2)异常处理与多态的关系

(3)异常处理代码实践

上一篇:Unbunt tab 自动补全


下一篇:【并发编程】synchronized底层原理:Monitor(管程/监视器)