• Nie Znaleziono Wyników

Programowanie obiektowe w C++

N/A
N/A
Protected

Academic year: 2021

Share "Programowanie obiektowe w C++"

Copied!
30
0
0

Pełen tekst

(1)

Programowanie obiektowe

w C++

Wykład 05a Temat wiodący:

Static, const, volatile

static, const,

volatile

(2)

Static – zmienne klasowe

n

zmienna klasowa o atrybucie static jest

wspólna dla wszystkich obiektów danej klasy

n

istnieje nawet gdy nie ma żadnego obiektu

n

można się do niej odwoływać za pomocą operatora zakresu

class ST {

static int s; // to jest tylko deklaracja int i;

public:

static int ps; // jak wyżej };

int ST::s=0, // to definicja ST::ps=0;

n deklaruje się w klasie, ale definiuje (już bez słowa kluczowego static) poza klasą

n można zdefiniować tylko raz

n dla bibliotek: definicje do *.cpp a dekl. klasy do *.h (nie trzeba i nie mozna dodatkowych extern ST::s)

(3)

Static – metody

n

mogą wtedy być wołane, nawet gdy nie ma obiektów (za pomocą operatora zakresu)

n

mogą korzystać jedynie ze statycznych pól i metod klasy

n

nie ma wskaźnika this

Static – metody

class ST {

static int s;

int i;

public:

static int ps;

ST(int i=0) :i(i) { s++;

static void ile_nas() {

cout << s << "\n";

}

void wszystko() // nie wolno static !!!

{

cout << s << " " << i cout << " " << ps << "\n";

} };

(4)

void main() {

int i;

// i=s; // nie ma ::s ani ::ps // i=ps;

// i=ST::s; // private i=ST::ps;

ST::ile_nas();

// ST::wszystko(); // niestatyczna metoda klasy ST a;

ST::ile_nas();

a.ile_nas();

a.wszystko();

}

const, volatile

n

const --- stałe

const int ci =7;

n

volatile --- ulotne

volatile int vi=8;

i++;

i--;

i=i+0;

(5)

const, volatile

n

obiekty również mogą być stałe bądź ulotne

n

na rzecz obiektów stałych bądź ulotnyvh można aktywować jedynie metody uprzywilejowane

n

metody uprzywilejowane definiujemy podając po nawiasie zamykającym listę argumentów słowa kluczowe const i/lub volatile.

n

UWAGA: uprzywilejowanie metody może powodować jej przeciążenie:

int f(); int f() const;

class CV { int i;

public:

CV(int i=1) :i(i) { };

void out() {

cout << i;

}

void out() const volatile {

cout << i;

}

void inc() { i++;

}

void inc_c() const {

// i++; // i jest const }

void inc_v() volatile {

i++;

}

void inc_cv() const volatile {

// i++;

}

(6)

void main() {

CV cv;

const CV cvc;

volatile CV cvv;

const volatile CV cvcv;

cv.inc();

cv.inc_c();

cv.inc_v();

cv.inc_cv();

// cvc.inc();

cvc.inc_c();

// cvc.inc_v();

cvc.inc_cv();

// cvv.inc();

// cvv.inc_c();

cvv.inc_v();

cvv.inc_cv();

// cvcv.inc();

// cvcv.inc_c();

// cvcv.inc_v();

cvcv.inc_cv();

cv.out(); //CV::out();

cvc.out(); //CV::out() const volatile;

cvv.out();

cvcv.out();

}

const, volatile

n

metody uprzywilejowane nie mogą być static.

n

konstruktor ani destruktor nie może być const volatile ani static.

n

deklarowanie metod uprzywilejowanych nie może prowadzić do niejednoznaczności

int f() const; // int f() const volatile;

(7)

Przykład: pola const i static

class info {

static int cntr, // licznik istniejacych obiektow serial; // statyczna zmienna pomocnicza const int nr; // unikalny numer seryjny obiektu public:

info()

info(const info & i);

~info();

info & operator=(const info &p);

};

int info::cntr=0, // static info::serial=0; // static

info::info() :nr(++serial) {

cntr++;

cout << "\nkonstruktor bezparametrowy obiektu nr" << nr;

cout << " jest " << cntr << " obiektow info";

cout.flush();

}

info::info(const info & i) :nr(++serial)

{ cntr++;

cout << "\nkonstruktor kopiujacy obiektu nr" << nr << " z obiektu " << i.nr;

cout << " jest " << cntr << " obiektow info";

cout.flush();

}

(8)

info::~info() {

cntr--;

cout << "\ndestruktor obiektu nr" << nr;

cout << " zostanie " << cntr << " obiektow info";

cout.flush();

}

info & info::operator=(const info &p) {

cout << "\noperator przypisania do obiektu nr " << nr;

cout << " z obiektu nr" << p.nr;

cout << " jest " << cntr << " obiektow info";

cout.flush();

return *this;

}

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

(9)

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

(10)

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

(11)

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++ ]

(12)

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.

(13)

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:

(14)

Łą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.

(15)

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ę

(16)

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

(17)

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

(18)

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.

(19)

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

(20)

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;

(21)

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

(22)

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;

}

(23)

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

(24)

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

(25)

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

(26)

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++

};

(27)

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;

};

(28)

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

};

(29)

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

(30)

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

Etap projektowania: należy zmodyfikować diagramy sekwencji, wstawiając linie życia kolekcji wszędzie tam, gdzie zachodzi potrzeba wykonanie metody dla wielu obiektów, a

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

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

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

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

Jednak język programowania wysokiego poziomu nie jest jasny dla komputera, który rozumie jedynie ciągi zer i jedynek. Dlatego musimy posłużyć się aplikacją,