• 1. 第5章 多态性与虚函数 5.1 多态性概述 5.2 运算符重载 5.3 虚函数 5.4 纯虚函数和抽象类
  • 2. 在OOP中: 窗口对象 行为3 移动 同一消息 棋子对象 行为45.1 多态性概述多态性是面向对象程序设计的重要特征之一。 多态性是指发出同样的消息被不同类型的对象接收时导致完全不同的行为。多态的实现: 1 函数重载 2 运算符重载 3 虚函数
  • 3. 在C++中,多态的实现和联编这一概念有关。所谓联编就是把函数名与函数体的程序代码连接(联系)在一起的过程。 静态联编 联编 动态联编 静态联编就是在编译阶段完成的联编。静态联编函数调用速度很快。效率高, 但缺乏灵活性; 动态联编是运行阶段完成的联编。动态联编在运行时才能确定调用哪个函数,它降低了程序的运行效率,但增强了程序的灵活性。 C++实际上是采用了静态联编和动态联编相结合的联编方法。
  • 4. 编译时的多态性 多态性 运行时的多态性 编译时的多态是通过静态联编来实现的。 运行时的多态是通过动态联编实现的。 在C++中: 编译时多态性主要是通过函数重载和运算符重载实现的。 运行时多态性主要是通过虚函数来实现的。
  • 5. 问题举例——复数的运算class complex //复数类声明 { public: complex(double r=0.0,double i=0.0) //构造函数 { real=r; imag=i; } void display(); //显示复数的值 private: double real; double imag; }; 5.2 运算符重载
  • 6. 问题举例——复数的运算用“+”、“-”能够实现复数的加减运算吗? 实现复数加减运算的方法 ——重载“+”、“-”运算符
  • 7. 运算符重载的实质运算符重载是对已有的运算符赋予多重含义 必要性 C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类) 实现机制 将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。 编译系统对重载运算符的选择,遵循函数重载的选择原则。
  • 8. 规则和限制可以重载C++中除下列运算符外的所有运算符: . .* :: ?: 只能重载C++语言中已有的运算符,不可臆造新的。 不改变原运算符的优先级和结合性。 不能改变操作数个数。 经重载的运算符,其操作数中至少应该有一个是自定义类型。
  • 9. 两种形式重载为类的非静态成员函数。 重载为非成员函数(通常为友元函数)。
  • 10. 运算符函数声明形式 函数类型 operator 运算符(形参) { ...... } 重载为类成员函数时 参数个数=原操作数个数-1 (后置++、--除外) 重载为友元函数时 参数个数=原操作数个数,且至少应该有一个自定义类型的形参。
  • 11. 运算符成员函数的设计双目运算符 B 如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2 所属的类型。 经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)
  • 12. 例 将“+”、“-”运算重载为复数类的成员函数。 规则: 实部和虚部分别相加减。 操作数: 两个操作数都是复数类的对象。
  • 13. 5-1.cpp #include class complex //复数类声明 { public: //外部接口 complex(double r=0.0,double i=0.0){real=r;imag=i;} //构造函数 complex operator + (complex c2); //+重载为成员函数 complex operator - (complex c2); //-重载为成员函数 void display(); //输出复数 private: //私有数据成员 double real; //复数实部 double imag; //复数虚部 };
  • 14. complex complex::operator +(complex c2) //重载函数实现 { complex c; c.real=c2.real+real; c.imag=c2.imag+imag; return complex(c.real,c.imag); }
  • 15. complex complex:: operator -(complex c2) //重载函数实现 { complex c; c.real=real-c2.real; c.imag=imag-c2.imag; return complex(c.real,c.imag); }
  • 16. void complex::display() { cout<<"("<
  • 17. 程序输出的结果为: c1=(5,4) c2=(2,10) c3=c1-c2=(3,-6) c3=c1+c2=(7,14)
  • 18. 运算符成员函数的设计前置单目运算符 U 如果要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,无形参。 经重载后, 表达式 U oprd 相当于 oprd.operator U()
  • 19. 后置单目运算符 ++和-- 如果要重载 ++或--为类成员函数,使之能够实现表达式 oprd++ 或 oprd-- ,其中 oprd 为A类对象,则 ++或-- 应被重载为 A 类的成员函数,且具有一个 int 类型形参。 经重载后,表达式 oprd++ 相当于 oprd.operator ++(0)运算符成员函数的设计
  • 20. 例运算符前置++和后置++重载为时钟类的成员函数。 前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有一个整型形参。 操作数是时钟类的对象。 实现时间增加1秒钟。
  • 21. //5-2.cpp #include class Clock //时钟类声明 { public: //外部接口 Clock(int NewH=0, int NewM=0, int NewS=0); void ShowTime(); Clock operator ++(); //前置单目运算符重载 Clock operator ++(int); //后置单目运算符重载 private: //私有数据成员 int Hour,Minute,Second; };
  • 22. Clock Clock::operator ++() //前置单目运算符重载函数 { Second++; if(Second>=60) { Second=Second-60; Minute++; if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } } cout<<“++Clock:”; }
  • 23. //后置单目运算符重载 Clock Clock::operator ++(int) { Second++; if(Second>=60) { Second=Second-60; Minute++; if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } } cout<<“++Clock:”; }
  • 24. //其它成员函数的实现略 int main() { Clock myClock(23,59,59); cout<<"First time output:"; myClock.ShowTime(); cout<<"Show myClock++:"; (myClock++).ShowTime(); cout<<"Show ++myClock:"; (++myClock).ShowTime(); }
  • 25. 程序运行结果为: First time output: 23:59:59 Show myClock++: 0:0:0 Show ++myClock: 0:0:1
  • 26. 非成员运算符函数的设计如果需要重载一个运算符,使之能够用于操作某类对象的私有成员,可以此将运算符重载为该类的非成员(通常为友元)函数。 函数的形参代表依自左至右次序排列的各操作数。(参数个数=操作数个数) 后置单目运算符 ++和--的重载函数,形参列表中要增加一个int,但不必写形参名。
  • 27. 非成员运算符函数的设计双目运算符 B重载后, 表达式oprd1 B oprd2 等同于operator B(oprd1,oprd2 ) 前置单目运算符 B重载后, 表达式 B oprd 等同于operator B(oprd ) 后置单目运算符 ++和--重载后, 表达式 oprd B 等同于operator B(oprd,0 )
  • 28. 例将+、-(双目)重载为复数类的友元函数。 两个操作数都是复数类的对象。
  • 29. 5-3.cpp #include class complex //复数类声明 { public: //外部接口 complex(double r=0.0,double i=0.0) { real=r; imag=i; } //构造函数 friend complex operator + (complex c1,complex c2); //运算符+重载为友元函数 friend complex operator - (complex c1,complex c2); //运算符-重载为友元函数 void display(); //显示复数的值 private: //私有数据成员 double real, imag; };
  • 30. complex operator + (complex c1,complex c2) //运算符重载友元函数实现 { return complex(c2.real+c1.real, c2.imag+c1.imag); } complex operator -(complex c1,complex c2) //运算符重载友元函数实现 { return complex(c1.real-c2.real, c1.imag-c2.imag); } // 其他函数和主函数同上例
  • 31. 5.3 虚 函 数 5.3.1 虚函数的引入 5.3.2 虚函数的作用和定义 5.3.3 虚析构函数 5.3.4 虚函数与重载函数的关系 5.3.5 多继承与虚函数
  • 32. #include class base { //例5.2-1 虚函数引例 int a,b; public: base(int x,int y) { a=x; b=y; } void show( ) { cout<<"调用基类base的show函数\n"; cout<<"a=" <33. #include //例5.2-1 虚函数引例 class base { int a,b; public: base(int x,int y) { a=x; b=y; } void show( ) { cout<<"调用基类base的show函数\n"; cout<<"a=" <34. 在C++中规定: 基类的对象指针可以指向它的公有派生的对象,但是当其指向公有派生类对象时, 它只能访问派生类中从基类继承来的成员, 而不能访问公有派生类中定义的成员,例如: class A { public:void print1() {. . .} }; class B:public A { public:void print2(){. . .} }; void main() {A *p1; //定义基类A的指针变量p1 B op2; //定义派生类B的对象op2 p1=&op2;//将指针变量p1指向派生类对象op2 p1->print1(); p1->print2(); } 正确, 基类指针变量p1可以访问派生类中从基类继承来的成员函数print1() 错误,基类指针变量p1不能访问派生类中定义的成员函数print2()
  • 35. #include //例5.2-1 虚函数引例 class base { int a,b; public: base(int x,int y) { a=x; b=y; } void show( ) { cout<<"调用基类base的show函数\n"; cout<<"a=" <36. 例如: void main( ) { base mb(50,50), *mp; dirive mc(10,20,30); mp=&mb; mp->show( ); mp=&mc; mp->show( ); } 问题:能否用同一个语句“mp->show()”既能访问基类又能访问派生类的同名函数呢? 希望分别调用基类base的show()函数和派生类dirive的 show()函数 C++中可以用虚函数来解决这个问题。
  • 37. 5.3.2 虚函数的作用和定义 1.虚函数的作用 虚函数就是在基类中被关键字virtual说明,并在派生类中重新定义的函数。 class base{ int a,b; public: . . . virtual void show( ){. . . } }; class dirive:public base{ int c; public: . . . void show( ){ . . . } };在基类中,show()定义为虚函数在派生类中,重新定义虚函数show()
  • 38. 虚函数定义后,就可以通过基类指针访问基类和派生类中的虚函数。 void main( ) { base mb(50,50), *mp; dirive mc(10,20,30); mp=&mb; mp->show(); mp=&mc; mp->show(); } 调用基类base的show()函数 调用派生类dirive的show()函数
  • 39. #include //例5.3-1 class base { int a,b; public: base(int x,int y) { a=x; b=y; } virtual void show( ) {cout<<"调用基类base的show函数\n"; cout<<"a=" <40. #include //例5.3-1 class base { int a,b; public: base(int x,int y) { a=x; b=y; } virtual void show( ) {cout<<"调用基类base的show函数\n"; cout<<"a=" <41. 为什么把基类中的show()函数定义为虚函数时,程序的运行结果就正确了呢? 这是因为,关键字virtual指示C++编译器,使用语句“mp->show()”调用虚函数时,采用动态联编的方式,即在运行时确定调用哪个show()函数。 我们把使用同一种调用形式“mp->show()”,调用同一类族中不同类的虚函数称为动态的多态性,即运行时的多态性。 可见,虚函数与派生类的结合可使C++支持运行时的多态性。
  • 42. 2.虚函数的定义 定义虚函数的格式如下: 重新定义虚函数时,要求: ①该函数与基类的虚函数有相同的名称; ②该函数与基类的虚函数有相同的参数个数及相同的对应参数类型; ③该函数与基类的虚函数有相同的返回类型或者满足赋值兼容规则的指针、引用型的返回类型。virtual 函数类型 函数名(形参表) { 函数体 }
  • 43. #include //例5.4虚函数的定义举例。 class Grandam{ //声明基类Grandam public: virtual void introduce_self() //定义虚函数introduce_self { cout<<"I am grandam."<
  • 44. int main() { Grandam *ptr; //定义指向基类的对象指针ptr Grandam g; //定义基类对象g Mother m; //定义派生类对象m Daughter d; //定义派生类对象d ptr=&g; //对象指针ptr指向基类对象g ptr->introduce_self(); //调用基类Grandam的虚函数 ptr=&m; //对象指针ptr指向派生类对象m ptr->introduce_self(); //调用派生类Mother的虚函数 ptr=&d; //对象指针ptr指向派生类对象d ptr->introduce_self(); //调用派生类Daughter的虚函数 return 0; }本程序运行的结果如下: I am grandam. I am mother. I am daughter.
  • 45. 说明: (1) 通过定义虚函数来使用多态性机制时,派生类必须从它的基类公有派生。 (2)在基类中,声明虚函数原型(需加上virtual ),而在类外定义虚函数时,则不必再加virtual。例如: class B0 { public: virtual void print(char* p); }; void B0::print(char* p ) { cout<
  • 46. (3) 如果在派生类中没有对基类的虚函数重新定义,则公有派生类继承其直接基类的虚函数。一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。 例如: class B0{ . . . public: virtual void show(){. . .} }; class B1:public B0{ ... }; 若在公有派生类B1中没有重新定义虚函数show, 则函数show在派生类中被继承,仍是虚函数。在基类B0中,定义show为虚函数
  • 47. (4) 在派生类中重新定义该虚函数时,关键字virtual可以写也可以不写。 但是,为了使程序更加清晰,最好在每一层派生类中定义该函数时都加上关键字virtual。 class B0{ . . . public: virtual void show(){. . .} }; class B1:public B0{ ... virtual void show (){. . .} }; (5) 虽然使用对象名和点运算符的方式也可以调用虚函数,但是这种调用是在编译时进行的,是静态联编,它没有利用虚函数的特性。 只有通过基类指针访问虚函数时才能获得运行时的多态性。在基类B0中,定义show为虚函数 在派生类中,重新定义虚函数show()时,最好也加上关键字virtual
  • 48. #include //例 class base { int a,b; public: base(int x,int y) { a=x; b=y; } virtual void show( ) {cout<<"调用基类base的show函数\n"; cout<<"a=" <49. (6) 虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数,因为虚函数调用要靠特定的对象来决定该激活哪个函数。 (7) 内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的。即使虚函数在类的内部定义,编译时仍将其看作是非内联的。 (8) 构造函数不能是虚函数,一般情况下,析构函数不必定义为虚函数。但在特殊情况下,例如当主程序中使用指向基类的指针指向新建立的派生类对象时,如不将析构函数定义为虚函数,则主程序结束时,派生类的析构函数将被忽略。
  • 50. 5.3.3 虚析构函数 #include // 虚析构函数的引例1。 class Base{ public: ~Base() { cout<<"调用基类Base的析构函数\n";} }; class Derived:public Base{ public: ~Derived () { cout<<"调用派生类Derived的析构函数\n";} }; int main() { Derived obj; return 0; }程序运行的结果为: 调用派生类Derived的析构函数 调用基类Base的析构函数
  • 51. #include // 虚析构函数的引例2。 class B{ public: ~B() { cout<<"调用基类B的析构函数\n";} }; class D:public B{ public: ~D() { cout<<"调用派生类D的析构函数\n";} }; int main() { B *p; //定义指向基类B的指针变量p p= new D; delete p;   return 0; } 用运算符new为派生类的无名对象动态地 分配了一个存储空间,并将地址赋给对象指针p用delete撤销无名对象,释放动态存储空间程序运行的结果为: 调用基类B的析构函数 本程序只执行了基类B的析构函数,而没有执行派生类D的析构函数。原因是当撤销指针P所指的派生类的无名对象, 调用析构函数时,采用了静态联编方式,只调用了基类B的析构函数。编译系统在编译时因为指针p定义为指向基类的指针,所以派生类对象只能通过p访问基类的析构函数而访问不到派生类的析构函数。
  • 52. 5.3.4 虚函数与重载函数的关系 (1) 在一个派生类中重新定义虚函数是函数重载的另一种形式,但它不同于一般的函数重载。 (2) 当普通的函数重载时,其函数的参数个数或参数类型必须有所不同,函数的返回类型也可以不同。 但是,当重载一个虚函数时,要求函数名、返回类型、参数个数、参数的类型和顺序与基类中的虚函数原型完全相同。
  • 53. (3) 当重载一个虚函数时, 如果仅仅返回类型不同,其余均相同,系统会给出错误信息; 例如: class base{ public: virtual void f3( ); }; class derived:public base { public: char f3( ); }; 两个函数f3() 仅仅返回类型不同,将给出错误信息。
  • 54. (4) 若仅仅函数名相同,而参数的个数、类型或顺序不同,系统将它作为普通的函数重载,这时将失去虚函数的特性。 例如: class base{ public: virtual void f2( ); }; class derived:public base{ public: void f2(int x); }; 两个函数f2()函数名相同,但参数的个数和类型不同,作为普通的函数重载,这时将失去虚函数的特性。
  • 55. #include class base{ public: virtual void func1( ); virtual void func2( ); virtual void func3( ); void func4( ); }; class derived:public base { public: void func1( ); //是虚函数,这里可不写virtual void func2(int x); //作为普通函数重载,虚特性消失 char func3( ); void func4( ); }; //是普通函数重载,不是虚函数 void base::func1( ){cout<<"--base func1--\n";} void base::func2( ){cout<<"--base func2--\n";} void base::func3( ){ cout<<"--base func3--\n";} // void base::func4( ){ cout<<"--base func4--\n";} void derived::func1( ){cout<<"-derived func1-\n";} void derived::func2(int x){cout<<"derived func2\n";} void derived::func4( ){cout<<"-derived func4-\n";} 错误,因为与基类中的func3只有返回类型不同
  • 56. #include class base{ public: virtual void func1( ); virtual void func2( ); void func4( ); }; class derived:public base{ public: void func1( ); //是虚函数,这里可不写virtual void func2(int x); //作为普通函数重载,虚特性消失 void func4( ); }; //是普通函数重载,不是虚函数 void base::func1( ){cout<<"--base func1--\n";} void base::func2( ){cout<<"--base func2--\n";} void base::func4( ){ cout<<"--base func4--\n";} void derived::func1( ){cout<<"-derived func1-\n";} void derived::func2(int x){cout<<"derived func2\n";} void derived::func4( ){cout<<"-derived func4-\n";}将错误语句去掉后,运行结果?void main( ) { base d1,*bp; derived d2; bp=&d2; bp->func1( ); bp->func2( ); bp->func4( ); } 程序执行结果如下: --derived func1-- --base func2— --base func4—
  • 57. 5.3.5 多继承与虚函数 多重继承可以视为多个单继承的组合。因此,多重继承情况下的虚函数的调用与单继承情况下的虚函数的调用有相似之处。
  • 58. #include class A1{ public: virtual void f( ){ cout<<“--A1--\n";} }; class A2{ public: void f( ){cout<<"--A2--\n";} }; class B: public A1,public A2{ public: void f( ){cout<<"--B--\n";} }; void main( ) {A1 obj1,*ptr1; //定义指向基类A1的指针ptr1 A2 obj2,*ptr2; //定义指向基类A2的指针ptr2 B obj3; //定义派生类B的对象obj3 ptr1=&obj1; ptr1->f( ); //调用基类A1的f() ptr2=&obj2; ptr2->f( ); //调用基类A2的f() ptr1=&obj3; ptr1->f( ); ptr2=&obj3; ptr2->f( ); } 此处调用的f()为非虚函数,因此调用基类B的f() 此处调用的f()为虚函数,因此调用派生类B的f() 虚函数 非虚函数,普通的成员函数对虚函数进行重定义运行结果: --A1-- --A2-- --B-- --A2--
  • 59. 5.4 纯虚函数和抽象类 5.3.1 纯虚函数的引例 1.纯虚函数的引例 应用C++的虚函数,计算三角形、矩形和圆的面积。 圆类 Circle 虚函数area() 三角形类Triangle 虚函数area()矩形类Square 虚函数area() 基类Figure 虚函数 area( )
  • 60. class Figure { protected: double x,y; public: Figure(double a,double b){ x=a;y=b; } virtual void area( ) { cout<<"在基类中定义的虚函数\n";} }; 定义一个公共基类Figure 定义虚函数area(),为派生类提供一个公共接口,以便派生类根据需要重新定义虚函数。
  • 61. class Triangle:public Figure { public: Triangle(double a,double ) :Figure(a,b) { } void area( ) { cout<<"三角形的高是"<
  • 62. class Square:public Figure { public: Square(double a,doubleb):Figure(a,b) { } void area( ) { cout<<"矩形的长是"<
  • 63. class Circle:public Figure { public: Circle(double a):Figure(a,a) { } void area( ) { cout<<"圆的半径是"<
  • 64. int main( ) { Figure *p; //定义基类指针p Triangle t(10.0,5.0); //定义三角形类对象t Square s(10.0,5.0); //定义矩形类对象s Circle c(10.0); //定义圆类对象c p=&t; p->area(); p=&s; p->area(); p=&c; p->area(); return 0; }计算三角形的面积计算矩形的面积计算圆的面积
  • 65. 2.纯虚函数 class Figure { protected: double x,y; public: Figure(double a,double b){ x=a; y=b; } virtual void area( ) { cout<<"在基类中定义的虚函数\n"; } }; 基类本身并不需要这个虚函数,而是为派生类提供一个公共接口,以便派生类根据需要重新定义虚函数。
  • 66. class Figure { protected: double x,y; public: Figure(double a,double b){ x=a; y=b;} virtual void area( )=0; }; 将函数area()简化表示成这种形式 纯虚函数纯虚函数是在声明虚函数时被“初始化”为0的虚函数。
  • 67. 纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行重新定义。 纯虚函数的特点: ① 纯虚函数没有函数体; ② 最后面的“=0”并不表示函数的返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”; ③ 纯虚函数不具备函数的功能,不能被调用。 纯虚函数的一般形式如下: virtual 函数类型 函数名(参数表)=0;
  • 68. 例 使用纯虚函数的例子。 #include class Circle { protected: int r; public: void setr(int x){ r=x; } virtual void show( )=0; }; class Area:public Circle{ public: void show( ) { cout<<"这个圆的面积是:"<<3.14*r*r<show(); ptr=&ob2; ptr->show(); }程序运行结果如下: 这个圆的面积是: 314 这个圆的周长是: 62.8
  • 69. 5.3.2.抽象类 什么是抽象类? 如果一个类至少有一个纯虚函数,那么就称该类为抽象类。 class Figure{ protected: double x,y; public: Figure(double a,double b){ x=a; y=b;} virtual void area( )=0; }; 纯虚函数抽象类
  • 70. 抽象类的作用 抽象类的作用是作为一个类族的共同基类,相关的派生类是从这个基类派生出来的。 使用抽象类的几点规定 (1) 由于抽象类中至少包含有一个没有定义功能的纯虚函数, 因此抽象类只能用作其他类的基类,不能建立抽象类对象。 (2) 不允许从具体类派生出抽象类。所谓具体类,就是不包含纯虚函数的普通类; (3) 抽象类不能用作函数的参数类型、函数的返回类型或显式转换的类型;
  • 71. (4) 可以声明指向抽象类的指针或引用,此指针可以指向它的派生类,进而实现多态性。 (5) 在派生类中,如果对基类的纯虚函数没有重新定义,则该函数在派生类中仍是纯虚函数,该派生类仍为抽象类。 例如: class B0 { public: virtual void show()=0; . . .}; class B1:public B0 { . . . }; 在基类定义show()为纯虚函数,基类B0为抽象类。 在派生类中show()函数没有重新定义,则函数show()在派生类中仍是纯虚函数。派生类B1仍是抽象类。
  • 72. 5.4 程序举例 应用抽象类,求圆、圆内接正方形和圆外切正方形的面积和周长。
  • 73. 例 应用抽象类,求圆、圆内接正方形和圆外切正方形的面积和周长。圆外切正方形类 Ex_square 虚函数 area( ) 虚函数 perimeter()圆内接正方形类 In_square 虚函数 area() 虚函数 perimeter() 基类Figure 纯虚函数 area() 纯虚函数 perimeter() 圆类Circle 虚函数area() 虚函数perimeter()
  • 74. 定义一个抽象类Figure class Figure { protected: double r; public: Figure(double x){ r=x; } virtual void area()=0; //纯虚函数 virtual void perimeter()=0; //纯虚函数 };
  • 75. class Circle:public Figure{ public: Circle(double x):Figure(x){ } void area() { cout<<"圆的面积是"<<3.14*r*r<
  • 76. class In_square:public Figure{ public: In_square(double x):Figure(x){ } void area() { cout<<" 圆内接正方形的面积是"; cout<<2*r*r<
  • 77. class Ex_square:public Figure{ public: Ex_square(double x):Figure(x){ } void area() { cout<<" 圆外切正方形的面积是"; cout<<4*r*r<
  • 78. int main() { Figure *ptr; //定义抽象类Figure的指针ptr Circle ob1(5); //定义类Circle的对象ob1 In_square ob2(5); //定义类In_square的对象ob2 Ex_square ob3(5); //定义类Ex_square的对象ob3 ptr=&ob1; ptr->area(); //求圆的面积 ptr->perimeter(); //求圆的周长 ptr=&ob2; ptr->area(); //求圆内接正方形的面积 ptr->perimeter(); //求圆内接正方形的周长 ptr=&ob3; ptr->area(); //求圆外切正方形的面积 ptr->perimeter(); //求圆外切正方形的周长 return 0; }
  • 79. 抽象类和虚函数使程序的扩充非常容易。 例如,在上述程序中,增加一个计算圆外切三角形面积和周长的功能: class Triangle∶public Figure{ public: Triangle(double x)∶Figure(x){ } void area() { cout<<"圆外切三角形的面积是"; cout<<3*1.732*r*r<
  • 80. 在main()函数中增加下述几条语句: Triangle ob4(5); ptr=&ob4; ptr->area(); ptr->perimeter(); 程序运行后,即可打印出相应圆外切三角形面积和周长: 圆外切三角形的面积是129.9 圆外切三角形的周长是51.96