定義在基類中的函數,子類必須對其進行覆寫!【必須對其進行覆寫?!】——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
這裏的方法已經不是覆寫了,而是父類的方法被私有化,子類是至關於新建了一個方法。