《第--章━━多态性与虚函数优秀文档.ppt》由会员分享,可在线阅读,更多相关《第--章━━多态性与虚函数优秀文档.ppt(36页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C+程序设计第8 章(2)多态性与虚函数主要内容l C+的多态性l 动态多态性的实现条件l 虚函数的声明l 虚函数的特性与调用l 静态关联、动态关联l 虚析构函数l 纯虚函数l 抽象类l 综合实例C+的多态性l 多态性:指对不同类型的对象发送同样的消息(即调用同名的函数),不同类型的对象在接收时会产生不同的行为(即执行各自同名的函数)。l 编译时多态性(静态多态性):指在编译阶段,系统就可根据所操作的对象,确定其具体的操作。编译时多态性是通过函数重载、运算符重载来实现的。函数重载是根据函数调用式中所给出的实参的类型或实参的个数,在编译阶段系统就可确定调用的是同名函数中的哪一个。运算符重载是根据
2、运算式中所给出的运算对象的类型,在编译阶段系统就可确定执行的是同种运算中的哪一个。l 运行时多态性(动态多态性):指在编译阶段,系统仅根据函数调用式是无法确定调用的是同名函数中的哪一个;必须在程序运行过程中,动态确定所要调用函数的当前对象,并根据当前对象的类型来确定调用的是同名函数中的哪一个。运行时多态性是通过“类的继承关系”加上“虚函数”联合起来实现的。动态多态性的实现条件l 要有类的继承层次结构:一个基类可以派生出不同的派生类,各派生类中可以新增与基类中的函数名字相同、参数个数及类型也相同的成员,这些同名的成员函数在不同的派生类中就有不同的含义!这样,在类的继承结构中,不同的层次上出现了名
3、字相同、参数个数及类型也相同、但功能不同的函数!l 引入虚函数:作用是在由一个基类派生出的类体系中实现“一个接口,多种方法”,主要用于建立通用程序。对于同一类体系中的各层次派生类,使用虚函数可实现统一的类接口,以便用相同的方式对各层次派生类的对象进行操作。虚函数是基类的成员函数,是为了实现某一种功能而假设的虚拟函数,在该基类的各层次派生类中对该虚函数可有各自不同的定义!l 要能体现虚函数的特性:必须通过基类的对象指针、基类的对象引用来调用各层次派生类对象的同名虚函数,才能体现虚函数的特性!因为只有这样才能用相同的调用方式去调用不同层次派生类对象的同名虚函数,从而实现动态多态性。虚函数的声明l
4、虚函数的声明:class 基类 virtual 返回值类型 成员函数名(形参表)函数体;当一个基类的某成员函数声明为虚函数,则在该基类的所有派生类中,与虚函数同名、参数个数及类型相同、且返回值类型也相同的,不论是否有关键字 virtual 修饰,都是虚函数,反之不然。但要注意:若在派生类中只是与虚函数同名,而参数个数或类型有不同时,属于函数重载,不是虚函数!virtual 只是用在类中声明虚函数,若在类外定义虚函数前面不要加 virtual。构造函数、静态成员函数不能声明为虚函数!析构函数可以声明为虚函数。虚函数的特性与调用l 如何体现虚函数的特性?只有通过基类的对象指针、基类的对象引用来调用
5、派生类对象的虚函数时,才能体现虚函数的特性!而通过派生类对象的对象名、对象指针、对象引用来调用虚函数时,无法体现虚函数的特性!l 派生类对象中一般成员函数的调用方法:可通过派生类对象的对象名、对象指针、对象引用来调用!调用过程:若派生类新增成员函数中存在该函数,则被调用;若不存在,则调用上一层基类中的该函数;若这一层基类中也不存在,就继续往上一层寻找,直至找到该函数并被调用。l 派生类对象中一般成员函数的调用方法:可通过基类的对象指针、基类的对象引用来调用派生类对象中的一般成员函数!但只能调用派生类中从该基类继承过来的那部分成员函数!虚函数的特性与调用l 派生类对象中虚函数的调用方法:派生类对
6、象的虚函数也是成员函数,可按一般成员函数的方式调用!即:可通过派生类对象的对象名、对象指针、对象引用来调用!调用过程与一般成员函数的调用过程相同!由此可见,这种调用方式无法体现虚函数的特性!l 派生类对象中虚函数的调用方法:可通过基类的对象指针、基类的对象引用来调用派生类对象的虚函数!调用过程:调用的是派生类中的虚函数!若派生类中没有重新定义该虚函数,则调用的是上一层基类中的该虚函数;若在这一层基类中也没有重新定义该虚函数,就继续往上一层寻找,直至基类的对象指针、基类的对象引用它们本身所属的那一层基类!l 动态多态性的实现:可以让基类的对象指针(或基类的对象引用)先后指向(或先后引用)同一类族
7、中不同派生类的对象,以便用相同的调用方式去调用不同派生类对象中的同名虚函数,从而实现动态多态性。【例】(派生类对象中一般成员函数的调用 方法:可通过派生类对象的对象名、对象指针、对象引用来调用!注意调用过程。)#includeclass A int x;public:A(int a)x=a;void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;void f()cout“B:y=”y endl;class C:public B int m;public:C(int a,int b,int c):
8、B(a,b)m=c;void g()cout“C:m=”m endl;class D:public C int n;public:D(int a,int b,int c,int d):C(a,b,c)n=d;void f()cout“D:n=”n f();pd-g();D&dd1=d1;dd1.f();dd1.g();运行:D:n=4C:m=3D:n=4C:m=3D:n=4C:m=3 从A 类继承的成员:int x;g();不是虚函数从B 类继承的成员:int y;f();不是虚函数从C 类继承的成员:int m;g();不是虚函数D 类新增的成员:int n;f();不是虚函数 pdd1 对
9、象 dd1【例】(派生类对象中虚函数的调用 方法:可通过派生类对象的对象名、对象指针、对象引用来调用!注意调用过程与一般成员函数相同!由此可见,这种调用方式无法体现虚函数的特性!)#includeclass A int x;public:A(int a)x=a;virtual void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;virtual void f()cout“B:y=”y endl;class C:public B int m;public:C(int a,int b,int c)
10、:B(a,b)m=c;void g()cout“C:m=”m endl;class D:public C int n;public:D(int a,int b,int c,int d):C(a,b,c)n=d;void f()cout“D:n=”n f();pd-g();D&dd1=d1;dd1.f();dd1.g();运行:D:n=4C:m=3D:n=4C:m=3D:n=4C:m=3 从A 类继承的成员:int x;g();虚函数从B 类继承的成员:int y;f();虚函数从C 类继承的成员:int m;g();虚函数D 类新增的成员:int n;f();虚函数 pdd1 对象 dd1【例
11、】(派生类对象中一般成员函数的调用 方法:可通过基类的对象指针、基类的对象引用来调用!但只能调用派生类中从该基类继承来的那部分成员函数!)#includeclass A int x;public:A(int a)x=a;void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;void g()cout“B:y=”y endl;class C:public B int z;public:C(int a,int b,int c):B(a,b)z=c;void g()cout“C:z=”z endl;v
12、oid f()cout“Bye!”g();aa1.g();/pa-f();为什么不行?B*pb=&c1,&bb1=c1;pb-g();bb1.g();/pb-f();为什么不行?C*pc=&c1,&cc1=c1;pc-g();cc1.g();pc-f();从A 类继承的成员:int x;g();不是虚函数从B 类继承的成员:int y;g();不是虚函数C 类新增的成员:int z;g();不是虚函数f();不是虚函数 pb pa pcC1 对象运行:A:x=1A:x=1B:y=2B:y=2C:z=3C:z=3 Bye!【例】(派生类对象中虚函数的调用 方法:可通过基类的对象指针、基类的对象引
13、用来调用!注意:调用的是派生类中的虚函数!若派生类中没有重新定义该虚函数,则调用的是上一层基类中的该虚函数,)#includeclass A int x;public:A(int a)x=a;virtual void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;void g()cout“B:y=”y endl;class C:public B int z;public:C(int a,int b,int c):B(a,b)z=c;void g()cout“C:z=”z endl;virtual
14、 void f()cout“Bye!”g();aa1.g();/pa-f();为什么不行?B*pb=&c1,&bb1=c1;pb-g();bb1.g();/pb-f();为什么不行?C*pc=&c1,&cc1=c1;pc-g();cc1.g();pc-f();从A 类继承的成员:int x;g();虚函数从B 类继承的成员:int y;g();虚函数C 类新增的成员:int z;g();虚函数f();虚函数 pb pa pcC1 对象运行:C:z=3C:z=3C:z=3C:z=3C:z=3C:z=3 Bye!【例】(请注意调用过程。)#includeclass A int x;public:A
15、(int a)x=a;virtual void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;void g()cout“B:y=”y endl;class C:public B int z;public:C(int a,int b,int c):B(a,b)z=c;virtual void f()cout“Bye!”g();aa1.g();/pa-f();为什么不行?B*pb=&c1,&bb1=c1;pb-g();bb1.g();/pb-f();为什么不行?C*pc=&c1,&cc1=c1;pc
16、-g();cc1.g();pc-f();从A 类继承的成员:int x;g();虚函数从B 类继承的成员:int y;g();虚函数C 类新增的成员:int z;f();虚函数 pb pa pcC1 对象运行:B:y=2B:y=2B:y=2B:y=2B:y=2B:y=2Bye!【例】(请注意调用过程。)#includeclass A int x;public:A(int a)x=a;void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;virtual void g()cout“B:y=”y e
17、ndl;class C:public B int z;public:C(int a,int b,int c):B(a,b)z=c;void g()cout“C:z=”z endl;void f()cout“Bye!”g();aa1.g();/pa-f();为什么不行?B*pb=&c1,&bb1=c1;pb-g();bb1.g();/pb-f();为什么不行?C*pc=&c1,&cc1=c1;pc-g();cc1.g();pc-f();从A 类继承的成员:int x;g();不是虚函数从B 类继承的成员:int y;g();虚函数C 类新增的成员:int z;g();虚函数f();不是虚函数 p
18、b pa pcC1 对象运行:A:x=1A:x=1C:z=3C:z=3 C:z=3C:z=3 Bye!【例】(注意,若派生类中的某函数只是与虚函数同名,但参数个数或类型有不同时,则属于函数的重载,而不是虚函数!)#includeclass A int x;public:A(int a)x=a;virtual void g()cout“A:x=”x endl;class B:public A int y;public:B(int a,int b):A(a)y=b;void g()cout“B:y=”y endl;class C:public B int z;public:C(int a,int
19、b,int c):B(a,b)z=c;void g(int a)cout“C:z=”z endl;/是重载函数,不是虚函数。void f()cout“Bye!”g();aa1.g();/pa-f();为什么不行?B*pb=&c1,&bb1=c1;pb-g();bb1.g();/pb-f();为什么不行?C*pc=&c1,&cc1=c1;pc-g(1);cc1.g(1);pc-f();从A 类继承的成员:int x;g();虚函数从B 类继承的成员:int y;g();虚函数C 类新增的成员:int z;g(int);不是虚函数f();不是虚函数 pb pa pcC1 对象运行:B:y=2B:y
20、=2B:y=2B:y=2C:z=3C:z=3 Bye!【例】(动态多态性的实现:可以让基类的对象指针,先后指向同一类族中不同派生类的对象,这样就可以用相同的调用方式去调用不同派生类对象中的同名虚函数,从而实现动态多态性。)#includeclass A int x;public:A(int a=0)x=a;cout“调用A 类构造了!n”;virtual void show()cout“A 类 x=”x endl;class B:public A int y;public:B(int a=0,int b=0):A(a)y=b;cout“调用B 类构造了!n”;void show()cout“B
21、 类 y=”y endl;class C:public B int z;public:C(int a=0,int b=0,int c=0):B(a,b)z=c;cout“调用C 类构造了!n”;void show()cout“C 类 z=”z endl;void main()A a1(1);B b1(20,30);C c1(400,500,600);A*p;p=&a1;p-show();p=&b1;p-show();p=&c1;p-show();注意:可以让基类的对象指针,先后指向同一类族中不同派生类的对象,这样就可以用相同的调用方式去调用不同派生类对象中的同名虚函数,从而实现动态多态性。运行
22、:调运A 类构造了!调运A 类构造了!调运B 类构造了!调运A 类构造了!调运B 类构造了!调运C 类构造了!A 类 x=1B 类 y=30C 类 z=600 基类指针Pint x;show()虚函数对象c1int y;show()虚函数int z;show()虚函数int x;show()虚函数int y;show()虚函数对象b1int x;show()虚函数对象a1静态关联、动态关联l 关联(绑定、联编):指程序自身彼此关联的过程,即程序中不同部分互相绑定的过程,以确定程序中的各个函数调用式与所执行的相应函数代码的关系,简单的说,关联就是把某个标识符与某个存储地址联系起来。l 静态关联(
23、静态绑定、静态联编):指关联(绑定、联编)工作出现在编译阶段。例如,程序中使用对象名来调用成员函数时,在编译阶段系统就能根据对象的类型确定所要调用的是哪一个类的成员函数并进行关联。再如,使用对象名来调用某个类族中的虚函数时,在编译阶段系统也能确定要调用的虚函数属于哪一个类并进行关联。此外,函数重载和运算符重载也是在编译阶段进行关联的。l 动态关联(动态绑定、动态联编):指绑定(联编、关联)工作出现在运行阶段。例如,程序中使用基类的对象指针(或基类的对象引用)来调用某个类族中的虚函数时,只有在程序运行过程中,才能根据其具体指向(或具体引用)该类族中的哪一个类的对象,确定出调用的是哪一个类的虚函数
24、。静态关联、动态关联l 虚函数的动态关联:只有通过基类的对象指针(或基类的对象引用)来调用同一类族中派生类的虚函数时,才属于动态关联!原因是:只有在程序运行过程中,系统才能根据该指针(或该引用)具体指向(或具体引用)的是同一类族中的哪一个派生类的对象,确定出调用的是哪一个派生类的虚函数。l 注意:虚函数与一般成员函数比较,调用时执行速度要慢一些。使用虚函数,系统要有一定的空间和时间开销。当一个类中有虚函数时,编译系统会为该类构造一个虚函数表,它是一个指针数组,存放该类的每个虚函数的入口地址。由于虚函数的调用机制是间接实现的,且动态关联是在程序运行阶段,相对会降低程序的运行效率。但通用性也是程序
25、追求的主要目标之一。l 虚函数必须是非静态的成员函数:因为静态成员函数不受限于某个对象。【例】(分别通过对象名、基类的对象指针去调用派生类的虚函数。)#includeclass A int x;public:A()x=10;virtual void show()cout“x=”x endl;class B:public A int y;public:B()y=20;void show()cout“y=”y endl;class C:public A int z;public:C()z=30;void show()cout“z=”z show();p=&b;p-show();/此处调用属于动态关
26、联!p=&c;p-show();/此处调用属于动态关联!运行:x=10y=20z=30 x=10y=20z=30基类指针pint x;show()虚函数int y;show()虚函数对象bint x;show()虚函数对象aint x;show()虚函数int z;show()虚函数对象c【例】(在成员函数中调用虚函数:关键是分析在调用该成员函数时,其 this 指针是基类的对象指针还是派生类的对象指针!)#includeclass A public:virtual void f1()cout f2();void f2()cout f3();virtual void f3()cout f4()
27、;virtual void f4()cout f5();void f5()cout“A:f5n”;class B:public A public:void f3()cout f4();void f4()cout f5();void f5()cout“B:f5n”;void main()B b;b.f1();对象bA 类继承:f1()虚函数f2()f3()虚函数f4()虚函数f5()B 类新增:f3()虚函数f4()虚函数f5()运行:A:f1A:f2B:f3B:f4B:f5【例】(在成员函数中调用虚函数:关键是分析在调用该成员函数时,其 this 指针是基类的对象指针还是派生类的对象指针!)#
28、includeclass A public:void f1()cout f2();void f2()cout f3();virtual void f3()cout f4();virtual void f4()cout f5();void f5()cout“A:f5n”;class B:public A public:void f1()cout f2();void f3()cout f4();void f4()cout f5();void f5()cout f1();A*pa=&b;pa-f1();对象bA 类继承:f1()f2()f3()虚函数f4()虚函数f5()B 类新增:f1()f3()
29、虚函数f4()虚函数f5()运行:B:f1A:f2B:f3B:f4B:f5A:f1A:f2B:f3B:f4B:f5【例】(切记!在构造函数中调用虚函数与在成员函数中调用虚函数有区别!)#includeclass A public:A()f();virtual void f()cout“A:fn”;class B:public A public:B()f();void g1()cout“B:fn”;void g2()f();class C:public B public:C()f();void f()cout“C:fn”;class D:public C public:D()f();void g
30、3()cout“D:fn”;void main()D d;d.g2();A 类继承:f()虚函数对象dB 类继承:g1()g2()C 类继承:f()虚函数D 类新增:g3()注意:在构造函数中调用虚函数时,调用的是本类中定义的虚函数,若本类中没有定义该虚函数,则调用的是上一层基类中定义的该函数。运行:A:fA:fC:fC:fC:f【例】(使用动态多态性,编写程序求球体、圆柱体的体积和表面积。)分析:若球体半径为r,则:其体积 V=4r3/3;表面积 S=4r2。若圆柱底圆半径为r,高为h,则:其体积 V=r2 h;表面积 S=2r(r+h)。由于球体、圆柱体均可从圆继承而来,所以先定义基类 C
31、ircle,在基类中定义两个虚函数求体积虚函数 volume()、求表面积虚函数 area()。球体类 Sphere 和圆柱体类 Cylinder 可由 圆类 Circle 派生而来,在两个派生类中分别对两个虚函数进行重新定义,用于计算球体、圆柱体的体积和表面积。在主函数 main()中,定义一个基类 Circle 的对象指针 p,先后用来指向球体类Sphere、圆柱体类 Cylinder 这同一类族中不同派生类的对象,当基类指针 p 指向包含虚函数的 Sphere 类、Cylinder 类的对象时,系统会根据该指针所指向对象的类型,来决定调用的是哪一个类的虚函数。#includeconst
32、double PI=3.14159;class Circle protected:float radius;public:Circle(float a=0):radius(a)virtual double volume()return 0;/虚函数 virtual double area()return(PI*radius*radius);/虚函数;class Sphere:public Circle public:Sphere(float a=0):Circle(a)double volume()return(4*PI*radius*radius*radius/3);/虚函数 double
33、area()return(4*PI*radius*radius);/虚函数;A*pa=&c1,&aa1=c1;pa-g();aa1.virtual double area()return(PI*radius*radius);/虚函数析构函数可以声明为虚函数。调用过程:调用的是派生类中的虚函数!Circle(float r=0);/构造函数f();不是虚函数virtual double volume()return 0;/虚函数Rectangle r1(7,8);cout g();cc1.class Triangle:public Shapevoid main()若派生类中没有重新定义该虚函数,
34、则调用的是上一层基类中的该虚函数,)g();不是虚函数class Cylinder:public Circle float h;public:Cylinder(float a=0,float b=0):Circle(a)h=b;double volume()return(PI*radius*radius*h);/虚函数 double area()return(2*PI*radius*(h+radius);/虚函数;void main()Circle*p;Sphere s(10);p=&s;cout“球体体积=”volume()endl;cout“球体表面积=”area()endl;Cylind
35、er c(20,30);p=&c;cout“圆柱体积=”volume()endl;cout“圆柱表面积=”area()endl;运行:球体体积=4188.79球体表面积=1256.64 圆柱体积=37699.1圆柱表面积=6283.18 虚析构函数l 虚析构函数的声明:若将基类的析构函数声明为虚函数,则由该基类派生的所有派生类的析构函数都自动成为虚函数,即使这些派生类的析构函数名与基类的析构函数名并不相同!l 必须声明虚析构函数的情况:在由一个基类派生出的类体系中,若需要动态创建派生类对象,就必须将析构函数声明为虚函数,以实现撤消对象时的多态性!这样,若程序中用 delete 运算符去撤消动态
36、分配的派生类对象,而 delete 运算符后面跟着的是指向派生类对象的基类指针,则系统调用的不是基类的析构函数,而是派生类的析构函数!l 习惯的做法:一般在基类中都将析构函数声明为虚析构函数,即使基类并不需要自定义析构函数时,也显式定义一个函数体为空的虚析构函数,以保证在撤消动态分配的派生类对象时能得到正确的处理。【例】(必须声明虚析构函数的情况。)#includeclass A public:A()cout“A 类构造了!n”;A()cout“A 类析构了!n”;virtual void f()cout“A:fn”;void g()f();class B:public A public:B(
37、)f();cout“B 类构造了!n”;B()cout“B 类析构了!n”;class C:public B public:C()f();cout“C 类构造了!n”;C()cout“C 类析构了!n”;void f()cout g();delete pa;运行:A 类构造了!A:fB 类构造了!C:fC 类构造了!C:fA 类析构了!改成:virtual A()cout“A 类析构了!n”;运行变为:A 类构造了!A:fB 类构造了!C:fC 类构造了!C:fC 类析构了!B 类析构了!A 类析构了!纯虚函数l 纯虚函数:定义一个基类时,若无法给出某虚函数的具体实现,而该虚函数的实现完全依赖
38、于不同的派生类,则在基类中将该虚函数声明为纯虚函数。l 纯虚函数的声明:class 抽象类 virtual 返回值类型 成员函数名(形参表)=0;因为无法给出纯虚函数的具体实现,所以没有函数体,但不是函数体为空!函数体为空的函数称为空函数,调用空函数时,不执行任何操作。将函数名赋值为0,本质上是将指向函数体的指针赋值为0。在派生类中没有重新定义纯虚函数之前,是不能调用这种函数的!抽象类只能作为基类使用,不能定义对象!但可定义抽象类的指针!l 抽象类包含有纯虚函数的类 当一个类中包含纯虚函数时,称其为抽象类。由于纯虚函数没有实现部分,所以抽象类不能定义对象。抽象类的唯一作用就是为派生类提供基类,
39、而纯虚函数的作用则是为派生类中的成员函数提供基础!方法是:定义一个抽象类的指针,使其指向不同派生类的对象,而这些派生类中重新定义了纯虚函数,从而实现动态多态性。l 抽象类构造函数或析构函数的访问属性为 protected 的类 当一个类的构造函数或析构函数的访问属性为保护型时,也称其为抽象类。由于构造函数或析构函数为保护型时,类外不能自动调用,所以不能定义对象。但这种抽象类可以作为基类来派生子类!原因是:在派生类中允许调用基类的保护成员。当创建派生类对象时,在派生类的构造函数中可以调用基类的保护型构造函数;当撤消派生类对象时,在析构函数中也可以调用基类的保护型析构函数。综合实例【例】(有各种平
40、面图形若干,例如:圆形、三角形、矩形,试通过定义纯虚函数的方法,求各平面图形的周长和面积。)分析:定义一个描述抽象平面图形的类 Shape,成员函数有:求周长的函数 distance()、求面积的函数 area()。对于抽象平面图形而言,求周长、求面积的公式均无法给出,所以 distance()、area()函数就没有函数体,是纯虚函数,Shape 类是抽象类!抽象类的唯一用途就是为派生类提供基类,纯虚函数的作用是为派生类中的成员函数提供基础,目的是实现动态多态性。圆形类 Circle、三角形类 Triangle、矩形类 Rectangle 均可由抽象类 Shape 派生而来,并在各自的类中对
41、纯虚函数 distance()、area()进行重新定义。通过基类 Shape 的指针,调用各派生类对象的求周长函数 distance()、求面积函数 area(),利用动态联编的方法获得派生类对象的具体函数。friend istream&operator(istream&in,Circle&c);/重载void main()D d1(1,2,3,4);d1.f();不是虚函数要能体现虚函数的特性:必须通过基类的对象指针、基类的对象引用来调用各层次派生类对象的同名虚函数,才能体现虚函数的特性!class Circle:public ShapeCylinder(float a=0,float b
42、=0):Circle(a)h=b;virtual void show()cout“x=”x endl;void g()cout“C:m=”m g();cc1.Circle*p;关联(绑定、联编):指程序自身彼此关联的过程,即程序中不同部分互相绑定的过程,以确定程序中的各个函数调用式与所执行的相应函数代码的关系,简单的说,关联就是把某个标识符与某个存储地址联系起来。void g()cout“C:m=”m(istream&in,Circle&c);/重载 friend ostream&operator(ostream&out,Circle&c);/重载三、由抽象类 Shape 派生出一个描述三角形
43、的类 Triangle,要求:Triangle 类的成员数据:float a,b,c;/存放三角形的三条边 Triangle 类的成员函数、友元函数:Triangle(float x=0,float y=0,float z=0);/构造函数 float distance();/求三角形周长 float area();/求三角形面积 friend istream&operator(istream&in,Triangle&t);/重载 friend ostream&operator(ostream&out,Triangle&t);/重载(istream&in,Rectangle&r);/重载 fr
44、iend ostream&operator(ostream&out,Rectangle&r);/重载#include#includeclass Shape public:virtual float distance()=0;virtual float area()=0;class Circle:public Shape float radius;public:Circle(float r=0)radius=r;float distance()return(2*3.14*radius);float area()return(3.14*radius*radius);friend istream&o
45、perator(istream&in,Circle&c);friend ostream&operator(istream&in,Circle&c)cout c.radius;return in;ostream&operator(ostream&out,Circle&c)out“圆半径=”c.radius endl;out“圆周长=”c.distance()“t 圆面积=”c.area()endl;return out;C*pc=&c1,&cc1=c1;pc-g();cc1.g();/pa-f();为什么不行?f();虚函数#includeiostream.virtual void f4()co
46、ut f5();int z;public:C(int a,int b,int c):B(a,b)z=c;return out;void f1()cout f2();C()cout g();aa1.void main()void main()函数时,调用的是本类中定class Triangle:public Shape float a,b,c;public:Triangle(float x=0,float y=0,float z=0)a=x;b=y;c=z;float distance()return(a+b+c);float area()float s=(a+b+c)/2;return sqr
47、t(s*(s-a)*(s-b)*(s-c);friend istream&operator(istream&in,Triangle&t);friend ostream&operator(istream&in,Triangle&t)cout t.a t.b t.c;return in;ostream&operator(ostream&out,Triangle&t)out“三角形三边=”t.a“、”t.b“、”t.c endl;out“三角形周长=”t.distance()“t 三角形面积=”t.area()endl;return out;class Rectangle:public Shape
48、float length,width;public:Rectangle(float L=0,float W=0)length=L;width=W;float distance()return(2*(length+width);float area()return(length*width);friend istream&operator(istream&in,Rectangle&r);friend ostream&operator(istream&in,Rectangle&r)cout r.length r.width;return in;ostream&operator(ostream&ou
49、t,Rectangle&r)out“矩形的长、宽=”r.length“、”r.width endl;out“矩形周长=”r.distance()“t 矩形面积=”r.area()endl;return out;void main()Shape*p;Circle c1(3);cout c1;p=&c1;cout“圆周长=”distance();cout“t 圆面积=”area()endl;Triangle t1(3,4,5);cout t1;p=&t1;cout“三角形周长=”distance();cout“t 三角形面积=”area()endl;Rectangle r1(7,8);cout r1;p=&r1;cout“矩形周长=”distance();cout“t 矩形面积=”area()endl;运行:圆半径=3 圆周长=18.84 圆面积=28.26请输入圆半径:4 圆周长=25.12 圆面积=50.24三角形三边长=3、4、5三角形周长=12 三角形面积=6请输入三角形的三边:4 5 6 三角形周长=15 三角形面积=9.92157矩形的长、宽=7、8矩形周长=30 矩形面积=56请输入矩形的长、宽:8 9 矩形周长=34 矩形面积=72
限制150内