• 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)

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ść 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)

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ąpić 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żyć np. do inicjalizacji pola statycznego (tzn. pole wspólne dla

(5)

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 dodać metodę finalize, która zostanie wywołana przed zniszczeniem obiektu, jest to metoda zanegowana (depreciated).

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

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

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

};

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 przypisać 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ążyć operator OP (OP oznacza dowolny operator np. +) należy użyć 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, b, c;

c = a + b;

(9)

class Wektor {

private:

double x, y;

public:

Wektor(double x, double y) {this -> x = x; this -> y = y;}

Wektor(){x = 0; y = 0;}

Wektor operator+(const Wektor &) const;

Wektor operator*(double) const;

};

Wektor Wektor::operator+(const Wektor & W) const {

return Wektor(x + W.x, y + W.y);

}

Wektor Wektor::operator*(double n) const {

return Wektor(n*x, n*y);

}

int main() {

Wektor v1(1,2);

Wektor v2(-1,3);

Wektor v3 = v1 + v2;

Wektor v4 = v1*2; //Wektor v4 = 2*v1 –źle dlaczego???

return 0;

}

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

(10)

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

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

• Nie można zmienić priorytetu operatorów.

• Nie można tworzyć nowych symboli operatorów.

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

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

Przeciążanie operatorów - ograniczenia

+ - * / % ^& |

~=

*= ! = < > += -=

/=

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

<<=

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

-

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

new[] delete[]

(11)

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 Wektor {

public:

friend Wektor operator*(double n, const Wektor &);

};

Wektor operator*(double n, const Wektor & W) {

return Wektor(n*W.x, n*W.y);

}

int main() {

Z z1(1,2);

Z z2 = z1*2;

Z z3 = 3*z1;

return 0;

}

(12)

Przeciążenie operatora <<

W częsci deklaracyjnej klasy:

friend void operator<<(ostream &, const Wektor &);

Definicja:

void operator<<(ostream & os, const Wektor & W) {

os << ’[’ << W.x << ’,’ << W.y << ’]’;

}

Użycie opeatora <<

Wektor W(2,3);

cout << W;

Czy zadziała?

Wektor W1(2,3);

Wektor W2(3,4);

cout << W1 << W2;

(13)

Lepsze przeciążenie operatora <<

W częsci deklaracyjnej klasy:

friend ostream & operator<<(ostream &, const Wektor &);

Definicja:

ostream & operator<<(ostream & os, const Wektor & W) {

os << ’[’ << W.x << ’,’ << W.y << ’]’;

return os;

}

A teraz lepiej?

Wektor W1(2,3);

Wektor W2(3,4);

cout << W1 << W2;

(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:

Wektor operator-() const;

Definicja:

Wektor Wektor::operator-() const {

return Wektor(-x, -y);

}

(15)

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 zdefiniować klasę spokrewnioną z typem podstawowym lub z inną klasą aby możliwe było wykonywanie konwersji między nimi. Przy czym należy określić czy konwersja ma być automatyczna, czy poprzez rzutowanie typów.

(16)

Konstruktor jako funkcja konwersji

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

class Wektor {

private:

double x,y;

public:

Wektor(double x) { this -> x = x; this -> y = 0;}

};

int main() {

Wektor W = 10;//użycie Wektor(double) do niejawnej konwersji liczby 10 na typ Wektor

return 0;

}

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

(17)

explicit

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

class Wektor {

private:

double x, y;

public:

explicit Wektor(double x) {this->x = x; this->y=0;} //automatyczna // konwersja

//nie jest możliwa };

int main() {

//Wektor W = 10; źle!!!

Wektor W1 = Wektor(10); //działa, jawna konwersja return 0;

}

(18)

Funkcje konwersji

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

Wektor W(-1.2,0);

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

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

operator nazwaTypu();

nazwaTypu oznacza typ do jakiego ma zostać przekształcony obiekt.

Warunki jakie musi spełniać funkcja konwersji:

• funkcja konwersji jest metodą klasy

• funkcja konwersji nie zwraca żadnego typu

• funkcja konwersji nie posiada argumentów

(19)

Przykład funkcji konwersji

class Wektor {

private:

double x, y;

public:

Wektor(double _x) { x = _x; y = 0;}

Wektor(double _x, double _y) { x =_x; y = _y;}

operator double() {return x;}

};

int main() {

Wektor W(10);

double a = W; // niejawna konwersja

Wektor Z(-1,2);

double b = double(Z); //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 Wektor {

private:

double x, y;

public:

Wektor(double _x) {x = _x; y = 0;}

Wektor(double _x, double _y) { x =_x; y = _y;}

friend Wektor operator+(const Wektor & W, const Wektor & V) {

return Wektor(W.x + V.x, W.y + W.y);

}

operator double() {return x;}

};

int main() {

Wektor A, B;

A = Wektor(1,2) + Wektor(-1,-2);

b = Wektor(1,2) + 2;

return 0;

}

Problem z niejednoznacznością w instrukcji Wektor(1,2) + 2. Z jednej strony liczba 2 zostaje skonwertowana na typ Wektor i zostaje wywołana funkcja operator+(…), z drugiej natomiast następuje konwersja Wektor(1,2) na 1 (double), czyli występuje double + int.

(21)

Dziękuję za uwagę

Cytaty

Powiązane dokumenty

Określenie najmniejszej/największej wartości danej funkcji na podstawie jej wykresu jest stosunkowo proste.. Na prezentacji zajmiemy się określaniem najmniejszej/najwięszkej

Jak wykazały badania, nawet pozornie nieistotny dotyk (którego być może nawet nie zarejestrowaliśmy) zapewnia kelnerkom większe napiwki, sprzedawcy lepszą

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

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

Zasadniczym elementem programu są funkcje pole1, pole2 i pole3 - wszystkie funkcje mają jedno podstawowe zadanie - liczą powierzchnię prostokąta, którego jeden bok ma