POwyklad10

background image

-1-

Programowanie Obiektowe

(j

ę

zyk C++)

Wykład 10.

-2-

FUNKCJE

WZORCOWE

-3-

Funkcje wzorcowe – wprowadzenie (1)

int max ( int a, int b )

{ return a>b ? a : b; }

Aby mie

ć

analogiczn

ą

funkcj

ę

działaj

ą

c

ą

na danych typu double

w j

ę

z. C musimy wprowadzi

ć

dla niej odr

ę

bny identyfikator:

double d_max ( double a, double b )

{ return a>b ? a : b; }

W j

ę

z. C++ mamy mo

ż

liwo

ść

przeci

ąż

ania identyfikatorów funkcji,

wi

ę

c mo

ż

emy u

ż

y

ć

tej samej nazwy:

double max ( double a, double b )

{ return a>b ? a : b; }

-4-

Funkcje wzorcowe – wprowadzenie (2)

W obu j

ę

zykach C i C++ mo

ż

emy si

ę

posłu

ż

y

ć

w tej sytuacji, jako pewnego

rodzaju alternatyw

ą

, tzw. makrodefinicj

ą

:

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

co daje nam mo

ż

liwo

ść

posługiwania si

ę

wyra

ż

eniami przypominaj

ą

cymi

wywołania 2-argumentowej funkcji o tej samej nazwie dla argumentów

ż

nych typów:

int i, j, k;

double A, B, C;

……………………….

k = MAX(2*i+5, j+i);

C = MAX(3.4+A, 7*B);

Ale wi

ąż

e si

ę

to z szeregiem niedogodno

ś

ci i pułapek.

background image

-5-

Funkcje wzorcowe (1)

W j

ę

zyku C++ mo

ż

emy si

ę

posłu

ż

y

ć

konstrukcj

ą

wzorca (szablonu) funkcji:

template < class TYPE >

TYPE max (TYPE a, TYPE b )

{ return a>b ? a : b; }

gdzie TYPE mo

ż

e by

ć

typem wbudowanym lub definiowanym w programie.

<class TYPE> nazywa si

ę

tu opisem parametru wzorca.

Maj

ą

c tak zdefiniowany szablon, mo

ż

e go wykorzysta

ć

do konkretyzacji funkcji

przyjmuj

ą

cych argumenty potrzebnych typów, n.p.:

int main ( )

{

int i, j, k; double A, B, C;

k = max( i, j + 5 );

// skonkretyzuje

int max ( int, int );

C = max( A, B + 0.5 );

// skonkretyzuje

double max ( double, double );

A = max ( B, 10 );

//

Ą

D!

– konieczna

ś

cisła zgodno

ść

typów

-6-

Funkcje wzorcowe (2)

1. Wzorzec funkcji mo

ż

e mie

ć

wi

ę

cej parametrów (ale nie mniej ni

ż

jeden!).

2. Ka

ż

dy opis parametru wzorca składa si

ę

ze słowa kluczowego class

( lub typename ) oraz wybranego identyfikatora ( nazwy parametru ).

3. Na li

ś

cie parametrów wzorca ka

ż

dy identyfikator mo

ż

e wyst

ą

pi

ć

tylko raz.

4. Parametr wzorca staje si

ę

specyfikatorem typu, którego mo

ż

na u

ż

ywa

ć

w pozostałej cz

ęś

ci definicji funkcji wzorcowej ( n.p. w deklaracjach zmiennych

lokalnych, operacjach rzutowania e.t.c. )

5. Ka

ż

dy parametr wzorca musi wyst

ą

pi

ć

co najmniej jeden raz w sygnaturze

funkcji wzorcowej.

-7-

Funkcje wzorcowe (3)

Wychodz

ą

c od definicji funkcji:

double

Summa (

double

tab[ ], int size )

{

double

sum = 0;

for ( int i = 0; i < size; i++ ) sum += tab[ i ];

return sum;

}

Mo

ż

emy łatwo utworzy

ć

jednoparametrowy wzorzec:

template < class

T

>

T

Summa (

T

tab[ ], int size )

{

T

sum = 0;

for ( int i = 0; i < size; i++ ) sum += tab[ i ];

return sum;

}

-8-

Funkcje wzorcowe (4)

Wychodz

ą

c od definicji tej samej funkcji:

double

Summa (

double

tab[ ],

int

size )

{

double

sum = 0;

for (

int

i = 0; i < size; i++ ) sum += tab[ i ];

return sum;

}

Mo

ż

emy równie łatwo utworzy

ć

wzorzec dwuparametrowy:

template < class

T

, class

S

>

T

Summa (

T

tab[ ],

S

size )

{

T

sum = 0;

for (

S

i = 0; i < size; i++ ) sum += tab[ i ];

return sum;

}

background image

-9-

Funkcje wzorcowe (5)

Rozró

ż

nianie przeci

ąż

onych funkcji wzorca i innych funkcji

o tej samej nazwie jest realizowane wg schematu:

1. Je

ż

eli istnieje funkcja o deskryptorze dokładnie pasuj

ą

cym do wywołania,

to j

ą

wywołaj.

2. Je

ż

eli istnieje wzorzec pozwalaj

ą

cy zrealizowa

ć

(skonkretyzowa

ć

)

funkcj

ę

o dokładnie pasuj

ą

cym deskryptorze, to u

ż

yj tego wzorca.

3. Je

ż

eli istnieje funkcja przeci

ąż

ona, któr

ą

mo

ż

na dopasowa

ć

wg zwykłych

reguł (tzn. z zastosowaniem konwersji), to jej u

ż

yj.

4. Je

ż

eli

ż

adnego z punktów 1., 2., 3. nie dało si

ę

zastosowa

ć

, to wywołanie

zostanie uznane za bł

ę

dne.

-10-

Funkcje wzorcowe (6)

Rozpatrzmy przykład:

template < class T >
T max ( T a, T b )
{ return a>b ? a : b; }

void test ( )
{

int a, b;
char c, d;

//int A = max( a, c ); //

Ą

D! – nie mo

ż

na wygenerowa

ć

int max(int,char);

int B = max( a, b );

// int max(int, int); - niejawne utworzenie egzemplarza

char C = max( c, d );

// char max(char,char); niejawne utworzenie egz.

int D = max( c, d );

// char max(char, char); typ zwracanej warto

ś

ci nie

// nie nale

ż

y do deskryptora

-11-

Funkcje wzorcowe (7)

Ale:

template < class T >

T max ( T a, T b )

{ return a>b ? a : b; }

int max ( int, int );

// deklaracja funkcji (by

ć

mo

ż

e zewn

ę

trznej)

void test ( )

{

int a, b;

char c, d;

int A = max( a, c );

//

O.K.!

– u

ż

yte b

ę

dzie int max(int,int);

// zgodnie z reguł

ą

3.

-12-

Funkcje wzorcowe (8)

Inne mo

ż

liwo

ś

ci:

template < class T >

T max ( T a, T b )

{ return a>b ? a : b; }

template int max ( int, int );

// wymuszone (jawne) utworzenie egzemplarza

// (tylko w zasi

ę

gu definicji szablonu)

template< >

char max ( char a, char b )

{ return a>b ? a : 0; }

// dostarczenie szczególnej definicji egzemplarza!

// (tylko w zasi

ę

gu definicji szablonu)

background image

-13-

WZORCE KLAS

( SZABLONY KLAS )

-14-

Klasy wzorcowe - wprowadzenie (1)

W j

ę

zyk C++ mo

ż

emy si

ę

posłu

ż

y

ć

równie

ż

konstrukcj

ą

wzorca (szablonu)

klasy. Podobnie jak w przypadku wzorców funkcji najpro

ś

ciej jest przyj

ąć

za

punkt wyj

ś

cia jak

ąś

konkretn

ą

klas

ę

(dobrze wcze

ś

niej sprawdzon

ą

w praktyce).

#define CSTACKSIZE 100

class CharStack
{

char tab [ CSTACKSIZE ];
int size, top;

public:

CharStack ( ) { size = CSTACKSIZE; top = 0; }
void Push ( char e ) { tab [ top++ ] = e; }
char Pop ( ) { return tab [ --top ]; }
char Top ( ) { return tab [ top - 1 ]; }
int Size ( ) const { return size; }
int Used ( ) const { return top; }
int Place ( ) const { return size - top; }
void Display ( ) const

{ cout << endl; for ( int i = 0; i < top; ++i ) cout << tab [ i ] << " "; }

};

-15-

Przy okazji …

(1)

Zauwa

ż

my,

ż

e dotychczas w definicji klasy podawali

ś

my zwykle jedynie

deklaracje metod. Ich definicje umieszczali

ś

my na zewn

ą

trz, najcz

ęś

ciej

w tzw. pliku implementacyjnym.

Tym razem definicje metod zostały podane od razu w definicji klasy.

Jaka ró

ż

nica?

1. Metoda definiowana w ciele definicji klasy otrzymuje domy

ś

lnie

atrybut inline. Metody (i zwykłe funcje) z takim atrybutem nie maj

ą

jednokrotnie wygenerowanego kodu o okre

ś

lonym adresie, który

jest uruchamiany przy ka

ż

dym odwołaniu do metody (funkcji).

Zamiast tego kompilator mo

ż

e (ale nie musi!) generowa

ć

kod metody

(funkcji) w ka

ż

dym miejscu jej wywołania. Mo

ż

e to da

ć

zysk na czasie

wykonania programu, chocia

ż

zwykle zwi

ę

ksza jego obj

ę

to

ść

.

UWAGA: Funkcja z atrybutem inline nazywa si

ę

te

ż

… funkcj

ą

rozwijaln

ą

albo … funkcj

ą

otwart

ą

-16-

Przy okazji …

(2)

2.

Atrybut inline mo

ż

e by

ć

podany jawnie a tre

ść

metody na zewn

ą

trz

definicji klasy, ale wtedy nale

ż

y j

ą

poda

ć

w pliku header'owym (.h)

klasy, a nie w pliku implementacyjnym.

Wynika to z faktu,

ż

e tre

ść

takiej metody potrzebna jest kompilatorowi

w ka

ż

dym miejscu jej wywołania.

3. U

ż

ycie odr

ę

bnego (niezale

ż

nie kompilowanego) pliku zawieraj

ą

cego

definicje metod ( posiadaj

ą

cych atrybut extern ) pozwala ukry

ć

szczegóły implementacyjne. U

ż

ytkownik naszej klasy b

ę

dzie korzystał

tylko z pliku nagłówkowego w postaci

ź

ródłowej (.h) i skompilowanego

pliku zawieraj

ą

cego kod metod (.obj). A wi

ę

c np. wcale nie musi

wiedzie

ć

, jaki algorytm zastosowali

ś

my do realizacji konkretnych

oblicze

ń

numerycznych

.

background image

-17-

Przy okazji …

(3)

// charstack.h

……………………………
class CharStack
{

……………………………

void Push ( char e ) { tab [ top++ ] = e; }
inline char Pop ( );
char Top ( ) const;

……………………………

};

……………………………

inline char CharStack :: Pop ( ) { return tab [ --top ]; }

……………………………..…………………………………..….… end of charstack.h

// charstack.cpp
……………………………

char CharStack :: Top ( ) const { return tab [ top - 1 ]; }

……………………………

W powy

ż

szym przykładzie metody:

Push i Pop maj

ą

atybut inline ( Push – domy

ś

lnie, Pop – jawnie )

metoda

Top ma atrybut extern.

-18-

Klasy wzorcowe - wprowadzenie (2)

Poszukajmy 'kandydatów' na parametry.
Zaznaczyłem je na kolorowo.
Oczywi

ś

cie nie wszystkie mo

ż

liwo

ś

ci musimy wykorzysta

ć

.

#define

CSTACKSIZE

100

class CharStack
{

char

tab [

CSTACKSIZE

];

int

size, top;

public:

CharStack ( ) { size =

CSTACKSIZE

; top = 0; }

void Push (

char

e ) { tab [ top++ ] = e; }

char

Pop ( ) { return tab [ --top ]; }

char

Top ( ) { return tab [ top - 1 ]; }

int

Size ( ) const { return size; }

int

Used ( ) const { return top; }

int

Place ( ) const { return size – top; }

void Display ( ) const

{ cout << endl; for (

int

i = 0; i < top; ++i ) cout << tab [ i ] << " "; }

};

-19-

Klasy wzorcowe (1)

A tak mo

ż

e wygl

ą

da

ć

nasz szablon:

#define STACKSIZE 100

template < class

T

= char, int

S

= STACKSIZE >

class Stack
{

T

tab [

S

];

int size, top;

public:

Stack ( ) { size =

S

; top = 0; }

void Push (

T

e ) { tab [ top++ ] = e; }

T

Pop ( ) { return tab [ --top ]; }

T

Top ( ) { return tab [ top - 1 ]; }

int Size ( ) const { return size; }
int Used ( ) const { return top; }
int Place ( ) const { return size - top; }
void Display ( ) const

{ cout << endl; for ( int i = 0; i < top; ++i ) cout << tab [ i ] << " "; }

};
#undef STACKSIZE

-20-

Klasy wzorcowe (2)

I funkcja main:

int main ( )
{

const int k = 10;
Stack<> S0; Stack<int> S1, S2; Stack<int,10> S3;
Stack<double> S4; Stack<double,55> S5;
Stack<double,k+5> S6; Stack<double,k+45> S7;

cout << endl << S0.Size() << endl << S1.Size() << endl << S3.Size();
cout << endl << S4.Size() << endl << S5.Size() << endl << S6.Size();

S1.Push(10); S1.Push(11); S1.Push(12); S1.Push(13); S1.Push(14);
S1.Display();
S2 = S1;
S1.Pop(); S1.Pop();
S1.Display(); S2.Display();

//S3 = S1; //

Ą

D!

//S4 = S1; //

Ą

D! ale S7 = S5; O.K.

while ( S1.Place() && S2.Used() ) S1.Push( S2.Pop() );
S1.Display();

}

background image

-21-

Klasy wzorcowe (3)

Program wy

ś

wietli (po zakomentowaniu wierszy z bł

ę

dami):

100

100

10

100

55

15

10 11 12 13 14

10 11 12

10 11 12 13 14

10 11 12 14 13 12 11 10

-22-

Klasy wzorcowe (4)

CharStack St;

// obiekt St jest typu CharStack

Stack<> S0;

// obiekt S0 jest typu Stack<char, 100>

Stack<int> S1, S2;

// obiekty S1 i S2 s

ą

typu Stack<int, 100>

Stack<int,10> S3;

// obiekt S3 jest typu Stack<int, 10>

Stack<double> S4;

// obiekt S4 jest typu Stack<double, 100>

Stack<double,k+5> S6;

// obiekt S6 jest typu Stack<double, 15>

Stack<CMPLX> SC;

// obiekt SC jest typu Stack<CMPLX, 100>

-23-

Klasy wzorcowe (1a)

Kusz

ą

ca (niebezpieczna) alternatywa:

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ];
T *p;
int size;

public:

Stack ( ) { p = tab; size = S; }
void Push ( T e ) { *p++ = e; }
T Pop ( ) { return *--p; }
T Top ( ) { return *(p – 1); }
int Size ( ) const { return size; }
int Used ( ) const { return p – tab; }
int Place ( ) const { return size – (p – tab); }
void Display ( ) const { T *r = tab; while ( r < p ) cout << *r++ << " "; }

};

Na czym polega niebezpiecze

ń

stwo

i jak mu zaradzi

ć

?

-24-

Klasy wzorcowe (5)

1. Wzorzec klasy mo

ż

e mie

ć

wi

ę

cej parametrów (ale nie mniej ni

ż

jeden!).

2. Opis parametru wzorca składa si

ę

ze słowa kluczowego class

(ew. typename) lub nazwy typu wbudowanego oraz wybranego
identyfikatora (nazwy parametru). Dla parametrów mo

ż

na okre

ś

la

ć

warto

ś

ci domy

ś

lne.

3. Na li

ś

cie parametrów wzorca ka

ż

dy parametr mo

ż

e wyst

ą

pi

ć

tylko raz.

4. Parametr wzorca poprzedzony słowem kluczowym class (ew. typename)

staje si

ę

specyfikatorem typu, którego mo

ż

na u

ż

ywa

ć

w pozostałej cz

ęś

ci

definicji klasy wzorcowej (n.p. w deklaracjach pól, specyfikacjach parametrów
metod et c.).

5. Parametr wzorca poprzedzony nazw

ą

typu wbudowanego staje si

ę

stał

ą

,

której mo

ż

na u

ż

ywa

ć

np. do zapisu rozmiarów tablic, inicjowania warto

ś

ci

zmiennych et c.

background image

-25-

Klasy wzorcowe (6)

Zobaczmy inny wariant zapisu szablonu.

// stack.h

#include <iostream>
using namespace std;

#define STACKSIZE 1000

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;
void Display ( ) const;

};

...................................................

-26-

Klasy wzorcowe (7)

W dalszym ci

ą

gu pliku stack.h podajemy szablony metod:

...................................................

template < class T, int S >
Stack< T, S > :: Stack ( ) { p = q = tab; size = S; }

template < class T, int S >
Stack< T, S >& Stack< T, S > :: operator= ( const Stack& rhs )

{ p = q; for ( T* r = rhs.q; r < rhs.p; *p++ = *r++ ); return *this; }

template < class T, int S >
void Stack< T, S > :: Push ( T e ) { *p++ = e; }

template < class T, int S >
T Stack< T ,S > :: Pop ( ) { return *--p; }

template < class T, int S >
T Stack< T, S > :: Top ( ) const { return *(p - 1); }

template < class T, int S >
int Stack< T, S > :: Size ( ) const { return size; }

...................................................

-27-

Klasy wzorcowe (8)

i dalej w pliku stack.h:

...................................................

template < class T, int S >

int Stack< T, S > :: Used ( ) const { return p - q; }

template < class T, int S >

int Stack< T, S > :: Place ( ) const { return size - (p - q); }

template < class T, int S >

void Stack< T, S > :: Display ( ) const

{ T* r = q; while ( r < p ) cout << *r++ << " "; }

#undef STACKSIZE

UWAGA! UWAGA!

To wszystko powinno by

ć

podane w pliku stack.h.

Dla klas wzorcowych nie tworzymy odr

ę

bnych plików implementacyjnych *.cpp.

-28-

funkcje zaprzyja

ź

nione

we wzorcach klas

background image

-29-

Klasy wzorcowe (6)

W rozpatrywanym wcze

ś

niej wzorcu klasy

...................................................

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;

void Display ( ) const;

};

...................................................

chcemy zast

ą

pi

ć

metod

ę

Display zaprzyja

ź

nionym operatorem << .

-30-

Wzorce i friend (1)

Próba zrealizowania tego zadania w nast

ę

puj

ą

cy sposób:

...................................................

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;

friend ostream& operator << (ostream&, const Stack<T,S>&);

};

...................................................

template < class T, int S>
ostream& operator << (ostream& out, const Stack<T,S>& st)
{ T* r = st.q; while ( r < st.p ) cout << *r++ << " ";

return out;

}

nie da oczekiwanego efektu. Operator << nie zostanie wygenerowany.

-31-

Wzorce i friend (2)

Poprawny efekt otrzymamy pisz

ą

c:

...................................................

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;

template < class Q, int R>

friend ostream& operator << (ostream&, const Stack<Q,R>&);

};

...................................................

template < class T, int S>
ostream& operator << (ostream& out, const Stack<T,S>& st)
{ T* r = st.q; while ( r < st.p ) out << *r++ << " ";

return out;

}

-32-

Wzorce i friend (3)

a nawet:

...................................................

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;

template < class Q, int R>

friend ostream& operator << (ostream&,

const Stack&

);

};

...................................................

template < class T, int S>
ostream& operator << (ostream& out, const Stack<T,S>& st)
{ T* r = st.q; while ( r < st.p ) out << *r++ << " ";

return out;

}

background image

-33-

Wzorce i friend (4)

Jeszcze innym rozwi

ą

zaniem jest:

...................................................

template < class T = char, int S = STACKSIZE >
class Stack
{

T tab [ S ]; T *p, *q; int size;

public:

Stack ( );
Stack& operator= ( const Stack& );
void Push ( T ); T Pop ( ); T Top ( ) const;
int Size ( ) const; int Used ( ) const; int Place ( ) const;

friend ostream& operator << (ostream& out, const Stack& st)

{ T* r = st.q; while ( r < st.p ) out << *r++ << " ";

return out;

}

};

...................................................

-34-

Koniec wykładu 10.


Wyszukiwarka

Podobne podstrony:
POwyklad2
POwyklad9
POwyklad3
POwyklad5
Pedagogika przewlekle chorych powyklejane, Ważne dla sudenta, Studia pedagogika
POwyklad9
POwyklad2
POwyklad1
POwyklad6
POwyklad4
POwyklad15 planowany
POwyklad4
POwyklad3
POwyklad7
POwyklad15 planowany
POwyklad1
POwyklad6
POwyklad2
POwyklad9

więcej podobnych podstron