虛擬函式

虛擬函式是您預期在衍生類別中重新定義的成員函式。當您參照使用指標或參考的基底類別的衍生的類別物件時,可以呼叫虛擬函式,該物件,並執行衍生的類別的函式版本。

虛擬函式確保在正確的函式會呼叫物件,不論用來呼叫該函式的運算式。

假設基底類別包含函式宣告為虛擬 ,並在衍生的類別定義相同的函式。從衍生類別函式是物件的衍生類別中,即使叫用呼叫使用的指標或在基底類別的參考。下列範例會示範提供實作的基底類別PrintBalance函式和兩個衍生的類別

// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class Account {
public:
   Account( double d ) { _balance = d; }
   virtual double GetBalance() { return _balance; }
   virtual void PrintBalance() { cerr << "Error. Balance not available for base type." << endl; }
private:
    double _balance;
};

class CheckingAccount : public Account {
public:
   CheckingAccount(double d) : Account(d) {}
   void PrintBalance() { cout << "Checking account balance: " << GetBalance() << endl; }
};

class SavingsAccount : public Account {
public:
   SavingsAccount(double d) : Account(d) {}
   void PrintBalance() { cout << "Savings account balance: " << GetBalance(); }
};

int main() {
   // Create objects of type CheckingAccount and SavingsAccount.
   CheckingAccount *pChecking = new CheckingAccount( 100.00 ) ;
   SavingsAccount  *pSavings  = new SavingsAccount( 1000.00 );

   // Call PrintBalance using a pointer to Account.
   Account *pAccount = pChecking;
   pAccount->PrintBalance();

   // Call PrintBalance using a pointer to Account.
   pAccount = pSavings;
   pAccount->PrintBalance();   
}

上述程式碼以呼叫PrintBalance除了都相同,物件pAccount所指向的點。因為PrintBalance是虛擬的每個物件稱為中定義的函式版本。PrintBalance在衍生類別中的函式CheckingAccount和SavingsAccount "覆寫 「 基底類別中的函式Account。

如果類別宣告並未提供覆寫的實作PrintBalance函式、 從基底類別的預設實作Account用。

函式,衍生類別中的覆寫基底類別中的虛擬函式,只有其型別是相同。在衍生類別中的函式不能與不同傳回型別 ; 基底類別虛擬函式 引數清單必須與也不同。

在呼叫的函式指標或參考時,便會套用下列規則:

  • 虛擬函式的呼叫就會根據它會呼叫的物件的基礎型別來解決。

  • 非虛擬的函式的呼叫就會根據指標或參考的型別來解決。

下列範例會示範如何虛擬和非虛擬函式透過指標呼叫時的行為:

// deriv_VirtualFunctions2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class Base {
public:
   virtual void NameOf();   // Virtual function.
   void InvokingClass();   // Nonvirtual function.
};

// Implement the two functions.
void Base::NameOf() {
   cout << "Base::NameOf\n";
}

void Base::InvokingClass() {
   cout << "Invoked by Base\n";
}

class Derived : public Base {
public:
   void NameOf();   // Virtual function.
   void InvokingClass();   // Nonvirtual function.
};

// Implement the two functions.
void Derived::NameOf() {
   cout << "Derived::NameOf\n";
}

void Derived::InvokingClass() {
   cout << "Invoked by Derived\n";
}

int main() {
   // Declare an object of type Derived.
   Derived aDerived;

   // Declare two pointers, one of type Derived * and the other
   //  of type Base *, and initialize them to point to aDerived.
   Derived *pDerived = &aDerived;
   Base    *pBase    = &aDerived;

   // Call the functions.
   pBase->NameOf();           // Call virtual function.
   pBase->InvokingClass();    // Call nonvirtual function.
   pDerived->NameOf();        // Call virtual function.
   pDerived->InvokingClass(); // Call nonvirtual function.
}

0y01k918.collapse_all(zh-tw,VS.110).gifOutput

Derived::NameOf
Invoked by Base
Derived::NameOf
Invoked by Derived

請注意,無論是否NameOf函式透過指標叫用Base或變數的指標, Derived,它會呼叫的函式Derived。它會呼叫的函式Derived因為NameOf虛擬函式,而且兩者都是pBase和pDerived指向的物件型別的Derived。

因為虛擬函式會呼叫物件的類別型別,所以您不能宣告為全域或靜態函式虛擬

虛擬宣告衍生的類別中覆寫函式時,就可以使用關鍵字,但不是必要的。 覆寫虛擬函式的一定是虛擬的。

必須定義在基底類別虛擬函式,除非宣告為使用純規範。(如需有關純虛擬函式的詳細資訊,請參閱抽象類別。)

虛擬函式呼叫的機制可以隱藏是明確地限定函式名稱使用範圍解析運算子 (::)。讓我們舉早涉及Account類別。若要呼叫PrintBalance在基底類別中,使用如下所示的程式碼:

CheckingAccount *pChecking = new CheckingAccount( 100.00 );

pChecking->Account::PrintBalance();  //  Explicit qualification.

Account *pAccount = pChecking;  // Call Account::PrintBalance

pAccount->Account::PrintBalance();   //  Explicit qualification.

這兩個呼叫PrintBalance在上述範例中隱藏的虛擬函式呼叫的機制。

請參閱

參考

虛擬函式的存取