• Nie Znaleziono Wyników

Programowanie obiektowe w C++

N/A
N/A
Protected

Academic year: 2021

Share "Programowanie obiektowe w C++"

Copied!
23
0
0

Pełen tekst

(1)

Programowanie obiektowe

w C++

Wykład 05 Temat wiodący:

Funkcje zaprzyjaźnione i operatory

this

n

dostępny w każdej niestatycznej metodzie

n

jest to wskaźnik do obiektu na rzecz którego wywołano metodę

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 zajmuje pamięci

n

nie wymaga deklarowania

(2)

this

person & person::timeTravel(int years) {

age += years;

return *this;

}

person Boss(40, „John”, „Kovalsky”);

( Boss.timeTravel(+20) ).timeTravel(-30)

friend

n

po polsku zaprzyjaźnianie

n

jest to deklaracja znosząca ograniczenia

dostępu do składowych klasy przez ściśle

określony kod

(3)

friend

class A {

friend C::theMethodNoArg();

// dotyczy konkretnej metody, tu bezargumentowej friend class B;

// dotyczy całej deklaracji klasy B

// również deklaracje wewnątrz B mają dostęp // do pól prywatnych A

friend fun();

// dotyczy konkretnej funkcji, tu bezargumentowej }

friend

n

zaprzyjaźnianie nie podlega dziedziczeniu

n

zaprzyjaźnianie nie jest przechodnie

n

deklarowane w dowolnej sekcji (nawet

prywatnej) klasy

(4)

friend

class M { friend void f() { …

} };

jest równoważne class M {

friend void f();

};

void f() { … }

Operatory

n

Co to jest operator?

a = b.val >> 4 + c * d[ e++ ]

(5)

Operatory

n

operator definiuje operacje wykonywane na swoich argumentach i typ wyniku takiej operacji – analogicznie do funkcji.

n

w C++ można definiować znaczenie

operatorów w przypadku wykorzystania ich do obiektów (przynajmniej jeden argument

obiektowy).

Operatory

n

Można przeciążać większość operatorów

n

Przeciążanie może poprawić wydajność

n

Nie ma obowiązku a zwykle nie ma i potrzeby

definiowania wielu operatorów, jeżeli operator

byłby sporadycznie używany to zaleca się

deklarowanie go jako metody bądź zwykłej

funkcji.

(6)

Operatory które można przeciążać

() [] ->

+ - ++ -- ! ~ * & new delete (typ) // jednoargumentowe ->*

* / % + -

<< >>

< <= > >=

== !=

&

^

|

&&

||

= += -= *= /= %= &= ^= |= <<= >>= // op. przypisania

Operatory

n

nie można przeciążać operatorów:

. .* :: ?: sizeof

n

operatory, które mogą być jedynie metodami klasy:

() [] -> new delete (typ) =

n

operatory domniemane, jeżeli nie zdefiniowane:

= &

(7)

Łączność

n

operatory przypisania i jednoargumentowe są prawostronnie łączne

a=b=c oznacza a=(b=c)

n

pozostałe lewostronnie

a+b+c oznacza (a+b)+c

Kolejność wartościowania

n

kolejność wartościowania argumentów niezdefiniowana, za wyjątkiem:

, || &&

n

„ , ”, „ || ”, „ && ” najpierw wartościowany lewy argument, potem prawy

n

dla operacji logicznych stosowane jest

skrócone wartościowanie.

(8)

Operatory a metody

n

inna składnia deklaracji, definicji i wywołania

n

operatory nie mogą mieć argumentów domyślnych (wyjątek: operator wywołania funkcji)

n

określona jest liczba argumentów (wyjątek: operator wywołania funkcji)

n

przyjęte jest zwyczajowe znaczenie operatorów

Operatory

n

Chcesz += to zdefiniuj go, nie wystarczy zdefiniować + i =

n

Chcesz ++ to zdefiniuj go,

kompilatorowi nie wtstarczy + i =

n

Zachowaj wierność zwyczajowemu znaczeniu operatorów

n niech jednoarg. & zwraca adres

n

Zachowaj konsekwencję

n ++i; i++; i+=1; i=i+1; // powinny dawać taki sam efekt.

(9)

Operatory jako metody

n

pierwszy argument będzie obiektem klasy

n

metoda ma mniej argumentów niż operator

n

deklarowanie

int point::operator<=(point);

n

wywoływanie

point point1, point2;

int i= point1 <= point2;

int j= point1.operator<=(point2);

Operatory jako funkcje

n przynajmniej jeden z argumentów musi być obiektem

n wszystkie argumenty są parametrami funkcji operatorowej

n nie ma this

n deklarowanie

int operator<=(point, point);

n wywoływanie

point point1, point2;

int i = point1 <= point2;

int j= operator<=(point1, point2);

(10)

Konwencja dla operatorów ++ i --

n

operator++() i operator--() są przedrostkowe

++i --i

n

operatory przyrostkowe deklarujemy i

definiujemy z dokładnie jednym nienazwanym parametrem int:

operator++(int); operator--(int);

i++ i--

Konwencja dla operatorów ++ i --

class X { public:

X& operator++(); // prefix ++a X operator++(int); // postfix a++

};

class Y { public:

};

Y& operator++(Y&); // prefix ++b

Y operator++(Y&, int); // postfix b++

(11)

Konwencja dla operatorów ++ i --

void f(X a, Y b) {

++a; // a.operator++();

a++; // a.operator++(0);

++b; // operator++(b);

b++; // operator++(b, 0);

a.operator++(); // odpowiada ++a;

a.operator++(0); // odpowiada a++;

operator++(b); // odpowiada ++b;

operator++(b, 0); // odpowiada b++;

}

Operatory

class X // składowe z niejawnym this {

X* operator&(); // przedrostkowy, jednoargumentowy &

X operator&(X); // dwuargumentowy X operator++(int); // przyrostkowy

// X operator&(X, X); // blad trzyargumentoowy // X operator/(); // blad jednoarg.

};

// funkcje globalne, bez this, często zaprzyjazniane X operator-(X); // przedrostkowy X operator-(X, X); // dwuargumentowy

X operator--(X&, int); // przyrostkowy dekrementacja // X operator-(X, X, X); // blad trzyargumentoowy // X operator/(); // blad jednoarg.

(12)

Operator przypisania

n

Musi być zdefiniowany jako metoda (niestatyczna) a nie jako funkcja

T& T::operator=(const T &)

n Nie ma listy inicjalizacyjnej bo to nie jest konstruktor !!!

Operator przypisania

person& person::operator=(const person &o) {

if (this == &o) // Tom = Tom;

return *this;

delete name;

name=new char[strlen(o.name) + 1];

strcpy(name, o.name);

delete lastName;

lastName=new char[strlen(o.lastName) + 1];

strcpy(lastName, o.lastName);

age=o.age;

return *this; // zwraca obiekt, będzie przekazany przez referencje }

(13)

Operator przypisania

n dlaczego operator przypisania zwraca referencję?

n chcemy napisać o1=o2=o3;

person& operator=(const person&);

n przypisania do: o2 z: o3

n operator przypisania do: o1 z: (o2=o3)

person operator=(const person&);

n przypisania do: o2 z: o3

n konstruktor kopiujący (powstaje tymczasowy obiekt t1)

n operator przypisania do: o1 z: t1

n konstruktor kopiujący (powstaje tymczasowy obiekt t2)

n destruktor t2

n destruktor t1

Operator przypisania

n

dlaczego operator przypisania zwraca referencję?

n

chcemy napisac o1=o2=o3;

n

void operator=(const person&);

n

można o2=o3;

n

nie da się o1=o2=o3;

(14)

Operator przypisania generowany automatycznie

n

jeżeli się nie zdefiniuje operatora przypisania to kompilator dostarczy własny operator przypisania, który skopiuje obiekt pole po polu.

n

nie będzie działał dla pól stałych

n

nie będzie działał dla dla referencji

n

dla wskaźników skopiuje wartość

Operatory strumieniowe

n

Wejście

friend istream & operator >>(istream &, T &);

n

Wyjście

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

(15)

Operatory strumieniowe

class point {

int x, y;

public:

friend istream &operator >>(istream & s, point & p);

friend ostream &operator <<(ostream & s, const point &);

}

point p, p2;

cin>>p;

cin>>p>>p2;

cout<<p2<<p;

Operatory strumieniowe

istream & operator >>(istream & s, point & p) {

s >> p.x >> p.y;

return s;

}

ostream &operator <<(ostream & s, const point & p) {

s << p.x << p.y;

return s;

}

(16)

Operator wyłuskania

T* operator->();

n

T t;

n

t->m jest interpretowane jako:

( t.operator-> )->m

n

można używać zmiennej jak wskaźnika

n

jeżeli operator-> nie zwraca wskaźnika to

n

można użyć go: a=t.operator->();

n

nie można: a=t->; bo to błąd składniowy

Operator wywołania funkcji

wynik T::operator()(args);

n

Dowolna liczba argumentów, można przeciążać

n

Wyjątek: tylko ten operator może mieć domyślne argumenty

n

potem można obiekt używać tak, jak funkcję

T ob;

ob();

n

stosuje się gdy

n nie istnieje odpowiedni operator, a by się przydał

n jest jakś metoda dominująca dla klasy (będzie bez nazwy ale łatwiejsza do wywołania – np. klasa counter i metoda inkrementacja)

(17)

operator indeksowania

wynik T::operator[](index);

n

typy wyniku i indeksu dowolne ale zdrowy rozsądek nakazuje indeks całkowity

n

można tworzyć inteligentne agregaty,

Operator konwersji

n

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

wywoływany

T::operator X();

n

operator konwersji klasy T do typu X:

n

nie określa się jawnie typu wartości zwracanej,

n

jest bezargumentowy

n

musi być metodą (nie funkcją)

(18)

Operator konwersji

n

np.:

person::operator int();

n

konwersja może następować niejawnie:

person o;

int i=o+5;

n

W wyrażeniach

n dla danego obiektu konwersja jest stosowana tylko jeżeli jest konieczna,

n może być tylko jedna konwersja danego składnika definiowana przez użytkownika

n nie może być niejednoznaczności

Operator konwersji

n

np. dla klas: A, B i C, zdefiniowano operatory konwersji B::operator A() i C::operator B();

int f(A a);

A a; B b; C c;

f(a); // ok..

f(b); // f(A(b))

// f(c); // nie ma operatora C::operator A()

// nie zrobi się niejawnie f(A(B(c)))

// (2 konwersje skladnika)

(19)

Operator konwersji

n

aby przekształcić obiekt jednego typu w obiekt innego typu można jawnie użyć konstruktora, ale:

n

nie da się przeprowadzić przekształcenia do typu podstawowego (np. int, bo to nie klasa),

n

nie da się przekształcić do starej klasy bez modyfikowania jej definicji.

Przykład

class counter {

int cnt;

public:

counter(int = 0):cnt(0){};

int operator()(int =1); // increment by arg. or by 1 operator int(); // convert to int

operator double(); // convert to double counter &operator+=(int);

friend counter operator+(int, counter); // int+counter, friend counter operator+(int); // counter+int

int operator++(); // ++counter

int operator++(int); // counter++

};

(20)

Przykład

inline int counter::operator ()(int i) {

return cnt+=i;

};

inline counter::operator int() {

return cnt;

};

inline counter::operator double() {

return double(cnt);

};

Przykład

counter & counter::operator +=(int i) {

cnt+=i; // operator()(i); (*this)(i)

return *this;

};

counter operator+(int i, counter l) {

l+=i; // zaprzyjaźnianie nie było konieczne return l;

};

(21)

Przykład

counter counter::operator +(int i) {

counter l=*this;

l+=i;

return l;

};

int counter::operator ++() {

cnt+=1;

return cnt;

};

int counter::operator ++(int) {

int ret=cnt;

cnt+=1;

return ret;

};

Przykład

class complex {

double re, im;

public:

complex(double re, double im):re(re), im(im) {};

complex & operator = (const complex &c);

complex & operator += (const complex &c);

complex operator + (const complex &);

friend complex & operator -= (complex &, const complex &);

friend complex operator - (const complex &, const complex &);

friend istream & operator >>(istream &, complex &);

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

};

(22)

Przykład

complex & complex::operator = (const complex &c) {

re=c.re;

im=c.im;

return *this;

};

Przykład

complex & complex::operator += (const complex &c) {

re+=c.re;

im+=c.im;

return *this;

};

inline complex complex::operator + (const complex & c) {

return complex(re+c.re, im+c.im); // complex(double, double) }

(23)

Przykład

inline complex & operator-=(complex & c1, const complex & c2) //friend {

c1.re-=c2.re;

c1.im-=c2.im;

return c1;

}

complex operator-(const complex & c1, const complex & c2) //friend {

complex c=c1;

c-=c2;

return c;

}

Przykład

istream & operator >>(istream & s, complex & c) {

s >> c.re >> c.im;

return s;

}

ostream &operator <<(ostream & s, const complex & c) {

s << c.re << c.im;

return s;

}

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¦

Składowe publiczne klasy bazowej są odziedziczone jako publiczne, a składowe chronione jako chronione.. Dziedziczenie chronione - składowe publiczne są dziedziczone jako

n Skojarzenie referencji do klasy bazowej z obiektem klasy potomnej jest dozwolone przy dziedziczeniu publicznym. n uwagi (konwersje wskaźników

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

będzie ciągiem niezależ- nych zmiennych losowych o jednakowym rozkładzie ze skończoną wartością oczekiwaną i skończoną,

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