1. 程式人生 > >c++中virtual關鍵字的作用與Java中多型的一點對比

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

轉載至:https://segmentfault.com/a/1190000006119157

動機

最近一直在使用C++寫win32程式,用了一些庫,裡面提供的類和demo各種是virtual這個關鍵字,一直不是很明白到底是啥用,於是查看了一些文件,寫小程式來實驗它的作用。

結論

virtual這個關鍵字的發揮作用是在子類去繼承父類的時候。比如:

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

像上面的程式碼,如果類Person

就一直被例項化使用,但是沒有類去繼承它的話,那麼這個virtual實際上並沒有什麼卵用。foo2()方法和foo1()是一樣的。

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

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

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

然後我們使用的時候,如果是子類的例項,呼叫foo2()方法,理所當然是執行子類中所定義的foo2()

方法體。但是當將這個子類的例項強制轉型成父類的例項(指標),再去執行foo2()方法的時候,對應的兩種情況:子類實現了父類中virtual方法的,呼叫子類的方法;子類中沒有override的,仍然是呼叫父類中的實現(這不是廢話麼……)

列個表格大概是這樣:

// 大前提是父類中有個`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實驗了一下,果然如此。

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中不需要這個關鍵字來達到多型,覆寫方法自帶這個功能。

參考