第十三章 类继承(5)多态公有继承
(五)多态公有继承
1.两种方法实现多态
有时候希望同一种方法在派生类和基类中是不相同的,也就是说方法的行为取决于调用该方法的对象,这种行为方式叫作多态。有两种方法可以实现多态公有继承:一是在派生类中重新定义基类的方法;二是使用虚方法。
2.重新定义基类的方法
在基类中使用的方法可以在派生类中重新定义,程序将根据调用方法的对象的不同来确定调用基类还是派生类的多态的方法。对于在两个类中行为相同的方法,则只会在基类当中声明。派生类重新定义基类方法,是重新声明一次该方法(函数名相同),并完成与基类不同的实现。
3.通过引用或指针调用方法(虚函数方法)
如果方法是通过引用或指针而不是对象调用的,它会如何选择调用哪个方法呢?如果不使用关键字virtual,程序将根据引用类型或指针类型来选择方法;如果使用了关键字virtual那么程序将根据引用或指针指向的对象的类型选择方法。
通常会在基类中将在派生类中会重新定义的方法声明为虚方法(目的就是让基类的指针或引用可以使用派生类对象的方法),这种方法在派生类中会自动成为虚方法,然而,我们在派生类中一般也会加上关键字virtual来指明哪个是虚方法。virtual表示虚方法,但是虚方法的意思不是该方法没有实现,而是便于用基类指针或引用来调用派生类对象的方法,因此虚方法不是空方法,虚方法也是有实现的,这一点要注意理解。
4.虚析构函数
在基类中声明一种虚析构函数也是一种惯例,用来对派生类可能的析构方式做准备。也就是基类的指针指向派生类对象的时候,析构时会调用派生类的析构函数(因为析构函数是虚函数嘛),而不会首先调用基类的析构函数,这样可以避免特殊情况下的内存泄漏。
还要注意的是virtual只用于函数的声明之中,而不用在函数的定义中。(目前为止,函数的定义中出现的关键字只有inline)
5.如何调用基类的方法和数据
派生类并不能直接访问基类的私有数据,而必须通过基类的公有或保护方法来实现访问,访问的方式取决于方法,构造函数使用一种技术(用初始化列表调用基类的构造函数)。
而其他的成员函数则使用另一种技术(用基类的公有方法来访问数据)。在派生类中,我们使用作用域解析运算符来调用基类的方法(比如在派生类重新定义的基类viewacct()方法中可以使用Brass::viewacct();来显示基类的数据成员),以免产生歧义。重新定义基类的方法时,调用基类的方法的标准做法是使用作用域解析运算符,用基类的类名::方法名来调用基类的方法。
如果派生类中并没有重新定义基类的公有数据和方法,那么我们可以直接使用基类的这些公有数据和方法的名称来调用它们,而不必使用作用域解析运算符(当然,使用也没有错)。只有基类的公有数据或方法被新成员覆盖的时候,我们才使用作用域解析运算符来调用基类的成员和方法,此时因为它们的作用域不同,因此不会造成歧义。
6.为何要使用虚析构函数?
如果析构函数不是虚的,那么只会调用对应于指针类型的对象的析构函数,而如果析构函数是虚析构函数,那么会调用指针指向的对象的析构函数,这会对派生类对象的析构有方便而不会产生错误(比如用基类指针指向派生类对象的时候)。