PDF

Transkrypt

PDF
Programowanie obiektowe
Wykład 7
Dariusz Wardowski
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
1/20
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.
class Wielokat
{
private:
int iloscWierzcholkow;
TabPunktow wierzcholki;
public:
…
};
class TabPunktow
{
private:
int rozmiar;
Punkt* tabP;
public:
…
};
class Punkt
{
private:
double x;
double y;
public:
double getX();
double getY();
};
W przypadku techniki obudowywania (kompozycji), klasa Wielokat dziedziczy jedynie dostęp
do implementacji, a nie do interfejsu obiektu klasy TabPunktow.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
2/20
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żywad 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;
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
class TabPunktow
{
private:
int rozmiar;
Punkt* tabP;
public:
TabPunktow(int r)
{
rozmiar = r;
tabP = new Punkt[r];
}
};
3/20
Programowanie obiektowe
Wykład 7
Metody publiczne obiektu obudowanego
Klasa obudowująca dany obiekt nie dziedziczy interfejsu publicznego, ale może z niego korzystad
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();
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
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()<<”)”;
}
}
4/20
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ą byd 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.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
5/20
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).
class Wielokat : private TabPunktow
{
…
public:
Wielokat(int iw);
};
Zauważmy, że na liście inicjatorów
konstruktora znajduje się nazwa
klasy.
Wielokat::Wielokat(int iw):TabPunktow(iw)
{
…
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
6/20
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 wykorzystad więcej niż jeden
podobiekt tej samej klasy, podczas gdy
dziedziczenie ogranicza nas do jednego.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
Zalety dziedziczenia prywatnego
• Dostęp do pól chronionych klasy
macierzystej
• Możliwośd redefinicji funkcji wirtualnych.
• Przy kompozycji można wykorzystad więcej
niż jeden podobiekt tej samej klasy,
podczas gdy dziedziczenie ogranicza nas do
jednego.
7/20
Programowanie obiektowe
Wykład 7
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śd 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
{
…
};
class C: private A
{
…
};
Zmienne y i z stają się chronionymi składowymi
klasy A.
Zmienne y i z stają się prywatnymi składowymi
klasy C.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
8/20
Programowanie obiektowe
Wykład 7
public potected private
składowe klasy
macierzystej
public
protected
private
składowe publiczne są
składowymi
publicznymi klasy
potomnej
składowymi
chronionymi
klasy potomnej
składowymi
prywatnymi
klasy potomnej
składowe chronione są
składowymi
chronionymi
klasy potomnej
składowymi
chronionymi
klasy potomnej
składowymi
prywatnymi
klasy potomnej
składowe prywatne są
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Ł
9/20
Programowanie obiektowe
Wykład 7
Dyrektywa using
class A
{
private:
int
int
public:
int
int
};
x;
y;
getX();
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 uzyskad 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Ł
10/20
Programowanie obiektowe
Wykład 7
Dziedziczenie wielokrotne
Dziedziczenie wielokrotne polega na dziedziczeniu z więcej niż jednej klasy macierzystej. Np.:
class
class
class
class
Dyrektor: public Pracownik, public Osoba {…};
Banan: public Owoc, public Produkt {…};
Student: public Osoba, private Wyniki {…};
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”.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
11/20
Programowanie obiektowe
Wykład 7
Problemy ze wspólnym potomkiem
Osoba
Chory
Pacjent
ChoryPacjent
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
12/20
Programowanie obiektowe
Wykład 7
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;
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
13/20
Programowanie obiektowe
Wykład 7
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;
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
14/20
Programowanie obiektowe
Wykład 7
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;
}
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
15/20
Programowanie obiektowe
Wykład 7
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 wskazad konkretny obiekt posługując się jawnym rzutowaniem:
Osoba* o1 = (Chory *) &cp;
Osoba* o2 = (Pacjent *) &cp;
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
16/20
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 {…};
class ChoryPacjent: public Chory, public Pacjent {…};
W ten sposób obiekt ChoryPacjent zawiera tylko jedną kopię podobiektu Osoba.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
17/20
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 zainicjalizowad pola obiektu klasy macierzystej przed utworzeniem obiektów klas
potomnych, należy wywoład 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ływad konstruktor tej drugiej klasy. (Uwaga
dotyczy wywołania innego niż domyślnego konstruktora macierzystej klasy wirtualnej).
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
18/20
Programowanie obiektowe
Wykład 7
Niejednoznacznośd metod
Dziedziczenie wielokrotne zazwyczaj prowadzi do niejednoznaczności wywołao 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ład. Patrz kod.
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
19/20
Programowanie obiektowe
Wykład 7

Dziękuję za uwagę
dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ
20/20

Podobne dokumenty