学习总结:
“多态”指的是同一名字的事物可以完成不同的功能。
这里主要总结和继承,虚函数等概念有关的运行时多态。
- 通过基类指针实现多态
对于通过基类指针调用基类和派生类中都有的同名、同参数表的虚函数的语句时,基类指针指向什么类的对象,运行时就调用哪个类的虚函数。(在声明前加了“virtual”的就是虚函数,virtual关键字只在类定义的成员函数声明处使用,不能在类外部写成员函数体时使用)
举个栗子:
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
class A{
public:
virtual void print(){cout<<"A::print()"<<endl;}
};
class B:public A{
public:
virtual void print(){cout<<"B::print()"<<endl;}
};
class C:public B{
public:
virtual void print(){cout<<"C::print()"<<endl;}
};
int main(){
A a;
A* pa = &a;
pa->print();
B b;
pa = &b;
pa->print();
C c;
pa = &c;
pa->print();
return 0;
}
运行结果:
A::print()
B::print()
C::print()
Process returned 0 (0x0) execution time : 0.453 s
Press any key to continue.
可以看出,基类指针指向哪个类的对象,就调用哪个类的虚函数,而到底调用哪个类的虚函数,编译时并不确定,是在运行到该语句时,才确定,所以叫运行时的多态,被称为“动态联编”。
- 通过基类引用实现多态
类似的,对于通过基类引用调用基类和派生类中都有的同名、同参数表的虚函数的语句时,其引用什么类的对象,运行时就调用哪个类的虚函数。
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
class A{
public:
virtual void print(){cout<<"A::print()"<<endl;}
};
class B:public A{
public:
virtual void print(){cout<<"B::print()"<<endl;}
};
class C:public B{
public:
virtual void print(){cout<<"C::print()"<<endl;}
};
void printInfo(A& r){
r.print();
}
int main(){
A a;
B b;
C c;
printInfo(a);
printInfo(b);
printInfo(c);
return 0;
}
运行结果:
A::print()
B::print()
C::print()
多态的作用:
举个栗子:编写一个几何形体处理程序,输入几何形体的个数以及每个几何形体的形状和参数,要求按面积从小到大依次输出每个几何形体的种类及面积。
例如输入:
3
R 3 5
R 1 2
表示有三个几何形体,R表示矩形,紧接着是矩阵的长和宽;C表示圆,紧接着是圆的半径。
输出:
Rectangle:2
Rectangle:15
circle:254.34
程序涉及多种几何形体(这里只简单给出了两种),如果不适用多态,就需要用n个数组分别存放n中几何形体,不但编码麻烦,而且如果未来要增加新的几何形体,就要增加新的数组,可扩充性不好。
这里将所有几何形体的共同点抽象出来形成基类CShape,矩形和圆都从基类中派生,每个类都有各自的计算面积虚函数。
#include<iostream>
#include<cmath>
using namespace std;
class CShape{ // 基类 形体类
public:
virtual double Area(){}; //求面积
virtual void printInfo(){};
};
class CRectangle:public CShape{ //派生类 矩形
public:
int w, h; // 宽和高
virtual double Area();
virtual void printInfo();
};
class CCircle:public CShape{ //派生类 圆
public:
int r;//半径
virtual double Area();
virtual void printInfo();
};
double CRectangle::Area(){return w*h;}
void CRectangle::printInfo(){cout<<"Rectangle:"<<Area()<<endl;}
double CCircle::Area(){return 3.14*r*r;}
void CCircle::printInfo(){cout<<"circle:"<<Area()<<endl;}
CShape* pShapes[10];
int Mycompare(const void* s1, const void* s2){
CShape** p1 = (CShape**)s1;//s1是指向指针的指针,其指向的指针的类型为CShape*类型
CShape** p2 = (CShape**)s2;
double a1 = (*p1)->Area(); //p1指向几何形体对象的指针,*p1指向几何形体对象
double a2 = (*p2)->Area();
if(a1<a2) //面积小的排前面
return -1;
else if(a1>a2)
return 1;
else
return 0;
}
int main(){
int i, n;
CRectangle* pr;
CCircle* pc;
cin>>n;
for(int i=0; i<n; i++){
char c;
cin>>c;
switch(c){
case'R'://矩形
pr = new CRectangle();
cin>>pr->w>>pr->h;
pShapes[i] = pr;
break;
case'C'://圆
pc = new CCircle();
cin>>pc->r;
pShapes[i] = pc;
break;
}
}
qsort(pShapes, n, sizeof(CShape*), Mycompare);//待排序的数组元素是指针,注意第三个参数
for(int i=0; i<n; i++){
pShapes[i]->printInfo();
delete pShapes[i];
}
return (0);
}
运行结果:
3
R 3 5
C 9
R 1 2
Rectangle:2
Rectangle:15
circle:254.34
Process returned 0 (0x0) execution time : 18.946 s
Press any key to continue.
*p1指向一个几何形体对象,通过(*p1)->Area 求该对象的面积。*p1指向哪种对象,就会调用相应类的面积函数Area(),正确求得面积。
多态的实现原理:每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着该虚函数表的指针,一个类的虚函数列表中列出了该类的全部虚函数地址。
多态的函数调用语句被编译成柑橘基类指针所指向的或者所引用的对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。
在成员函数(静态成员函数、构造函数和析构函数除外)中调用其他虚函数的语句是多态的。