9.6.6 间接基类
本章一开始就说过,子类的基类可能是从另一个基类派生出来的。我们对上一个示例稍加扩充,就能提供这句话的例证,并示范跨越第二级继承的虚函数的用法。
试一试:多层继承
我们只需要在上一个示例得到的几个类中再添加一个CGlassBox类。现在拥有的几个类之间的关系如图9-6所示。
|
| (点击查看大图)图 9-6 |
完全像以前那样,CGlassBox类是从CBox类派生出来的,但为了表明基类的函数版本还能通过派生类传播,我们故意省略了ShowVolume()函数的派生类版本。在上面显示的类层次结构中,CContainer类是CGlassBox类的间接基类,是CBox和CCan类的直接基类。
该示例的GlassBox.h头文件包含以下代码:
- // GlassBox.h for Ex9_11
- #pragma once
- #include "Box.h" // For CBox
-
- class CGlassBox: public CBox // Derived class
- {
- public:
-
- // Function to calculate volume of a CGlassBox
- // allowing 15% for packing
- virtual double Volume() const
- { return 0.85*m_Length*m_Width*m_Height; }
-
- // Constructor
- CGlassBox(double lv, double wv, double hv): CBox(lv, wv, hv){}
- };
Container.h、Can.h和Box.h头文件包含的代码与Ex9_10示例中的相同。
下面是本示例中的源文件,其中的main()函数为使用层次结构中新添加的类而作了更新:
- // Ex9_11.cpp
- // Using an abstract class with multiple levels of inheritance
- #include "Box.h" // For CBox and CContainer
- #include "Can.h" // For CCan (and CContainer)
- #include "GlassBox.h" // For CGlassBox (and CBox and CContainer)
- #include <iostream> // For stream I/O
- using std::cout;
- using std::endl;
-
- const double PI = 3.14159265; // Global definition for PI
-
- int main()
- {
- // Pointer to abstract base class initialized with CBox object address
- CContainer* pC1 = new CBox(2.0, 3.0, 4.0);
-
- CCan myCan(6.5, 3.0); // Define CCan object
- CGlassBox myGlassBox(2.0, 3.0, 4.0); // Define CGlassBox object
-
- pC1->ShowVolume(); // Output the volume of CBox
- delete pC1; // Now clean up the free store
-
- // initialized with address of CCan object
- pC1 = &myCan; // Put myCan address in pointer
- pC1->ShowVolume(); // Output the volume of CCan
-
- pC1 = &myGlassBox; // Put myGlassBox address in pointer
- pC1->ShowVolume(); // Output the volume of CGlassBox
- cout << endl;
- return 0;
- }
示例说明
如图9-6所示的类层次结构共有3层,其中CContainer是抽象的基类,因为它包含纯虚函数Volume()。main()函数现在使用指向基类的同一个指针,调用了ShowVolume()函数3次,但每次使该指针包含不同类的对象地址。因为这里的任何派生类都没有定义ShowVolume()函数,所以每个实例中调用的都是基类版本。基类CContainer的独立分支定义了派生类CCan。
该示例产生下面的输出:
- CBox usable volume is 24
- Volume is 45.9458
- CBox usable volume is 20.4
输出显示,程序的执行根据每次所涉及对象的类型,从3个不同的Volume()函数中选择了正确的版本。
注意:
在赋予指针另一个地址值之前,我们必须从自由存储器中删除CBox对象。如果不这样做,以后我们将无法清理自由存储器,因为已经失去了原来对象的地址。当重新给指针赋值以及使用自由存储器时,这是个易犯的错误。