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
- Idiom CRTP
- Polimorfizm statyczny
- Mixiny z CRTP
- Kiedy używać CRTP