例子:几何形体处理程序
需求:输入若干个几何形体的参数,要求按面积排序输出。输出时要指明形状。
input:
第一行是几何形体数目n(不超过100).下面有n行,每行以一个字母c开头
若c是‘R’,代表是一个矩形,本行后面跟着两个整数,分别是矩形的宽和高;
若c是‘C’,代表是一个圆,本行后面跟着一个整数代表其半径;
若c是‘T’,代表是一个三角形,本行后面跟着三个整数,代表三条边的长度;
output:
按照面积从小到大依次输出每个几何形体的种类以及面积。每行一个几何形体,输出格式为:
形体名称: 面积
sample Input
3
R 3 5
C 9
T 3 4 5
sample Output
Triangle:6
Rectangle:15
Circle:254.34
整体实例(见本工程jihexingti.cpp)
//
// Created by z on 19-10-15.
//
#include <iostream>
#include <cmath>
using namespace std;
/**
* 问题:为啥不为CShape编写求面积和打印信息的函数呢
* 解答:因为在这个程序中,任何一个几何形体,要么是矩形,要么是圆形,要么是三角形,不存在CShape类型的几何形体,所以不需要为CShape类编写具体的如何求面积,打印信息的函数
*/
class CShape
{
public:
virtual double Area() = 0; // 纯虚函数
virtual void PrintInfo() = 0;
};
class CRectangle:public CShape
{
public:
int w, h;
virtual double Area(){
return w * h;
}
virtual void PrintInfo(){
cout << "Rectangle:" << Area() << endl;
}
};
class CCircle:public CShape
{
public:
int r;
virtual double Area(){
return 3.14*r*r;
}
virtual void PrintInfo(){
cout << "Circle:" << Area() << endl;
}
};
class CTriangle:public CShape
{
public:
int a, b, c;
virtual double Area(){
double p = (a + b + c) / 2.0;
return sqrt(p * (p - a) * (p - b) * (p - c));
}
virtual void PrintInfo(){
cout << "Triangle:" << Area() << endl;
}
};
/**
* 使用什么来存储各种类型的几何形体?
* 一共有三种几何形体,如果开三个数组去存放,比较啰嗦;将来有四个几何形体,就需要再加一个数组,比较麻烦
* 有了多态,就可以采取简单的做法,此处定义了一个基类的指针数组pShapes用来存放各种几何形体。
* 准确的说,这个数组中的各个元素都是一个基类指针,可以指向派生类对象,因此可以让数组中的元素指针指向不同的几何形体
*
*
* 使用多态的好处:
* 如果添加新的几何形体,比如五边形,则只需要从CShape派生处CPentagon,以及在main中的switch语句中增加一个case,其余部分不变。
*
*/
CShape* pShapes[100];
// 排序函数
int MyCompare(const void* s1, const void* s2);
int main()
{
int i; int n;
// 定义三个不同几何形体类型的指针
CRectangle* pr; CCircle* pc; CTriangle* pt;
// 读入几何形体数目n
cin >> n;
// 处理每一个几何形体
for(i = 0; i < n; i++)
{
// 读入几何形体的种类
char c;
cin >> c;
// 判定c是什么
switch(c)
{
case 'R':
pr = new CRectangle();
cin >> pr->w >> pr->h;
// 使得基类数组指针元素指向new出来的派生类对象。
pShapes[i] = pr;
break;
case 'C':
pc = new CCircle();
cin >> pc->r;
pShapes[i] = pc;
break;
case 'T':
pt = new CTriangle();
cin >> pt->a >> pt->b >> pt->c;
pShapes[i] = pt;
break;
}
}
// 对基类指针数组pShapes进行排序,里面有n个元素,每个元素的大小都是CShape*指针的大小,用比较函数MyCompare进行排序
qsort(pShapes, n, sizeof(CShape*), MyCompare);
// 遍历基类指针数组pShapes,输出每个指针数组元素指向的几何形体对象的信息。
for(i = 0; i < n; i++)
pShapes[i]->PrintInfo(); // 多态语句
return 0;
}
/**
*
* @param s1
* @param s2
* @return
*
* 两个参数都会指向pShapes数组中待比较的元素
*
* s1所指向的是pShapes数组里面的元素,数组里面的元素是CShape*类型的,现在想让p1指针指向CShape*这种类型的指针,p1为指向指针的指针,因此需要使用两个*
*/
int MyCompare(const void* s1, const void* s2)
{
double a1, a2;
CShape** p1; // s1, s2 是void*,不可写“*s1”来取得s1指向的内容
CShape** p2;
p1 = (CShape**)s1; // s1, s2指向pShapes数组中的元素,数组元素类型为CShape*
p2 = (CShape**)s2; // 故p1, p2都是指向指针的指针,类型为CShape**
a1 = (*p1)->Area(); // *p1的类型是CShape*,是基类指针,故此句为多态
a2 = (*p2)->Area();
if(a1 < a2)
return -1;
else if(a2 < a1)
return 1;
else
return 0;
}
1 用基类指针数组存放指向各种派生类对象的指针,然后遍历该数组,就能对各个派生类对象做各种操作,是很常用的做法。
2 多态的另外一个例子(见本工程jihexingti2.cpp)
//
// 多态的又一个例子
//
#include <iostream>
using namespace std;
class Base
{
public:
void fun1()
{
fun2(); // 等价于this->fun2(); this是基类指针,fun2是虚函数,所以是多态
}
virtual void fun2()
{
cout << "Base::fun2()" << endl;
}
};
class Derived:public Base
{
public:
virtual void fun2()
{
cout << "Derived:fun2()" << endl;
}
};
int main()
{
Derived d;
Base* pBase = &d;
pBase->fun1();
return 0;
}
3 通过上述例子得出结论:在非构造函数中,非析构函数的成员函数中调用虚函数,是多态!!!
4 在构造函数和析构函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数
5 多态例子(见本工程jihexingti3.cpp)
//
// Created by z on 19-10-16.
//
#include <iostream>
using namespace std;
class myclass
{
public:
virtual void hello()
{
cout << "hello from myclass" << endl;
}
virtual void bye()
{
cout << "bye from myclass" << endl;
}
};
class son:public myclass
{
public:
void hello() // 派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
{
cout << "hello from son" << endl;
}
son(){hello();} // 在构造函数和析构函数中调用虚函数不是多态
~son(){bye();} // bye()在基类中是虚函数 但不是多态,因为在构造函数和析构函数中调用虚函数不是多态,
};
class grandson:public son
{
public:
void hello()
{
cout << "hello from grandson" << endl;
}
void bye()
{
cout << "bye from grandson" << endl;
}
grandson()
{
cout << "constructing grandson" << endl;
}
~grandson()
{
cout << "destructing grandson" << endl;
}
};
int main()
{
grandson gson; // 有grandson对象生成,所以从基类到各级派生类依次执行构造函数
son* pson;
pson = &gson;
pson->hello(); // 多态
return 0;
} // 函数结束 gson对象消亡,从底层派生类依次向上层基类执行析构函数
6 派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
7 问题:为什么在构造函数和析构函数中调用虚函数不是多态?
解答:当一个派生类的对象在初始化的时候会先执行里面基类对象的构造函数,在基类对象的构造函数执行期间,派生类对象它自己的那部分成员变量还没有被初始化,如果在基类的
构造函数执行期间调用了虚函数,又允许这个虚函数时多态,那么在基类构造函数执行期间就会调用了派生类的虚函数,但此时派生类对象自己的成员变量还没有初始化,在
派生类对象上面执行了成员函数,这个成员函数执行的结果就是不正确的,因此我们不能够在基类的构造函数中就去执行派生类的虚函数