C++远征之多态篇 视频教程 笔记 方便自己查阅和复习,温故而知新。
目录
多态
多态指相同对象接收到不同消息或不同对象收到相同消息时产生不同的动作。
多态分为静态多态和动态多态。
(1) 静态多态也称为早绑定。
(2) 动态多态也称为晚绑定,必须以封装和继承为基础。
1 虚函数及实现原理
(1) 静态多态(早绑定)
例如,对于下面的两个函数重载,那么,实例化对象后,可以分别调用这两个函数,因为参数个数不同,计算机在编译的时候,自动识别时调用的函数。在程序运行之前,在编译阶段,就已经确定了使用哪个函数,我们称之为:静态多态 (早绑定)。
(2) 动态多态(晚绑定)
注: 动态多态必须以 封装 和 继承 为基础
调用的都是父类计算面积的函数。那么如何去调用子类的计算面积的函数呢? 这是 将用到 虚函数(virtual) ,这个在继承篇也用到虚函数了。
直接在 父类和子类计算面积的函数 声明前面 加上关键字 virtual 即可
注:子类可以不添加关键字 virtual,但是推荐在子类前面加上,方便查看代码。
2 代码示例1
要求:
动态多态 虚函数
1 定义Shape类
成员函数:calcArea() 构造函数 析构函数
2 线段类:Line
数据成员: m_dWidth m_dHeight
成员函数:calcArea() 构造函数 析构函数
3 定义Circle类
成员函数: m_dR
数据成员:calcArea() 构造函数 析构函数
//Shape.h
#pragma once
#include<iostream>
using namespace std;
class Shape
{
public:
Shape();
~Shape();
virtual double calcArea();
};
//Shape.cpp
#include"Shape.h"
Shape::Shape()
{
cout << "Shape()--构造" <<endl;
}
Shape::~Shape()
{
cout << "~Shape()--析构" << endl;
}
double Shape::calcArea()
{
cout << "Shape--calcArea()" << endl;
return 0;
}
//Rect.h
#pragma once
#include"Shape.h"
class Rect :public Shape
{
public:
Rect(double width,double height);
~Rect();
virtual double calcArea();
protected:
double m_dWith;
double m_dHight;
};
//Rect.cpp
#include"Rect.h"
Rect::Rect(double width, double height)
{
m_dHight = height;
m_dWith = width;
cout <<"Rect()--构造"<<endl;
}
Rect::~Rect()
{
cout << "Rect()--析构" << endl;
}
double Rect::calcArea()
{
cout << "Rect--calcArea()" << endl;
return m_dWith*m_dHight;
}
//Circle.h
#pragma once
#include"Shape.h"
class Circle :public Shape
{
public:
Circle(double r);
~Circle();
virtual double calcArea();
private:
double m_dR;
};
//Circle.cpp
#include"Circle.h"
Circle::Circle(double r)
{
m_dR = r;
cout << "Circle()--构造"<< endl;
}
Circle::~Circle()
{
cout << "~Circle()--析构" << endl;
}
double Circle::calcArea()
{
cout << "Circle--calcArea()" << endl;
return 3.14*m_dR*m_dR;
}
//main.cpp
#include<iostream>
#include<string>
#include"Circle.h"
#include"Rect.h"
using namespace std;
/**
动态多态 虚函数
1 定义Shape类
成员函数:calcArea() 构造函数 析构函数
2 线段类:Line
数据成员: m_dWidth m_dHeight
成员函数:calcArea() 构造函数 析构函数
3 定义Circle类
成员函数: m_dR
数据成员:calcArea() 构造函数 析构函数
**/
int main()
{
Shape *shape1 = new Rect(3, 6);
Shape *shape2 = new Circle(5);
cout << "---------1-----------" << endl;
shape1->calcArea();
cout << "---------2-----------" << endl;
shape2->calcArea();
cout << "---------3-----------" << endl;
delete shape1;
shape1 = NULL;
delete shape2;
shape2 = NULL;
cin.get();
return 0;
}
运行结果:
注:可以去掉关键字 virtual 查看运行结果,其运行结果如下:
从两个结果可以看出 如果不在成员函数前面加入关键字 virtual,将会只执行父类的成员函数,不执行子类的成员函数。
3 代码示例2
注:由上面的例子例子看出,在父类指针 指向 子类时,释放内存 只执行了父类的析构函数[ ~shape() ],这可能会造成内存泄漏 这时 需要用 虚函数/虚析构函数 (在父类和子类的虚构函数前加入 virtual ) 即可。
要求和上面的代码示例相同,只在父类 和 子类 的析构函数前面添加 关键字 virtual 即可。
运行结果:
从上面的运行结果可以看出 :父类和子类的析构函数都执行。
virtual在函数中的使用限制
(1) 普通函数不能是虚函数
virtual修饰的必须是某一个类的成员函数,不能是全局函数。
(2) 静态函数不能是虚函数
(3) 内联函数不能是虚函数
(4) 构造函数不能是虚函数
关于C++其他内容笔记参见
[2] C++ 封装(1): 类和对象
[4] C++ 封装(3): 对象成员与对象数组, 深拷贝和浅拷贝
[6] C++ 继承(1): 继承方式(public, protected, private), 继承中的特殊关系(隐藏 , is-a)
[7] C++ 继承(2): 多重继承, 多继承, 虚继承(virtual)
参考资料
[1] C++远征之多态篇 (注:图片均来自视频中PPT)