Standard Template Library STL. Behållarklasser



Relevanta dokument
Algoritmbiblioteket (STL) Designstrategi Generiska algoritmer som fungerar på godtyckliga samlingsdatatyper, vilka har iteratorer.

Intro till standardbiblioteket. Eric Elfving

Programmering i C++ EDA623 Containerklasser och algoritmbibliotek. EDA623 (Föreläsning 10) HT / 33

Innehåll. Klasserna vector och deque

2D1387, Programsystemkonstruktion med C++ 01/02 1

Tentamen *:85/2I4123 C

Föreläsning Standardbiblioteket

Kapitel 4 - Mallar. Kapitel 4. Introduktion till mallar STL. 2D1387 Programsystemkonstruktion med C++ 1

Kapitel 4. Funktionsmallar. Mallar. Introduktion till mallar STL

Kapitel 4. Funktionsmallar. Mallar. Introduktion till mallar STL

Innehåll. Parametriserade typer. Klassmallar. Klassmallen Vektor Konstructor med std::initializer_list. Klassmallen Vektor Medlemsfunktioner

Innehåll. Introduktion till objektorientering. OOP (objektorienterad programmering) Objekt, instanser, klasser

Innehåll. Pekare. Datatyper Pekare, Arrayer och Referenser. Pekare Syntax. Pekare Syntax, operatorer. 4. Standard-containers. Pekare och arrayer

Användning av typeid. Operatorerna typeid och. Filen typeinfo.h måste inkluderas. typeid

Tommy Färnqvist, IDA, Linköpings universitet

Innehåll. Pekaren this Självreferens. Klasser Resurshantering, representation. Överlagring av operatorer. Överlagring av operatorer

Innehåll. Datatyper Pekare, Arrayer och Referenser. Pekare. Pekare Syntax. Pekare Syntax, operatorerna * och & 5. Pekare och arrayer. Algoritmer.

4. Standard-containers. Strömmar och filer

Programmering i C++ EDA623 Mallar. EDA623 (Föreläsning 12) HT / 29

Tentamen EDAF30 Programmering i C++

Abstrakta datatyper. Primitiva vektorer. Deklarera en vektor

Programmering i C++ EDA623 Objektorienterad programutveckling. EDA623 (Föreläsning 5) HT / 33

Programmering i C++ EDAF30 Dynamiska datastrukturer. EDAF30 (Föreläsning 11) HT / 34

Föreläsning 11 Genomgång av inlämningsuppgiften

maxlist-1. Indexerad lista Länkad lista

ADT Kö. Seminarium 4 Köer och Stackar Innehåll. Operationer. ADT Stack. Definition. Definition

TDDI14 Objektorienterad programmering

DD2387 Programsystemkonstruktion med C++ Tentamen 1 Torsdag 7 januari 2016, 14:00-18:00

Föreläsning 4 Innehåll. Abstrakta datatypen lista. Implementering av listor. Abstrakt datatypen lista. Abstrakt datatyp

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Programmering i C++ EDA623 Mer om klasser. EDA623 (Föreläsning 6) HT / 26

Generell (template) programmering. Effektiv C++ Slutliga tips Genomgång av gammal tenta. Daniel Aarno Allt som fungerar som x ÄR x

Innehåll. 1 Funktionsmalllar. 2 Klassmallar. struct Name { string s; //... }; const Name & minimum ( const Name & a, const Name & b) { if(a.s < b.

Lektionsuppgifter. TDDI14 Objektorienterad programmering. Lektionsplanering Lektion Lektion Lektion

TDIU01 - Programmering i C++, grundkurs

TDIU20 - Objektorienterad programmering i c++ - föreläsning 4

TDIU01 Programmering i C++

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Datastrukturer, algoritmer och programkonstruktion (DVA104, VT 2015) Föreläsning 6

TDIU01 - Programmering i C++, grundkurs

F12 - Collections. ID1004 Objektorienterad programmering Fredrik Kilander

Del6 Strömmar Ämnesområden denna föreläsning:

Programmering i C++ EDA623 Dynamiska datastrukturer. EDA623 (Föreläsning 11) HT / 31

Operatoröverlagring. endast operatorsymboler definierade i C++ kan överlagras = += -= *= /= %= ^= &= = <<= >>= < > <= >= ==!= && > ->*, [ ] ( )

Innehåll. 1 Kort om dynamisk polymorfism. 2 Arv i C++ 3 Multipelt arv. 4 Något om statisk polymorfism. class Container {

Innehåll. Användardefinierade typer. Användardefinierade typer Kategorier. Konstruktorer. Konstruktorer Två sätt att skriva initiering av medlemmar

Föreläsning 4 Innehåll

Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson 2

Dagens föreläsning Programmering i Lisp Fö 5

Introduktion till arv

Hitta k största bland n element. Föreläsning 13 Innehåll. Histogramproblemet

Länkade strukturer. (del 2)

Föreläsning 5-7 Operatoröverlagring

Funktionspekare, inledning: funktionsanropsmekanismen. Anrop via pekare

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (type casts) Explicita, namngivna typomvandlingar (C++-11)

Programmering i C++ EDA623 Arv. EDA623 (Föreläsning 6) HT / 42

TDIU01 - Programmering i C++, grundkurs

1 Klasser och objektorientering Vad är objektorientering?

Datastrukturer. föreläsning 3. Stacks 1

TDIU01 - Programmering i C++, grundkurs

Objektorienterad Programkonstruktion. Föreläsning 9 30 nov 2016

7 Templates och Exceptions

Standard Template biblioteket, eller STL, är ett C++ bibliotek innehållande: STL tillhandahåller många grundläggande algoritmer och datastrukturer:

Arrayer (vektorer) Murach s: kap Elektronikcentrum i Svängsta AB

Innehåll. Pekare Exempel

Länkade strukturer, parametriserade typer och undantag

Sammansatta datatyper Generics: Parametrisk polymorfism

Tentamen i Algoritmer & Datastrukturer i Java

Innehåll. Exceptionella händelser (exceptions, undantag ) Felhantering Tre nivåer av felhantering: Nivå 2: exceptions (eller returvärde)

Tentamen EDAF30 Programmering i C++

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Laboration 2 Datastrukturer En liten uppgift Frågor

Innehåll. Pekare Exempel

TDDC76 - Programmering och Datastrukturer

Föreläsning 4. ADT Kö Kö JCF Kö implementerad med en cirkulär array Kö implementerad med en länkad lista

Tentamen i TDP004 Objektorienterad Programmering Lösningsförslag

Datastrukturer. Arrayer. Arrayer. Arrayer. Array av arrayer. Array av arrayer

Tentamen Programmeringsteknik II Inledning. Anmälningskod:

Objektorienterad programmering i Java

Föreläsning 6: Introduktion av listor

Övningsuppgifter. TDIU04 Programmering i C++, standardbibliotek. Innehåll. Vt Lektion Lektion Lektion Lektion 4...

TDIU01 - Programmering i C++, grundkurs

Introduktion. Klasser. TDP004 Objektorienterad Programmering Fö 2 Objektorientering grunder

Programsystemkonstruktion med C++: Övning 2. Karl Palmskog september 2010

Kapitel 5. Strömmar. Utmatning

Laboration A Objektsamlingar

DD2387 Programsystemkonstruktion med C++ Tentamen 2

TDDC76 - Programmering och Datastrukturer

Tentamen i Objektorienterad Programmering 5p, Au, D, Fri, Pr,

Objektorienterad programmering

Tentamen i TDP004 Objektorienterad Programmering Lösningsförslag

Objektorienterad Programmering DAT043. Föreläsning 9 12/2-18 Moa Johansson (delvis baserat på Fredrik Lindblads material)

Klassen BST som definierar binära sökträd med tal som nycklar och enda data. Varje nyckel är unik dvs förekommer endast en

Datalogi, grundkurs 1. Lösningsförslag till tentamen

ÖREBRO UNIVERSITET. Lösningarna till tentamensuppgifterna sätts ut på kurssidan på nätet i dag kl 13.

Datastrukturer och algoritmer. Innehåll. Tabell. Tabell - exempel. Gränsyta till Tabell. Tabell. Modell. Hashtabell Relation, lexikon.

Den som bara har en hammare tror att alla problem är spikar

Innehåll. 1 Algoritmer. 2 Strömmar och filer. 3 Iteratorer. 1 Söka, räkna. 2 Jämföra, genomlöpa. 3 Generera nya data. 4 Kopiera och flytta element

Föreläsning 5 (6) Metoder. Metoder Deklarera. Metoder. Parametrar Returvärden Överlagring Konstruktorer Statiska metoder tostring() metoden javadoc

Ett problem. Kontrollstrukturer och arrayer. Arrayer. Lösningen. Arrayer och hakparanteser. Exempel int[] results; results = new int[10]; // 0..

Tentamen *:58/ID100V Programmering i C Exempel 3

Transkript:

Standard Template Library STL Kärnan av STL utgörs av behållarklasser (containers, datasamlingar, collections...) och algoritmer för operationer på sekvenser av element. Utvecklad av Alexander Stepanov som forskade sedan 1970-talet på tillämpning av generisk programmering på skapande av generella algoritmer för behandling av data oberoende av deras fysiska lagring. STL kan sägas bestå av tre beståndsdelar: behållare d.v.s. datasamlingar som vector<>, list<> o.s.v. som lagrar dataelement algoritmer som sort, find, copy, transform o.s.v. som opererar på sekvenser av data utan att bry sig om varifrån dessa kommer iteratorer som förenar behållare med algoritmer genom att erbjuda ett standardiserat gränssnitt mot behållare vad beträffar åtkomst av data Behållare Iteratorer Behållare Iteratorer Algoritm Behållare STL är inte objektorienterat, det tillämpar generisk programmering och bygger på standardiserad namngivning, inte på typhierarkier. I OOP strävar man efter att integrera och kapsla in data och operationer, i STL separerar man på dem. Samtidigt lyckas man abstrahera generella operationer från lagringssätt, något som OOP aldrig riktigt lyckats med. Dessa två angreppssätt kompletterar varandra. Bild 231 Headerfil Beskrivning Behållarklasser Sekvenser <vector> dynamiska vektorer (effektiva tillägg i slutet, indexering) <list> dubbellänkade listor (effektiva tillägg överallt, ingen indexering) <deque> som vector men med effektiva tillägg även i början Adaptorer <queue> kö med tillägg i slutet och borttag i början <stack> stack med tillägg och borttag i samma ända Associativa behållare <map> map och multimap, avbildningar av nycklar på värden <set> set och multiset, mängder av värden med effektiv sökning Övrigt <bitset> bitmängder Inte riktigt behållare... Följande bilder gäller inte dessa helt <valarray> numeriska vektorer och operationer på dessa <string> strängar priority_queue definieras i <queue>, multimap i <map>, multiset i <set>. Stödbiblioteket består av <utility> och <iterator> Obs att det konstigt nog inte finns någon hashtabell. STL utgör dock ett utbyggbart ramverk och det finns många hashtabell implementeringar att tillgå t.ex. på webben (det finns också en implementering i Stroustrups bok). Bild 232 1

Behållarklasser - sammanställning Indexering Operationer i Iteratorkategori mitten början slutet vector O(1) O(n) O(1) Random list O(1) O(1) O(1) Bidirectional deque O(1) O(n) O(1) O(1) Random stack O(1) queue O(1) O(1) priority_q O(log n) O(log n) map O(log n) O(log n) Bidirectional multimap O(log n) Bidirectional set O(log n) O(log n) Bidirectional multiset O(log n) Bidirectional string O(1) O(n) O(n) O(1) Random array O(1) Random valarray O(1) Random bitset O(1) Random O(1) - konstant tid (snabbt) O(n) - proportionellt till antalet element (ej snabbt) O(log n) - proportionellt till logaritmen av antalet element (ganska snabbt) Bild 233 Iteratorkategorier Output Input Forward Bidirectional Random avläsning =*p =*p =*p =*p åtkomst -> -> -> -> [] skrivning *p= *p= *p= *p= stegning ++ ++ ++ ++ -- ++ -- + - += -= jämförelse ==!= ==!= ==!= ==!= < > >= <= Input Output Informell hierarki Forward Bidirectional Random Bild 234 2

Behållarklasser - typer Varje behållarklass definierar ett antal typnamn: value_type - elementtypen iterator - beter sig som pekare till värden i behållaren const_iterator - som ovan, får användas i const-behållare reverse_iterator - baklängespekare, går baklänges vid ++ const_reverse_iterator - som ovan, får användas i const-behållare reference - beter sig som referens till element i beh. const_reference - som ovan, får användas i const-behållare size_type - typen för index, elementräknare osv. difference_type - typen för skillnaden mellan två iteratorer Associativa behållare definierar ett par typnamn till, t.ex. key_type - typen för nyckelvärden Bild 235 Behållarklasser - konstruktorer mm container nedan står för behållarklassens namn, t.ex. vector, list osv. Prototyperna är starkt förkortade - se klassexempel för bättre beskrivning. container() - tom behållare container(n) - n element med defaultvärde (ej associativa beh.) container(n, val) - n element med värdet val (ej associativa beh.) container(first,last)- kopior av element från en annan sekvens container(other) - copy-konstruktorn, other måste vara av samma typ ~container() - destruktorn, tar bort behållarinnehållet operator=(other) - tilldela en kopia av en annan behållare (samma typ) assign(n, val) - tilldela n elem. med värdet val (ej associativa beh.) assign(first, last)- tilldela element från en annan sekvens (ej assoc. b.) swap(other) - byt element med other Exempel: vector<int> vic(10, 137); int arr[]={10,11,16,20,23,43,53,61}; vector<int> vec(arr, &arr[8]); vic.assign(vec.begin(), vec.begin()+4); Bild 236 3

Behållarklasser - iteratorer och elementåtkomst Medlemsfunktioner som returnerar iteratorer: begin() - iteratorn till första elementet end() - iteratorn till elementet efter sista rbegin() - iteratorn till första elementet i omvända sekvensen - sista rend() - iteratorn till elementet efter sista i omvända sekvensen Exempel: for(vector<int>::iterator it=v.begin(); it!=v.end(); it++) cout << *it << ; for(vector<int>::reverse_iterator rit=v.rbegin(); rit!=v.rend(); rit++) cout << *rit << ; // Baklänges! Medlemsfunktioner som returnerar elementvärden: front() - första elementet back() - sista elementet operator[] - i-te elementet, indexering utan kontroll - ej list at(i) - i-te elementet, som indexering men med kontroll, kastar undantaget out_of_range, ej list Bild 237 push_back() pop_back() push_front() pop_front() Behållarklasser - tillägg och borttag - lägg till ett element på slutet - ta bort ett element från slutet - lägg till ett element i början (end. list och deque) - ta bort ett element från början (list och deque) insert(p, x) - lägg in x före p insert(p, n, x) - lägg in n kopior av x före p insert(p, first, last) - lägg in kopior av element från en annan sekvens före p erase(p) - ta bort elementet som pekas ut av p erase(first, last)- ta bort elementen fr.o.m first till (exklusive) last clear() - ta bort alla element Obs! För vector<> och deque<> gör insättning eller borttag av ett nytt element alla referenser, pekare och iteratorer till efterföljande element ogilltiga. Vad värre är, om operationen framkallar en omallokering görs alla referenser, pekare och iteratorer till element ogilltiga. Detta gäller inte list<> Bild 238 4

Behållarklasser - övriga operationer size() - aktuellt antal element empty() - är behållaren tom? max_size() - största möjliga storleken (antal element) för denna behållare capacity() - hur många element ryms just nu? (endast vector) reserve(n) - allokera utrymme till så många element (endast vector) resize(n) - lägg till så många element på slutet, defaultvärde resize(n, val) - lägg till så många kopior av val på slutet operator==(other) - har denna behållare samma innehåll som other? operator<(other) - är innehållet lexikografiskt mindre än others? Bild 239 Speciella operationer i list void remove(const T& value); - tar bort alla element == value void unique(); - tar bort dubletter, använder == void unique(op) - tar bort dubletter, använder op() void merge(other);- samsorterar med element från other, tömmer den void merge(other,op);- som ovan men använder op(), inte < void sort(); - sorterar listan, använder < void sort(op); - sorterar listan, använder op() void splice(pos, other); - flytta allt från other till före pos void splice(pos, other, p); - flytta *p från other till före pos void splice(pos, other, first, last); - flytta elementen fr.o.m. first till (exklusive) last i other till före pos void reverse(); - vänder på listan Bild 240 5

Associativa behållare Associativa behållare består av map, multimap, set och multiset. map och multimap definieras i <map>, set och multiset i <set>. Värden som hanteras i en map eller multimap definierade på nyckeltypen Ktype och värdetypen Vtype är av typen pair<const Ktype, Vtype>. I set och multiset hanteras endast Ktype-värden. Följande operationer är speciella för associativa behållare: Vtype& operator[](ktype k) - endast map, returnerar referensen till Vtype-värdet associerat med k (finns det inget sådant par så läggs det in ett med default-värde på Vtype-värdet...) find(k) - returnerar iteratorn till paret som identifieras av k lower_bound(k) - returnerar iteratorn till första paret identifierat av k upper_bound(k) - returnerar iteratorn till paret efter de som innehåller k equal_range(k) - returnerar ett par av iteratorer som avgränsar paren som identifieras av k count(k) - returnerar antalet par som identifieras av k Bild 241 map-hantering Antag map<string, Person> folk; Vilket värde som är associerat med ett visst nyckelvärde i en map kan avläsas bekvämt med indexeringsoperatorn: Person p = folk[ Urban ]; Insättning av ett nytt värde kan göras på samma sätt (men det är olämpligt, se nedan): folk[ Urban ] = Person( Urban, 67); Problemet med detta är att indexeringsoperatorn måste returnera referensen till ett existerande utrymme, så att det kan tilldelas - om det inte finns något sådant utrymme måste det allstå skapas först (med Person-objektet initierat med default-konstruktorn). För att undvika detta brukar man sätta in nya värden genom att skapa ett pair-objekt och sätta in det manuellt : folk.insert(make_pair( Urban, Person( Urban, 67))); Bild 242 6

map-hantering, forts. För att kontrollera om ett nyckelvärde redan finns kan man använda find() (som returnerar en iterator till pair-objektet eller end() om nyckelvärdet inte finns) eller count() (som för en map<> bara kan returera 0 eller 1): map<string, Person>::iterator pos = folk.find( Urban ); if (pos!= folk.end()) folk.insert(make_pair( Urban, Person( Urban, 67))); eller if (folk.count( Urban )==0) folk.insert(make_pair( Urban, Person( Urban, 67))); Bild 243 Exempel på generiska algoritmer template <class InIter, class Function> Function for_each(initer first, InIter last, Function f) { for ( ; first!= last; ++first) f(*first); return f; } template <class InIter, class T> InIter find(initer first, InIter last, const T& value) { while (first!= last && *first!= value) ++first; return first; } template <class InIter, class Predicate> InIter find_if(initer first, InIter last, Predicate pred) { while (first!= last &&!pred(*first)) ++first; return first; } Bild 244 7

Generiska algoritmer: användningsexempel #include <string> #include <iostream> #include <list> #include <algorithm> // Här finns de generiska funktionerna class Person{ public: string namn; // Data publika för att förenkla exemplen int nr, ant; Person(string na,int no,int a=0):namn(na),nr(no),ant(a){} }; int main(){ list<person *> lip; lip.push_back(new Person("Jozef", 53, 13)); lip.push_back(new Person("Stefan", 61, 5)); lip.push_back(new Person("Martin", 73, 81));... int vek[]={5, 17, 3, 23, 13, 8}; list<int> lint(vek, &vek[sizeof(vek)/sizeof(int)]);... Bild 245 Exempel på for_each Uppgift: skriv ut information om alla personer Vi behöver en operation att skicka till for_each, t.ex.: void skrivpers(person *p){ cout << p->namn << " " << p->ant << endl; } Utskriften kan nu göras så här: int main(){... for_each(lip.begin(), lip.end(), skrivpers);... } Bild 246 8

Funktioner som predikat Uppgift: hitta talet 23: list<int>::iterator pos=find(lint.begin(), lint.end(), 23); if (pos!=lint.end())... Uppgift: hitta Stefan : vi kan inte använda find, eftersom den använder operator!=, dvs. vi skulle behöva redan ha en iterator till Stefan -objektet. Vi kan använda find_if och skapa ett predikat som den kan använda för att avgöra om ett objekt uppfyller vårt villkor. En möjlig lösning är en boolsk funktion: bool isstefan(person *p){ return p->namn== Stefan ; }... list<person *>::iterator pers; pers=find_if(lip.begin(), lip.end(), isstefan); if (pers!=lip.end())... Bild 247 Nackdelar med funktioner som predikat mm Två stora nackdelar med en funktion som predikat : eftersom find_if anropar sitt predikat-argument med ett argument (avrefererad iterator) så måste funktionen ta ett argument. Skulle vi vilja leta efter ett annat namn så måste en annan funktion skrivas, eller också får man skriva en funktion som tar namnet den skall jämföra med från en global variabel den funktion som genereras ur find_if-mallen kommer att ta en en funktionspekare som sitt tredje argument (en bool (*)(Person *)) Anrop via funktionspekare måste utföras under exekveringen, dvs. inline-optimering är inte möjlig. Bild 248 9

Funktionsobjekt Ett bättre alternativ är att använda funktionsobjekt. Repetition: ett funktionsobjekt är objekt av en klass som överlagrar anropsoperatorn operator(). Effekten blir att parenteser med aktuella argument kan skrivas direkt efter objektets namn - syntaktiskt ser det ut som om objektet vore en funktion som anropas, i själva verket anropar man en medlemsfunktion i objektet, nämligen just operator(). Den viktigaste användning av funktionsobjekt är som operations- eller predikatargument i stället för funktioner. De två nackdelarna vid användning av funktioner försvinner: funktionsobjektet kan bära med sig data som dess operator() kan arbeta på. Dessa data kan initieras vid objektets skapande genom konstruktorn och de kan vid behov avläsas efteråt. Ett funktionsobjekt kan med andra ord bära med sig en omgivning till den anropande funktionen. inlining av den anropade funktionen kan göras. Bild 249 Exempel på funktionsobjekt Ett funktionsobjekt för att söka efter personer med ledning av namnet: det sökta namnet lagras i funktionsobjektet, dess värde ges till objektet vid skapande genom konstruktorn: class DennaPers{ string namn; public: DennaPers(string n):namn(n){} bool operator()(person *p){ return p->namn==namn;} }; Sökningar kan nu göras så här: list<person *>::iterator pos; pos=find_if(lip.begin(), lip.end(), DennaPers( Stefan )); if (pos!=lip.end())... pos=find_if(lip.begin(), lip.end(), DennaPers( Jozef )); if (pos!=lip.end())... Obs! att det är ett objekt som skickas, oftast (som här) ett temporärt objekt skapat genom explicit konstruktoranrop. Bild 250 10

Anrop av medlemsfunktioner i algoritmerna Säg att vi utökar Person-klassen med ett par medlemsfunktioner: class Person{ public: string namn; int nr, ant; Person(string na,int no,int a=0):namn(na),nr(no),ant(a){} void visanamn() { cout << namn << endl; } void visaallt() { cout << namn << nr << ant << endl; } }; Säg att vi vill använda for_each för att skriva ut namn på alla personer i listan lip. Men for_each anropar en global funktion med varje sekvenselement som argument, inte en medlemsfunktion hos varje sekvenselement. Vi måste alltså göra en funktion eller ett funktionsobjekt som tar en pekare till ett Personobjekt som argument och anropar den aktuella medlemsfunktionen i detta objekt (här en funktion): void visaen(person *p) { p->visanamn(); } // Global funktion... for_each(lip.begin(), lip.end(), visaen); Med användning av funktionsobjekt och medlemspekare kan man ange vilken medlemsfunktion som skall anropas vid for_each-anropet. Dessutom ger standardbiblioteket stöd för sådana konstruktioner. Bild 251 Källor till funktionsobjektsklasser Standardbiblioteket använder uteslutande funktionsobjekt för angivande av operationer, relationer och villkor. Funktionsobjektsklasser fås på ett av tre sätt: genom att definiera klassen själv i headerfilen <functional> finns fördefinierade klasser för de vanliga operationerna och villkoren i headerfilen <functional> finns även adaptorer som genererar de oftast behövliga funktionsobjektsklasser Dessa funktionsobjekt beter sig ju som funktioner, vissa av dem som funktioner med ett argument, andra som funktioner med två argument. För att de skall kunna kombineras till sammansatta funktionsobjekt måste de ha vissa egenskaper som de ärver från två structar (också i <functional>): struct unary_function och struct binary_function. Egna funktionsobjektklasser behöver inte ärva från dessa, men kan då inte kombineras med de fördefinierade klasser eller adaptorerna. Bild 252 11

Fördefinierade funktionsobjektsklasser #include <functional> Realtionsfunktionsobjekt: equal_to<typ>, not_equal_to<typ>, greater<typ>, less<typ>, greater_equal<typ>, less_equal<typ> Aritmetiska funktionsobjekt: plus<typ>, minus<typ>, multiplies<typ>, divides<typ>, modulus<typ>, negate<typ> Logiska funktionsobjekt: logical_and<typ>, logical_or<typ>, logical_not<typ> Exempel på användning: sortera listan lint i avtagande ordning: lint.sort(greater<int>()); Bild 253 Funktionsobjektsadaptorer I headerfilen <functional> finns även adaptorer som kan generera de vanligaste typerna av funktionsobjekt. Det finns dessutom hjälpfunktioner som skapar dessa objekt. Följande är de viktigaste: bind1st bind2nd mem_fun - skapar en unär funktion från en binär genom att binda 1a argumentet - skapar en unär funktion från en binär genom att binda 2a argumentet - skapar en global unär funktion från en argumentlös medlemsfunktion eller en global binär funktion från en unär medlemsfunk. anropad via pekare mem_fun_ref - skapar en global unär funktion från en argumentlös medlemsfunktion eller en global binär funk. från en unär medlemsfunk. anropad via referens (eller objekt) ptr_fun not1 not2 - gör ett funktionsobjekt av en funktion - negerar ett unärt predikat - negerar ett binärt predikat Bild 254 12

Exempel på användning av adaptorer Vi utökar vår klass person på följande sätt: class Person{ public: string namn; int nr, ant; Person(string na,int no,int a=0):namn(na),nr(no),ant(a){} bool ismyname(string n) const {return namn==n;} void skriv() const {cout << namn <<, << nr << endl;} }; Skriv ut alla personer: for_each(lip.begin(), lip.end(), mem_fun(&person::skriv)); Hitta personen med namnet Jozef : pos=find_if(lip.begin(), lip.end(), bind2nd(mem_fun(&person::ismyname), Jozef )); Bild 255 Kategorier av generiska algoritmer Sökning, t.ex.: find(), find_if(), count(), count_if() Sortering, t.ex.: sort(), stable_sort(), reverse() Borttag och ersättning, t.ex.: copy(), remove(), remove_if() Generering och genomgång, t.ex.: for_each(), fill(), transform() Relationsalgoritmer, t.ex.: equal(), lexicographical_compare() Mängdoperationer, t.ex.: set_union(), set_intersection() Permutationer, numeriska algoritmer, heap-algoritmer. Bild 256 13

Exempel på modifierande algoritmer - copy template <class In, class Out> Out copy(in first, In last, Out firstout){ while (first!=last) *firstout++ = *first++; return firstout; } Användningsexempel: list<int> li2(lint.size()); copy(lint.begin(), lint.end(), li2.begin()); copy() (liksom andra modifierande sekvensalgoritmer) ändrar alltså värden i existerande sekvenselement (se dock nästa bild) - detta p.g.a. att deras argument är iteratorer medan insättnings- eller borttagsoperationer kräver normalt tillgång till själva behållaren. Det finns även copy_backward() som kopierar baklänges - denna används om inputsekvensen och outputsekvensen överlappar, d.v.s. att operationen egentligen går ut på att flytta elementen inom en sekvens. Bild 257 insert-iteratorer Algoritmer som framställer en outputsekvens (som t.ex. copy()) kan fås att sätta in nya element istället för att skriva över existerande sådana genom användning av s.k. insert-iteratorer. En insert-iterator är en iterator där tilldelningsoperatorn operator= har definierats så att den använder en insättningsoperation (push_back(), push_front()eller insert()) innan tilldelning. För att göra detta behöver de ha tillgång till behållaren där insättningsoperationen skall göras. Genom #include <iterator> får man tillgång till mallar för insert-iterator-klasser. Dessutom finns där hjälpfunktioner för skapande av objekt av instansierade insert-iterator-klassmallar: front_inserter(), back_inserter() och inserter(). Exempel på användning: list<int> li1, li2, li3(3, 13); copy(lint.begin(), lint.end(), front_inserter(li1)); copy(lint.begin(), lint.end(), back_inserter(li2)); list<int>::iterator pos=li3.begin(); pos++; copy(lint.begin(), lint.end(), inserter(li3, pos)); Bild 258 14

iostream-iteratorer Strömmar (filer) kan ses som sekvenser: en istream (t.ex. cin) kan ses som en inputsekvens och en ostream (t.ex. cout) som en outputsekvens. Det finns därför speciella iteratorer som tillåter användning av strömmar i sekvensalgoritmer. En istream_iterator fungerar så att en avläsning av den framkallar användning av inläsningsoperatorn operator>>, medan avläsning av en ostream_iterator framkallar utskriftsoperatorn operator<<. Ström-iteratorer är mallar - måste instansieras med typen för element i sekvensen. En iterator som betecknar strömmens början (motsvarande begin()) fås genom att skapa ett iteratorobjekt med strömmen som argument, en iterator till strömmen slut (motsvarande end()) fås genom att skapa ett iteratorobjekt med defaultkonstruktorn (utan argument). Exempel på nästa bild. Bild 259 Exempel på iostream-iteratorer #include <fstream> // Filströmmar, inkluderar iostream #include <vector> #include <algorithm> #include <iterator> int main(){ ifstream fil("tal.txt"); // Fil-in-ström, öppnas autom. istream_iterator<int> fbegin(fil); istream_iterator<int> fend; vector<int> vi; } // Fyll vektorn med tal från filen copy(fbegin, fend, back_inserter(vi)); // Skriv ut vektorn på cout (utan mellanslag) copy(vi.begin(),vi.end(),ostream_iterator<int>(cout)); Bild 260 15

Borttagande algoritmer Sekvensalgoritmerna gör inga tillägg eller borttag av element i behållarna. T.ex. fungerar remove() och remove_if() så att de återstående elementens värden kopieras in i elementen i början av sekvensen och en iterator till det första överflödiga elementet returneras. De överflödiga elementen kan sedan tas bort med behållarens erase()-medlemsfunktion. Exempel: ta bort alla som heter Jozef ur listan lip (antag att själva Personobjekten inte skall tas bort): list<person *>::iterator p; p=remove_if(lip.begin(),lip.end(),dennapers( Jozef )); if (p!=lip.end()) lip.erase(p, lip.end()); Bild 261 16