11.3.2 抽象类

    一个类可以包含多个纯虚函数。只要类中含有一个纯虚函数,该类便为抽象类。一个抽象类只能作为基类来派生新类,不能创建抽象类的对象,如代码11.6中的类A便是抽象类,创建类A的对象是非法的,如下所示。


    A a;//错误:A为抽象类但可声明一个指向抽象类的指针,如下所示。 A*a=NULL; A*a=new B;

    注意

    “A*a=new A;”非法,因为该语句试图创建A的对象。

    同普通的虚函数不同,纯虚函数不能被自动继承,在派生类中必须对基类中的虚函数进行重定义,或者在派生类中再次将该虚函数声明为纯虚函数,否则编译器将提示错误信息。这说明,抽象类的派生类也可以是抽象类,只有在派生类中给出了基类中所有纯虚函数的实现时,该派生类便不再是抽象类。同纯虚函数一样,抽象类只起到提供统一接口的作用。

    注意

    所谓不能继承,指的是纯虚函数的“纯”不会被自动继承,virtual属性还是会保持下来的,也就是说,在代码11.6中,即使类B的disp函数在定义时去掉virtual,该函数仍旧是虚函数。

    在成员函数内可以调用纯虚函数,但在构造函数或者析构函数内调用一个纯虚函数将导致程序运行错误,因为在函数中没有纯虚函数定义代码。此外,还要注意区分纯虚函数和空的虚函数的区别,如下所示。


    virtual void disp()=0;//纯虚函数 virtual void disp()//空的虚函数 { };

    空的虚函数指的是函数体为空,不执行任何操作,而纯虚函数是未定义任何操作。

    先来看一个抽象类的使用范例,如代码11.7所示。

    代码11.7 抽象类的应用AbstractClass


    <———————————————-文件名:shape.h———————————————> 01 #include<iostream> 02 #include<cmath> 03 using namespace std; 04 #define PI 3.1415926//宏定义 05 class Figure//图形基类定义 06 { 07 public: 08 virtual float Area()=0;//纯虚函数,因此Figure类是抽象类,无法声明其对象 09 virtual void DispName()=0; 10 }; 11 class Circle:public Figure//在抽象类Figure的基础上派生Circle圆类 12 { 13 private://private成员列表 14 float radius;//半径 15 public: 16 Circle(float r=0)//构造函数 17 { 18 radius=r; 19 } 20 virtual void DispName()//覆盖实现了虚函数DispName,此处去掉virtual没有影响 21 { 22 cout<<"圆:"<<endl; 23 } 24 25 virtual float Area()//覆盖实现了虚函数Area,用来计算圆的面积,去掉virtual同样没有影响 26 { 27 return PIradiusradius; 28 } 29 }; 30 class Rectangle:public Figure//在抽象类Figure的基础上派生Rectangle矩形类 31 { 32 private: 33 float x;//两个边长x和y 34 float y; 35 public: 36 Rectangle(float xp=0,float yp=0)//构造函数 37 { 38 x=xp; 39 y=yp; 40 } 41 42 virtual void DispName()//覆盖实现了虚函数DispName,此处去掉virtual没有影响 43 { 44 cout<<"矩形:"<<endl; 45 } 46 //覆盖实现了虚函数Area,用来计算矩形面积,此处去掉virtual同样没有影响 47 virtual float Area() 48 { 49 return x*y; 50 } 51 52 }; 53 class Triangle:public Figure//在抽象类Figure的基础上派生Triangle三角形类 54 { 55 private://三角形的三个边长 56 float x; 57 float y; 58 float z; 59 public: 60 Triangle(float xp=0,float yp=0,float zp=0)//构造函数 61 { 62 x=xp; 63 y=yp; 64 z=zp; 65 } 66 67 virtual void DispName(){//覆盖实现了虚函数DispName,此处去掉virtual没有影响 68 cout<<"三角形:"<<endl; 69 } 70 71 virtual float Area()//覆盖实现了虚函数Area,用来计算三角形的面积,此处去掉virtual没有影响 72 { 73 float p=(x+y+z)/2; 74 return sqrt(p(p-x)(p-y)*(p-z)); 75 } 76 }; <————————————-文件名:example1107.cpp——————————————> 77 #include"shape.h" 78 int main() 79 { 80 Figure*pF=NULL;//虽然不能创建Figure类对象,但可声明Figure型的指针 81 Circle c(3);//声明一个圆对象,半径为3 82 Rectangle r(1. 2f,3.6f);//声明一个矩形对象,其边长分别为1.2和3.6 83 Triangle t(6,7,8);//声明一个三角形对象,其边长分别为6、7和8 84 pF=&c;//用圆对象c的地址为pF赋值 85 pF->DispName();//调用函数DispName时,对应着Circle类中的版本 86 cout<<pF->Area()<<endl;//调用函数Area时,对应着Circle类中的版本 87 pF=&r;//用矩形对象r的地址为pF赋值 88 pF->DispName();//调用函数DispName时,对应着Rectangle类中的版本 89 cout<<pF->Area()<<endl;//调用函数Area时,对应着Rectangle类中的版本 90 pF=&t;//用三角形对象t的地址为pF赋值 91 pF->DispName();//调用函数DispName时,对应着Triangle类中的版本 92 cout<<pF->Area()<<endl;//调用函数Area时,对应着Triangle类中的版本 93 return 0; 94 }

    输出结果如下所示。


    圆: 28 .2743 矩形: 4.32 三角形: 20 .3332

    【代码解析】shape.h文件中定义了抽象基类Figure,并由其派生出3个新类如下所示。


    Circle、Rectangle、Triangle

    代码第8行,在抽象类Figure中定义的纯虚函数Area用于求图形的面积,代码第9行的DispName()用于输出类名,在Figure类中,求面积和输出类名的操作没有实际意义,所以将两个函数定义为纯虚函数,为派生类中对应的虚函数提供了访问接口。

    在main函数中只定义了指向抽象基类Figure的指针pF,使用不同的派生类对象为该指针赋值时,“pF->Area()”和“pF->DispName()”执行不同的操作,真正实现了“一个接口,多种方法”,体现了多态性的程序设计理念。