C++ virtual 的兩種用法

C++ 的 virtual 關鍵字分成虛擬函式(virtual function)與純虛函式(pure virtual function)兩種用法,以下將說明這兩種的差異與使用時機。

虛擬函式 virtual function

C++ virtual 關鍵字第一種用法為 virtual function,中文翻譯為虛擬函式,
不強制子類一定要實作,子類不實作的話會以父類的實作為主,子類實作的話會以子類的實作為主,
另外子類在覆載 override 父類時,加上 override 關鍵字是個好習慣,使用 C++11 時編譯器在編譯階段可確保子類的函式的覆寫 override 是否成功,
使用時機:在子類不覆寫 override 時,有個預設實作(父類的實作)。

cpp-virtual.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// g++ cpp-virtual.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Base {
public:
virtual void func1() {
cout << "Base func1\n";
}
};

class Derived : public Base {
public:
virtual void func1() override {
cout << "Derived func1\n";
}
};

int main() {
Base b;
b.func1();
Derived d;
d.func1();
Base *d2 = new Derived();
d2->func1();
return 0;
}

輸出如下:

1
2
3
Base func1
Derived func1
Derived func1

這邊另外舉個多型 polymorphism 的例子,有個父類叫 Animal 類別並且提供了 eat() 的預設實作內容,Dog 與 Cat 分別都繼承 Animal 類別,但各自的 eat() 內容都不一樣,

cpp-virtual2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// g++ cpp-virtual2.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Animal {
public:
virtual void eat() {
cout << "I eat food\n";
}
};

class Dog : public Animal {
public:
virtual void eat() override {
cout << "I eat meat\n";
}
};

class Cat : public Animal {
public:
virtual void eat() override {
cout << "I eat fish\n";
}
};

class Unknown : public Animal {
};

int main() {
Animal* animal;
Dog dog;
Cat cat;
Unknown u;
animal = &dog;
animal->eat();

animal = &cat;
animal->eat();

animal = &u;
animal->eat();
return 0;
}

輸出如下,根據多型的特性,使得我們可以在 runtime 程式執行期間再依據各種情形去選擇我們要用哪種子類,就像例子中的 dog 與 cat,如果有個新類別 Unknown 繼承了 Animal 類別沒有實作 eat() 時也能由父類的預設實作來代替,

1
2
3
I eat meat
I eat fish
I eat food

純虛函式 pure virtual function

C++ virtual 關鍵字第二種用法為 pure virtual function,中文翻譯為純虛函式,
如下例子,Base 類別的 func1 為純虛函式,純虛函式寫法為 virtual void func1() = 0;,然後沒有實作,
Base 是抽象類別不能被實體化,
強制子類一定要覆寫實作,在某種情況下有這種設計需求。
子類實作的話當然就會以子類的實作為主。
使用時機:設計一些介面 interface 時,強制別人一定要這樣實作。

cpp-virtual3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// g++ cpp-virtual3.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Base {
public:
virtual void func1() = 0;
};

class Derived : public Base {
public:
virtual void func1() override {
cout << "Derived func1\n";
}
};

int main() {
//Base b; // error: cannot declare variable ‘b’ to be of abstract type ‘Base’
Derived d;
d.func1();
Base *d2 = new Derived();
d2->func1();
return 0;
}

這邊舉個例子,假設今天我就是要規定繼承 Animal 的子類都一樣要給我實作 eat() ,那麼我就可以這樣寫,把 Animal 類別的 eat 改成純虛函式 pure virtual function,這樣每個繼承 Animal 的子類都必須要實做 eat(),假如有個新的 Unknown 類別繼承 Animal 類別但沒有實作 eat() 的話,就會收到編譯器大哥的編譯錯誤訊息,

cpp-virtual4.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// g++ cpp-virtual4.cpp -o a.out -std=c++11
#include <iostream>
using namespace std;

class Animal {
public:
virtual void eat() = 0;
};

class Dog : public Animal {
public:
virtual void eat() override {
cout << "I eat meat\n";
}
};

class Cat : public Animal {
public:
virtual void eat() override {
cout << "I eat fish\n";
}
};

class Unknown : public Animal {
};

int main() {
Animal* animal;
Dog dog;
Cat cat;
//Unknown u; // error: cannot declare variable ‘u’ to be of abstract type ‘Unknown’
animal = &dog;
animal->eat();

animal = &cat;
animal->eat();
return 0;
}

以上就是 C++ virtual 的兩種用法介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

參考
虛擬函式 - 維基百科,自由的百科全書
https://zh.m.wikipedia.org/zh-tw/%E8%99%9A%E5%87%BD%E6%95%B0
虛擬函式 | Microsoft Docs
https://docs.microsoft.com/zh-tw/cpp/cpp/virtual-functions?view=vs-2019
虛擬函式
https://openhome.cc/Gossip/CppGossip/VirtualFunction.html
純虛擬函式(一)
https://openhome.cc/Gossip/CppGossip/PureVirtualFunction.html
純虛擬函式(二)
https://openhome.cc/Gossip/CppGossip/PureVirtualFunction2.html
C++中virtual(虚函数)的用法_foreverhuylee的专栏-CSDN博客
https://blog.csdn.net/foreverhuylee/article/details/34107615
比較安全的 C++ 虛擬函式寫法:C++11 override 與 final – Heresy’s Space
https://kheresy.wordpress.com/2014/10/03/override-and-final-in-cpp-11/

其它相關文章推薦
C/C++ 新手入門教學懶人包
C++ 設計模式 - 單例模式 Singleton Pattern
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ std::sort 排序用法與範例完整介紹
std::queue 用法與範例
std::thread 用法與範例
C/C++ 一堆名詞解釋