C++ Polimorfizm z przykładem
Na czym polega polimorfizm C++?
In C++, polimorfizm powoduje, że funkcja członkowska zachowuje się inaczej w zależności od obiektu, który ją wywołuje/wywołuje. Polimorfizm to greckie słowo oznaczające mieć wiele form. Dzieje się tak, gdy masz hierarchię klas powiązaną poprzez dziedziczenie.
Załóżmy na przykład, że mamy funkcję makeSound(). Gdy kot wywoła tę funkcję, wyda dźwięk miauczenia. Kiedy krowa wywoła tę samą funkcję, wyda dźwięk mow.
Chociaż mamy jedną funkcję, zachowuje się ona inaczej w różnych okolicznościach. Funkcja ma wiele postaci; stąd osiągnęliśmy polimorfizm.
Rodzaje polimorfizmu
C++ obsługuje dwa typy polimorfizmu:
- Polimorfizm w czasie kompilacji i
- Polimorfizm środowiska wykonawczego.
Polimorfizm czasu kompilacji
Przeciążone funkcje wywołujesz, dopasowując liczbę i typ argumentów. Informacje są obecne w czasie kompilacji. Oznacza to C++ kompilator wybierze właściwą funkcję w czasie kompilacji.
Polimorfizm w czasie kompilacji uzyskuje się poprzez przeciążanie funkcji i operatorów.
Przeciążenie funkcji
Przeciążenie funkcji ma miejsce, gdy mamy wiele funkcji o podobnych nazwach, ale różnych argumentach. Argumenty mogą różnić się liczbą lub typem.
1 przykład
#include <iostream> using namespace std; void test(int i) { cout << " The int is " << i << endl; } void test(double f) { cout << " The float is " << f << endl; } void test(char const *ch) { cout << " The char* is " << ch << endl; } int main() { test(5); test(5.5); test("five"); return 0; }
Wyjście:
Oto zrzut ekranu kodu:
Wyjaśnienie kodu:
- Dołącz plik nagłówkowy iostream do naszego kodu. Będziemy mogli korzystać z jego funkcji.
- Uwzględnij przestrzeń nazw std w naszym kodzie. Będziemy mogli korzystać z jego klas bez wywoływania go.
- Utwórz funkcję o nazwie test, która przyjmuje parametr całkowity, tj. { oznacza początek treści testu funkcji.
- Instrukcja wykonywana w przypadku wywołania/wywołania powyższego testu funkcji.
- Koniec treści powyższego testu funkcji.
- Utwórz funkcję o nazwie test, która przyjmuje parametr float f. { oznacza początek treści testu funkcji.
- Instrukcja wykonywana w przypadku wywołania/wywołania powyższego testu funkcji.
- Koniec treści powyższego testu funkcji.
- Utwórz funkcję o nazwie test, która przyjmuje parametr znakowy ch. { oznacza początek treści testu funkcji.
- Instrukcja wykonywana w przypadku wywołania/wywołania powyższego testu funkcji.
- Koniec treści powyższego testu funkcji.
- Wywołaj funkcję main(). { oznacza początek treści funkcji.
- Wywołaj funkcję test i przekaż jej 5 jako wartość argumentu. Wywołuje to funkcję testową, która przyjmuje argument w postaci liczby całkowitej, czyli pierwszą funkcję testową.
- Wywołaj funkcję test i przekaż jej 5.5 jako wartość argumentu. Spowoduje to wywołanie funkcji testowej, która akceptuje argument zmiennoprzecinkowy, czyli drugą funkcję testową.
- Wywołaj funkcję test i przekaż jej pięć jako wartość argumentu. Spowoduje to wywołanie funkcji testowej, która akceptuje argument znakowy, czyli trzecią funkcję testową.
- Program musi zwrócić wartość, jeśli działa pomyślnie.
- Koniec treści funkcji main().
Mamy trzy funkcje o tej samej nazwie, ale różnych typach argumentów. Osiągnęliśmy polimorfizm.
OperaTor Przeciążenie
In Operator Przeciążenie, definiujemy nowe znaczenie a C++ operator. Zmienia również sposób działania operatora. Na przykład możemy zdefiniować operator +, aby połączyć dwa ciągi znaków. Znamy go jako operator dodawania do dodawania wartości liczbowych. Po naszej definicji, gdy zostanie umieszczony między liczbami całkowitymi, doda je. Gdy zostanie umieszczony między ciągami znaków, połączy je.
2 przykład
#include<iostream> using namespace std; class ComplexNum { private: int real, over; public: ComplexNum(int rl = 0, int ov = 0) { real = rl; over = ov; } ComplexNum operator + (ComplexNum const &obj) { ComplexNum result; result.real = real + obj.real; result.over = over + obj.over; return result; } void print() { cout << real << " + i" << over << endl; } }; int main() { ComplexNum c1(10, 2), c2(3, 7); ComplexNum c3 = c1+c2; c3.print(); }
Wyjście:
Oto zrzut ekranu kodu:
Wyjaśnienie kodu:
- Dołącz plik nagłówkowy iostream do naszego programu, aby móc korzystać z jego funkcji.
- Dołącz przestrzeń nazw std do naszego programu, aby móc korzystać z jej klas bez wywoływania jej.
- Utwórz klasę o nazwie ComplexNum. { oznacza początek treści klasy.
- Użyj modyfikatora dostępu prywatnego, aby oznaczyć zmienne jako prywatne, co oznacza, że można uzyskać do nich dostęp tylko z poziomu klasy.
- Zdefiniuj dwie zmienne całkowite, rzeczywistą i większą.
- Użyj modyfikatora dostępu publicznego, aby oznaczyć konstruktor jako publiczny, co oznacza, że będzie on dostępny nawet spoza klasa.
- Utwórz konstruktor klasy i zainicjuj zmienne.
- Zainicjuj wartość zmiennej real.
- Zainicjuj wartość zmiennej over.
- Koniec ciała konstruktora.
- Musimy zmienić znaczenie operatora +.
- Utwórz typ danych result typu ComplexNum.
- Użyj operatora + z liczbami zespolonymi. Ten wiersz doda część rzeczywistą liczby do części rzeczywistej innej liczby.
- Użyj operatora + z liczbami zespolonymi. Ten wiersz doda część urojoną liczby do części urojonej innej liczby.
- Program zwróci wartość zmiennej wynik po pomyślnym wykonaniu.
- Koniec definicji nowego znaczenia operatora +, czyli przeciążania.
- Wywołaj metodę print().
- Wydrukuj nową liczbę zespoloną po dodaniu na konsoli.
- Koniec treści funkcji print().
- Koniec treści klasy ComplexNum.
- Wywołaj funkcję main().
- Przekaż wartości obu części rzeczywistych i zespolonych, które mają zostać dodane. Pierwsza część c1 zostanie dodana do pierwszej części c2, czyli 10+3. Druga część c1 zostanie dodana do drugiej części c, czyli 2+7.
- Wykonaj operację używając przeciążonego operatora + i zapisz wynik w zmiennej c3.
- Wydrukuj wartość zmiennej c3 na konsoli.
- Koniec treści funkcji main().
Polimorfizm w czasie wykonywania
Dzieje się tak, gdy metoda obiektu jest wywoływana/wywoływana w czasie wykonywania, a nie w czasie kompilacji. Polimorfizm środowiska wykonawczego osiąga się poprzez nadpisanie funkcji. Funkcja, która ma zostać wywołana/wywołana, jest ustalana w czasie wykonywania.
Nadpisywanie funkcji
Przesłonięcie funkcji ma miejsce, gdy funkcja klasy bazowej otrzymuje nową definicję w klasie pochodnej. Można wówczas powiedzieć, że funkcja bazowa została nadpisana.
Na przykład:
#include <iostream> using namespace std; class Mammal { public: void eat() { cout << "Mammals eat..."; } }; class Cow: public Mammal { public: void eat() { cout << "Cows eat grass..."; } }; int main(void) { Cow c = Cow(); c.eat(); return 0; }
Wyjście:
Oto zrzut ekranu kodu:
Wyjaśnienie kodu:
- Zaimportuj plik nagłówkowy iostream do naszego programu, aby móc korzystać z jego funkcji.
- Dołącz przestrzeń nazw std do naszego programu, aby móc korzystać z jej klas bez wywoływania jej.
- Utwórz klasę o nazwie Mammal. Znak { oznacza początek treści klasy.
- Użyj modyfikatora dostępu publicznego, aby ustawić funkcję, którą mamy zamiar utworzyć, jako publicznie dostępną. Będzie dostępny spoza tej klasy.
- Utwórz funkcję publiczną o nazwie eat. { oznacza początek treści funkcji.
- Wydrukuj instrukcję dodaną do funkcji cout po wywołaniu funkcji eat().
- Koniec treści funkcji eat().
- Koniec ciała klasy Mammal.
- Utwórz klasę o nazwie Cow, która dziedziczy klasę Mammal. Cow jest klasą pochodną, natomiast Mammal jest klasą bazową. The { oznacza początek tej klasy.
- Użyj modyfikatora dostępu publicznego, aby oznaczyć funkcję, którą za chwilę utworzymy, jako publicznie dostępną. Będzie dostępny spoza tej klasy.
- Zastąp funkcję eat() zdefiniowaną w klasie bazowej. { oznacza początek treści funkcji.
- Instrukcja do wydrukowania na konsoli po wywołaniu tej funkcji.
- Koniec treści funkcji eat().
- Koniec ciała klasy Krowa.
- Wywołaj funkcję main(). { oznacza początek treści tej funkcji.
- Utwórz instancję klasy Cow i nadaj jej nazwę c.
- Wywołaj funkcję eat() zdefiniowaną w klasie Cow.
- Program musi zwrócić wartość po pomyślnym zakończeniu.
- Koniec funkcji main().
C++ Funkcja wirtualna
Funkcja wirtualna to kolejny sposób implementacji polimorfizmu w czasie wykonywania w C++. Jest to specjalna funkcja zdefiniowana w klasie bazowej i przedefiniowana w klasie pochodnej. Aby zadeklarować funkcję wirtualną, należy użyć słowa kluczowego virtual. Słowo kluczowe powinno poprzedzać deklarację funkcji w klasie bazowej.
Jeśli dziedziczona jest klasa funkcji wirtualnej, klasa wirtualna na nowo definiuje funkcję wirtualną, aby odpowiadała jej potrzebom. Na przykład:
#include <iostream> using namespace std; class ClassA { public: virtual void show() { cout << "The show() function in base class invoked..." << endl; } }; class ClassB :public ClassA { public: void show() { cout << "The show() function in derived class invoked..."; } }; int main() { ClassA* a; ClassB b; a = &b; a->show(); }
Wyjście:
Oto zrzut ekranu kodu:
Wyjaśnienie kodu:
- Dołącz plik nagłówkowy iostream do kodu, aby móc korzystać z jego funkcji.
- Dołącz przestrzeń nazw std do naszego kodu, aby używać jej klas bez wywoływania jej.
- Utwórz klasę o nazwie ClassA.
- Użyj modyfikatora dostępu publicznego, aby oznaczyć członka klasy jako publicznie dostępnego.
- Utwórz funkcję wirtualną o nazwie show(). Będzie to funkcja publiczna.
- Tekst do wydrukowania po wywołaniu show(). Koniec jest a C++ słowo kluczowe, które oznacza linię końcową. Przesuwa kursor myszy do następnej linii.
- Koniec treści funkcji wirtualnej show().
- Koniec ciała klasy ClassA.
- Tworzenie nowej klasy o nazwie ClassB, która dziedziczy klasę ClassA. Klasa A staje się klasą bazową, a Klasa B klasą pochodną.
- Użyj modyfikatora dostępu publicznego, aby oznaczyć członka klasy jako publicznie dostępnego.
- Zdefiniuj na nowo funkcję wirtualną show() wywodzącą się z klasy bazowej.
- Tekst, który ma zostać wydrukowany na konsoli po wywołaniu funkcji show() zdefiniowanej w klasie pochodnej.
- Koniec treści funkcji show().
- Koniec treści klasy pochodnej, ClassB.
- Wywołaj funkcję main(). Logikę programu należy dodać w jego treści.
- Utwórz zmienną wskaźnikową o nazwie a. Wskazuje na klasę o nazwie ClassA.
- Utwórz instancję klasy o nazwie ClassB. Instancja ma nazwę b.
- Przypisz wartości zapisane pod adresem b w zmiennej a.
- Wywołaj funkcję show() zdefiniowaną w klasie pochodnej. Zaimplementowano późne wiązanie.
- Koniec treści funkcji main().
Polimorfizm w czasie kompilacji vs. Polimorfizm w czasie wykonywania
Oto główne różnice między nimi:
Polimorfizm w czasie kompilacji | Polimorfizm w czasie wykonywania |
---|---|
Nazywa się to również wczesnym wiązaniem lub polimorfizmem statycznym | Nazywa się to również późnym/dynamicznym wiązaniem lub dynamicznym polimorfizmem |
Metoda jest wywoływana/wywoływana w czasie kompilacji | Metoda jest wywoływana/wywoływana w czasie wykonywania |
Zaimplementowano poprzez przeciążanie funkcji i przeciążanie operatorów | Zaimplementowane poprzez nadpisywanie metod i funkcje wirtualne |
Przykład, przeciążenie metody. Wiele metod może mieć podobne nazwy, ale inną liczbę lub typy argumentów | Przykład, przesłonięcie metody. Wiele metod może mieć podobną nazwę i ten sam prototyp. |
Szybsze wykonanie, ponieważ wykrywanie metod odbywa się w czasie kompilacji | Wolniejsze wykonanie, ponieważ wykrywanie metod jest wykonywane w czasie wykonywania. |
Less zapewniona jest elastyczność rozwiązywania problemów, ponieważ wszystko jest znane w czasie kompilacji. | Metody odkrywane są w trakcie działania programu, co zapewnia dużą elastyczność przy rozwiązywaniu złożonych problemów. |
Podsumowanie
- Polimorfizm oznacza posiadanie wielu form.
- Ma to miejsce, gdy istnieje hierarchia klas powiązana poprzez dziedziczenie.
- W przypadku polimorfizmu funkcja może zachowywać się inaczej w zależności od obiektu, który ją wywołuje/wywołuje.
- W przypadku polimorfizmu w czasie kompilacji funkcja, która ma zostać wywołana, jest ustalana w czasie kompilacji.
- W polimorfizmie wykonawczym funkcja, która ma zostać wywołana, jest ustalana w czasie wykonywania.
- Polimorfizm w czasie kompilacji jest określany poprzez przeciążanie funkcji i przeciążanie operatorów.
- W przypadku przeciążenia funkcji istnieje wiele funkcji o podobnych nazwach, ale różnych argumentach.
- Parametry mogą różnić się liczbą lub typem.
- W przeciążaniu operatora zdefiniowano nowe znaczenie C++ operatorzy.
- Polimorfizm środowiska wykonawczego osiąga się poprzez nadpisanie funkcji.
- Podczas przesłaniania funkcji klasa pochodna nadaje nową definicję funkcji zdefiniowanej w klasie bazowej.