1.多态要解决的问题(引入)
任何一种机制的存在,必然是有其存在的意义的,例如我们前面学过的函数重载,运算符重载,以及引用等等,都是解决一些特殊问题的;
下面通过一些具体的例子,说明一下多态这种机制是解决什么问题的?
(1)上面的代码看着比较多,其实都是一些极其简单的语句,定义两个类,对于其中的变量进行设置,定义一个函数可以打印输出类里面的变量的结果;其中这两个类的定义的函数作用都是一样的,一个多变量的值进行设置,一个对于变量的值进行打印输出;
(2)这个a类就是我们熟知的父类,b类就是派生类,也叫做子类;
(3)在主函数里面,我们分别使用a类和b类定义了一个对象,里面使用注释表明了三种不同的情况,这三种情况就是我们多态就要解决的三类问题;
(4)第一种情况就是让b1对象赋值给a1,然后我们想使用a1这个对象调用相应的函数,我们在写的时候就会发现这个a1虽然是b1的父类,但是a1这个对象是没有办法调用b1类里面的任何函数的
上面的就是编译器自动识别的函数(只有这些弹出的函数才可以被a1这个对象进行调用) ,我们发现在b这个类里面定义的函数,是没有办法被我们的a1对象使用的,这个就是第一个问题;
(5)第二种情况是定义了一个指针变量,这个指针变量指向的是b1这个对象,我们会发现这个指针虽然指向的是b1这个对象,他仍然只能调用和a这个类相关的函数,对于b这个类里面进行定义的一些函数,他是没有权限进行使用的,这个也是一个问题;
(6)第三类问题是关于引用的问题,我们使用af1作为b1这个对象的别名,我们使用af1这个别名进行函数调用的时候,就会发现这个af1引用能够调用的函数也不会有b这个类里面的一些函数的;
(7)通过上面的案例,针对这些共性的问题:父类无法调用子类里面定义的一些函数 ,无论是普通的对象,还是指针变量和引用类型都是没有办法调用子类里面定义的一些函数的,这个就是多态将要解决的问题;
我们在主函数里面原本是对a1进行设置的100,但是这三种情况的打印结果都是一个比较大的随机值,可见是有问题的;
(8)对于打印的结果是随机值的这种情况,我们简单的介绍一下它的原理,就是a类是父类,b类是子类,我们的子类是继承父类一些东西的,我们在父类a里面定义了ii这个变量,因为b类是父类,就会继承a类里面的ii这个变量,自己是创建了jj这个变量的,所以b1里面就有ii和jj这两个变量
我们上面的这张图就是原理的概括,你现在可能看的不是很明白,经过我下面的介绍,相信你就可以明白了:
首先,我们要清楚a1对象里面的i这个成员变量我们原本是赋值100的,b1对象是继承a类创建的a1对象,但是这里的继承并不是真正的全部拷贝过来,而是吸收了父类里面的成员变量i,但是我们并没有对于这个成员变量进行任何的操作,因此这个时候b1这里的对象的值是随机值,接下来我们写的是a1=b1,相当于是把b1拷贝给了a1这个对象,这个时候b1里面的随机值(打印出来的负8亿多的那个数字)就带入到了a1对象里面去,就把a1对象里面原来的100这个数字给覆盖掉了;
其实通过上面的图也是可以看出来的,我们在执行a1=b1这条语句的时候,j=200这个成员变量已经同步到了父类a1里面,但是我们的a1对象不可以直接对这个对象进行使用;
这个时候我们再调用showa这个函数进行打印输出结果就会看到这个随机数字,这个兼容性规则就是一种弊端,什么弊端呢?我们的a1=b1这句代码的原本的意思是想要把b1这个对象里面的东西赋值给a1,这样我们使用a1就可以调用b1里面的一些函数了,但是这个兼容性的规则不但不会这样做,反而会把我们的父类里面的100给覆盖掉,这样就无功而返了。
2.虚函数
(1)什么是虚函数,顾名思义,虚函数就是函数里面的一种,在我们的普通的函数里面加上virtual就可以把普通的函数变成虚函数;使用虚函数就可以解决我们上面遇到的子类的b1因为赋值使得a1里面的ii也改变成为随机值
上面的就是我们修改之后的函数,两个类里面的4个函数都是没有返回值的,我们在void前面加上virtual就可以把普通的函数修改为虚函数,针对前面体积道德第二种情况,我们不进行任何修改,调用showa函数,打印的结果就是我们设置的200,而不是那个随机数;
(2)虚函数是怎么在这个题目里面发挥自己的作用的:
之前我们是无法使用父类对象调用子类里面的新增成员变量的,但是加上虚函数之后;
我们重新进行设计,还是定义一个b1对象,因为我们已经把两个类里面的函数的名字改为相同的了,因此我们写下b1.set的时候就会出现同名覆盖的现象;
为了达到效果,我们可以加上类名和域作用限定符,这样我们的39行代码就是对b类里面的j变量进行赋值,40行里面就是对a类里面的i变量进行赋值;
pa1这个指针变量指向的是b1这个对象,这个对象是a类的,这个时候是可以使用父类a创建的指针访问子类里面的函数show的,这个就解决了因为兼容性而出现的缺陷,50行就是对我们定义的指针进行初始化;
3.多态的表现(2)------引用
之前我们没有多态的时候,使用这个引用是打印的随机值,多态加入之后,让af1作为b1对象的别名,我们使用af1进行调用函数,就会打印我们设置给b1对象的200;(实际上这个和编译器的版本有关,高版本的编译其实可以实现这个效果的,低版本的编译器无法实现)
在我们之前讲的第一种场景下面,我们打印的随机值,引入多态之后,打印结果就是300,可见b1对象对于a类这个作用域里面的set函数的调用的是发挥作用的,因为我们原本是a1.set(100),正常输出的结果应该是100,但是打印结果是300,说明这个b1.a::set()可以让b1这个 父类里面的a1对象的值从原来的100重置为现在的300;
但是当我们想使用父类a1调用b类里面的函数的时候,发现还是不行,这个时候就说明多态无法解决这个问题;