C++11 #8: Przeciążanie funkcji wirtualnych: specyfikator final.

|

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.

Dodaj komentarz