200 likes | 437 Views
Programowanie obiektowe. Wykład 7. Programowanie obiektowe Wykład 7 Dziedziczenie prywatne i chronione, dziedzicznie wielokrotne (C++). Dariusz Wardowski. d r Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ. Programowanie obiektowe. Wykład 7. Relacja „ma”.
E N D
Programowanie obiektowe Wykład 7 Programowanie obiektowe Wykład 7 Dziedziczenie prywatne i chronione, dziedzicznie wielokrotne (C++) Dariusz Wardowski dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Relacja „ma” Przykłady relacji typu „ma”: Student ma nazwisko Wielokąt ma wierzchołki (punkty) Stos ma elementy Dla obiektów między którymi zachodzi relacja typu „ma” stosujemy technikę obudowywania, czyli tworzymy klasę złożoną z obiektów innych klas. classTabPunktow { private: int rozmiar; Punkt* tabP; public: … }; class Punkt { private: double x; double y; public: double getX(); double getY(); }; classWielokat { private: intiloscWierzcholkow; TabPunktowwierzcholki; public: … }; W przypadku techniki obudowywania (kompozycji), klasa Wielokatdziedziczy jedynie dostęp do implementacji, a nie do interfejsu obiektu klasy TabPunktow. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Inicjalizacja obiektów wewnętrznych (obudowanych) Z uwagi na to, że klasy obudowujące obiekty nie dziedziczą interfejsów publicznych tych obiektów, nie można używać konstruktorów klasa tychże obiektów w sposób jawny, tylko przy pomocy listy inicjatorów. Przy czym na liście inicjatorów umieszczamy nazwy obiektów, a nie klas. classWielokat { private: intiloscWierzcholkow; TabPunktowwierzcholki; public: Wielokat(int iw, TabPunktowtp); }; Wielokat::Wielokat(int iw):wierzcholki(iw) { iloscWierzcholkow = iw; } classTabPunktow { private: int rozmiar; Punkt* tabP; public: TabPunktow(intr) { rozmiar = r; tabP = new Punkt[r]; } }; dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Metody publiczne obiektu obudowanego Klasa obudowująca dany obiekt nie dziedziczy interfejsu publicznego, ale może z niego korzystać w metodach swojej klasy. classTabPunktow { private: int rozmiar; Punkt* tabP; public: TabPunktow(intr) { rozmiar = r; tabP = new Punkt[r]; } void wypisz(); }; voidTabPunktow::wypisz() { for (int i=0; i<rozmiar; i++) { cout<<”(”<<tabP[i].getX() << ”,”; cout << tabP[i].getY()<<”)”; } } classWielokat { private: intiloscWierzcholkow; TabPunktowwierzcholki; public: Wielokat(int iw, TabPunktowtp); voidwypiszWierzcholki(); }; Wielokat::Wielokat(int iw):wierzcholki(iw) { iloscWierzcholkow = iw; } voidWielokat::wypiszWierzcholki() { wierzcholki.wypisz(); } dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Dziedziczenie prywatne Dziedziczenie prywatne to inny sposób zaimplementowania relacji typu ma między dwiema klasami. Podczas dziedziczenia prywatnego zarówno publiczne jak i chronione składowe klasy macierzystej stają się prywatnymi składowymi klasy potomnej. W konsekwencji publiczne metody klasy macierzystej nie wchodzą w skład publicznego interfejsu obiektu, który dziedziczy. Metody te jednak mogą być używane wewnątrz metod klasy potomnej. Zatem w przypadku dziedziczenia prywatnego klasa potomna nie dziedziczy interfejsu publicznego klasy macierzystej, ale dziedziczy jej implementację. classWielokat : privateTabPunktow { … }; Dziedziczenie prywatne jest domyślnym kwalifikatorem dostępu. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Kompozycja klas a dziedziczenie prywatne Obudowanie (kompozycja) polega na dodaniu nazwanego obiektu do klasy, jako kolejnego pola składowego. Dziedziczenie prywatne natomiast dodaje obiekt do klasy, ale jako nienazwany obiekt dziedziczony (podobiekt). classWielokat : privateTabPunktow { … public: Wielokat(intiw); }; Wielokat::Wielokat(intiw):TabPunktow(iw) { … } Zauważmy, że na liście inicjatorów konstruktora znajduje się nazwaklasy. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Kompozycja vs. dziedziczenie prywatne Zarówno technika obudowywania obiektu jak i technika dziedziczenia realizuje relację typu „ma”. Zatem który sposób jest lepszy? • Zalety kompozycji • Prostsza metoda. • Jawnie nazwane obiekty obudowane. • Można wykorzystać więcej niż jeden podobiekt tej samej klasy, podczas gdy dziedziczenie ogranicza nas do jednego. • Zalety dziedziczenia prywatnego • Dostęp do pól chronionych klasy macierzystej • Możliwość redefinicji funkcji wirtualnych. • Przy kompozycji można wykorzystać więcej niż jeden podobiekt tej samej klasy, podczas gdy dziedziczenie ogranicza nas do jednego. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Dziedziczenie chronione (protected) Podczas dziedziczeniachronionego, składowe publiczne i chronione klasy macierzystej stają się składowymi chronionymi klasy potomnej. W ten sposób dziedziczenie z klasy potomnej daje możliwość klasie trzeciej generacji (tj. dziedziczącej po potomnej) dostęp do interfejsu publicznego klasy macierzystej. class A { private: int x; protected: int y; public: int z; }; class B: protected A { … }; Zmienne y i z stają się chronionymi składowymi klasy A. class C: private A { … }; Zmienne y i z stają się prywatnymi składowymi klasy C. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 public potectedprivate składowymi publicznymi klasy potomnej składowymi chronionymi klasy potomnej składowymi prywatnymi klasy potomnej składowymi chronionymi klasy potomnej składowymi chronionymi klasy potomnej składowymi prywatnymi klasy potomnej Dostępne poprzez interfejs publiczny klasy macierzystej Dostępne poprzez interfejs publiczny klasy macierzystej Dostępne poprzez interfejs publiczny klasy macierzystej dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Dyrektywausing class A { private: int x; int y; public: intgetX(); intgetY(); }; class B: private A { public: intgetX() { return A::getX(); } }; Klasa B udostępnia składową prywatna x klasy A poprzez opakowanie jednej funkcji drugą. Analogiczny efekt można uzyskać stosując dyrektywę using. class B: private A { public: using A::getX; //bez nawiasów! }; Dyrektywa using udostępnia metodę getX() klasy A tak jakby była metodą publiczną klasy B pomimo dziedziczenia prywatnego. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Dziedziczenie wielokrotne Dziedziczenie wielokrotne polega na dziedziczeniu z więcej niż jednej klasy macierzystej. Np.: class Dyrektor: public Pracownik, public Osoba {…}; class Banan: public Owoc, public Produkt {…}; class Student: public Osoba, private Wyniki {…}; classChoryPacjent: public Chory , public Pacjent {…}; Analogicznie jak przy dziedziczeniu z jednej klasy macierzystej, wielokrotne dziedziczenie publiczne wyrazi relację typu „jest”, natomiast wielokrotne dziedziczenie prywatne wyrazi relację typu „ma”. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Problemy ze wspólnym potomkiem Osoba Chory Pacjent ChoryPacjent dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Klasa Osoba Osoba::Osoba() { strcpy(imie,"brak"); strcpy(nazwisko,"brak"); } Osoba::Osoba(const char* i, const char* n) { strncpy(imie,i,20); strncpy(nazwisko,n,20); } Osoba::Osoba(const Osoba & o) { strcpy(imie,o.imie); strcpy(nazwisko,o.nazwisko); } Osoba::~Osoba() {} voidOsoba::wprowadzDane() { cout << "Podaj imie: "; cin >> imie; cout << "Podaj nazwisko: "; cin >> nazwisko; } voidOsoba::pokaz() const { cout << "Imie i nazwisko: " << imie << " " << nazwisko << endl; } class Osoba { private: char imie[20]; char nazwisko[20]; public: Osoba(); Osoba(const char* i, const char* n); Osoba(const Osoba & o); virtual ~Osoba() = 0; virtualvoidwprowadzDane(); virtualvoid pokaz() const; }; dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Klasa Chory Chory::Chory() : Osoba() { strcpy(choroba,"nierozpoznana"); } Chory::Chory(const char* i, const char* n, const char* c) : Osoba(i,n) { strncpy(choroba,c,30); } Chory::Chory(const Osoba & o, const char* c) : Osoba(o) { strncpy(choroba,c,30); } voidChory::wprowadzDane() { Osoba::wprowadzDane(); cout << "Rozpoznanie choroby: "; cin >> choroba; } voidChory::pokaz() const { Osoba::pokaz(); cout << "Rozpoznanie choroby: " << choroba << endl; } class Chory: public Osoba { private: char choroba[30]; public: Chory(); Chory(const char* i, const char* n, const char* c); Chory(const Osoba & o, const char* c); virtual void wprowadzDane(); virtual void pokaz() const; }; dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Klasa Pacjent class Pacjent: public Osoba { protected: intnrPrzychodni; public: Pacjent() : Osoba(),nrPrzychodni(0) {} Pacjent(const char* i, const char* n, int nr): Osoba(i,n), nrPrzychodni(nr) {} Pacjent(const Osoba & o, int nr) : Osoba(o), nrPrzychodni(nr) {} virtual void wprowadzDane(); virtual void pokaz() const; }; voidPacjent::wprowadzDane() { Osoba::wprowadzDane(); cout << "Nr przychodni: "; cin >> nrPrzychodni; } voidPacjent::pokaz() const { Osoba::pokaz(); cout << "Nr przychodni: " << nrPrzychodni << endl; } dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Klasa ChoryPacjent classChoryPacjent: public Chory, public Pacjent { }; Klas ChoryPacjent dwukrotnie dziedziczy po klasie Osoba, w konsekwencji dziedziczy dwa podobiektyOsoba, co jest powodem następującej niejednoznaczności: ChoryPacjentcp; Osoba* o = &cp; //błąd! Niejednoznaczne odwołanie do adresu Osoba Należy wskazać konkretny obiekt posługując się jawnym rzutowaniem: Osoba* o1 = (Chory *) &cp; Osoba* o2 = (Pacjent *) &cp; dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Wirtualna klasa macierzysta W celu rozwiązania problemu wielości kopii tych samych obiektów wewnątrz jednego (chory pacjent ma dwa imiona i dwa nazwiska), stosuje się tzw. wirtualne klasy macierzyste. Mechanizm wirtualnych klas macierzystych powoduje, że obiekty dziedziczące po wielu klasach współdzielących tę samą klasę macierzystą, dziedziczą tylko jeden obiekt tej wspólnej klasy. class Chory: virtual public Osoba {…}; class Pacjent: virtual public Osoba{…}; classChoryPacjent: public Chory, public Pacjent {…}; W ten sposób obiekt ChoryPacjent zawiera tylko jedną kopię podobiektuOsoba. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Problem z konstruktorem W dziedziczeniu wielokrotnym mechanizm polegający na wywoływaniu konstruktorów bezpośrednich klas macierzystych nie działa w przypadku macierzystych klas wirtualnych. //źle ChoryPacjent(const char* i, const char* n, char* c, int nr) : Chory(i,n,c), Pacjent(i, n, nr) {} Gdyby powyższy konstruktor był prawidłowy, informacje o imieniu i nazwisku do obiektu Osoba trafiłyby dwiema ścieżkami. Działanie takie jest blokowane, jeżeli klasa macierzysta jest wirtualną. Aby zainicjalizować pola obiektu klasy macierzystej przed utworzeniem obiektów klas potomnych, należy wywołać odpowiedni konstruktor klasy macierzystej w sposób jawny. ChoryPacjent(const char* i, const char* n, char* c, int nr) : Osoba(i,n), Chory(i,n,c), Pacjent(i, n, nr) {} Jeżeli klasa dziedziczy z klas, które dziedziczą po wirtualnej klasie macierzystej, wówczas konstruktor tej pierwszej klasy musi jawnie wywoływać konstruktor tej drugiej klasy. (Uwaga dotyczy wywołania innego niż domyślnego konstruktora macierzystej klasy wirtualnej). dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Niejednoznaczność metod Dziedziczenie wielokrotne zazwyczaj prowadzi do niejednoznaczności wywołań funkcji. Najlepszym rozwiązaniem jest przedefiniowanie wszystkich metod w klasie, która dziedziczy z wielu klas macierzystych. W przedefiniowanych metodach przeważnie wskazujemy w sposób jawny, które wersje metod chcemy wywołać. Patrz kod. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Programowanie obiektowe Wykład 7 Dziękuję za uwagę dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ