Funkcje wirtualne
Funkcja wirtualna jest funkcją członkowską, której ponowna definicja jest oczekiwana w klasach pochodnych.Przy odwoływaniu się do obiektu klasy pochodnej za pomocą wskaźnika lub odwołania do klasy podstawowej, można wywołać funkcję wirtualną dla tego obiektu i wykonywać wersję klasy pochodnej tej funkcji.
Funkcje wirtualne zapewniają wywołanie poprawnej funkcji dla obiektu, bez względu na wyrażenie użyte do wywołania funkcji.
Załóżmy, że klasa podstawowa zawiera funkcję zadeklarowaną jako virtual, a klasa pochodna definiuje tę samą funkcję.Funkcja z klasy pochodnej jest wywoływana dla obiektów klasy pochodnej, nawet jeśli jest wywoływana przy użyciu wskaźnika lub odwołania do klasy podstawowej.W poniższym przykładzie przedstawiono klasę podstawową, która dostarcza implementację funkcji PrintBalance i dwie klasy pochodne
// 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();
}
W powyższym kodzie, wywołania PrintBalance są identyczne, z wyjątkiem tego, na co wskazuje obiekt pAccount.Ponieważ PrintBalance jest wirtualna, wywoływana jest wersja funkcji zdefiniowana dla każdego obiektu.Funkcja PrintBalance w klasach pochodnych CheckingAccount i SavingsAccount "zastępuje" funkcję w klasie podstawowej Account.
Jeżeli zadeklarowano klasę, która nie dostarcza zastąpionej implementacji funkcji PrintBalance, używana jest domyślna implementacja z klasy podstawowej Account.
Funkcje w klasach pochodnych zastępują funkcje wirtualne w klasach podstawowych tylko wtedy, gdy ich typ jest taki sam.Funkcja w klasie pochodnej nie może różnić się od funkcji wirtualnej w klasie podstawowej tylko pod względem zwracanego typu; lista argumentów również musi się różnić.
Podczas wywoływania funkcji za pomocą wskaźników lub odwołań, obowiązują następujące zasady:
Wywołanie funkcji wirtualnej jest rozwiązywane zgodnie z podstawowym typem obiektu, dla którego jest wywoływane.
Wywołanie funkcji niewirtualnej jest rozwiązywane zgodnie z typem wskaźnika lub odwołania.
W poniższym przykładzie pokazano sposób w jaki wirtualne i niewirtualne funkcje zachowują się po wywołaniu przez wskaźniki:
// 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.
}
Dane wyjściowe
Derived::NameOf
Invoked by Base
Derived::NameOf
Invoked by Derived
Należy zauważyć, że bez względu na to czy funkcja NameOf jest wywoływana za pomocą wskaźnika do Base, czy wskaźnika do Derived, wywołuje funkcję dla Derived.Wywołuje funkcję dla Derived, ponieważ NameOf jest funkcją wirtualną i zarówno pBase, jak i pDerived wskazują obiekt typu Derived.
Ponieważ wirtualne funkcje są wywoływane tylko dla obiektów typu klasy, nie można zadeklarować globalnych lub statycznych funkcji jako virtual.
Słowo kluczowego virtual można użyć podczas deklarowania zastępowanych funkcji w klasie pochodnej, ale nie jest to konieczne; przeciążenia funkcji wirtualnych są zawsze wirtualne.
Funkcje wirtualne muszą być zdefiniowane w klasie podstawowej, chyba że są one zadeklarowane za pomocą czystego specyfikatora. (Aby uzyskać więcej informacji dotyczących czystych funkcji wirtualnych, zobacz Klasy abstrakcyjne.)
Mechanizm wywołania funkcji wirtualnych można pominąć przez jawną kwalifikację nazwy funkcji za pomocą operatora rozpoznawania zakresu (::).Rozważmy wcześniejszy przykład obejmujący klasę Account.Aby wywołać PrintBalance w klasie podstawowej, należy użyć następującego kodu:
CheckingAccount *pChecking = new CheckingAccount( 100.00 );
pChecking->Account::PrintBalance(); // Explicit qualification.
Account *pAccount = pChecking; // Call Account::PrintBalance
pAccount->Account::PrintBalance(); // Explicit qualification.
Oba wywołania PrintBalance w poprzednim przykładzie pomijają mechanizm wirtualnego wywołania funkcji.