Programowanie obiektowe w C++ Dziedziczenie

Transkrypt

Programowanie obiektowe w C++ Dziedziczenie
Programowanie
obiektowe
w C++
Wykład 04
Temat wiodący:
Dziedziczenie cz. 1
Dziedziczenie
1
Po co nam 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();
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();
}
2
Dziedziczenie
n
Wyraża związki hierarchiczne między klasami
n
Rozbudowywanie istniejących klas, bez ich
modyfikowania lub rekompilacji
n
Wykorzystanie już gotowego kodu
(definiujemy tylko to , co dodane lub
zmienione)
Dziedziczenie
n
Klasa bazowa
osoba
n
Klasa pochodna
dorosly
każdy dorosły jest osobą
dorosły jest przypadkiem szczególnym osoby
3
Klasa pochodna
n
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
n
n
n
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
4
Klasa pochodna
osoba o;
dorosly s;
o.wypisz_imie();
o.wypisz_nazwisko();
o.wypisz_wszystko();
s.wypisz_imie();
s.wypisz_nazwisko();
s.wypisz_numer();
s.wypisz_wszystko();
s.osoba::wypisz_wszystko();
// z osoba
// z osoba
// z dorosly
// z dorosly
// 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
5
Dostęp do pól kl. bazowej
sekcja w kl. bazowej
private
: private
protected
niedostępne private
public
private
: protected niedostępne protected
protected
: public
public
niedostępne protected
Dostęp do pól kl. bazowej
class a
{
public:
int f(int);
};
class b:private a
{
public:
a::f;
};
//można „osłabić” ograniczenie dostępu
// teraz a::f jest dziedziczone publicznie
6
Dostęp do pól kl. bazowej
class baza
{
public:
int ba_pub,
i;
class prot: protected baza
{
public:
int i;
void metoda();
protected:
int ba_prot;
private:
int ba_priv;
};
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;
}
7
Dostęp do pól kl. bazowej
void not_a_friend()
{
baza ba;
prot pr;
// 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;
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_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;
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;
int i;
i=ba.i;
i=ba.ba_pub;
// i=ba.ba_prot;
// i=ba.ba_priv;
// i=pr.ba_priv;
// i=pr.baza::ba_priv;
// i=pr.prot::ba_priv;
i=pr.baza::i; // !!!!!
i=pr.i;
i=pr.prot::i;
i=pr.prot_prot;
i=pr.prot::prot_prot;
}
8
Konstruktor i destruktor dla klasy
potomnej
n
Konstruktorów się nie dziedziczy, ale
n
Kolejność konstrukcji obiektu klasy:
n
n
n
n
n
wirtualne klasy bazowe
bezpośrednie klasy bazowe
obiektowe zmienne klasowe
dana klasa
Destruktory - odwrotnie
Konstruktor i destruktor dla klasy
potomnej – lista inicjalizacyjna
n
Opisuje sposób inicjalizacji zmiennych
klasowych zadeklarowanych w klasie
(a nie odziedziczonych).
n
Opisuje sposób wywołania konstruktorów
przodków wirtualnych oraz bezpośrednich.
9
Przykład
class osoba
{
protected:
char * imie,
* nazwisko;
~osoba()
{
delete imie;
delete nazwisko;
}
char * aloc_string(const char * s);
public:
osoba(const char * im, const char * naz)
:imie(aloc_string(im)),
nazwisko(aloc_string(naz))
{
}
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();
}
10
Przykład - konstruktor klasy
pochodnej
class dorosly:public osoba
{
protected:
char * nr_dowodu;
~dorosly()
{
delete nr_dowodu;
};
public:
dorosly(const char * imie,
const char * nazwisko,
const char * dowod)
:osoba(imie, nazwisko),
nr_dowodu(aloc_string(dowod))
{
};
void wypisz_numer();
void wypisz_wszystko();
};
Przykład
void dorosly::wypisz_numer()
{
cout << "dowod: " << nr_dowodu << " ";
}
void dorosly::wypisz_wszystko()
{
osoba::wypisz_wszystko();
wypisz_numer();
}
11
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();
};
12
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
13
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))
{ // klase 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;
}
14
Struktury
n
Struktury w C++ to też klasy
struct A { coś tam }
odpowiada
class A {public: coś tam }
Struktury i klasy
n
Inicjatorem klamrowym można inicjalizować wyłącznie pola
klasy w której wszystkie dane są publiczne i nie zdefiniowano
konstruktora.
struct s_xy
{
int x,y;
};
struct xy
{
int x,y;
xy(int x, int y)
: x(x), y(y) {}
};
s_xy sxy={10,20}; // ok.
//s_xy sxy1(10,10) - źle
//xy xy1={10,20}; — źle
xy xy1(10,10); //ok
15
Unie
n
W C++ unia jest klasą, ale
n
n
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
Przykład – kl. bazowe i pochodne
punkt
W klasach zadeklarować zmienne:
n
punkt:
n
okrąg
odcinek
n
okrag :public punkt
n
n
prostokąt
n
int promien
odcinek :public punkt
n
n
int kolor; xy polozenie
xy rozmiar;
prostokąt :public odcinek
Zadeklarować klasy, konstruktory, przesuń, +=
16
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;
}
};
17
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;
}
};
18
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;
}
};
Przykład – kl. bazowe i pochodne
xy
Jak się zmieni hierarchia klas?
n
xy polozenie
n
punkt
n
punkt :public xy
n
n
okrąg
odcinek
n
int promien
odcinek :public punkt
n
prostokąt
int kolor;
okrag :public punkt
n
n
int x,y
xy rozmiar;
prostokąt :public odcinek
19
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
n
Automatyczna (niejawna) konwersja wskaźnik do
klasy bazowej na wskaźnik do potomnej jest
dozwolona przy dziedziczeniu publicznym
n
n
rationale
Przykład
osoba o(…);
dorosly d(…);
dziecko x(„Jaś”, „Kowalski”, &o, &d);
x.wypisz_wszystko() // nie wypisze dowodu d
20
Konwersja potomna ? bazowa
n
Automatyczna (niejawna) konwersja wskaźnik do
klasy bazowej na wskaźnik do potomnej jest
dozwolona przy dziedziczeniu publicznym
n
uwaga: destrukcja przez wskaźnik po konwersji
osoba *o=new dorosly("aa", "bb", "cc");
delete o; // nie zwolni nr_dowodu
n
rozwiązanie: destruktor wirtualny.
Konwersja potomna ? bazowa
n
Skojarzenie referencji do klasy bazowej z obiektem
klasy potomnej jest dozwolone przy dziedziczeniu
publicznym
n
uwagi (konwersje wskaźników oraz referencji):
n
n
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)
21
Konwersja potomna ? bazowa
class A {};
class B: public A {};
class C: private A {};
// fb(a);
fb(b);
// fb(c);
int fa(A a) {};
int fb(B b) {};
-----------------------------A a;
B b;
C c;
A *pa0=&a;
A *pa1=&b;
// A *pa2=&c;
// B *pb=&a;
// C *pc=&a;
fa(a);
// konstruktor kopiujący A
fa(b);
// jak wyżej
// fa(c); // błąd
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
22
Konstruktor kopiujący klasy
pochodnej
n
to jest konstruktor, a zatem
n
obowiązuje koleność konstrukcji obiektów: zawsze
wywolaja sie konstruktory klas bazowych.
n
mozna uzyc listy inicjalizacyjnej by określić sposób
wywołania konstruktora klasy bazowej
n
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
{ ... };
23
Konstruktor kopiujący klasy
pochodnej
n
podobnie jak konstruktor domyślny i
destruktor, konstruktor kopiujący może być
generowany automatycznie
n
n
n
jezeli w klasie potomnej nie zdefiniujemy go to
zostanie wygenerowany
przed nim wywola sie konstruktor kopiujący klasy
bazowej (a nie bezparametrowy, czyli odwrotnie
niz w przypadku konstr. kop. nie-generowanego)
musi byc dostepny k. kopiujacy. klasy bazowej
(byc i byc publiczny)
Operator przypisania klasy
pochodnej
n
Wygenerowany automatycznie operator przypisania
kopiuje skladowe danej klasy pole po polu
n
n
n
n
najpierw klasa bazowa kopiowana jest operatorem
przypisania, potem obecna.
nie zostanie wygenerowany jeżeli klasa zawiera pola stałe
albo referencje (w klasie albo przodkach), ani jeżeli w
klasie bazowej lub obiekcie zawartym op.= byl prywatny.
prawdopodobnie nie sprawdzi się dla pól wskaźnikowych
jeżeli napiszemy wlasny operator to odziedziczony
operator nie wywola sie sam, trzeba to zrobic jawnie.
24
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
Przykład
n
Konstruktory kopiujące i operatory przypisania dla
klas osoba i dorosły
class osoba
{
char * imie,
* nazwisko;
…
}
class dorosly: public osoba
{
char * nr_dowodu;
…
}
25
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;
}
Przykład
dorosly::dorosly(const dorosly & d)
:osoba(d), nr_dowodu(aloc_string(d.nr_dowodu))
{}
dorosly & dorosly::operator=(const dorosly & d)
{
if (this == &d)
return *this;
this->osoba::operator=(d);
delete nr_dowodu;
nr_dowodu=aloc_string(d.nr_dowodu);
return *this;
}
26