定义在基类中的函数,子类必须对其进行覆写!【必须对其进行覆写?!】——Java中的接口、Abstract方法中的抽象类也有这样的要求。ios
C++中定义:编程
virtual void deal();//子类必需要对这个函数进行覆写
(1)定义子类对象,并调用对象中未被子类覆盖的基类函数A。同时在该函数A中,又调用了已被子类覆盖的基类函数B。那此时将会调用基类中的函数B,可咱们本应该调用的是子类中的覆盖函数B。虚函数即能解决这个问题。ide
①没有使用虚函数的例子:函数
#include<iostream> using namespace std; class Father //基类 Father { public: void display() {cout<<"Father::display()\n";} //在函数中调用了,子类覆盖基类的函数display() void fatherShowDisplay() {display();} }; class Son:public Father //子类Son { public: //重写基类中的display()函数 void display() {cout<<"Son::display()\n";} }; int main() { Son son; //子类对象 son.fatherShowDisplay(); //经过基类中未被覆盖的函数,想调用子类中覆盖的display函数 }
输出结果为:Father::display() spa
指望出现的结果应该为:Son::display.net
问题出现啦:指针
子类Son继承父类Father,而且覆写了父类中的dispaly方法,讲道理咱们后续调用子类中的dispaly方法应该想要使用咱们覆写的方法;code
可是因为咱们调用的父类中未被覆写的fatherShowDisplay方法调用了父类的display方法,因此致使程序调用的时候调用了父类的display方法!xml
通俗说法①:咱们国家数据局早已经更新【覆写】了国民经济指数,可是你局里员工【内部其余方法】给外人调数据的时候仍是调用的之前的老旧数据!!!这样不太合适吧!对象
②使用虚函数的状况,再来看看输出结果
#include<iostream> using namespace std; class Father //基类 Father { public: virtual void display() {cout<<"Father::display()\n";} //在函数中调用了,子类覆盖基类的函数display() void fatherShowDisplay() {display();} }; class Son:public Father //子类Son { public: //重写基类中的display()函数 void display() {cout<<"Son::display()\n";} }; int main() { Son son; //子类对象 son.fatherShowDisplay(); //经过基类中未被覆盖的函数,想调用子类中覆盖的display函数 }
输出结果为:Son::display()
输出结果是咱们指望的结果。
③先不查资料,本身在Java中实现上面①中的函数看看输出结果
class Father{ public void display(){ System.out.println("父类显示000000000"); } public void fatherShowDisplay(){ display(); } } class Son extends Father{ @Override/ public void display() { System.out.println("子类显示111111111"); } } public class Client { public static void main(String[] args) { Father father=new Father(); Son son=new Son(); father.fatherShowDisplay(); son.fatherShowDisplay(); } }
显示结果【Java没有出现C++中遇到的相关问题!】:
父类显示000000000
子类显示111111111
(2)在使用指向子类对象的基类指针,并调用子类中的覆盖函数时,若是该函数不是虚函数,那么将调用基类中的该函数;若是该函数是虚函数,则会调用子类中的该函数。
①有使用虚函数的例子
#include<iostream> using namespace std; class Father //基类 Father { public: void display() {cout<<"Father::display()\n";} }; class Son:public Father //子类Son { public: void display() //覆盖基类中的display函数 {cout<<"Son::display()\n";} }; int main() { Father *fp; //定义基类指针 Son son; //子类对象 fp=&son; //使基类指针指向子类对象 fp->display(); //经过基类指针想调用子类中覆盖的display函数 }
输出结果:Father::display()
果真是调用了父类中的原始方法,没有调用子类覆写的方法
②使用虚函数的例子
#include<iostream> using namespace std; class Father //基类 Father { public: void virtual display() //定义了虚函数 {cout<<"Father::display()\n";} }; class Son:public Father //子类Son { public: void display() //覆盖基类中的display函数 {cout<<"Son::display()\n";} }; int main() { Father *fp; //定义基类指针 Son son; //子类对象 fp=&son; //使基类指针指向子类对象 fp->display(); //经过基类指针想调用子类中覆盖的display函数 }
使用虚函数的输出结果:Son::display()
确实调用的子类覆写方法
通俗说法②:如今一名将军【本质上也是士兵】-用手指指着【指针】-士兵【实例化对象】的询问名字【调用方法】,一对父子也在军队中服役,当询问老父亲【父类对象】的名字的时候,父亲向将军报上本身的名字;当询问儿子【子类对象】的名字时,他居然报了他爹的名字!!!
③Java中的例子【Java对象引用-C++指针】
class Father{ public void display(){ System.out.println("父类显示000000000"); } public void fatherShowDisplay(){ display(); } } class Son extends Father { @Override public void display() { System.out.println("子类显示111111111"); } } public class Client { public static void main(String[] args) { // Father son=new Son();//这种和下面的实际上是同样的,【对象向上转型】 Father father=new Father(); father.fatherShowDisplay(); father=new Son(); father.fatherShowDisplay(); } }
程序输出:
父类显示000000000
子类显示111111111
这种状况下,Java依然没有C++虚函数那样的问题!!!
经过2-主要做用,他解决的问题知道:虚函数是为了解决子类覆写方法的一致性,一旦覆写后续调用所有都使用最新的方法,不出现调用之前方法的状况!对比一下Java语言中的方法覆写!
虚函数就是为了解决编程中的多态特性吗!
编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序运行时才能肯定将要调用的函数,为此要确切知道该调用的函数,要求联编工做要在程序运行时进行,这种在程序运行时进行联编工做被称为动态联编。 动态联编必须包括如下方面: (1)成员函数必须声明为virtual (2)若是基类中声明了为虚函数,则派生类中没必要再声明。 调用方式: 经过对象的指针或引用调用成员函数;或经过成员函数调用,反之就没法实现动态联编。
首先声明,Java中没有虚函数这个概念、也不存在C++虚函数这样的问题,缘由:Java自己【面向对象都有】的三大特性中就有多态这个特性。
Java的普通函数就至关于C++的虚函数,动态绑定是Java的默认行为【核心:Java没有虚函数的本质缘由所在】。
代码以下:
class Father{ private void display(){//私有化该方法,子类就不能覆写该方法 System.out.println("父类显示000000000"); } public void fatherShowDisplay(){ display(); } } class Son extends Father{ public void display() {//这里不是覆写父类方法,而是建立了一个全新的方法 System.out.println("子类显示111111111"); } } public class Client { public static void main(String[] args) { Father father=new Father(); Son son=new Son(); Father fs=new Son();//向上转型 father.fatherShowDisplay(); son.fatherShowDisplay(); son.display(); fs.fatherShowDisplay(); ((Son)fs).display();//【强制】向下转型 } }
数据输出:
父类显示000000000
父类显示000000000
子类显示111111111
父类显示000000000
子类显示111111111
这里的方法已经不是覆写了,而是父类的方法被私有化,子类是至关于新建了一个方法。