20
Programowanie obiektowe Wykład 7 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/20 Programowanie obiektowe Wykład 7 Dziedziczenie prywatne i chronione, dziedzicznie wielokrotne (C++) Dariusz Wardowski

Programowanie obiektowe

  • Upload
    gautam

  • View
    59

  • Download
    0

Embed Size (px)

DESCRIPTION

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”. - PowerPoint PPT Presentation

Citation preview

Page 1: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/20

Programowanie obiektoweWykład 7

Dziedziczenie prywatne i chronione, dziedzicznie wielokrotne (C++)

Dariusz Wardowski

Page 2: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 2/20

Relacja „ma”

Przykłady relacji typu „ma”:Student ma nazwiskoWieloką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.

class Wielokat{ private: int iloscWierzcholkow; TabPunktow wierzcholki; public:

…};

class TabPunktow{ private: int rozmiar; Punkt* tabP; public:

…};

W przypadku techniki obudowywania (kompozycji), klasa Wielokat dziedziczy jedynie dostęp do implementacji, a nie do interfejsu obiektu klasy TabPunktow.

class Punkt{ private: double x; double y; public: double getX(); double getY();};

Page 3: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 3/20

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. class Wielokat{ private: int iloscWierzcholkow; TabPunktow wierzcholki; public:

Wielokat(int iw, TabPunktow tp);};

Wielokat::Wielokat(int iw):wierzcholki(iw){ iloscWierzcholkow = iw;}

class TabPunktow{ private: int rozmiar; Punkt* tabP; public:

TabPunktow(int r){ rozmiar = r; tabP = new Punkt[r];}

};

Page 4: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 4/20

Metody publiczne obiektu obudowanego

Klasa obudowująca dany obiekt nie dziedziczy interfejsu publicznego, ale może z niego korzystać w metodach swojej klasy.

class Wielokat{ private: int iloscWierzcholkow; TabPunktow wierzcholki; public:

Wielokat(int iw, TabPunktow tp);void wypiszWierzcholki();

};

Wielokat::Wielokat(int iw):wierzcholki(iw){ iloscWierzcholkow = iw;}

void Wielokat::wypiszWierzcholki(){ wierzcholki.wypisz();}

class TabPunktow{ private: int rozmiar; Punkt* tabP; public:

TabPunktow(int r){ rozmiar = r; tabP = new Punkt[r];}void wypisz();

};

void TabPunktow::wypisz(){ for (int i=0; i<rozmiar; i++) { cout <<”(”<<tabP[i].getX() << ”,”; cout << tabP[i].getY()<<”)”; }}

Page 5: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 5/20

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ę.

class Wielokat : private TabPunktow { …};

Dziedziczenie prywatne jest domyślnym kwalifikatorem dostępu.

Page 6: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 6/20

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).

class Wielokat : private TabPunktow { … public:

Wielokat(int iw);};

Wielokat::Wielokat(int iw):TabPunktow(iw){ …}

Zauważmy, że na liście inicjatorów konstruktora znajduje się nazwa klasy.

Page 7: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 7/20

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.

Page 8: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 8/20

Dziedziczenie chronione (protected)

Podczas dziedziczenia chronionego, 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.

Page 9: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 9/20

public potected private

składowe klasy macierzystej public protected private

składowe publiczne są

składowe chronione są

składowe prywatne są

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 prywatnymiklasy potomnej

Dostępne poprzez interfejs publiczny klasy macierzystej

Dostępne poprzez interfejs publiczny klasy macierzystej

Dostępne poprzez interfejs publiczny klasy macierzystej

Page 10: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 10/20

Dyrektywa usingclass A{ private:

int x; int y; public:

int getX();

int getY();};

class B: private A{ public:

int getX(){ 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.

Page 11: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 11/20

Dziedziczenie wielokrotneDziedziczenie 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 {…};class ChoryPacjent: 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”.

Page 12: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 12/20

Problemy ze wspólnym potomkiem

ChoryPacjent

PacjentChory

Osoba

Page 13: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 13/20

Klasa Osoba

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; virtual void wprowadzDane(); virtual void pokaz() const;};

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() {}

void Osoba::wprowadzDane(){ cout << "Podaj imie: "; cin >> imie; cout << "Podaj nazwisko: "; cin >> nazwisko; }

void Osoba::pokaz() const{ cout << "Imie i nazwisko: " << imie << " " << nazwisko << endl; }

Page 14: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 14/20

Klasa Chory

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; };

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); }

void Chory::wprowadzDane(){ Osoba::wprowadzDane(); cout << "Rozpoznanie choroby: "; cin >> choroba;}

void Chory::pokaz() const{ Osoba::pokaz(); cout << "Rozpoznanie choroby: " << choroba << endl; }

Page 15: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 15/20

Klasa Pacjent

class Pacjent: public Osoba{ protected: int nrPrzychodni; 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; };

void Pacjent::wprowadzDane(){ Osoba::wprowadzDane(); cout << "Nr przychodni: "; cin >> nrPrzychodni;}

void Pacjent::pokaz() const{ Osoba::pokaz(); cout << "Nr przychodni: " << nrPrzychodni << endl; }

Page 16: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 16/20

Klasa ChoryPacjent

class ChoryPacjent: public Chory, public Pacjent{ };

Klas ChoryPacjent dwukrotnie dziedziczy po klasie Osoba, w konsekwencji dziedziczy dwa podobiekty Osoba, co jest powodem następującej niejednoznaczności:

ChoryPacjent cp;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;

Page 17: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 17/20

Wirtualna klasa macierzystaW 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 {…};class ChoryPacjent: public Chory, public Pacjent {…};

W ten sposób obiekt ChoryPacjent zawiera tylko jedną kopię podobiektu Osoba.

Page 18: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 18/20

Problem z konstruktoremW dziedziczeniu wielokrotnym mechanizm polegający na wywoływaniu konstruktorów bezpośrednich klas macierzystych nie działa w przypadku macierzystych klas wirtualnych.

//źleChoryPacjent(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).

Page 19: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 19/20

Niejednoznaczność metodDziedziczenie 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.

Page 20: Programowanie obiektowe

Programowanie obiektowe Wykład 7

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 20/20

Dziękuję za uwagę