Code Apprentice

Virtual Function in C++

· [Jack Shih]

最近跟朋友談論到這樣的問題 「解構式應加上 virtual 關鍵字」 (TL;DR 如果預期會有人繼承這個物件,請在解構式加上 virtual)

上網查了一下發現挺有趣的所以在這裡記錄下來。

virtual 關鍵字代表的意思是向其他人暗示,這個 function(method),“應該"要被子類別覆寫(override)。方式是用子類別也用一樣的 function 名稱。

也許這時候會有疑問,其實不加 virtual 也是可以的,C++ 有所謂 overload 機制。

例如我有一個 Class A 跟 Class B 且 B 繼承 A。

class A {
  public:
    void sayHello() {
      cout<<"hello from A"<<endl;
    }
    void hey() {
      cout<<"hey from A"<<endl;
    }
};
class B: public A {
  public:
    void sayHello() {
      cout<<"hello from B"<<endl;
    }
};

然後這樣呼叫

A *a = new A();
B *b = new B();
a->sayHello(); // hello from A
b->sayHello(); // hello from B
b->hey(); // hey from A

一切看起來都很正常,但是繼承體系下,要用子類別也是父類別的一種,也就是說可以用父類別指標指向子類別。

A *ab = new B();
ab->sayHello() // hello from A

有過 Java 經驗或許會直覺是 hello from B,畢竟不論被當成什麼東西,物件是什麼就該是什麼。這也是所謂的多型。 但這樣的情況下 C++ 會印出的是 hello from A. 如果想要印出 hello from B 就應該要在 function 前面加上 virtual 關鍵字。

由以上 C++ 的行為就衍生出所謂 virtual destructors 如果沒有 virtual 關鍵字,如果 B 物件是在被 A 指標指的情況下對 A 所指向的物件釋放,會變成以 A 解構式解構 B 物件,這樣下來會發生錯誤也不意外了。

事實上在 C++11 前 C++ 是沒有 final 關鍵字來阻止別人繼承物件的。所以 C++ 內有種程式設計師的默契,如果類別中的解構式沒有 virtual 關鍵字,會是在暗示您不應該繼承這個物件。

另外 C++ 中並沒有像 Java 有所謂 abstract 或是 interface 的關鍵字,而是 pure virtual function。

virtual function foo() = 0;

挺有趣。