c++中virtual關鍵字的作用與Java中多型的一點對比
阿新 • • 發佈:2019-01-26
轉載至: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中不需要這個關鍵字來達到多型,覆寫方法自帶這個功能。