• Nie Znaleziono Wyników

Dariusz Wardowski

N/A
N/A
Protected

Academic year: 2021

Share "Dariusz Wardowski"

Copied!
21
0
0

Pełen tekst

(1)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Dariusz Wardowski

(2)

Konstruktory w Javie

Konstruktory definiują początkowy stan obiektów. W języku Java również możliwe jest przeładowanie konstruktorów. Poniżej przedstawiono przykłady:

GregorianCalendar dataDzisiejsza = new GregorianCalendar();

GregorianCalendar data = new GregorianCalendar(2010, Calendar.NOVEMBER, 28);

Gdy w konstruktorze nie ma zainicjalizowanych wartości składowych klasy, wówczas zostają im nadane wartości domyślne (w C++ ten mechanizm nie występuje!):

-Dla liczb – wartośd zero

-Dla wartości logicznych – false -Dla referencji – null

W przypadku, gdy klasa nie posiada zdefiniowanych konstruktorów, definiowany jest konstruktor domyślny, który nadaje wszystkim zmiennym składowym wartości domyślne.

W Javie możliwa jest bezpośrednia inicjalizacja pól składowych klasy (Uwaga w C++ nie!)

class Przykladowa {

String skladowa = „wartosc domyslna”;

(3)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Konstruktory w Javie c.d.

W definicji konstruktora możliwe jest wywołanie innego konstruktora tej samej klasy. Patrz przykład poniżej.

class Prostokat {

private int a;

private int b;

public Prostokat(int _a, int _b) {

a = _a;

b = _b;

}

public Prostokat(int x) {

this(x,x);

} }

Gdy tworzony jest obiekt Prostokat p = new Prostokat(10), wówczas konstruktor

Prostokat(int) wywołuje konstruktor Prostokat(int,int).

Użycie w konstruktorze składni this(…), w celu wywołania innego konstruktora, musi wystąpid jako pierwsze polecenie tego konstruktora. (W C++ niemożliwe jest wywołanie jednego

konstruktora przez drugi).

(4)

Bloki inicjalizacji w Javie

W języku Java istnieje jeszcze inny mechanizm nadawania zmiennym składowych klasy początkowych wartości za pomocą tzw. bloków inicjalizacji. Instrukcje zawarte w takich blokach wykonywane są za każdym razem, gdy tworzony jest obiekt.

class Prostokat {

private int a;

private int b;

public Prostokat(int _a, int _b) {

a = _a;

b = _b;

}

public Prostokat(int x) {

this(x,x);

} {

a = 10;

b = 20;

} }

Bloków inicjalizacji można użyd np. do inicjalizacji pola statycznego (tzn. pole wspólne dla

(5)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Niszczenie obiektów w Javie

Język Java obsługuje automatyczne usuwanie pamięci przy pomocy tzw. garbage collector.

public class A {

private int x;

public A(int x) {

this.x = x;

}

public void finalize(int x) {

System.out.println(„Zaraz obiekt zostanie zniszczony”);

}

public static void main(String[] args) {

A a = new A(10);

System.runFinalizersOnExit(true);

} }

Język Java nie obsługuje destruktorów. Do każdej klasy można dodad metodę finalize, która zostanie wywołana przed zniszczeniem obiektu, jest to metoda zanegowana (deprecated).

(6)

this

Wszystkie funkcje składowe klasy posiadają wskaźnik this, który wskazuje na obiekt wywołujący. Zobacz przykłady poniżej.

C++

class A {

private:

int x;

public:

A(int _x) {

x = _x;

}

A podwoj() {

x *= 2;

return *this;

} };

java

public class A {

private int x;

public A(int _x) {

x = _x;

}

A podwoj() {

x *= 2;

return this;

} };

W C++ do zwrócenia samego obiektu użyjemy *this (operator wyłuskania), natomiast w języku Java this jest referencją obiektu.

(7)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Poniżej przedstawiono deklaracje klas, które realizują mechanizm do przechowywania danych, w których dane są zawsze dodawane lub usuwane ze szczytu stosu.

Zastosowanie klas do typów abstrakcyjnych

class Element {

private:

int x;

Element* next;

public:

Element(int _x);

int getx();

Element* getnext();

void setnext(Element* n);

};

class Stos {

private:

Element* top;

public:

Stos();

void push(Element& e);

Element* pop();

};

Zastosowanie powyższych klas

int main() {

Stos s;

Element e1(10); s.push(e1);

Element e2(20); s.push(e2);

Element e3(30); s.push(e3);

while (Element* tmp = s.pop())

cout << tmp->get() << endl;

return 0;

}

(8)

Technika przeciążania operatorów to pomysł rozszerzenia przeciążania funkcji na operatory.

Czyli standardowym operatorom z C++ można przypisad inne znaczenie. C++ udostępnia mechanizm przeciążania operatorów dla typów niestandardowych np. obiektów klasy.

Przeciążanie operatorów (C++)

Dzięki przeciążaniu operatorów kod wygląda przejrzyście.

Aby przeciążyd operator OP (OP oznacza dowolny operator np. +) należy użyd tzw. funkcji operatora postaci:

operatorOP(argumenty)

Załóżmy, że w klasie A zdefiniowano metodę operator+(), w ten sposób przeciążono operator dodawania, możliwe jest zatem dodanie dwóch obiektów klasy A.

A a = new A();

A b = new A();

A c;

c = a + b;

(9)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

class Z {

private:

double Re, Im;

public:

Z(double _Re, double _Im) {Re = _Re; Im = _Im;}

Z(){}

Z operator+(Z & x);

Z operator*(double n);

};

Z Z::operator+(Z & x) {

Z w;

w.Re = Re + x.Re;

w.Im = Im + x.Im;

return w;

}

Z Z::operator*(double n) {

Z w;

w.Re = n*Re; w.Im = n*Im;

return w;

}

int main() {

Z x(1,2);

Z y(-1,3);

Z w1 = x + y;

Z w2 = x*2; //Z w2 = 2*x --źle return 0;

}

Przeciążanie operatorów (C++) - przykład

(10)

• Przynajmniej jeden z argumentów musi byd zdefiniowany przez użytkownika.

• Nie można przeciążyd operatora tak aby naruszał zasady składniowe stosowane podczas używania oryginalnego znaczenia operatora.

• Nie można zmienid priorytetu operatorów.

• Nie można tworzyd nowych symboli operatorów.

• Operatory =, (), [], -> można przeciążad wyłącznie w metodach klasy.

• Następujące operatory mogą byd przeciążone:

Przeciążanie operatorów - ograniczenia

+ - * / % ^& |

~=

*= ! = < > += -=

/=

>>= %= ^= &= \= << >>

<<=

++ == != <= >= && ||

-

delete , ->* -> () [] new

new[] delete[]

(11)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Funkcja zaprzyjaźniona z daną klasą ma takie same przywileje jak metoda tej klasy, w szczególności ma dostęp do pól prywatnych tej klasy.

Funkcje zaprzyjaźnione

Prototyp funkcji zaprzyjaźnionej:

friend typZwracany operatorOP(argumenty);

W części implementacyjnej słowo friend pomijamy.

class Z {

public:

friend Z operator*(double n, Z & z);

};

Z operator*(double n, Z & z) {

Z w;

w.Re = z.Re * n;

w.Im = z.Im * n;

return w;

}

int main() {

Z z1(1,2);

Z z2 = z1*2;

Z z3 = 3*z1; //poprawnie return 0;

}

(12)

Przeciążenie operatora <<

W częsci deklaracyjnej klasy:

friend void operator<<(ostream & os, const Z & z);

Definicja:

void operator<<(ostream & os, const Z & z) {

os << z.Re << „+” << z.Im <<„I”;

}

Użycie opeatora <<

Z z1(2,3);

cout << z1;

Czy zadziała?

Z z1(2,3);

Z z2(3,4);

cout << z1 << z2;

(13)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Przeciążenie operatora << cd.

W częsci deklaracyjnej klasy:

friend ostream & operator<<(ostream & os, const Z & z);

Definicja:

ostream & operator<<(ostream & os, const Z & z) {

os << z.Re << „+” << z.Im <<„I”;

return os;

}

A teraz lepiej?

Z z1(2,3);

Z z2(3,4);

cout << z1 << z2;

(14)

Przeciążenie operatora unarnego

Operator unarny, to operator, który wymaga tylko jednego argumentu (np. operator negacji).

Poniżej przedstawiono przykład przeciążenia operatora -, który zwraca negację liczby zespolonej.

Deklaracja:

Z operator-() const;

Definicja:

Z Z::operator-() const {

return Z(-Re, -Im);

}

(15)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Konwersja i rzutowanie typów (C++)

Przykłady automatycznej konwersji między typami standardowymi:

double x = 2; liczba 2 (int) konwertowana na typ double long y = 1; liczba 1 (int) konwertowana na typ long

int z = 2.98; liczba 2.98 (double) konwertowana na typ int int p = ‘ ‘; znak ‘ ‘ (char) konwertowany na typ int

int * q = 100; błąd konwersji

int * q = (int *) 100; rzutowanie liczby 10 do wskaźnika do typu int.

W C++ można tak zdefiniowad klasę spokrewnioną z typem podstawowym lub z inną klasą aby możliwe było wykonywanie konwersji między nimi. Przy czym należy określid czy konwersja ma byd automatyczna, czy poprzez rzutowanie typów.

(16)

Konstruktor jako funkcja konwersji

Konstruktory, które posiadają jeden argument mogą pełnid rolę funkcji konwersji, czyli mechanizmu, który przekształca wartośd argumentu na typ klasy. Zobacz przykład:

class Z {

private:

double Re;

double Im;

public:

Z(double _Re) {Re=_Re; Im = 0;}

};

int main() {

Z x = 10; //użycie konstruktora Z(double) do niejawnej konwersji liczby 10 na typ Z return 0;

}

Konstruktory, które posiadają więcej niż jeden argument nie mogą pełnid roli funkcji konwersji.

(17)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

explicit

Konstruktory, których definicje poprzedzone są słowem explicit, nie można wykorzystad do niejawnej konwersji typów. Jednakże nadal możliwa jest jawna konwersja za pomocą rzutowania

class Z {

private:

double Re;

double Im;

public:

explicit Z(double _Re) {Re=_Re; Im = 0;} //automatyczna konwersja nie jest możliwa };

int main() {

Z x = 10; //błąd

Z y = Z(10); //działa, jawna konwersja return 0;

}

(18)

Funkcje konwersji

Funkcje konwersji to niestandardowe rzutowania typów, których można używad tak samo jak zwykłych operacji rzutowania. Np.:

Z z(-1.2,0);

double x = double(z); jawna konwersja double x = z; niejawna konwersja

Aby możliwe było wykonanie powyższych konwersji należy utworzyd funkcję konwersji o następującej deklaracji:

operator nazwaTypu();

nazwaTypu oznacza typ do jakiego ma zostad przekształcony obiekt.

Warunki jakie musi spełniad funkcja konwersji:

• funkcja konwersji jest metodą klasy

• funkcja konwersji nie zwraca żadnego typu

• funkcja konwersji nie posiada argumentów

(19)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Przykład funkcji konwersji

class Z {

private:

double Re;

double Im;

public:

Z(double _Re) {Re=_Re; Im = 0;}

Z(double _Re, double _Im) {Re=_Re; Im = _Im;}

operator double() {return Re;}

};

int main() {

Z x(10);

double a = x; // niejawna konwersja Z y(-1,2);

double b = double(y); //jawna konwersja return 0;

}

Gdy w klasie zdefiniowane są dwie lub więcej funkcji konwersji, wówczas w celu uniknięcia niejednoznaczności używamy rzutowania.

(20)

Problem z konwersją

class Z {

private:

double Re, Im;

public:

Z(double _Re) {Re=_Re; Im = 0;}

Z(double _Re, double _Im) {Re=_Re; Im = _Im;}

friend Z operator+(const Z & z, const Z & w) {

return Z(z.Re + w.Re, z.Im + w.Im);

}

operator double() {return Re;}

};

int main() {

Z a, b;

a = Z(1,2) + Z(-1,-2);

b = Z(1,2) + 2;

return 0;

}

Problem z niejednoznacznością w instrukcji Z(1,2) + 2. Z jednej strony liczba 2 zostaje skonwertowana na typ Z i zostaje wywołana funkcja operator+(double, double), z

(21)

dr Dariusz Wardowski, Katedra Analizy Nieliniowej

KONIEC

Dziękuję za uwagę 

Cytaty

Powiązane dokumenty

Gdy dziedziczenie jest publiczne, to wszystkie składowe publiczne klasy macierzystej stają się składowymi publicznymi klasy potomnej.. Dostęp do odziedziczonych

Zgodność referencji typu klasy macierzystej z obiektami klasy potomnej daje również możliwość wykorzystywania obiektów klasy potomnej do tworzenia obiektów klasy macierzystej..

Innymi słowy, funkcja (w sensie matematycznym) definiuje wartośd, ale nie określa ciągu działao na wartościach w pamięci, które mogłyby ją wytworzyd... dr Dariusz Wardowski,

Filtrowanie listy polega na wybraniu z listy tylko tych elementów, które spełniają podane kryterium, i zwróceniu ich jako nowej (zwykle krótszej) listy.. dr Dariusz Wardowski,

W przypadku ukonkretniania całkowitego zmienna utożsamia się z termem niezawierającym zmiennych i nie podlega dalszemu ukonkretnianiu. W tym momencie można ją postrzegad

•Jeżeli obiekt utworzono w sposób dynamiczny (tzn. za pomocą operatora new), wówczas destruktor tego obiektu jest wywoływany automatycznie, gdy użyjemy delete do

Konstruktory, które posiadają jeden argument mogą pełnić rolę funkcji konwersji, czyli mechanizmu, który przekształca wartość argumentu na typ

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... Wiązanie statyczne