Specyfikator final.
W poprzednim wpisie z serii C++11 opisałem specyfikator override. Niejako w parze z nim występuje specyfikator final, który również odnosi się do przeciążania funkcji wirtualnych w klasach pochodnych.
Mówiąc krótko: jeśli nie chcemy, żeby funkcja wirtualna była dalej przeciążana (w kolejnych klasach pochodnych w hierarchii), możemy użyć słowa final w deklaracji funkcji – dokładnie tak samo, jak wykorzystujemy override.
Posłużę się tutaj przykładem z poprzedniego wpisu:
#include <iostream> class ClassA { public: virtual ~ClassA() {} virtual void Function(int a_Argument) final { std::cout << "ClassA::Function" << std::endl; } }; class ClassB : public ClassA { public: virtual void Function(int a_Argument) { std::cout << "ClassB::Function" << std::endl; } }; int main() { ClassA* ptrA = new ClassB; ptrA->Function(4); delete ptrA; return 0; }
Dodałem specyfikator final na końcu deklaracji funkcji w klasie ClassA. Może się wydawać dziwne, że deklaruję funkcję wirtualną w klasie bazowej i od razu zabraniam jej przeciążania, ale ma to tylko pokazać efekt, jaki powoduje nasz kod.
Przy próbie kompilacji mamy błąd:
error: overriding final function 'virtual void ClassA::Function(int)'
Nie możemy więc przeciążyć funkcji function w klasie pochodnej.
Override i final.
Kolejną ciekawą konstrukcją jest użycie jednocześnie obu specyfikatorów. Czy to ma sens ? Oczywiście.
Mamy więc:
#include <iostream> class ClassA { public: virtual ~ClassA() {} virtual void Function(int a_Argument) { std::cout << "ClassA::Function" << std::endl; } }; class ClassB : public ClassA { public: virtual void Function(int a_Argument) override final { std::cout << "ClassB::Function" << std::endl; } }; int main() { ClassA* ptrA = new ClassB; ptrA->Function(4); delete ptrA; return 0; }
W ten sposób wykorzystujemy cechy obu specyfikatorów, które są od siebie niezależne. Jesteśmy pewni, że funkcja zostanie poprawnie przeciążona w klasie ClassB. Jeśli natomiast chcielibyśmy utworzyć klasę dziedziczącą po ClassB i przeciążyć funkcję function – otrzymamy jasny komunikat o błędzie w czasie kompilacji.
Specyfikatory a słowa kluczowe.
Niektóre nowe funkcjonalności w C++ wprowadziły do języka nowe słowa kluczowe (np. nullptr). Jednak override i final są przypadkami specjalnymi. Oba słowa zostały zdefiniowane jako tzw. contextual keywords.
W dosłownym tłumaczeniu musiałbym powiedzieć, że są to kontekstowe słowa kluczowe, ale uważam takie określenie za nie do końca trafne. Z tego powodu nazywam je specyfikatorami, co wydaje się dość sensownym tłumaczeniem na język polski.
Oba specyfikatory nie są słowami kluczowymi. Są rozpoznawane jedynie w kontekście deklaracji funkcji wirtualnej. Oznacza to, że całkowicie poprawny jest poniższy kod:
int override = 17; bool final = true;
Możemy użyć wspomnianych słów jako zwykłych identyfikatorów w C++ – np. dla nazw zmiennych. Osobną kwestią jest jednak sensowność takiego podejścia 🙂
Kontynuuj poznawanie C++11: C++11 #9: Wyłączanie funkcji z użycia: słowo kluczowe delete.