Biblioteka STL funktory

background image

1

Biblioteka STL - funktory

Andrzej Walczak

Pierwsza edycja-WAT 2006

background image

2

Typy funkcyjne

• Zanim

zajmiemy

się

bardziej

skomplikowanymi obiektami jakimi są
funktory,

wyjaśnijmy

kilka

faktów

dotyczących funkcji i wskaźników do nich.
Funkcje nie są obiektami. Nie można ich
kopiować ani przypisywać do siebie:

void f(double x) {};

void g(double) = f; niedozwolonevoid
h(double);h=f; niedozwolone

background image

3

Typy funkcyjne

• Funkcje posiadają jednak typ. Typ funkcji

(nazywany typem funkcyjnym) jest określony
przez typ jej wartości zwracanej i typy jej
argumentów. Typy funkcyjne możemy używać np.
w poleceniu typedef. Wyrażenie:

• typedef void f_type(double) definiuje f_type jako

typ funkcji o jednym argumencie typu double i nie
zwracającej żadnej wartości. Taki typ ma jednak
ograniczone zastosowanie, możemy go używać do
deklarowania, ale nie definiowania innych funkcji:

• f_type g;

background image

4

Typy funkcyjne

• Typ funkcyjny może też być użyty jako

parametr szablonu:

• template<typename F> Function {F

_fun;};Function<void (double)>

• Niewiele jednak będziemy mieli pożytku z pola

_fun, bo jak już widzieliśmy, nie będziemy w
stanie nic do niego przypisać ani go
zainicjalizować.

background image

5

Typy funkcyjne

• Możemy również używać typów funkcyjnych w

deklaracjach argumentów funkcji. Wyrażenie:

• double sum(double (double),...) oznacza że

funkcja sum oczekuje jako pierwszego argumentu

funkcji zwracającej double o jednym argumencie

typu double. Ten zapis jest jednak mylący! W

rzeczywistości nie można przekazać funkcji jako

argumentu wywołania i dlatego w deklaracjach

argumentów typ funkcyjny jest automatycznie

zamieniany na typ wskaźnika do funkcji i

powyższa deklaracja jest równoważna deklaracji:

• double sum(double (*)(double),...)

background image

6

Wskaźniki do funkcji

• Wskaźniki do funkcji są

normalnymi obiektami i mogą być
kopiowane i przypisywane:

• void f(double x) {};

void (*g)(double) = &f;void (*h)
(double);h=&f;(*h)(0.0);(*g)(1.0);

background image

7

Wskaźniki do funkcji

• C++ posiada wygodną własność, która jednak

zwiększa konfuzję pomiędzy funkcjami i
wskaźnikami do nich. Otóż operatory * i & są
aplikowane automatycznie do funkcji i
powyższy kod można zapisać jako:

• void f(double x) {};

void (*g)(double) = f;void (*h)
(double);h=&f;h(0.0);g(1.0); Jest to dość
wygodne, ale powoduje, że część ludzi słabo
rozróżnia funkcje od wskaźników do nich

background image

8

Referencje do funkcji

• Żeby już skończyć ten temat i zupełnie

zamieszać Państwu w głowach napiszę, że
można też definiować referencje do funkcji:

• void f(double x) {};typedef void f_type(double)

f_type &g = f; f_type &h = g;const f_type
&ch = g; równoważne z wyrażeniem f_type &ch
= g;
h=g; niedozwolone h jest refencją do
stałej
Należy dodać, że typ const f_type & nie
jest obsługiwany przez kompilator g++-3.3 ale
przez g++-3.4 już tak

background image

9

Dedukcja typów funkcyjnych

• Ponieważ funktory i funkcje często przekazywane są jako

argumenty wywołania szablonów, których typ podlega
dedukcji, warto wiedzieć jak ten mechanizn rozpoznaje
typ przekazywanej funkcji. Rozważmy najpierw
następujacą definicję:

• template<typename F> test(F f) { F _fun(f);} Jeśli teraz

wywołamy

• void (*g)(double) = f ; void (&h)(double) = f;

test(f); F = void (*)(double)test(g); F = void (*)
(double)
test(h); F = void (*)(double)to w każdym
przypadku typ F zostanie wydedukowany jako wskaźnik
do funkcji void (*)(double).

background image

10

Dedukcja typów funkcyjnych

• Jeśli przypomnimy sobie, że

argumenty do funkcji można dla
oszczędności przekazywać jako
referencje do stałych, to możemy
napisać:

• template<typename F> test(const

F &f) { F _fun(f);} czeka nas
jednak niepospodzianka, kod

background image

11

Dedukcja typów funkcyjnych

• void (*g)(double) = f ; void (&h)(double) = f;

test(f); F = void ()(double), nie skompiluje
się
test(g); F = void (*)(double)test(h); F =
void ()(double) nie skompiluje się
zachowuje się już
inaczej. W przypadku przekazania funkcji lub referencji
do funkcji, wededukowanym typem będzie typ
funkcyjny. Ponieważ nie można inicjalizować zmiennych
typu funkcyjnego, wyrażenie F _fun(f) się nie
skompiluje. Nie będzie kłopotów jeśli przekażemy
wskaźnik do funkcji, ale tym razem musimy to zrobić
jawnie, nie nastąpi automatyczna konwersja nazwy
funkcji na wskaźnik do niej.

background image

12

Dedukcja typów funkcyjnych

• Można by przedefiniować funkcję test następująco:
• template<typename F> test(const F &f) { F *_fun(f);}

Teraz wywołania

• test(f); F = void ()(double)test(h); F = void ()

(double) skompilują się, ale nie skompiluje się linijka

• test(g); F = void (*)(double) bo pole _fun stanie się

typu void (**)(double). Kompilator g+-3.3 nawet tego
kodu nie skompiluje, bo nie zezwala na referencje do
stałych typów funkcyjnych.

• Widać więc, że przy przekazywaniu funkcji najlepiej

używać wywołania przez wartość, która i tak jest
automatycznie konwertowana na przekazywanie
wskaźnika do funkcji.

background image

13

Funktory jako obiekty o cechach

funkcji

• W C++ operator wywołania funkcji ( ) może

być traktowany jak każdy inny operator. W
szczególności może być przeciążany czyli
mogą mu być przypisywane różne zadania do
wykonania. Ograniczeniem jest to, że

może

być przeciążany tylko jako metoda klasy

.

Każdy obiekt zawierający definicję

operatora wywołania funkcji nazywany
jest funktorem. Taki obiekt zachowuje się
jakby był funkcją.

background image

14

Funktory jako obiekty o cechach

funkcji

• Przykład zwykłej funkcji, która sumuje liczby z

przedziału {n,m}:

double f(double x) {

return 2*x

;}

double sum(double (*f)(double),int n,int m){
double result=0;
for(int i=n;i<=m,i++) result = f(i);
return result; }
Teraz taką funkcję możemy wywołać jako:
cout<<sum(f,1,5)<<endl;
Albo jako dowolną inną funkcję:
cout<<sum(sin,1,5)<<endl;

background image

15

Funktory jako obiekty o cechach

funkcji

•Taka sama funkcja wykorzystująca obiekt funkcyjny czyli
funktor:
class classf{public:
classf(){}//

definiuje funktor, czyli obiekt klasy classf

wywołany przez ()

};
double sum2(classf, int n, int m){ double result = 0;
for (int i=n; i<+m,i++)result+=f(i);
return result;}
W tej funkcji pierwszy argument nie jest wskaźnikiem na
funkcję tylko obiektem funkcyjnym czyli
funktorem.Utworzony jest konstruktorem domniemanym.
Wywołanie takiej funkcji może być następujące:
cout<<sum2(classf(),2,5)<<endl;

background image

16

Funktory jako obiekty o cechach

funkcji

• Obiekt funkcyjny to zwykły obiekt klasy. Jego cechą

szczególną jest to, że przeciąża operator wywołania
funkcji. Można dla niego wywołać implementowaną
metodę klasy albo kilka metod operatorem () tak jak
dla zwykłej funkcji.

• Może zawierać swoje metody i zmienne czyli ma

swój stan przy wywołaniu i stan operacji, którą
wykonuje.

• Funkcja, którą reprezentuje funktor może mieć

jednocześnie różne stany, czego zwykła funkcja nie
potrafi. Czyli funktor przechowuje swoje stany
pośrednie.

background image

17

Funktory jako obiekty o cechach

funkcji

• Weźmy ciąg Fibonacciego: 1,1,2,3,5,8,13,...
• Do obliczeń zapiszmy kod:
class Fib{ public: Fib( ):a0_(1),a1_(1) { }; int

operator() (); private: int a0_,a1_;};

int Fib::operator () {int temp=a0; a0=a1;

a1=temp+a0; return temp;}

• Teraz możemy zrobić wywołanie:
Fib fib;///...cout<<”ELEMENTY

CIĄGU”<<fib()<<‘ ’<<fib()<<endl;

background image

18

Funktory jako obiekty o cechach

funkcji

fib() będzie rozpoznany jako wywołanie na

rzecz obiektu fib metody przeciążającej
operator(). Taki sam efekt daje fib.operator().

• Korzyścią jest to, że obiekt zachowuje stan

między wywołaniami co pozwala zapamiętać
kolejne elementy ciągu w obiekcie klasy Fib.
Przy zwykłej funkcji nie jest to możliwe,
Trzeba stosować posiłkowe zmienne statyczne
albo globalne, albo jawnie przekazywać
wartość do funkcji.

background image

19

Funktory jako obiekty o cechach

funkcji

• Rozpatrzmy problem całkowania funkcji. Musimy

w takiej operacji przechowywać wartości

policzone w poszczególnych krokach całkowania.

typedef double(*F)*(double);
double integrate (F f, double low, double high){
const int numsteps=9;
double step=(high-low)/numsteps;
double area=0.0;
while(low<high){area+=f(low)*step; low+=step;}
return area;}

background image

20

Funktory jako obiekty o cechach

funkcji

• Musimy do funkcji całkującej przekazać wskaźnik funkcji

całkowanej:

double aFunc(double x){...}//
double area=integrate (aFunc,0.0,2.7128)

NIE

PRZECHOWA

ON

JEDNAK WARTOŚCI W

POSZCZEGÓLNYCH

KROKACH

CAŁKOWANIA.

Nie

scałkuje także metod klas czyli funkcji składowych

będących metodami obiektów.

Należy zdefiniować funktor:
class Func{ public:
virtual ~Func(); virtual double operator()(double)=0;};
double integrate (Func &f, double low, double high);

Teraz w ramach dziedziczenia każda funkcja polimorficzna

z Func może być całkowana. Także wtedy, kiedy jest metodą

w klasie dziedziczącej.

background image

21

Funktory jako obiekty o cechach

funkcji

Obiekty funkcyjne posiadają więc składnię
wywołania funkcji. Ale są obiektami czyli mają
konstruktory, destruktory, metody, zmienne, stan
zapisany w składowych obiektu. Definiowanie
własnych funktorów wykonujemy poprzez
przeciążenie operatora (). Możemy zastosować
strukturę w miejsce klasy:

strukt Sin( double operator())(double x) {return

sin(x);}}

Możemy z tego funktora korzystać jak z funkcji sinus.

Ma on te przewagę nad funkcją sinus, że
przechowuje swój stan.

background image

22

Funktory jako obiekty o cechach

funkcji-wskaźniki do metod

• Przy wskaźniku do zwykłej funkcji nie musimy stosować

* ani &. Przypadek funkcji jako metody obiektu zmusza

do jawnego pobrania jej adresu, zniesienia referencji

(aliasu) na ten adres. Metoda ma także niejawny

argument, który jest wskaźnikiem this na obiekt, który ją

wywołuje. Bez niego nie możemy się odwołać do metody:

class X { void f() {std::cout<<``f1``<<`` ``};};
main(){typedef void(X::*f_ptr)();
f_ptr pf1=&X::f;// pobieranie adresu f pod wskaźnik f_ptr
X x;
X *px=new X;
(x.*pf1)(); //wykonanie funkcji f na rzecz obiektu x
(px->*pf1)();}

background image

23

Predefiniowane obiekty funkcyjne

Arithmetic operations

plus

minus

multiplies

(formerly called "times")

divides

modulus

negate

background image

24

Predefiniowane obiekty funkcyjne

Comparisons

equal_to

not_equal_to

less

greater

less_equal

greater_equal

background image

25

Predefiniowane obiekty funkcyjne

Logical operations

logical_and

logical_or

logical_not

background image

26

Predefiniowane obiekty funkcyjne

Generalized identity operations

identity

project1st

project2nd

select1st

select2nd

background image

27

Predefiniowane obiekty funkcyjne

-arytmetyczne

Plus<T> is a

function object

.

Specifically, it is an

Adaptable Binary Function

. If f is

an object of class plus<T> and x
and y are objects of class T, then
f(x,y) returns x+y.

background image

28

Predefiniowane obiekty funkcyjne

-arytmetyczne

Example
• Each element in V3 will be the sum of the

corresponding elements in V1 and V2

• const int N = 1000;

vector

<double>

V1(N);vector<double> V2(N);vector<double> V3(N);

•  

iota

(V1.begin(), V1.end(), 1)//przypisuje do zakresu

wartosc rosnąco

fill

(V2.begin(), V2.end(), 75); //przypisuje do zakresu

wartosc

• assert(V2.size() >= V1.size() && V3.size() >= V1.size());

transform

(V1.begin(), V1.end(), V2.begin(), V3.begin(),

plus<double>());//plus jest obiektem funkcyjnym

background image

29

Predefiniowane obiekty funkcyjne

-arytmetyczne

Member

Where defined

Description

first_argument_type

Adaptable Binary
Function

The type of the first argument:

T

second_argument_type

Adaptable Binary
Function

The type of the second argument:

T

result_type

Adaptable Binary
Function

The type of the result:

T

T operator()(const T& x, const T& y)

Adaptable Binary
Function

Function call operator. The return
value is

x + y

.

plus()

Default
Constructible

The default constructor.

background image

30

Predefiniowane obiekty funkcyjne

-arytmetyczne

• Multiplies<T> [1] is a function

object. Specifically, it is an
Adaptable Binary Function. If f is
an object of class multiplies<T>
and x and y are objects of class T,
then f(x,y) returns x*y.

background image

31

Predefiniowane obiekty funkcyjne

-arytmetyczne

Example
• Each element in V3 will be the product of the

corresponding elements in V1 and V2

• const int N = 1000;vector<double>

V1(N);vector<double> V2(N);vector<double>
V3(N); iota(V1.begin(), V1.end(),
1);fill(V2.begin(), V2.end(),
75); transform(V1.begin(), V1.end(),
V2.begin(), V3.begin(),
multiplies<double>());

background image

32

Funktory c.d.

• Funktory są więc także rozumiane jako uogólnione

wskaźniki do funkcji. Można je wywoływać

bezpośrednio, tak, jak funkcje. Można także

stosować je w algorytmach STL do wykonywania

zadania jako odpowiednik funkcji na liście

argumentów innej funkcji.

• Często takie uogólnienie wskaźnika służy do

rozdzielenia miejsca i czasu wykonania definicji

funkcji od jej zastosowania. Wygląda to tak, ze

klient definiuje funkcje i wskaźniki do nich

przekazuje do aplikacji. Klient nie wie kiedy i jak

zostaną wykonane, a aplikacja nie wie jak działają.

Nazywa się to techniką punktów zwrotnych.

background image

33

Adaptery funktorów

• Obiekty mogą jednak posiadać więcej informacji

niż tylko swój stan, mogą zawierać również

informacje o typie. Przede wszystkim funktory

same posiadają typ, konsekwencje tego faktu

omówię poźniej, teraz zajmę się typami

stowarzyszonymi. Nasuwająca się od razu

możliwość, to stowarzyszenie z funktorem typu

wartości zwracanej oraz typów jego argumentów.

Można stowarzyszyć również informację o liczbie

argumentów. W przypadku szablonu moglibyśmy

dodać na przykład

• typedef double result_type;typedef double

argument_type;enum {n_arguments = 1};


Document Outline


Wyszukiwarka

Podobne podstrony:
Biblioteka STL funktory
Algorytmy biblioteki STL(1)
wyklad12 wybrane Algorytmy biblioteki STL
Algorytmy biblioteki STL
Biblioteki naukowe gromadzenie
bibliografia 4
OPRACOWANIE FORMALNE ZBIORÓW W BIBLIOTECE (książka,
Fotografia Bibliografia
III semestr INiB Teoria i organizacja bibliografi0003

więcej podobnych podstron