上一节鸡啄米讲了虚基类及其派生类的构造函数,本节来讲讲赋值兼容规则。

       前面说过,派生类如果是从基类公有继承的,则它会包含基类中除构造函数和析构函数外的所有成员,基类的公有成员也成为派生类的公有成员,又因为对象只能访问类的公有成员,所以基类对象具有的功能,派生类对象都有。这样就引出了赋值兼容规则。

       赋值兼容规则就是指在基类对象可以使用的地方都可以用公有派生类对象来代替。注意必须是公有派生类。赋值兼容规则中的代替有三种方式。鸡啄米通过一个例子分别说明。

       假设有基类Base,类Child是Base的公有派生类,base为Base类的对象,pBase为Base类指针,child为Child类的对象。代码如下:

       class Base
       {
              ...
       };
       class Child : public Base
       {
              ...
       };
       Base base, *pBase;
       Child child;

       那么根据赋值兼容规则,可以使用类Base对象的地方都可以使用类Child的对象来代替。这里的代替有三种:

       1.派生类的对象可以赋值给基类的对象。也就是将派生类对象从基类继承的成员的值分别赋值给基类对象相应的成员。例如:

           base = child;

       2.派生类对象的地址可以赋值给基类类型的指针。例如:

           pBase = &child;

       3.派生类对象可以用来初始化基类的引用。例如:

           Base &b = child;

       因为有了赋值兼容规则,有了上述三种赋值方式,所以函数的参数中有基类对象或者基类指针又或者基类引用时,我们可以直接传入派生类对象或者派生类对象的地址作为实参来执行相同的操作。这样的好处是什么呢?那就是我们想对基类及派生类的对象做相同的操作时,只要定义一个函数就行了,它的参数为基类对象或者基类指针也或者是基类引用。这样就大大提高了软件开发的效率。

       公有派生类对象可以代替基类对象使用,但是我们只能使用它从基类继承的成员,而无法使用它的新添成员。

鸡啄米:C++编程入门系列之四十三(继承与派生:赋值兼容规则)

       鸡啄米给大家举个例子说明下赋值兼容规则:

       类Base为基类,类Child0为Base的公有派生类,类Child1为类Child0的公有派生类。三个类中都定义了成员函数show()。

       #include <iostream>
       using namespace std;
       class Base           // 基类Base的声明
       {
       public:
                  void show()    { cout << "Base::show()" << endl; }      // 公有成员函数show
       };
       class Child0 : public Base     // 类Base的公有派生类Child0的声明
       {
       public:
                 void show()    { cout << "Child0::show()" << endl; }    // 公有成员函数show
       };
       class Child1 : public Child0   // 类Child0的公有派生类Child1的声明
       {
       public:
                 void show()    { cout << "Child1::show()" << endl; }    // 公有成员函数show
       };
       void CallShow(Base *pBase)     // 一般函数,参数为基类指针
       {
                pBase->show();
       }
       int main()
       {
               Base base;                 // 声明Base类的对象
               Base *pBase;             // 声明Base类的指针
               Child0 ch0;                 // 声明Child0类的对象
               Child1 ch1;                 // 声明Child1类的对象
               pBase = &base;        // 将Base类对象base的地址赋值给Base类指针pBase
               CallShow(pBase);
               pBase = &ch0;            // 将Child0类对象ch0的地址赋值给Base类指针pBase
               CallShow(pBase);
               pBase = &ch1;            // 将Child1类对象ch1的地址赋值给Base类指针pBase
               CallShow(pBase);
               return 0;
       }

       程序运行结果为:

       Base::show()
       Base::show()
       Base::show()

       我们首先定义了一个函数CallShow,其参数pBase为基类Base类型的指针,根据赋值兼容规则,我们可以用公有派生类对象的地址为基类指针赋值,那么CallShow函数就可以处理这个类族的所有对象。在主函数中我们就分别把基类对象base的地址、派生类对象ch0的地址和派生类对象ch1的地址赋值给基类指针pBase,然后将pBase作为实参调用CallShow,在CallShow中调用了成员函数show。

       但是,根据上面所讲,将派生类对象的地址赋值给pBase以后,通过pBase只能访问派生类从基类继承的成员。所以即使指针pBase指向的是派生类对象ch0或者ch1,在CallShow中通过pBase也只能调用从基类Base继承的成员函数show,而不会调用Child0类或者Child1类的成员函数show。因此主函数中三次调用CallShow函数,都是访问的基类Base的成员函数show,输出都是Base::show()。

       这时我们深切的感受到,即使派生类对象代替了基类对象,它也只能产生基类的功能,自己的新功能无法体现。要想在代替以后同样能够实现自己的功能,就要用到面向对象设计的另一个特性--多态性。而本节学的赋值兼容规则是多态性的基础。

       鸡啄米最后提醒大家,只有在编程入门的时候把赋值兼容规则搞明白,才能更好的学习多态性。今天就讲到这了。有问题欢迎到鸡啄米博客交流。