• Nie Znaleziono Wyników

Programowanie obiektowe w C++

N/A
N/A
Protected

Academic year: 2021

Share "Programowanie obiektowe w C++"

Copied!
26
0
0

Pełen tekst

(1)

Programowanie obiektowe

w C++

Wykład 04 Temat wiodący:

Dziedziczenie cz. 1

Dziedziczenie

(2)

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

}

(3)

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

(4)

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

zawiera wszystkie pola bazowej

n

dziedziczy wszystkie metody, może je przedefiniowywać

n

do dostępnych lecz przysłoniętych nazw z

zakresu klasy bazowej odwołujemy się za

pomocą operatora zakresu

(5)

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

(6)

Dostęp do pól kl. bazowej

sekcja w kl. bazowej

public protected

niedostępne : public

protected protected

niedostępne : protected

private private

niedostępne : private

public protected

private

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

(7)

Dostęp do pól kl. bazowej

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;

}

(8)

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;

}

(9)

Konstruktor i destruktor dla klasy potomnej

n

Konstruktorów się nie dziedziczy, ale

n

Kolejność konstrukcji obiektu klasy:

n

wirtualne klasy bazowe

n

bezpośrednie klasy bazowe

n

obiektowe zmienne klasowe

n

dana klasa

n

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.

(10)

Przykład

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

}

(11)

Przykład - konstruktor klasy pochodnej

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

void dorosly::wypisz_numer() {

cout << "dowod: " << nr_dowodu << " ";

}

void dorosly::wypisz_wszystko() {

osoba::wypisz_wszystko();

wypisz_numer();

}

(12)

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

};

(13)

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

(14)

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

};

void posel::wypisz_wszystko() {

dorosly::wypisz_wszystko();

cout << "immunitet : " << nr_immunitetu;

}

Przykład

(15)

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;

};

s_xy sxy={10,20}; // ok.

//s_xy sxy1(10,10) - źle

struct xy {

int x,y;

xy(int x, int y) : x(x), y(y) {}

};

//xy xy1={10,20}; — źle xy xy1(10,10); //ok

(16)

Unie

n

W C++ unia jest klasą, ale

n

nie może zawierać obiektów z zdefiniowanymi konstruktorami bądź destruktorami

n

nie wolno ograniczać dostępu do pól - wszystkie pola muszą być publiczne

Przykład – kl. bazowe i pochodne

punkt

okrąg odcinek

prostokąt

n

Zadeklarować klasy, konstruktory, przesuń, +=

W klasach zadeklarować zmienne:

n punkt:

n int kolor; xy polozenie

n okrag :public punkt

n int promien

n odcinek :public punkt

n xy rozmiar;

n prostokąt :public odcinek

(17)

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;

} };

(18)

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;

}

};

(19)

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

punkt

okrąg odcinek

prostokąt

Jak się zmieni hierarchia klas?

n xy polozenie

n int x,y

n punkt :public xy

n int kolor;

n okrag :public punkt

n int promien

n odcinek :public punkt

n xy rozmiar;

n prostokąt :public odcinek

(20)

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 rationale

n Przykład

osoba o(…);

dorosly d(…);

dziecko x(„Jaś”, „Kowalski”, &o, &d);

x.wypisz_wszystko() // nie wypisze dowodu d

(21)

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 te konwersje są przechodnie

n dozwolone wyłącznie dla dziedziczenia publicznego (aby uniemożliwić obejście ograniczenia dostępu do pól odziedziczonych jako private/protected)

(22)

Konwersja potomna ? bazowa

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

(23)

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

{ ... };

(24)

Konstruktor kopiujący klasy pochodnej

n

podobnie jak konstruktor domyślny i

destruktor, konstruktor kopiujący może być generowany automatycznie

n

jezeli w klasie potomnej nie zdefiniujemy go to zostanie wygenerowany

n

przed nim wywola sie konstruktor kopiujący klasy bazowej (a nie bezparametrowy, czyli odwrotnie niz w przypadku konstr. kop. nie-generowanego)

n

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 najpierw klasa bazowa kopiowana jest operatorem przypisania, potem obecna.

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

n prawdopodobnie nie sprawdzi się dla pól wskaźnikowych

n

jeżeli napiszemy wlasny operator to odziedziczony

operator nie wywola sie sam, trzeba to zrobic jawnie.

(25)

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;

}

(26)

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;

}

Cytaty

Powiązane dokumenty

(4 pkt) W klasie Stos zdeniuj metody dost¦pu, wstawiania oraz usuwania elementu stosu  pa- mi¦taj, »e do stosu dost¦p jest tylko z jednej strony.. (4 pkt) W klasie Stos

DODATKOWE - na dodatkowe punkty lub wy»sz¡ ocen¦ (zadania 1-3 musz¡ by¢ wykonane!) Do realizacji podobnego jak wy»ej zadania i budowy klas wyj¡tków wykorzystaj bibliotek¦

n można go wykorzystać jeżeli mamy zwrócić wskaźnik bądź referencję do obiektu na rzecz którego wywoływana jest metoda.. n nie

n operator konwersji tworzy obiekt określonego typu lub klasy z obiektu na rzecz którego

n Dla obiektu, którego klasy nie można jednoznacznie określić na etapie kompilacji, odwołania do metody, bądź metod zadeklarowanych jako wirtualne będą się odbywały

Metody określone przez klasę odwołują się do atrybutów przechowywanych w obiekcie.. Czy klasy mogą

Pokazać, że istnieje algorytm należ¸ acy do P, który rozpoznaje po kodzie wprowadzanego grafu (jako ci¸ agu z B ∗ ), czy istnieje p¸etla (=domkni¸eta ścieżka bez

Za pomocą klas programista stara się opisać obiekty, ich właściwości, zbudować konstrukcje, interfejs, dzięki któremu będzie można wydawać polecenia realizowane potem