Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Metody wirtualne. Dlaczego metody wirtualne? W C++ dla wskaźników i referencji dozwolona jest konwersja potomna bazowa. W C++ dla wskaźników i referencji.

Podobne prezentacje


Prezentacja na temat: "Metody wirtualne. Dlaczego metody wirtualne? W C++ dla wskaźników i referencji dozwolona jest konwersja potomna bazowa. W C++ dla wskaźników i referencji."— Zapis prezentacji:

1 Metody wirtualne

2 Dlaczego metody wirtualne? W C++ dla wskaźników i referencji dozwolona jest konwersja potomna bazowa. W C++ dla wskaźników i referencji dozwolona jest konwersja potomna bazowa. przez taki wskaźnik lub referencję można odwoływać się jedynie do danych zadeklarowanych w klasie bazowej, oraz jedynie do metod klasy bazowej przez taki wskaźnik lub referencję można odwoływać się jedynie do danych zadeklarowanych w klasie bazowej, oraz jedynie do metod klasy bazowej na podstawie klasy wskaźnika/referencji kompilator zdecyduje o wywołaniu metody kl. bazowej nazwet jeżeli obiekt jest klasy potomnej. na podstawie klasy wskaźnika/referencji kompilator zdecyduje o wywołaniu metody kl. bazowej nazwet jeżeli obiekt jest klasy potomnej.

3 class punkt { int x,y; int x,y;public: void pokaz(); //rysuje punkt void pokaz(); //rysuje punkt void ukryj(); void ukryj();}; class okrag: public punkt { int r; int r;public: void pokaz(); //rysuje punkt void pokaz(); //rysuje punkt void ukryj(); void ukryj();}; okrag o; punkt &rp=o; rp.pokaz(); //punkt::pokaz

4 Dlaczego metody wirtualne? okrag o; punkt &rp=o; rp.pokaz(); // niech wywoła się okrag::pokaz jak to zrealizować? jak to zrealizować?

5 class punkt { int x,y; int x,y;public: char klasa; char klasa; void pokaz(); //rysuje punkt void pokaz(); //rysuje punkt punkt(int x, int y) punkt(int x, int y) :x(x), y(y), :x(x), y(y), { klasa=p; klasa=p; }}; Rozwiązanie niedoskonałe Rozwiązanie niedoskonałe Wadliwe Wadliwe class okrag: public punkt { int r; int r;public: void pokaz(); //rysuje punkt void pokaz(); //rysuje punkt okrag(int x, int y, int r) okrag(int x, int y, int r) :punkt(x,y), r(r) :punkt(x,y), r(r) { klasa=o; klasa=o; }}; okrag o; punkt &rp=o; if (rp.klasa==p) rp.punkt::pokaz(); rp.punkt::pokaz();else rp.okrag::pokaz(); rp.okrag::pokaz();

6 Metody wirtualne Jeżeli zadeklarujemy metodę jako wirtualną to kompilator uzupełni obiekty o pole determinujące klasę obiektu i przy wywoływaniu wybranych przez nas metod wywoła metodę z właściwej klasy. Jeżeli zadeklarujemy metodę jako wirtualną to kompilator uzupełni obiekty o pole determinujące klasę obiektu i przy wywoływaniu wybranych przez nas metod wywoła metodę z właściwej klasy. void virtual punkt::ukryj(); Metodę (albo operator) wystarczy raz zadeklarować jako wirtualny, w klasach pochodnych możemy, ale nie musimy używać słowa kl. virtual. Metodę (albo operator) wystarczy raz zadeklarować jako wirtualny, w klasach pochodnych możemy, ale nie musimy używać słowa kl. virtual.

7 class punkt { int x,y; int x,y;public: void virtual pokaz(); void virtual pokaz(); void virtual ukryj(); void virtual ukryj();}; class okrag: public punkt { int r; int r;public: void pokaz(); // virtual void pokaz(); // virtual void ukryj(); // virtual void ukryj(); // virtual}; Metody wirtualne

8 Metody wirtualne - działanie Do pierwszej klasy w której w hierarchii klas pojawi się metoda wirtualna dodane zostanie dodatkowe niejawne pole adres tablicy metod wirtualnych. Do pierwszej klasy w której w hierarchii klas pojawi się metoda wirtualna dodane zostanie dodatkowe niejawne pole adres tablicy metod wirtualnych. zwiększy się rozmiar obiektów tej klasy. zwiększy się rozmiar obiektów tej klasy. Dla obiektu, którego klasy nie można jednoznacznie określić na etapie kompilacji, odwołania do metody, bądź metod zadeklarowanych jako wirtualne będą się odbywały pośrednio poprzez tablicę metod wirtualnych Dla obiektu, którego klasy nie można jednoznacznie określić na etapie kompilacji, odwołania do metody, bądź metod zadeklarowanych jako wirtualne będą się odbywały pośrednio poprzez tablicę metod wirtualnych będzie to działało wolniej niż odwołanie bezpośrednie, będzie to działało wolniej niż odwołanie bezpośrednie, metody wirtualne nie będą rozwijane inline, metody wirtualne nie będą rozwijane inline, będzie to działało szybciej, niż gdybyśmy taką sztuczkę robili ręcznie, będzie to działało szybciej, niż gdybyśmy taką sztuczkę robili ręcznie, kompilator nie pomyli się (człowiek – wiadomo). kompilator nie pomyli się (człowiek – wiadomo).

9 Metody wirtualne - działanie Odwołania przez wskaźnik i referencje będą pośrednie Odwołania przez wskaźnik i referencje będą pośrednie Odwołania przez kwalifikację obiektem będą bezpośrednie Odwołania przez kwalifikację obiektem będą bezpośrednie a więc szybsze, metody (nawet zadeklarowane z virtual) mogą być rozwijane inline. a więc szybsze, metody (nawet zadeklarowane z virtual) mogą być rozwijane inline. Uwaga: metody klasy mogą zostać odziedziczone i aktywowane na rzecz obiektu klasy pochodnej, a więc odwołania do wirtualnych metod danej klasy z innych metod tej klasy będą też pośrednie! Uwaga: metody klasy mogą zostać odziedziczone i aktywowane na rzecz obiektu klasy pochodnej, a więc odwołania do wirtualnych metod danej klasy z innych metod tej klasy będą też pośrednie!

10 class punkt { int x,y; int x,y;public: void virtual pokaz(); void virtual pokaz(); void virtual ukryj(); void virtual ukryj(); void przesun(int dx, int dy) void przesun(int dx, int dy) { ukryj(); // wirtualna w punkt ukryj(); // wirtualna w punkt x+=dx; x+=dx; y+=dy; y+=dy; pokaz(); // wirtualna w punkt pokaz(); // wirtualna w punkt }}; class okrag: public punkt { int r; int r;public: void pokaz(); void pokaz(); void ukryj(); void ukryj();}; okrag o; punkt &rp=o; rp.pokaz(); //okrag::pokaz rp.przesun(); //punkt::przesun wywoła //okrag::pokaz i okrag::ukyj !!!

11 Klasa polimorficzna klasa polimorficzna to taka w której występuje przynajmniej jedna metoda wirtualna klasa polimorficzna to taka w której występuje przynajmniej jedna metoda wirtualna Przykład korzyści z polimorfizmu: Przykład korzyści z polimorfizmu: deklarujemy listę przechowującą wskaźniki do punktów (klasa polimorficzna) - na liście umieszczać punkty okręgi i inne figury. deklarujemy listę przechowującą wskaźniki do punktów (klasa polimorficzna) - na liście umieszczać punkty okręgi i inne figury. przez wskaźniki możemy pokazać wszystkie figury (wywołując metodę wirtualną pokaz() – pośrednio), możemy też przesuwać figury – wyw. się niewirtualna metoda przesuń, ona wywoła właściwe, bo wirtualne pokaz i ukryj. przez wskaźniki możemy pokazać wszystkie figury (wywołując metodę wirtualną pokaz() – pośrednio), możemy też przesuwać figury – wyw. się niewirtualna metoda przesuń, ona wywoła właściwe, bo wirtualne pokaz i ukryj.

12 Wczesne i późne wiązanie Wczesne wiązanie: Wczesne wiązanie: gdy metoda nie jest wirtualna lub jest wirtualna ale można określić z której klasy ma pochodzić to nazwa metody jest kojarzona z jej kodem (wywołanie metody albo nawet rozwinięcie inline) już na etapie kompilacji/linkowania. gdy metoda nie jest wirtualna lub jest wirtualna ale można określić z której klasy ma pochodzić to nazwa metody jest kojarzona z jej kodem (wywołanie metody albo nawet rozwinięcie inline) już na etapie kompilacji/linkowania. Późne wiązanie Późne wiązanie decyzja co do wyboru klasy z zakresu której metodę wykonać, zostaje podjęta podczas biegu programu. decyzja co do wyboru klasy z zakresu której metodę wykonać, zostaje podjęta podczas biegu programu.

13 Wczesne i późne wiązanie Metody nie-wirtualne – zawsze wczesne wiązanie. Metody nie-wirtualne – zawsze wczesne wiązanie. Zadeklarowanie metody jako wirtualnej nie wyklucza jej wczesnego wiązania, nastąpi ono, gdy: Zadeklarowanie metody jako wirtualnej nie wyklucza jej wczesnego wiązania, nastąpi ono, gdy: jawnie (operatorem zakresu) podamy o którą klasę nam chodzi, jawnie (operatorem zakresu) podamy o którą klasę nam chodzi, wywołamy metodę bezpośrednio na rzecz obiektu (nie przez wskaźnik lub referencję). wywołamy metodę bezpośrednio na rzecz obiektu (nie przez wskaźnik lub referencję).

14 Metody wirtualne zaleta: ogromna łatwość rozbudowy programu, zaleta: ogromna łatwość rozbudowy programu, Pisząc kod możemy wykorzystywać metody których jeszcze nie napisano ! Pisząc kod możemy wykorzystywać metody których jeszcze nie napisano ! Nie musimy powielać takiego samego kodu w metodach różnych klas (vide przesun()). Nie musimy powielać takiego samego kodu w metodach różnych klas (vide przesun()). Nie grozi nam uzupełnienie już gotowego kodu o nowe błędy. Nie grozi nam uzupełnienie już gotowego kodu o nowe błędy.

15 Konstruktory i destruktory Konstruktor nie może być wirtualny (dlaczego?) Konstruktor nie może być wirtualny (dlaczego?) Destruktor może i czasami powinien być virtual (dlaczego?) Destruktor może i czasami powinien być virtual (dlaczego?) Uwaga: Gdy w jakiejś klasie zadeklarujemy destruktor jako wirtualny, to w klasach pochodnych destruktory też będą wirtualne (mimo że ich nazwy w klasach pochodnych będą inne). Uwaga: Gdy w jakiejś klasie zadeklarujemy destruktor jako wirtualny, to w klasach pochodnych destruktory też będą wirtualne (mimo że ich nazwy w klasach pochodnych będą inne).

16 Static i virtual Metoda statyczna nie może być wirtualna (dlaczego?). Metoda statyczna nie może być wirtualna (dlaczego?).

17 Dziedziczenie metod wirtualnych meoda wirtualna może zostać odziedziczona przez klasę pochodną meoda wirtualna może zostać odziedziczona przez klasę pochodną może zostać przedefiniowana, w jej ciele możemy wywołać metodę wirtualną klasy bazowej. może zostać przedefiniowana, w jej ciele możemy wywołać metodę wirtualną klasy bazowej. np.: np.: void okrag::pokaz() { punkt::pokaz(); // narysuj środek okręgu punkt::pokaz(); // narysuj środek okręgu //narysuj okrąg //narysuj okrąg}

18 Przeciążanie a wirtualność wirtualność dotyczy tylko tej metody/operatora która została w danej klasie lub przodku zadeklarowana jako virtual, wirtualność dotyczy tylko tej metody/operatora która została w danej klasie lub przodku zadeklarowana jako virtual, inne metody (o innych parametrach) są zwykłymi metodami/operatorami. inne metody (o innych parametrach) są zwykłymi metodami/operatorami. np. metoda nie wirtualna: np. metoda nie wirtualna: void punkt::pokaz(char * opis){...};

19 Zaprzyjaźnianie a wirtualność Wirtualność jest niezależna od zaprzyjaźniania. Wirtualność jest niezależna od zaprzyjaźniania. Zaprzyjaźnianie nie jest przechodnie, Zaprzyjaźnianie nie jest przechodnie, Zaprzyjaźnianie dotyczy tylko tej metody (klasa::metoda) która została zaprzyjaźniona, Zaprzyjaźnianie dotyczy tylko tej metody (klasa::metoda) która została zaprzyjaźniona, Metoda przedefiniowana w klasie pochodnej, wirtualna czy nie, nie będzie automatycznie zaprzyjaźniona. Metoda przedefiniowana w klasie pochodnej, wirtualna czy nie, nie będzie automatycznie zaprzyjaźniona.

20 Widoczność metod wirtualnych Widoczność jest rozstrzygana na etapie kompilacji Widoczność jest rozstrzygana na etapie kompilacji zatem decyduje typ wskaźnika/referencji. zatem decyduje typ wskaźnika/referencji. np., przyjmijmy, że wszystkie składowe klasy okrąg sa prywatne: np., przyjmijmy, że wszystkie składowe klasy okrąg sa prywatne: okrag o; punkt &rp=o; rp.pokaz(); // OK. – dlaczego?

21 Klasa abstrakcyjna Klasa abstrakcyjna, to klasa, która nie zawiera żadnych obiektów. Klasa abstrakcyjna, to klasa, która nie zawiera żadnych obiektów. Klasa abstrakcyjna służy do definiowania interfejsu/cech wspólnych rodziny innych klas (jej potomków) Klasa abstrakcyjna służy do definiowania interfejsu/cech wspólnych rodziny innych klas (jej potomków) np. klasa abstrakcyjna figura liczba np. klasa abstrakcyjna figura liczba Dziedziczenie klas abstrakcyjnych Dziedziczenie klas abstrakcyjnych

22 Metoda czysto wirtualna W C++ klasa abstrakcyjna to taka, która zawiera przynajmniej jedną metodę czysto wirtualną – tj. taką która jest wirtualna, i nie ma zdefiniowanego ciała. W C++ klasa abstrakcyjna to taka, która zawiera przynajmniej jedną metodę czysto wirtualną – tj. taką która jest wirtualna, i nie ma zdefiniowanego ciała. void virtual figura::rysuj()=0;

23 RTTI Ponieważ wskaźnikowi na przodka można przypisać adres potomka to typ wskaźnika nie determinuje klasy do której należy obiekt wskazywany. Ponieważ wskaźnikowi na przodka można przypisać adres potomka to typ wskaźnika nie determinuje klasy do której należy obiekt wskazywany. Czasami w trakcie działania programu pojawia się potrzeba sprawdzenia do jakiej klasy dany obiekt należy. Jeżeli wskaźnik bądź referencja dotyczy klasy polimorficznej to obiekt zawiera pole umożliwiające identyfikację klasy (wskaźnik do tablicy metod wirtualnych), na jego podstawie mechanizm RTTI pozwala sprawdzać typy w trakcie biegu programu. Czasami w trakcie działania programu pojawia się potrzeba sprawdzenia do jakiej klasy dany obiekt należy. Jeżeli wskaźnik bądź referencja dotyczy klasy polimorficznej to obiekt zawiera pole umożliwiające identyfikację klasy (wskaźnik do tablicy metod wirtualnych), na jego podstawie mechanizm RTTI pozwala sprawdzać typy w trakcie biegu programu. Jeżeli wskaźnik, bądź referencja dotyczy typu podstawowego bądź klasy nie-polimorficznej to typ określany jest na podstawie typu wskaźnika/referencji a nie rzeczywistego obiektu/zmiennej bo nie ma danych by to zrobić inaczej. Jeżeli wskaźnik, bądź referencja dotyczy typu podstawowego bądź klasy nie-polimorficznej to typ określany jest na podstawie typu wskaźnika/referencji a nie rzeczywistego obiektu/zmiennej bo nie ma danych by to zrobić inaczej.

24 RTTI RTTI RunTime Type Information RTTI RunTime Type Information operator typeid operator typeid type_info typeid(arg) arg: typ, klasa, zmienna bądź obiekt arg: typ, klasa, zmienna bądź obiekt

25 RTTI class type_info { // tu prywatny konstruktor kopiujący i operator przypisania // tu prywatny konstruktor kopiujący i operator przypisania // nie można z zewnątrz tworzyć kopii obiektów tej klasy // nie można z zewnątrz tworzyć kopii obiektów tej klasypublic: virtual ~type_info(); virtual ~type_info(); bool operator==(const type_info& rhs) const; bool operator==(const type_info& rhs) const; bool operator!=(const type_info& rhs) const; bool operator!=(const type_info& rhs) const; bool before(const type_info& rhs) const; bool before(const type_info& rhs) const; const char* name() const; const char* name() const;};

26 RTTI class A { virtual void a(){}; virtual void a(){};}; class B:public A {}; class C:public B {}; A a, *pa; C c; pa=&c; typeid(C)==typeid(pa)// 0 typeid(C)==typeid(pa)// 0 typeid(a)==typeid(*pa)// 0 typeid(a)==typeid(*pa)// 0 typeid(c)==typeid(*pa) // 1 typeid(c)==typeid(*pa) // 1 typeid(B).name() // B typeid(B).name() // B typeid(pa).name() // A * typeid(pa).name() // A * typeid(*pa).name() // C typeid(*pa).name() // C typeid(A).before(typeid(*pa)) // 1 typeid(A).before(typeid(*pa)) // 1 typeid(B).before(typeid(*pa)) // 1 typeid(B).before(typeid(*pa)) // 1 typeid(C).before(typeid(*pa)) // 0 typeid(C).before(typeid(*pa)) // 0 typeid(C).before(typeid(a)) // 0 typeid(C).before(typeid(a)) // 0

27 *_cast operatory rzutowania dla zastąpienia rzutowania w stylu języka C static_cast ( wyrażenie ) dynamic_cast ( wyrażenie ) reinterpret_cast ( wyrażenie ) const_cast ( wyrażenie ) to 4 operatory i 4 słowa kluczowe C++ to 4 operatory i 4 słowa kluczowe C++ zaprojektowane by umożliwić większą kontrolę na poziomie kompilacji i wykonania kodu nad (podatnymi na błędy) rzutowaniami zaprojektowane by umożliwić większą kontrolę na poziomie kompilacji i wykonania kodu nad (podatnymi na błędy) rzutowaniami pozwalają wyrazić intencję programisty pozwalają wyrazić intencję programisty

28 static_cast static_cast ( wyrażenie ) rzutownie oparte na typach znanych podczas kompilacji rzutownie oparte na typach znanych podczas kompilacji podobnie niebezpiczne jak rzutowanie C: (type-id ) wyrażenie podobnie niebezpiczne jak rzutowanie C: (type-id ) wyrażenie używać ostożnie używać ostożnie działa szybko działa szybko nie dopuszcza się modyfikacji kwalifikacji const i volatile wyrażenia nie dopuszcza się modyfikacji kwalifikacji const i volatile wyrażenia

29 static_cast static_cast ( wyrażenie ) class A {}; class B:public A { int method(); }; int fun(A * pa) int fun(A * pa){ static_cast (pa) -> method() // miejmy nadzieję, że pa wskazuje na B }

30 dynamic_cast dynamic_cast ( wyrażenie ) rzutowanie oparte o RTTI, używane ze wskaźnikami i referencjami rzutowanie oparte o RTTI, używane ze wskaźnikami i referencjami dla wskaźników zwraca NULL, jeśli type-id nie jest ani typu wyrażenia ani jego rodzica dla wskaźników zwraca NULL, jeśli type-id nie jest ani typu wyrażenia ani jego rodzica dla referencji w ww. przypadku zgłaszany jest wyjątek dla referencji w ww. przypadku zgłaszany jest wyjątek bezpieczne, mniej szybkie bezpieczne, mniej szybkie nie dopuszcza się modyfikacji kwalifikacji const i volatile wyrażenia nie dopuszcza się modyfikacji kwalifikacji const i volatile wyrażenia

31 dynamic_cast dynamic_cast ( wyrażenie ) class A {}; class B:public A {}; int fun() int fun(){ A *pa, *aptr=new A; B *pb, *bptr=new B;... pa=dynamic_cast (bptr); // OK pb=dynamic_cast (aptr); // NULL !!! pb=dynamic_cast (pa); // OK }

32 reinterpret_cast reinterpret_cast ( wyrażenie ) rzutownie oparte na typach znanych podczas kompilacji rzutownie oparte na typach znanych podczas kompilacji używane dla arytmetyki adresów (wskaźników), do konwersji typu w górę używane dla arytmetyki adresów (wskaźników), do konwersji typu w górę używaj dla wskaźników i liczb całkowitych (wyraź swoją intencję) używaj dla wskaźników i liczb całkowitych (wyraź swoją intencję) niech Twój szef myśli że jesteś guru programowania w C++ ;) niech Twój szef myśli że jesteś guru programowania w C++ ;) CLASS *ptr; CLASS *ptr;... unsigned int adress=reinterpret_cast (ptr); unsigned int adress=reinterpret_cast (ptr);

33 const_cast const_cast ( wyrażenie ) rzutownie oparte na typach znanych podczas kompilacji rzutownie oparte na typach znanych podczas kompilacji używaj dla wskaźników i referencji używaj dla wskaźników i referencji używaj dla usunięcia kwalifikacji cost i/lub volatile wyrażenia używaj dla usunięcia kwalifikacji cost i/lub volatile wyrażenia class CLASS { int member; int CLASS::constmethod() const { const_cast (this) -> member++; // const_cast (member)++; ERROR }}

34 mutable słowo kluczowe mutable sprawia, że nie-stała i nie-statyczna zmienna klasowa pozostanie nie- stała w stałych metodach klasy słowo kluczowe mutable sprawia, że nie-stała i nie-statyczna zmienna klasowa pozostanie nie- stała w stałych metodach klasy class CLASS { mutable int member; int CLASS::constmethod() const { member++; // bo member jest mutable }}


Pobierz ppt "Metody wirtualne. Dlaczego metody wirtualne? W C++ dla wskaźników i referencji dozwolona jest konwersja potomna bazowa. W C++ dla wskaźników i referencji."

Podobne prezentacje


Reklamy Google