c 语言编程简单实例多态性程序,C++多态性的一个典型例子

下面是一个承上启下的例子。一方面它是有关继承和运算符重载内容的综合应用的例子,通过这个例子可以进一步融会贯通前面所学的内容,另一方面又是作为讨论多态性的一个基础用例。

希望大家耐心、深入地阅读和消化这个程序,弄清其中的每一个细节。

[例12.1] 先建立一个Point(点)类,包含数据成员x,y(坐标点)。以它为基类,派生出一个Circle(圆)类,增加数据成员r(半径),再以Circle类为直接基类,派生出一个Cylinder(圆柱体)类,再增加数据成员h(高)。要求编写程序,重载运算符“<>”,使之能用于输出以上类对象。

这个例题难度不大,但程序很长。对于一个比较大的程序,应当分成若干步骤进行。先声明基类,再声明派生类,逐级进行,分步调试。

1) 声明基类Point

类可写出声明基类Point的部分如下:

#include

//声明类Point

class Point

{

public:

Point(float x=0,float y=0); //有默认参数的构造函数

void setPoint(float ,float); //设置坐标值

float getX( )const {return x;} //读x坐标

float getY( )const {return y;} //读y坐标

friend ostream & operator <

protected: //受保护成员

float x, y;

};

//下面定义Point类的成员函数

Point::Point(float a,float b) //Point的构造函数

{ //对x,y初始化

x=a;

y=b;

}

void Point::setPoint(float a,float b) //设置x和y的坐标值

{ //为x,y赋新值

x=a;

y=b;

}

//重载运算符“<

ostream & operator <

{

output<

return output;

}

以上完成了基类Point类的声明。

为了提高程序调试的效率,提倡对程序分步调试,不要将一个长的程序都写完以后才统一调试,那样在编译时可能会同时出现大量的编译错误,面对一个长的程序,程序人员往往难以迅速准确地找到出错位置。要善于将一个大的程序分解为若干个文件,分别编译,或者分步调试,先通过最基本的部分,再逐步扩充。

现在要对上面写的基类声明进行调试,检查它是否有错,为此要写出main函数。实际上它是一个测试程序。

int main( )

{

Point p(3.5,6.4); //建立Point类对象p

cout<

p.setPoint(8.5,6.8); //重新设置p的坐标值

cout<

return 0;

}

getX和getY函数声明为常成员函数,作用是只允许函数引用类中的数据,而不允许修改它们,以保证类中数据的安全。数据成员x和y声明为protected,这样可以被派生类访问(如果声明为private,派生类是不能访问的)。

程序编译通过,运行结果为:

x=3.5,y=6.4

p(new):[8.5,6.8]

测试程序检查了基类中各函数的功能,以及运算符重载的作用,证明程序是正确的。

2)声明派生类Circle

在上面的基础上,再写出声明派生类Circle的部分:

class Circle:public Point //circle是Point类的公用派生类

{

public:

Circle(float x=0,float y=0,float r=0); //构造函数

void setRadius(float ); //设置半径值

float getRadius( )const; //读取半径值

float area ( )const; //计算圆面积

friend ostream &operator <

private:

float radius;

};

//定义构造函数,对圆心坐标和半径初始化

Circle::Circle(float a,float b,float r):Point(a,b),radius(r){}

//设置半径值

void Circle::setRadius(float r){radius=r;}

//读取半径值

float Circle::getRadius( )const {return radius;}

//计算圆面积

float Circle::area( )const

{

return 3.14159*radius*radius;

}

//重载运算符“<

ostream &operator <

{

output<

return output;

}

为了测试以上Circle类的定义,可以写出下面的主函数:

int main( )

{

Circle c(3.5,6.4,5.2); //建立Circle类对象c,并给定圆心坐标和半径

cout<

c.setRadius(7.5); //设置半径值

c.setPoint(5,5); //设置圆心坐标值x,y

cout<

Point &pRef=c; //pRef是Point类的引用变量,被c初始化

cout<

return 0;

}

程序编译通过,运行结果为:

original circle:(输出原来的圆的数据)

x=3.5, y=6.4, r=5.2, area=84.9486

new circle:(输出修改后的圆的数据)

Center=[5,5], r=7.5, area=176.714

pRef:[5,5] (输出圆的圆心“点”的数据)

可以看到,在Point类中声明了一次运算符“ <

请注意main函数第8行:

Point & pRef = c;

定义了 Point类的引用变量pRef,并用派生类Circle对象c对其初始化。前面我们已经讲过,派生类对象可以替代基类对象为基类对象的引用初始化或赋值(详情请查看:C++基类与派生类的转换)。现在 Circle是Point的公用派生类,因此,pRef不能认为是c的别名,它得到了c的起始地址, 它只是c中基类部分的别名,与c中基类部分共享同一段存储单元。所以用“cout<

3) 声明Circle的派生类Cylinder

前面已从基类Point派生出Circle类,现在再从Circle派生出Cylinder类。

class Cylinder:public Circle// Cylinder是Circle的公用派生类

{

public:

Cylinder (float x=0,float y=0,float r=0,float h=0); //构造函数

void setHeight(float ); //设置圆柱高

float getHeight( )const; //读取圆柱高

loat area( )const; //计算圆表面积

float volume( )const; //计算圆柱体积

friend ostream& operator <

protected:

float height;//圆柱高

};

//定义构造函数

Cylinder::Cylinder(float a,float b,float r,float h):Circle(a,b,r),height(h){}

//设置圆柱高

void Cylinder::setHeight(float h){height=h;}

//读取圆柱高

float Cylinder::getHeight( )const {return height;}

//计算圆表面积

float Cylinder::area( )const { return 2*Circle::area( )+2*3.14159*radius*height;}

//计算圆柱体积

float Cylinder::volume()const {return Circle::area()*height;}

ostream &operator <

{

output<

return output;

} //重载运算符“<

可以写出下面的主函数:

int main( )

{

Cylinder cy1(3.5,6.4,5.2,10);//定义Cylinder类对象cy1

cout<

<

<

cy1.setHeight(15);//设置圆柱高

cy1.setRadius(7.5);//设置圆半径

cy1.setPoint(5,5);//设置圆心坐标值x,y

cout<

Point &pRef=cy1;//pRef是Point类对象的引用变量

cout<

Circle &cRef=cy1;//cRef是Circle类对象的引用变量

cout<

return 0;

}

运行结果如下:

original cylinder:(输出cy1的初始值)

x=3.5, y=6.4, r=5.2, h=10 (圆心坐标x,y。半径r,高h)

area=496.623, volume=849.486 (圆柱表面积area和体积volume)

new cylinder: (输出cy1的新值)

Center=[5,5], r=7.5, h=15 (以[5,5]形式输出圆心坐标)

area=1060.29, volume=2650.72(圆柱表面积area和体积volume)

pRef as a Point:[5,5] (pRef作为一个“点”输出)

cRef as a Circle:Center=[5,5], r=7.5, area=176.714(cRef作为一个“圆”输出)

说明:在Cylinder类中定义了 area函数,它与Circle类中的area函数同名,根据前面我们讲解的同名覆盖的原则(详情请查看:C++多重继承的二义性问题),cy1.area( ) 调用的是Cylinder类的area函数(求圆柱表面积),而不是Circle类的area函数(圆面积)。请注意,这两个area函数不是重载函数,它们不仅函数名相同,而且函数类型和参数个数都相同,两个同名函数不在同 —个类中,而是分别在基类和派生类中,属于同名覆盖。重载函数的参数个数和参数类型必须至少有一者不同,否则系统无法确定调用哪一个函数。

main函数第9行用“cout<

main函数中最后4行的含义与在定义Circle类时的情况类似。pRef是Point类的引用变量,用cy1对其初始化,但它不是cy1的别名,只是cy1中基类Point部分的别名,在输出pRef时是作为一个Point类对象输出的,也就是说,它是一个“点”。同样,cRef是Circle类的引用变量,用cy1对其初始化,但它只是cy1中的直接基类Circle部分的别名, 在输出 cRef 时是作为Circle类对象输出的,它是一个"圆”,而不是一个“圆柱体”。从输 出的结果可以看出调用的是哪个运算符函数。

在本例中存在静态多态性,这是运算符重载引起的(注意3个运算符函数是重载而不是同名覆盖,因为有一个形参类型不同)。可以看到,在编译时编译系统即可以判定应调用哪个重载运算符函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值