当前位置:C++技术网 > 精选软件 > 关于父子对象相互赋值以及多态特性的分析:4 构建多态特性三要素的理解

关于父子对象相互赋值以及多态特性的分析:4 构建多态特性三要素的理解

更新时间:2017-08-22 08:39:14浏览次数:1+次

        接上篇。。。

    5.基类(父类)指针、继承、virtual关键字实现的多态特性的理解

    我们经常会提到面向对象的多态特性。多态特性的实现需要借助成员函数的virtual关键字、继承、基类指针。缺一不可。所以,我们前面在类对象赋值时使用了virtual也是无法实现多态的。因为对象就是对象,对象调用对象的成员函数,始终都是调用对象自己的成员函数,是无法调用赋值的对象的成员函数的。当然,vitrual和继承没有的话,就谈不上多态。而要实现多态,必然要借助基类指针。为什么呢??
    我们来看一段代码:
AA aa;
A *pa = &aa;
pa->show();
//pa->say();//不能访问
BB bb;
pa = &bb;
pa->show();
//pa->say();//不能访问
    我们对比前面的代码,发现这里没有A类对象了,而只有A类的指针pa了。pa指针只是指向aa对象或bb对象。然后pa就可以调用两个对象的成员函数了。在A类中,show函数使用了virtual关键字修饰了。AA和BB都继承于A。然后这里用了A类指针。所以他们一起就可以构成多态特性。所以pa由于被不同的对象的地址赋值而产生了不同的输出,这就是多态特性。
    为什么这个多态特性该如何理解呢?我们来看一张示意图:
    关于父子对象相互赋值以及多态特性的分析4:父类指针、继承、virtual与多态特性的理解
    pa指针指向aa或bb,指向的都是对象的内存的起始地址。然而因为A类指针是由A类确定的,所以pa指针的范围只有图中括号括住的范围。所以说,pa指针并不能调用say()函数,也不操作在范围外的变量。但是却可以操作show函数以及show函数所在内存的变量。
    如果在AA类中不写show函数的定义,那么也就只有被继承的show函数。那么我们调用aa对象的show函数就只有调用继承过来的show函数。但是如果我们在AA类中再定义一个函数,如果此时A类中的show函数没有加virtual关键字。那么AA类的新增show函数和被继承过来的show函数是相互独立的。如果此时直接调用A类对象的show,那么肯定调用的是额外新增的show函数,而不是被继承过来的show函数。如果要调用被继承过来的show函数,应该使用类型转换或者使用类作用域,代码如下:
AA aa;
aa.show();
((A)aa).show();
aa.A::show();
    这种情况叫做覆盖。覆盖的原因就是直接当做两个独立的函数在使用。然后使用在函数时,我们无法自动做一个调用的切换。然而多态则不一样。多态的特性就是改变覆盖特性的。通过父类指针指向不同的子类对象时,会将基类被重载的函数穿越到子类对象的函数里执行。也就是说,当加入了virtual关键字的函数,被重载后,编译器会感知到,当调用到了父类的这个重载函数时,就会自动跳到子类的这个重载函数里执行。这个过程是语言标准保证的。
    而这个过程的多态特性的发生是由父类被重载的函数的调用而触发。所以我们一定是要让父类的函数被调用。这才需要父类指针。父类指针来调用函数,自然就会调用子类中继承自父类的函数。而这个函数如果被virtual修饰,就会进而去这个子类对象中寻找重载的函数,然后执行这个函数。如果没有virtual修饰,调用就直接发生在了继承自父类的函数,此时就没有函数跳转调用的过程。virtual的作用就在于此。
    那么为什么需要父类指针呢?为什么父类对象不可以呢?
    对象和对象指针是两回事。对象是一个独立的个体,而对象指针则是指向另外一个对象。对象执行的函数调用发生在自己身上,而对象指针执行的函数调用则发生在被指向的对象身上。我们要的多态特性,就是需要发生在被指向的对象的身上。所以靠对象是无法传播这个特性的,而指针就能够实现这个效果。因为指针指向了对象,那么指针调用函数,自然就是执行的被指向的对象的函数,然后有virtual,那就继续找被重载的函数的定义。也就是说,此时的对象是有两个函数的,一个是继承过来带有virtual版本的函数,另一个是子类重载的版本。你一样可以通过类型转换和类作用域来调用继承过来的版本的函数。如:
AA aa;
AA *pa = &aa;
pa->show();
((A*)pa)->show();
pa->A::show();
    总结一下,要实现多态特性,需要父类指针,原因在于父类指针可以调用被指向的子类的继承自父类的函数。需要virtual关键字,,父类指针调用到这个关键字的函数时,就可以去查找子类重载的版本的函数。需要继承,原因在于没有继承,就无法构成父子类的关系,父类指针也就无法指向子类,也就无法触发上面两个过程的执行。另外,子类的继承只是克隆了父类的内容而已,并没有多出来一个父类对象,始终就只有一个子类对象,对象中有多个版本的重载函数定义。所以就会有覆盖和多态调用的现象。
        未完继续,请继续阅读后续文章。。。