C++随笔-类继承::简单继承与多态继承

继承

什么是继承?个人的理解就是将基类的成员作为派生类的成员,注意是作为派生类的成员,即加入派生类的类作用域中,叫做继承。作为派生类的成员意味着可以直接通过派生类对象或者派生类方法对其进行访问。

派生类对基类数据成员的继承:将数据成员存储至派生类中,作为派生类的数据成员。

派生类对基类方法成员的简单继承:将方法提供给派生类,派生类继承了基类方法的接口。

值得注意的是,继承了基类方法的接口,表示方法参数(类型、数目等)并没有发生改变,而与基类方法的区别在于,调用方法的对象不一样了。原先是基类方法,所以使用基类对象可以对其进行调用;现在是派生类方法,使用的是派生类对象对其进行调用。当派生类没有对继承来的基类方法进行重新定义时(多态继承的内容,还涉及到隐藏基类方法的问题,后面会介绍),派生类的方法和基类的同名方法所实现的功能、参数均是完全一致的。但是注意不可以认为此时派生类实际上是通过基类对象进行该方法的调用,要充分理解继承的含义,这个方法就是从基类继承过来的,这个方法可以说是“存储”在派生类中,因此且不看是否是继承而来的方法,实际上就是调用派生类的成员方法。、

 

继承链

基类、派生类的概念是相对的,某一个派生类可以是他的进一步派生类的基类;某一个基类可以是某一个类的派生类。由此,形成一个继承链,并有相邻基类等概念,用于描述在继承链中的状态(相邻基类:某一个类在继承链中与之相邻的那个基类)。

 

多态继承

前述提到派生类对基类方法的简单继承,但是如果要对方法的行为进行更改,使得同一个名称的方法在基类与派生类中不同,则需要对该方法进行相应的操作。通过使用两种机制能够实现该目的。

  • 派生类中对基类方法的重新定义
  • 使用虚函数

其中,这两种机制最重要的是对基类方法的重新定义,而虚方法的使用,是为了针对指针、引用下能正确地调用类方法而存在。

在派生类中对基类方法的重新定义

首先要明确,在派生类中对基类方法的重新定义与函数重载完全不同,虽然二者都是实现多态特征的方式。在派生类中对基类方法进行重新定义时,会将初始继承过来的基类方法进行隐藏。注意这个隐藏不代表不存在或者删除,而是对一种可见性的描述。一旦对该基类方法进行重新定义,则在派生类作用域中,该方法名称说表示的就是派生类中重新定义的类方法,而不再是基类中同名的类方法。同样,通过派生类对象对该方法进行调用时,也只能调用该重新定义的属于派生类的该名称的方法。所以这里的重新定义,更多体现的是一种overwrite覆盖的含义。但是如果要在派生类作用域下调用属于基类的该名称类方法也不是不可以,只不过此时需要通过作用域解析符,通过基类的作用域调用属于基类的该名称的方法。

所以总结一下在派生类中重新定义基类方法带来的多态继承和简单的类方法继承的区别与联系:

  • 简单的类方法继承:可以理解为将基类的该名称的类方法加入到派生类作用域中,使其属于派生类。而该方法的参数名称等未发生改变,只不过this指针此时指向的是派生类对象,所以通过派生类对象调用此名称的类方法,实际上就是在调用属于派生类的类方法,和派生类对象中内嵌的基类对象并无关系。很纯粹的继承。
  • 重新定义:可以理解为这么两个步骤@1.先在派生类中继承基类中的该名称方法,也就是将基类中的该名称方法添加进派生类作用域中。 @2.重新在派生类中定义了一个与从基类中继承的同名的类方法,而该在派生类中重新定义的类方法会将从基类继承得到的同名类方法隐藏,从此在派生类或者通过派生类对象进行调用的该名称方法都是这个在派生类中经过重新定义的方法。

对派生类中重新定义的基类方法进行重载

有一个情况值得注意:当基类中对某一个方法进行了重载,如果派生类中并没有对其进行重新定义,而仅仅是简单继承了该基类方法,则在派生类中同样具有该方法的重载,并且各个重载方法的参数与基类方法一致(这里借助了向上转换特性)。但是如果一旦在派生类中,对该继承的该基类方法进行了重新定义,则这个重新定义的属于派生类的类方法将会把所有重载的基类的该名称的方法隐藏,或者说是覆盖,此时在派生类中将不再具有该类方法的重载。所以如果想要在派生类中重新定义该类方法,同时又希望能保持该类方法的多个重载版本,就需要在派生类中,对基类中的该方法的各个重载分别进行重新定义,这样才能在派生类中对该名称类方法进行重载。

虚方法(函数)

虚方法实际上是为了向上转换特性而准备的。在通过指针或者引用访问类对象中的成员的情境下,未使用虚方法时,所访问的类对象的类型是由指针或引用决定的;使用了虚方法后,所访问的类对象的类型则是由指针或引用所指向、所引用的类型所决定的。正是由于向上转换类型的存在,基类类型的指针或引用可以指向、引用基类类型的对象和派生类类型的对象。因此如果基类类型的指针、引用所指向、引用的对象类型是派生类类型,但是是通过指针、引用来对对象成员进行访问,则此时访问的将是基类对象的成员,而并非是所指向、引用的派生类对象的成员,这在某些情况下将会带来影响。因此引入了虚方法,通过virtual关键字进行声明。值得注意的是,虚方法只需要在基类中用virtual关键字进行声明即可,经过基类virtual声明过的虚方法继承到派生类中时会自动声明为虚方法而无需再显式使用virtual进行声明(若要显式进行声明也可以)。

又正如前述,基类、派生类的概念是相对的,所以对于某一个基类的派生类而言,其也可以作为一个基类衍生出派生类。所以在某一个派生类中,相对于基类新增加的方法也可以使用virtual声明为虚方法(注意,此时这个新添的方法需要显式地使用virtual进行声明,因为此时派生类被视为基类)。

虚方法是实现动态联编的重要机制。

-虚方法(函数)的实现机制

在各个类对象中,会自动生成一个虚函数表vtbl,vtbl的成员是所属类的所有虚方法的地址。基类与派生类中的虚函数表是相互独立的。每一个基类对象(相对概念)都会维护一个虚函数表,并且被派生类所继承。基类的vtbl被派生类继承时,是将基类的vtbl独立地复制给派生类的vtbl,并且如果在派生类中有对基类的虚函数进行重新定义,这会将重新定义的虚函数的地址写入派生类的vtbl中。以此在继承链中类推下去。要使用虚方法时,则会访问所属类的vtbl,访问相应地址指向的虚函数。

你可能感兴趣的:(C++,C++)