14.2 继承语法
组合的语法是清晰的,而对于继承,则有新的不同的形式。
当继承时,我们会发现“这个新类很像原来的类”。我们规定,在代码中和原来一样给出该类的名字,但在类的左括号的前面,加一个冒号和基类的名字(对于多重继承,要给出多个基类名,它们之间用逗号分开)。当做完这些时,将会自动地得到基类中的所用数据成员和成员函数。下面是一个例子:
我们可以看到Y对X进行了继承,这意味着Y将包含X中的所有数据成员和成员函数。实际上,正如没有对X进行继承,而在Y中创建了一个X的成员对象一样,Y是包含了X的一个子对象。无论是成员对象还是基类存储,都被认为是子对象。
所有X中的私有成员在Y中仍然是私有的,这是因为Y对X进行了继承并不意味着Y可以不遵守保护机制。X中的私有成员仍然占有存储空间,只是不可以直接地访问它们罢了。
在main()中,从sizeof(Y)是sizeof(X)的两倍可以看出,Y的数据成员是同X的成员结合在一起了。
我们注意到,本例中的基类前面是public。由于在继承时,基类中所有的成员都是被预设为私有的,所以如果基类的前面没有public,这意味着基类的所有公有成员将在派生类中变为私有的。这显然不是所希望的[1],我们希望基类中的所有公有成员在派生类中仍是公有的。这可以在继承时通过使用关键字public来实现。
在change()中,基类的permute()函数被调用。即派生类可以直接访问所有基类的公有函数。
派生类中的set()函数重新定义了基类中set()函数。这即是说,如果调用一个Y类型对象的read()和permute()函数,将会使用基类中的这些函数(这可在main()中表现出来)。但如果调用一个Y类型对象的set()函数,将会使用派生类中的重定义版本。这意味着如果不想使用某个继承而来的函数,我们可以改变它的内容(当然我们也可以增加全新的函数,例如change())。
然而,当我们重新定义了一个函数的后,仍可能想调用基类的函数。但如果对于set(),只是简单地调用set()函数,将得到这个函数的本地版本—一个递归的函数调用。为了调用基类的set()函数,必须使用作用域运算符来显式地标明基类名。
[1]在Java中,编译器不会因继承而让程序员减少对成员的访问能力。