• Nie Znaleziono Wyników

Dariusz Wardowski

N/A
N/A
Protected

Academic year: 2021

Share "Dariusz Wardowski"

Copied!
14
0
0

Pełen tekst

(1)

Dariusz Wardowski

(2)

Wirtualne destruktory

class A {

private:

int* a;

public:

A(int _a) {a = new int(_a);}

virtual ~A() {delete a;}

};

class B: public A {

private:

double* b;

public:

B(int _a, double _b): A(_a){

b = new double(_b);}

virtual ~B() {delete b;}

};

Poprzedzenie destruktorów słowem virtual powoduje, że podczas destrukcji obiektu zostanie wywołany odpowiedni kod destruktora. Jeżeli wskaźnik (referencja) wskazuje na obiekt typu B, wówczas nastąpi wywołanie destruktora ~B() klasy potomnej, a następnie ~A() klasy macierzystej. Innymi słowy, wykorzystanie wirtualnych destruktorów zapewnia odpowiednią kolejnośd ich wywołania.

int main() {

A* wsk1 = new A(3);

A* wsk2 = new B(4,2.9);

delete wsk1; //działa destruktor ~A() delete wsk2; //działają kolejno

//destruktory ~B(), ~A().

return 0;

}

(3)

Wiązanie statyczne i dynamiczne

Wiązanie nazwy funkcji polega na określeniu odpowiedniego bloku wykonywalnego (w kodzie skompilowanym), który ma zostad użyty.

Wiązanie statyczne to wiązanie, które jest realizowane podczas kompilacji kodu źródłowego.

Wiązanie dynamiczne, to odpowiedni mechanizm, który pozwala wybrad odpowiednią metodę wirtualną podczas działania programu.

Uwaga Wiązanie dynamiczne zachodzi wówczas, gdy odpowiednie metody wywoływane są przez wskaźnikilub referencje.

(4)

Rzutowanie w górę

Rzutowanie w górę jest to konwersja referencji (wskaźnika) do klasy potomnej na referencję (wskaźnik) do klasy macierzystej. Podczas dziedziczenia publicznego konwersja taka jest zawsze możliwa i zachodzi bez jawnego rzutowania typów.

A* wsk = new B(3,4.5);

A & ref = B(4,4.3);

B b(3,4.5);

A & ref = b;

Wszelkie metody jakie można wykonywad na obiekcie klasy A, można wykonywad na obiekcie klasy B. Funkcja, której argumentem jest wskaźnik (referencja) do obiektu klasy A, będzie działad na obiekcie klasy B.

Rzutowanie w górę jest przechodnie. Tzn. w przypadku, gdy klasa B dziedziczy z A, a klasa C dziedziczy z B, wówczas wskaźniki (referencje) do klasy A, mogą dotyczyd obiektów zarówno klasy B jak i C.

A* a, b;

a = new B();

b = new C();

(5)

Rzutowanie w dół

Rzutowanie w dół polega na konwersji referencji lub wskaźnika do klasy macierzystej na referencję lub wskaźnik do klasy potomnej. Rzutowanie w dół nie jest wykonywane bez jawnej konwersji typów.

(6)

Funkcje wirtualne – podsumowanie

• Jeżeli w deklaracji klasy daną metodę poprzedzimy słowem kluczowym virtual, wówczas metoda będzie metodą wirtualną w klasie macierzystej, potomnej i innych klasach dziedziczących po klasie potomnej.

• Jeżeli metoda wirtualna wywoływana jest na rzecz referencji lub wskaźnika, to program użyje tej wersji metody, która odpowiada typowi obiektu na który dana referencja czy wskaźnik wskazuje.

• Na metody wirtualne wybieramy te, które w klasach potomnych będą przedefiniowane.

• Jeżeli w którejś klasie potomnej nie zostanie przedefiniowana metoda wirtualna, wówczas obiekt tej klasy będzie korzystał z funkcji wirtualnej najbliższego „przodka”.

• Konstruktory nie mogą byd metodami wirtualnymi, gdyż klasa potomna nie dziedziczy konstruktorów klasy macierzystej.

• Jeżeli dana klasa będzie stanowid klasę macierzystą, wówczas jej destruktory powinny byd wirtualne.

• Funkcje zaprzyjaźnione nie mogą byd wirtualne, gdyż nie są metodami klasowymi.

(7)

Kontrola dostępu

Do kontrolowania dostępu do pól składowych klasy stosujemy słowa kluczowe:

• public

• private

• protected

Składowe chronione, czyli te które umieszczone są w sekcji protected, nadal dostępne są tylko dla metod tej samej klasy. Mogą byd one udostępnione poza klasą tylko przy pomocy publicznych metod udostępniających te dane (analogiczne jak private).

class A {

private:

int x;

protected:

double y;

public:

int getX();

double getY();

};

(8)

protected

class A {

private:

int x;

protected:

double y;

public:

int getX();

double getY();

};

class B: public A {

public:

void setY(double _y) {

y = _y;

} };

Klasa B ma bezpośredni dostęp do składowej y. W wyniku metody setY(double) „psuje się”

enkapuslacja tej zmiennej.

Najlepiej umieszczad w sekcji protected te pola składowe, do których dostęp w klasie potomnej jest możliwy tylko za pomocą interfejsu publicznego klasy macierzystej.

(9)

Klasa Rownoleglobok – macierzysta czy nie?

class Rownoleglobok {

private:

double a;

double h;

double alfa;

public:

double pole() const;

double obwod() const;

void zmienKat(double a);

};

Każdy prostokąt jest równoległobokiem, zatem czy jest sens klasę Prostokat wyprowadzid za pomocą dziedziczenia z klasy Rownoleglobok?

(10)

Relacja „jest” a dziedziczenie

class Prostokat : public Rownoleglobok {

};

Wady klasy Prostokat:

• Do opisania prostokąta mamy trzy pola składowe (alfa zbędne, bo zawsze 90).

• Dziedziczona metoda zmienKat() nie ma dla prostokąta sensu, gdyż obiekt przestanie byd prostokątem.

(11)

Może jednak bez dziedziczenia?

class Rownoleglobok {

private:

double a;

double h;

double alfa;

public:

double pole() const;

double obwod() const;

void zmienKat(double a);

};

class Prostokat {

private:

double a;

double h;

public:

double pole() const;

double obwod() const;

};

Deklaracja klasy Prostokat posiada już tylko te pola i metody składowe, które są potrzebne.

Wydaje się jednak, że z uwagi na widoczny wspólny kod tych klas, można wprowadzid inne rozwiązanie.

(12)

Abstrakcyjna klasa macierzysta

class AbstrRownoleglobok {

private:

double a;

double h;

public:

AbstrRownoleglobok(double _a = 0, double _h = 0) : a(_a), h(_h) {};

virtual ~AbstrRownoleglobok() {};

double pole() const {return a*h;}

virtual double obwod() const = 0; //funkcja w pełni wirtualna };

Rozwiązanie to polega na wyodrębnieniu wspólnych cech tych klas i umieszczeniu ich w tzw.

abstrakcyjnej klasie macierzystej, po której klasy Prostokat i Rownoleglobok będą dziedziczyły.

Z uwagi na brak danych, nie można zaimplementowad metody obwod(). Dodanie „= 0” na koocu deklaracji metody wirtualnej powoduje, że metoda ta staje się w pełni wirtualna, a w konsekwencji klasa AbstrRowoleglobok jest abstrakcyjna (tzn. posiada co najmniej jedną metodę w pełni wirtualną).

(13)

Zastosowanie klas abstrakcyjnych

• Nie można tworzyd obiektów klasy abstrakcyjnej:

AbstRownoleglobok ap; //błąd!

AbstRownoleglobok * wsk; //OK

• Klasy abstrakcyjne służą jako klasy macierzyste, a więc tworzymy je po to by z nich dziedziczyd.

class Rownoleglobok: public AbstRownoleglobok {

… };

class Prostokat: public AbstRownoleglobok {

… };

• Mechanizm abstrakcyjnych klas macierzystych pozwala projektowad hierarchię klas w sposób bardziej usystematyzowany i zdyscyplinowany.

(14)

Dziękuję za uwagę

Cytaty

Powiązane dokumenty

Rozwiązania możesz sprawdzić na stronie od razu po

Rozwiązania możesz sprawdzić na stronie od razu po

Czy następujące stwierdzenia są

Meine Schule heiβt Henryk- Dobrzański- Schule – moja szkoła jest imienia Henryka.. Dobrzańskiego Sie liegt in Bircza- Ona leży w Birczy der Unterricht

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.

Dobrym rozwiązaniem jest zamiana powyższych klas na jedną definicję szablonu, posługując się tzw..

Definiując szablon klasy możemy korzystać zarówno z wartości domyślnych dla parametrów typu jaki i wartości domyślnych dla argumentów wyrażeń (nie-typów).. Definiując szablon

Metoda end() zwraca iterator wskazujący na element, który znajduje się za ostatnim elementem kontenera, element ten nazywany jest elementem ograniczającym. it