0Pricing
C++ Academy · Lekcja

Polimorfizm statyczny

Unikaj narzutu wywołań wirtualnych

Polimorfizm statyczny to bezpłatna lekcja C++ Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C++ Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C++ Academy zawiera 4 lekcji w sumie.

Dwa rodzaje polimorfizmu

C++ obsługuje dwa rodzaje polimorfizmu.

  • Dynamiczny: funkcje wirtualne rozstrzygane w czasie działania za pomocą vtable
  • Statyczny: szablony i CRTP rozstrzygane w czasie kompilacji

Polimorfizm statyczny zapewnia większą szybkość kosztem elastyczności.

Koszt virtual

Wywołanie funkcji virtual wymaga pośredniego skoku przez vtable. Kompilator zazwyczaj nie może go rozwinąć, co blokuje wiele optymalizacji.

#include <iostream>

struct Shape {
    virtual double area() const = 0;
    virtual ~Shape() = default;
};

struct Square : Shape {
    double s;
    Square(double x) : s(x) {}
    double area() const override { return s * s; }
};

int main() {
    Shape* p = new Square(3);
    std::cout << p->area() << "\n";
    delete p;
    return 0;
}

Statyczne rozstrzyganie wywołań z CRTP

W CRTP klasa bazowa wywołuje metodę klasy pochodnej bezpośrednio za pomocą rzutowania wykonywanego w czasie kompilacji, dzięki czemu kompilator może rozwinąć wszystkie wywołania.

#include <iostream>

template <typename T>
struct Shape {
    double area() const { return static_cast<const T*>(this)->area(); }
};

struct Square : Shape<Square> {
    double s;
    Square(double x) : s(x) {}
    double area() const { return s * s; }
};

int main() {
    Square sq(3);
    std::cout << sq.area() << "\n";
    return 0;
}

Nazewnictwo funkcji przekazujących wywołanie

Gdy zarówno klasa bazowa, jak i pochodna definiują area, należy nadać funkcji przekazującej wywołanie w klasie bazowej odrębną nazwę, na przykład compute, aby jednoznacznie wywoływała implementację klasy pochodnej.

#include <iostream>

template <typename T>
struct Shape {
    double compute() const { return static_cast<const T*>(this)->area(); }
};

struct Circle : Shape<Circle> {
    double r;
    Circle(double x) : r(x) {}
    double area() const { return 3.14159 * r * r; }
};

int main() {
    Circle c(2);
    std::cout << c.compute() << "\n";
    return 0;
}

Szablony jako polimorfizm statyczny

Zwykłe szablony funkcji również są przykładem polimorfizmu statycznego: ten sam kod działa dla każdego typu udostępniającego wymagany interfejs (duck typing w czasie kompilacji).

#include <iostream>

struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };

template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }

int main() {
    make_speak(Dog{});
    make_speak(Cat{});
    return 0;
}

Gdy typ jest znany

Polimorfizm statyczny ma zastosowanie tylko wtedy, gdy typ konkretny jest znany w czasie kompilacji. Jeśli muszą Państwo przechowywać heterogeniczne obiekty w jednym kontenerze i wybierać sposób działania w czasie wykonania, nadal potrzebują Państwo funkcji wirtualnych.

Brak heterogenicznych kontenerów

Nie można umieścić Shape<Square> i Shape<Circle> w tym samym std::vector jako wspólnej klasy bazowej, ponieważ są to niezależne typy. To główne ograniczenie polimorfizmu statycznego.

Korzyść wydajnościowa

Wstawiając wywołanie do kodu wynikowego, kompilator może scalić stałe i całkowicie wyeliminować narzut wywołania funkcji. W ciasnych pętlach operujących na jednym znanym typie polimorfizm statyczny może działać znacznie szybciej.

#include <iostream>

template <typename T>
struct Op {
    int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};

struct Doubler : Op<Doubler> {
    int apply(int x) const { return x * 2; }
};

int main() {
    Doubler d;
    int sum = 0;
    for (int i = 0; i < 5; ++i) sum += d.apply(i);
    std::cout << sum << "\n";
    return 0;
}

Łączenie obu podejść

Mogą Państwo użyć CRTP na ścieżce krytycznej oraz cienkiej otoczki wirtualnej, gdy potrzebne jest wymazanie typu. Zapewnia to szybkość tam, gdzie ma ona znaczenie, i elastyczność tam, gdzie jest wymagana.

Statyczne sprawdzanie interfejsu

Jeśli klasa pochodna zapomni zaimplementować wymaganą metodę, błąd pojawi się w czasie kompilacji, gdy zostanie utworzony obiekt przekazujący wywołanie z klasy bazowej, a nie w czasie wykonania.

#include <iostream>

template <typename T>
struct Greeter {
    void greet() const { static_cast<const T*>(this)->hello(); }
};

struct English : Greeter<English> {
    void hello() const { std::cout << "Hello\n"; }
};

int main() {
    English{}.greet();
    return 0;
}

Wybór właściwego narzędzia

Proszę używać polimorfizmu statycznego, gdy:

  • Typ jest znany w czasie kompilacji
  • Wydajność ma kluczowe znaczenie
  • Nie potrzebują Państwo wspólnego typu bazowego w czasie wykonania

W przeciwnym razie proszę preferować funkcje wirtualne ze względu na czytelność i elastyczność.

Szybkie sprawdzenie

Proszę wskazać najważniejszy kompromis związany z polimorfizmem statycznym.

Podsumowanie

Porównali Państwo polimorfizm statyczny i dynamiczny.

  • Virtual = wybór funkcji w czasie wykonania, vtable, heterogeniczne kontenery
  • CRTP/templates = wybór funkcji w czasie kompilacji, możliwość wstawienia do kodu wynikowego, większa szybkość
  • Polimorfizm statyczny wymaga znajomości typu w czasie kompilacji
  • Wyboru należy dokonać na podstawie potrzeb w zakresie elastyczności i wydajności

Często zadawane pytania

Czy lekcja „Polimorfizm statyczny” jest bezpłatna?

Tak — pełny tekst „Polimorfizm statyczny” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C++ Academy, przejdź na CoddyKit PRO. Kurs C++ Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Polimorfizm statyczny”?

Unikaj narzutu wywołań wirtualnych Ćwiczysz C++ Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C++ Academy?

Nie wymagamy żadnego doświadczenia. C++ Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Polimorfizm statyczny”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C++ Academy?

Tak. Każda lekcja C++ Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Idiom CRTP
  2. Polimorfizm statyczny
  3. Mixiny z CRTP
  4. Kiedy używać CRTP
← Powrót do C++ Academy