设为首页 加入收藏

TOP

9.6.5 抽象类
2013-10-07 12:38:33 来源: 作者: 【 】 浏览:53
Tags:9.6.5 抽象

9.6.5  抽象类

包含纯虚函数的类被称为抽象类,因为我们不能定义包含纯虚函数的类的对象。抽象类存在的唯一用途,就是定义派生类。如果抽象类的派生类将基类的纯虚函数仍然定义为纯虚函数,则该派生类也是抽象类。

我们不应该从上一个CContainer类的示例中得出抽象类不能拥有数据成员的结论。抽象类可以拥有数据成员和函数成员。纯虚函数是否存在是判断给定的类是否是抽象类的唯一条件。同样的道理,抽象类可以拥有多个纯虚函数。这种情况下,派生类必须给出基类中每个纯虚函数的定义,否则将仍然是抽象类。如果我们忘记将派生类的Volume()函数指定为const,则派生类同样将仍然是抽象类,因为它不仅包含我们定义的非const函数Volume(),还包含const纯虚函数成员Volume()。

试一试:抽象类

我们可以连同原来的CBox类一起再实现一个CCan类-- 它可能表示啤酒或可乐罐,令这两个类都派生自9.6.4小节定义的CContainer类。作为CContainer类的子类,CBox类的定义如下所示:

  1. // Box.h for Ex9_10  
  2. #pragma once  
  3. #include "Container.h" // For CContainer definition  
  4. #include <iostream> 
  5. using std::cout;  
  6. using std::endl;  
  7.  
  8. class CBox: public CContainer   // Derived class  
  9. {  
  10. public:  
  11.  
  12. // Function to show the volume of an object  
  13. virtual void ShowVolume() const  
  14. {  
  15. cout << endl 
  16. << "CBox usable volume is " << Volume();  
  17. }  
  18.  
  19. // Function to calculate the volume of a CBox object  
  20. virtual double Volume() const  
  21. { return m_Length*m_Width*m_Height; }  
  22.  
  23. // Constructor  
  24. CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0)  
  25.                                  :m_Length(lv), 
    m_Width(wv), m_Height(hv){}  
  26. protected:  
  27. double m_Length;  
  28. double m_Width;  
  29. double m_Height;  
  30. }; 

不带阴影的行与以前CBox类的版本相同。CBox类实质上与以前的示例中一样,只是这次我们将其指定为从CContainer类派生。Volume()函数在这个类中被完全定义(如果CBox类要用来定义对象,那么就必须如此)。仅有的其他选择是将其指定为纯虚函数,因为该函数在基类中就是纯虚函数,但那样我们将不能创建CBox对象。

我们可以像下面这样在Can.h头文件中定义CCan类:

  1. // Can.h for Ex9_10  
  2. #pragma once  
  3. #include "Container.h" // For CContainer definition  
  4. extern const double PI; // PI is defined elsewhere  
  5.  
  6. class CCan: public CContainer  
  7. {  
  8. public:  
  9. // Function to calculate the volume of a can  
  10. virtual double Volume() const  
  11. { return 0.25*PI*m_Diameter*m_Diameter*m_Height; }  
  12.  
  13. // Constructor  
  14. CCan(double hv = 4.0, double dv = 2.0): 
    m_Height(hv), m_Diameter(dv){}  
  15.  
  16. protected:  
  17. double m_Height;  
  18. double m_Diameter;  
  19. };  

CCan类也定义了Volume()函数,只不过依据的公式是hπr2,这里的h是罐的高度,r是罐体横截面的半径。CCan对象的体积是高乘以底面积。该函数中的表达式假定有一个已定义的全局常量PI,因此我们使用一条extern语句来指出PI是个在其他地方定义的const double类型的全局变量-- 该变量在本程序中是在Ex9_10.cpp文件中定义的。另外注意,我们在CBox类中重新定义了ShowVolume()函数,但在CCan类中却没有这样做。当我们得到程序的输出时,将看出结果有所不同。

我们可以用下面这个包含main()函数的源文件,练习一下这些类的用法。

  1. // Ex9_10.cpp  
  2. // Using an abstract class  
  3. #include "Box.h" // For CBox and CContainer  
  4. #include "Can.h" // For CCan (and CContainer)  
  5. #include <iostream>     // For stream I/O  
  6. using std::cout;  
  7. using std::endl;  
  8.  
  9. const double PI3.14159265;    // Global definition for PI  
  10.  
  11. int main(void)  
  12. {  
  13. // Pointer to abstract base class  
  14. // initialized with address of CBox object  
  15. CContainer* pC1 = new CBox(2.0, 3.0, 4.0);  
  16.  
  17. // Pointer to abstract base class  
  18. // initialized with address of CCan object  
  19. CContainer* pC2 = new CCan(6.5, 3.0);  
  20.  
  21. pC1->ShowVolume(); // Output the volumes of the two  
  22. pC2->ShowVolume(); // objects pointed to  
  23. cout << endl;  
  24.  
  25. delete pC1;     // Now clean up the free store  
  26. delete pC2;     // ....  
  27.  
  28. return 0;  

示例说明

在这个程序中,我们声明了两个指向基类CContainer的指针。我们虽然不能定义CContainer对象(因为CContainer是抽象类),但仍然可以定义指向CContainer的指针,然后即可使用这种指针来存储派生类对象的地址。事实上,我们可以使用该指针存储类型为CContainer的直接或间接子类的任何对象的地址。指针pC1被赋予在自由存储器中由new运算符创建的CBox对象的地址。第二个指针以类似的方式被赋予CCan对象的地址。

注意:

当然,由于派生类对象是动态创建的,因此我们不再需要它们时必须使用delete运算符清理自由存储器。我们可以翻到第4章回顾关于delete运算符的内容。

该示例产生的输出如下:

  1. CBox usable volume is 24  
  2. Volume is 45.9458 

因为我们已经在CBox类中定义了ShowVolume()函数,所以CBox对象将调用该函数的派生类版本。我们在CCan类中没有定义这个函数,因此CCan对象将调用从基类继承的基类版本。因为Volume()函数在两个派生类中都是以虚函数形式实现的(必须如此,因为该函数在基类中是纯虚函数),所以对该函数的调用是在程序执行时被解析的,被选择的函数版本将属于被指向的对象所属的类。因此,对指针pC1来说,被调用的是CBox类的函数版本。对指针pC2而言,被调用的却是CCan类的函数版本。因此在每种情况下,我们都能获得正确的结果。

我们还可以只使用一个指针,并赋予该指针对象CCan的地址(在调用CBox对象的Volume()函数之后)。基类指针可以包含任何派生类对象的地址,即使相同的基类派生出多个不同的子类也无妨。因此,我们可以在整个派生类的范围内自动选择适当的虚函数。本小节的内容确实给我们留下了深刻的印象,不是吗?

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇9.5.1 友元类 下一篇9.9.2 C++/CLI类的继承(3)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: