c++中virtual關鍵字的做用與Java中多態的一點對比

動機

最近一直在使用C++寫win32程序,用了一些庫,裏面提供的類和demo各類是virtual這個關鍵字,一直不是很明白究竟是啥用,因而查看了一些文檔,寫小程序來實驗它的做用。java

結論

virtual這個關鍵字的發揮做用是在子類去繼承父類的時候。好比:c++

class Person
{
public:
    void foo1()
    {
        // do ...
    }
    
    virtual void foo2() 
    {
        // do ...
    }
};

像上面的代碼,若是類Person就一直被實例化使用,可是沒有類去繼承它的話,那麼這個virtual實際上並無什麼卵用。foo2()方法和foo1()是同樣的。小程序

當它被繼承的時候,有兩種狀況,覆寫(override)這個foo2()方法,或者不覆寫它。好比這樣:ide

class Student : public Person
{
public:
    void foo2() { // do something.. }
};

class Teacher : public Person
{
public:
    // no override
};

而後咱們使用的時候,若是是子類的實例,調用foo2()方法,理所固然是執行子類中所定義的foo2()方法體。可是當將這個子類的實例強制轉型成父類的實例(指針),再去執行foo2()方法的時候,對應的兩種狀況:子類實現了父類中virtual方法的,調用子類的方法;子類中沒有override的,仍然是調用父類中的實現(這不是廢話麼……)ui

列個表格大概是這樣:指針

// 大前提是父類中有個`virtual`方法`foo2()`
        是否override foo2()    調用子類實例的foo2()    強轉成父類後調用foo2()
子類1          是                執行子類1的foo2()        執行子類1的foo2()
子類2          否                執行父類的foo2()         執行父類的foo2()

// 另外一種狀況
// 大前提是父類中有個方法`foo2()`,可是沒有virtual關鍵字修飾
        是否override foo2()    調用子類實例的foo2()    強轉成父類後調用foo2()
子類1          是                執行子類1的foo2()        執行父類的foo2()
子類2          否                執行父類的foo2()         執行父類的foo2()

與Java的對比

個人感受好像Java自帶這個多態的特性,不須要用什麼關鍵字修飾,某個實例轉換成父類後調用方法,默認就會調用子類的實現(若是有的話)。寫了個小demo實驗了一下,果真如此。code

public class Main {

    public static void main(String[] args) {
        Person p = new Person();
        p.foo(); // output: Person foo
        
        Student s = new Student();
        s.foo(); // output: Student foo
        
        Person ps = s;
        ps.foo(); // output: Student foo

    }
    
    static class Person {
        
        public void foo() {
            System.out.println("Person foo");
        }
    }
    
    static class Student extends Person {
        
        public void foo() {
            System.out.println("Student foo");
        }
    }

}

在《Effective Java 2e》中,做者也說了,儘量的在申明,傳參,返回值的時候使用父類和接口,而不要使用實現類。繼承

大概是這樣:接口

ArrayList<String> strList = new ArrayList<String>();    //這樣是耿直的寫法
List<String> strList = new ArrayList<String>();    //這樣更好,由於你能夠換後面這個new

// 返回值和參數也是同樣,通常能使用接口就儘可能使用接口,而不要寫死成實現類,這樣帶來更大的靈活性
public List<String> buildStrList(List<String> raw, AnyInterface interf) {
    // do xxxx
}

總結

  • virtual關鍵字修飾的方法在子類繼承實現後,就能夠達到多態的目的(使用父類的指針依然能夠調用到子類的實現)。文檔

  • Java中不須要這個關鍵字來達到多態,覆寫方法自帶這個功能。

參考

相關文章
相關標籤/搜索