虛擬函式

在面向物件程式設計領域，C++、Object Pascal 等語言中有虛擬函式-{zh:多型;zh-tw:多型;zh-cn:多型}-的重要組成部分。簡言之，虛擬函式可以給出目標函式的定義，但該目標的具體指向在編譯期可能無法確定。虛擬函式在設計模式方面扮演重要角色。例如，《設計模式》一書中提到的23種設計模式中，僅5個物件建立模式就有4個用到了虛擬函式，只有單例沒有用到。目的.虛擬函式概念的引入可以解決這樣的問題：在面向物件程式設計中，派生類繼承自基類。使用指標或引用訪問派生類物件時，指標或引用本身所指向的型別是基類而不是派生類。如果派生類覆蓋了基類中的方法，透過上述指標或引用呼叫該方法時，可以有兩種結果：虛擬函式的效果屬於後者。如果問題中基類的函式是「虛」的，則呼叫到的都是最終派生類中的函式實現，與指標或引用的型別無關。反之，如果函式非「虛」，呼叫到的函式就在編譯期根據指標或者引用所指向的型別決定。有了虛擬函式，程式甚至能夠呼叫編譯期還不存在的函式。在 C++ 中，在基類的成員函式宣告前加上關鍵字 codice_1 即可讓該函式成為 虛擬函式，派生類中對此函式的不同實現都會繼承這一修飾符，允許後續派生類覆蓋，達到遲繫結的效果。即便是基類中的成員函式呼叫虛擬函式，也會呼叫到派生類中的版本。程式範例.例如，一個基礎類別 codice_2 有一個虛擬函式 codice_3。子類別 codice_4 要實做一個函式 codice_5，這個子類別 codice_4 與子類別 codice_7 是完全不同的，但是你可以引用類別 Animal 底下的函式 codice_5 定義，而使用子類別 codice_4 底下函式 codice_5 的處理程式。C++.以下程式碼是 C++ 的程式範例。要注意的是，這個範例沒有異常處理的程式碼。尤其是 new 或是 vector::push_back 丟出一個異常時，程式在執行時有可能會出現崩潰或是錯誤的現象。using namespace std;class Animalpublic:class Wolf : public Animalpublic:class Fish : public Animalpublic:class GoldFish : public Fishpublic:class OtherAnimal : public Animal};int main()std::vector animals;animals.push_back( new Animal() );animals.push_back( new Wolf() );animals.push_back( new Fish() );animals.push_back( new GoldFish() );animals.push_back( new OtherAnimal() );for( std::vector::const_iterator it = animals.begin();it != animals.end(); ++it)(*it)-&gt;eat();delete *it;return 0;以下是虛擬函式 codice_11 的輸出：I eat like a generic Animal.I eat like a wolf!I eat like a fish!I eat like a goldfish!I eat like a generic Animal.當 codice_11 不是被宣告為虛擬函式時，輸出如下所示：I eat like a generic Animal.I eat like a generic Animal.I eat like a generic Animal.I eat like a generic Animal.I eat like a generic Animal.Java.在Java語言中, 所有的方法預設都是"虛擬函式". 只有以關鍵字 final 標記的方法才是非虛擬函式. 以下是 Java 中虛方法的一個例子:import java.util.*;public class Animal {public static void main(String[] args) {List();animals.add(new Animal());animals.add(new Wolf());animals.add(new Fish());animals.add(new OtherAnimal());for (Animal currentAnimal : animals) {currentAnimal.eat();public class Wolf extends Animal {public class Fish extends Animal {輸出:I eat like a generic Animal.I eat like a wolf!I eat like a fish!I eat like a generic Animal.C#.在 C# 語言中, 對基類中的任何虛方法必須用 codice_1 修飾, 而衍生類中由基類繼承而來的過載方法必須用 codice_14 修飾. 以下是 C# 的一個程式例項:using System;using System.Collections.Generic;namespace ConsoleApplication1public class Animalpublic virtual void Eat()Console.WriteLine("I eat like a generic Animal.");public class Wolf : Animalpublic override void Eat()Console.WriteLine("I eat like a wolf!");public class Fish : Animalpublic override void Eat()Console.WriteLine("I eat like a fish!");public class GoldFish : Fishpublic override void Eat()Console.WriteLine("I eat like a goldfish!");public class OtherAnimal : Animal// Eat() method is not overridden, so the base class method will be used.public class Programpublic static void Main(string[] args)IList();animals.Add(new Animal());animals.Add(new Wolf());animals.Add(new Fish());animals.Add(new GoldFish());animals.Add(new OtherAnimal());foreach (Animal currentAnimal in animals)currentAnimal.Eat();輸出:抽象類和純虛擬函式.純虛擬函式或純虛方法是一個需要被非抽象的派生類覆蓋的虛擬函式. 包含純虛方法的類被稱作抽象類; 抽象類不能被直接例項化。 一個抽象基類的一個子類只有在所有的純虛擬函式在該類(或其父類)內給出實現時, 才能直接例項化. 純虛方法通常只有宣告(簽名)而沒有定義(實現)，但有特例情形要求純虛擬函式必須給出函式體定義.作為一個例子, 抽象基類"MathSymbol"可能提供一個純虛擬函式 codice_15, 和衍生類 "Plus" 和 "Minus" 提供codice_15 的具體實現. 由於 "MathSymbol" 是一個抽象概念, 為其每個子類定義了同一的動作, 在 "MathSymbol" 類中執行 codice_15 沒有任何意義. 類似的, 一個給定的 "MathSymbol" 子類如果沒有 codice_15 的具體實現是不完全的.雖然純虛方法通常在定義它的類中沒有實現, 在 C++ 語言中, 允許純虛擬函式在定義它的類中包含其實現, 這為衍生類提供了備用或預設的行為. C++的虛基類的虛解構函式必須提供函式體定義，否則連結時在析構該抽象類的派生例項物件的語句處會報錯。C++.在C++語言中, 純虛擬函式用一種特別的語法["=0"]定義, 見以下示例.class Abstract {public:virtual void pure_virtual() = 0;純虛擬函式的定義僅提供方法的原型. 雖然在抽象類中通常不提供純虛擬函式的實現, 但是抽象類中可以包含其實現, 而且可以不在宣告的同時給出定義. 每個非抽象子類仍然需要過載該方法, 抽象類中實現的呼叫可以採用以下這種形式:void Abstract::pure_virtual() {// do somethingclass Child : public Abstract {virtual void pure_virtual(); // no longer abstract, this class may be// instantiated.void Child::pure_virtual() {Abstract::pure_virtual(); // the implementation in the abstract class// is executed構造與析構時的行為.在物件的建構函式和解構函式中涉及虛擬函式時，不同的語言有不同的規定。在以 C++ 為代表的一些語言中，虛擬函式排程機制在物件的構造和析構時有不同的語義，一般建議儘可能避免在 C++ 的建構函式中呼叫虛擬函式。而在 C#、Java 等另一些語言中，構造時可以呼叫派生類中的實現，抽象工廠模式等設計模式也鼓勵在支援這一特性的語言中使用這種用法。