510 likes | 637 Views
Dziedziczenie. class osoba { char * imie, * nazwisko; public: void wypisz_imie(); void wypisz_nazwisko(); void wypisz_wszystko(); }. class dorosly { char * imie, * nazwisko, * nr_dowodu; public: void wypisz_imie(); void wypisz_nazwisko();
E N D
class osoba { char * imie, * nazwisko; public: void wypisz_imie(); void wypisz_nazwisko(); void wypisz_wszystko(); } class dorosly { char * imie, * nazwisko, * nr_dowodu; public: void wypisz_imie(); void wypisz_nazwisko(); void wypisz_numer(); void wypisz_wszystko(); } Po co nam dziedziczenie?
class osoba { char * imie, * nazwisko; public: void wypisz_imie(); void wypisz_nazwisko(); void wypisz_wszystko(); } class dorosly: public osoba { char * nr_dowodu; public: void wypisz_numer(); void wypisz wszystko(); } Po co nam dziedziczenie?
Dziedziczenie • Wyraża związki hierarchiczne między klasami • Rozbudowywanie istniejących klas, bez ich modyfikowania lub rekompilacji • Wykorzystanie już gotowego kodu (definiujemy tylko to , co dodane lub zmienione)
Dziedziczenie • Klasa bazowa osoba • Klasa pochodna dorosly każdy dorosły jest osobą dorosły jest przypadkiem szczególnym osoby
Klasa pochodna • dorosły zawiera zmienne i metody: char * imie, * nazwisko — odziedziczone, niedostępne dla kl. dorosly char *nr_dowodu — prywatne dla dorosly wypisz_imie(), wypisz_nazwisko() — publiczne, odziedziczone z osoba osoba::wypisz_wszystko() — publiczne, odziedziczone i przysłonięte, dostępne po kwalifikacji op. zakresu
Klasa pochodna • zawiera wszystkie pola bazowej • dziedziczy wszystkie metody, może je przedefiniowywać • do dostępnych lecz przysłoniętych nazw z zakresu klasy bazowej odwołujemy się za pomocą operatora zakresu
Klasa pochodna osoba.o; dorosly s; o.wypisz_imie(); o.wypisz_nazwisko(); o.wypisz_wszystko(); s.wypisz_imie(); // z osoba s.wypisz_nazwisko(); // z osoba s.wypisz_numer(); // z dorosly s.wypisz_wszystko(); // z dorosly s.osoba::wypisz_wszystko(); // z osoba
Dostęp do pól kl. bazowej class pochodna : public bazowa; class pochodna : protected bazowa; class pochodna : private bazowa; class pochodna : bazowa; // : private
sekcja w kl. bazowej Dostęp do pól kl. bazowej
Dostęp do pól kl. bazowej class a { public: int f(int); }; class b:private a//można „osłabić” ograniczenie dostępu { public: a::f; // teraz a::f jest dziedziczone publicznie };
class baza { public: int ba_pub, i; protected: int ba_prot; private: int ba_priv; }; class prot: protected baza { public: int i; void metoda(); protected: int prot_prot; friend void friend_prot(); }; Dostęp do pól kl. bazowej
void prot::metoda() { int i; i=baza::i; i=i; i=prot::i; i=ba_pub; i=baza::ba_pub; i=prot::ba_pub; i=ba_prot; i=baza::ba_prot; i=prot::ba_prot; // i=ba_priv; // i=baza::ba_priv; // i=prot::ba_priv; i=prot_prot; i=prot::prot_prot; } Dostęp do pól kl. bazowej
void not_a_friend() { baza ba; prot pr; int i; i=ba.i; i=ba.ba_pub; // i=ba.ba_prot; // i=ba.ba_priv; // i=pr.baza::i; !!!!! i=pr.i; i=pr.prot::i; // i=pr.ba_pub; // i=pr.baza::ba_pub; // i=pr.prot::ba_pub; // i=pr.ba_prot; // i=pr.baza::ba_prot; // i=pr.prot::ba_prot; // i=pr.ba_priv; // i=pr.baza::ba_priv; // i=pr.prot::ba_priv; // i=pr.prot_prot; // i=pr.prot::prot_prot; } Dostęp do pól kl. bazowej
void friend_prot(void) { baza ba; prot pr; int i; i=ba.i; i=ba.ba_pub; // i=ba.ba_prot; // i=ba.ba_priv; i=pr.baza::i;// !!!!! i=pr.i; i=pr.prot::i; i=pr.ba_pub; i=pr.baza::ba_pub; i=pr.prot::ba_pub; i=pr.ba_prot; i=pr.baza::ba_prot; i=pr.prot::ba_prot; // i=pr.ba_priv; // i=pr.baza::ba_priv; // i=pr.prot::ba_priv; i=pr.prot_prot; i=pr.prot::prot_prot; } Dostęp do pól kl. bazowej
Konstruktor i destruktor dla klasy potomnej • Konstruktorów się nie dziedziczy, ale • Kolejność konstrukcji obiektu klasy: • wirtualne klasy bazowe • bezpośrednie klasy bazowe • obiektowe zmienne klasowe • dana klasa • Destruktory - odwrotnie
Konstruktor i destruktor dla klasy potomnej – lista inicjalizacyjna • Opisuje sposób inicjalizacji zmiennych klasowych zadeklarowanych w klasie(a nie odziedziczonych). • Opisuje sposób wywołania konstruktorów przodków wirtualnych oraz bezpośrednich.
class osoba { protected: char * imie, * nazwisko; char * aloc_string(const char * s); public: osoba(const char * im, const char * naz) :imie(aloc_string(im)), nazwisko(aloc_string(naz)) { } ~osoba() { delete [] imie; delete [] nazwisko; } void wypisz_imie(); void wypisz_nazwisko(); void wypisz_wszystko(); } Przykład
char * osoba::aloc_string(const char * s) { char * cp=new char[strlen(s) + 1]; strcpy(cp, s); return cp; } void osoba::wypisz_imie() { cout << imie << " "; } void osoba::wypisz_nazwisko() { cout << nazwisko << " "; } void osoba::wypisz_wszystko() { wypisz_imie(); wypisz_nazwisko(); } Przykład
class dorosly:public osoba { protected: char * nr_dowodu; public: dorosly(const char * imie, const char * nazwisko, const char * dowod) :osoba(imie, nazwisko), nr_dowodu(aloc_string(dowod)) { }; ~dorosly() { delete [] nr_dowodu; }; void wypisz_numer(); void wypisz_wszystko(); }; Przykład - konstruktor klasy pochodnej
Przykład void dorosly::wypisz_numer() { cout << "dowod: " << nr_dowodu << " "; } void dorosly::wypisz_wszystko() { osoba::wypisz_wszystko(); wypisz_numer(); }
Przykład – wiele kl. pochodnych osoba dorosly dziecko
class dziecko:public osoba { protected: osoba * mama, * tata; public: dziecko(const char * imie, const char * nazwisko, osoba * mama, osoba * tata) :osoba(imie, nazwisko), mama(mama), tata(tata) { }; // ~dziecko() //wystarczy wygenerowany automatycznie – dlaczego? void wypisz_wszystko(); };
Przykład void dziecko::wypisz_wszystko() { osoba::wypisz_wszystko(); cout << "mama: " ; mama->wypisz_wszystko(); cout << "tata: " ; tata->wypisz_wszystko(); }
Przykład – kl. bazowe i pochodne osoba dorosly dziecko posel
class posel:public dorosly { protected: char * nr_immunitetu; public: posel(const char * imie, const char * nazwisko, const char * dowod, const char * immunitet) :dorosly(imie, nazwisko, dowod), nr_immunitetu(aloc_string(immunitet)) { // klasę osoba możemy skonstruować tylko pośrednio }; ~posel() { delete [] nr_immunitetu; } void wypisz_wszystko(); };
Przykład void posel::wypisz_wszystko() { dorosly::wypisz_wszystko(); cout << "immunitet : " << nr_immunitetu; }
Struktury • Struktury w C++ to też klasy struct A { coś tam } odpowiada class A {public: coś tam }
struct xy { int x,y; xy(int x, int y) : x(x), y(y) {} }; //xy xy1={10,20}; — źle xy xy1(10,10); //ok Struktury i klasy • Inicjatorem klamrowym można inicjalizować wyłączniepola klasy w której wszystkie dane są publiczne i nie zdefiniowano konstruktora. struct s_xy { int x,y; }; s_xy sxy={10,20}; // ok. //s_xy sxy1(10,10) - źle
Unie • W C++ unia jest klasą, ale • nie może zawierać obiektów z zdefiniowanymi konstruktorami bądź destruktorami • nie wolno ograniczać dostępu do pól - wszystkie pola muszą być publiczne
W klasach zadeklarować zmienne: punkt: int kolor; xy polozenie okrag:public punkt int promien odcinek:public punkt xy rozmiar; prostokąt:public odcinek Przykład – kl. bazowe i pochodne punkt okrąg odcinek prostokąt • Zadeklarować klasy, konstruktory, przesuń, +=
Przykład – kl. bazowe i pochodne struct xy { int x,y; xy(int x, int y): x(x), y(y) { } };
class punkt { int kolor; xy polozenie; public: punkt(int kolor, xy poz):kolor(kolor), polozenie(poz) { } void przesun(int dx, int dy) { polozenie.x+=dx; polozenie.y+=dy; } punkt & operator+=(int d) { return *this; } };
class okrag:public punkt { int promien; public: okrag(int kolor, xy poz, int promien) :punkt(kolor, poz), promien(promien) { } okrag & operator+=(int d) { (promien*=100+d)/=100; return *this; } };
class odcinek:public punkt { xy rozmiar; public: odcinek(int kolor, xy poz, int p2_x, int p2_y) :punkt(kolor, poz), rozmiar(p2_x, p2_y) { } odcinek & operator+=(int d) { (rozmiar.x*=100+d)/=100; (rozmiar.y*=100+d)/=100; return *this; } };
class prostokat:public odcinek { public: prostokat(int kolor, xy poz, int p2_x, int p2_y) :odcinek(kolor, poz, p2_x, p2_y) { } prostokat & operator+=(int d) { odcinek::operator+=(d); return *this; } };
Jak się zmieni hierarchia klas? xy polozenie int x,y punkt :public xy int kolor; okrag:public punkt int promien odcinek:public punkt xy rozmiar; prostokąt:public odcinek Przykład – kl. bazowe i pochodne xy punkt okrąg odcinek prostokąt
class punkt:public xy { int kolor; // xy polozenie public: punkt(int kolor, xy poz) :kolor(kolor), xy(poz) { } void przesun(int dx, int dy) { x+=dx;// publiczne w przodku y+=dy; } punkt & operator+=(int d) { return *this; } };
Konwersja potomna → bazowa • Automatyczna (niejawna) konwersja wskaźnika do klasy potomnej na wskaźnik do bazowej jest dozwolona przy dziedziczeniu publicznym • rationale • Przykład osoba o(…); dorosly d(…); dziecko x(„Jaś”, „Kowalski”, &o, &d); x.wypisz_wszystko() // nie wypisze dowodu d
Konwersja potomna → bazowa • Automatyczna (niejawna) konwersja wskaźnika do klasy potomnej na wskaźnik do bazowej jest dozwolona przy dziedziczeniu publicznym • uwaga: destrukcja przez wskaźnik po konwersji osoba *o=new dorosly("aa", "bb", "cc"); delete o; // nie zwolni nr_dowodu • rozwiązanie: destruktor wirtualny.
Konwersja potomna → bazowa • Skojarzenie referencji do klasy potomnej z obiektem klasy bazowej jest dozwolone przy dziedziczeniu publicznym • uwagi (konwersje wskaźników oraz referencji): • te konwersje są przechodnie • dozwolone wyłącznie dla dziedziczenia publicznego (aby uniemożliwić obejście ograniczenia dostępu do pól odziedziczonych jako private/protected)
class A {}; class B: public A {}; class C: private A {}; int fa(A a) {}; int fb(B b) {}; ------------------------------ A a; B b; C c; fa(a); // konstruktor kopiujący A fa(b); // jak wyżej // fa(c); // błąd // fb(a); fb(b); // fb(c); A *pa0=&a; A *pa1=&b; // A *pa2=&c; // B *pb=&a; // C *pc=&a; A &ra0=a; A &ra1=b; // A &ra2=c; // B &rb=a; // C &rc=a; Konwersja potomna → bazowa
class A {}; class B: public A {}; class C: private A {}; class D: public B{}; int fa(A a) {}; --------------------------- D d; fa(d); // przechodnie Konwersja potomna → bazowa
Konstruktor kopiujący klasy pochodnej • to jest konstruktor, a zatem • obowiązuje koleność konstrukcji obiektów: zawsze wywołają się konstruktory klas bazowych. • można użyć listy inicjalizacyjnej by określić sposób wywołania konstruktora klasy bazowej • Przypomnienie: konstruowane będą również obiekty zawarte w danym.
Konstruktor kopiujący klasy pochodnej class potomek:public baza { ... }; potomek::potomek(const potomek & p) :baza(p)// niejawna konwersja do kl. bazowej { ... };
Konstruktor kopiujący klasy pochodnej • podobnie jak konstruktor domyślny i destruktor, konstruktor kopiującymoże być generowany automatycznie • jeżeli w klasie potomnej nie zdefiniujemy go to zostanie wygenerowany • przed nim wywoła się konstruktor kopiujący klasy bazowej (a nie bezparametrowy, czyli odwrotnie niż w przypadku konstr. kop. nie-generowanego) • musi być dostępny k. kopiujący. klasy bazowej (być i być publiczny)
Operator przypisania klasy pochodnej • Wygenerowany automatycznie operator przypisania kopiuje składowe danej klasy pole po polu • najpierw klasa bazowa kopiowana jest operatorem przypisania, potem obecna. • nie zostanie wygenerowanyjeżeli klasa zawiera pola stałe albo referencje(w klasie albo przodkach), ani jeżeli w klasie bazowej lub obiekcie zawartym op.= był prywatny. • prawdopodobnie nie sprawdzi się dla pól wskaźnikowych • jeżeli napiszemy własny operator to odziedziczony operator nie wywoła się sam, trzeba to zrobić jawnie.
Operator przypisania klasy pochodnej class potomek:public baza{ ... }; potomek & potomek::operator=(const potomek & p) { if (this == &p) return *this; this->baza::operator=(p); // alternatywy: baza *pb=this; (*pb)=p; // baza *pb=this; pb->operator=(p); // baza &rb=*this; rb=p; // przypisanie dla własnej klasy return *this; // zwrócenie wartości };
class dorosly: public osoba { char * nr_dowodu; … } Przykład • Konstruktory kopiujące i operatory przypisania dla klas osoba i dorosły class osoba { char * imie, * nazwisko; … }
Przykład osoba::osoba(const osoba &o) :imie(aloc_string(o.imie)),nazwisko(aloc_string(o.nazwisko)) {} osoba & osoba::operator=(const osoba &o) { if (this == &o) return *this; delete [] imie; imie=aloc_string(o.imie); delete [] nazwisko; nazwisko=aloc_string(o.nazwisko); return *this; }