Föreläsning Standardbiblioteket

Relevanta dokument
Standard Template Library STL. Behållarklasser

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

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

4. Standard-containers. Strömmar och filer

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

Innehåll. Klasserna vector och deque

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

Kapitel 4. Funktionsmallar. Mallar. Introduktion till mallar STL

operator T*() const throw(); // Konvertering till vanlig pekare Jozef Swiatycki DSV Implementering av vissa medlemmar i Ptr-mallen

Kapitel 4. Funktionsmallar. Mallar. Introduktion till mallar STL

Tentamen *:85/2I4123 C

TDIU01 - Programmering i C++, grundkurs

Programmering i C++ EDA623 Strömmar och filer. EDA623 (Föreläsning 9) HT / 19

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

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

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

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

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

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

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

TDIU01 Programmering i C++

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

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

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

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

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

Föreläsning 5-7 Operatoröverlagring

Tommy Färnqvist, IDA, Linköpings universitet

Kapitel 5. Strömmar. Utmatning

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

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

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

TDIU01 - Programmering i C++, grundkurs

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

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

Tentamen EDAF30 Programmering i C++

Strömmar och strängar

C++ - En introduktion

Strängar. Strängar (forts.)

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

*:85/ID200V C++ HT07. Föreläsning 8 Medlemspekare Undantagshantering Namnrymder

7 Templates och Exceptions

Innehåll. Pekare Exempel

Föreläsning 4: Filer och strömmar

TDIU01 - Programmering i C++, grundkurs

Tillämpad programmering

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

Lektionsuppgifter. TDDI14 Objektorienterad programmering. Lektionsplanering Lektion Lektion Lektion

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

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

Introduktion till arv

Klasser. Kapitel 2. Kapitel 2 - Klasser, medlemmar och arv. Klasser. Klasser Medlemmar Arv

Innehåll. Pekare Exempel

TDIU01 - Programmering i C++, grundkurs

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

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

Introduktionslaboration

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

TDDI14 Objektorienterad programmering

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

Innehåll. 1 Deklarationer, scope och livstid. 2 Användardefinierade typer. 4 Operatoröverlagring. 5 In- och utmatning. 6 namnrymder (namespace)

2D1387 Programsystemkonstruktion med C++ Laboration 1: Grundläggande C++ 31 augusti 2005

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

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Tillämpad programmering

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

TDIU01 (725G67) - Programmering i C++, grundkurs

Funktionspekare, inledning: funktionsanropsmekanismen. Anrop via pekare

Tentamen EDAF30 Programmering i C++

Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning:

Tentamen Programmeringsteknik II Inledning. Anmälningskod:

Innehåll. Resurshantering. Resource handles. Minnesallokering. Minnesallokering Exempel: allokering på stacken. 6. Resurshantering

Tentamen EDAF30 Programmering i C++

Objektorienterad programmering

Länkade strukturer, parametriserade typer och undantag

Sammansatta datatyper Generics: Parametrisk polymorfism

Abstrakta datatyper. Primitiva vektorer. Deklarera en vektor

maxlist-1. Indexerad lista Länkad lista

Nedan skapar vi klassen Person innehållande datamedlemmar för förnamn, efternamn, ålder, längd och vikt:

Innehåll. 1 Deklarationer, scope och livstid. 2 Användardefinierade typer. 4 In- och utmatning. 5 Operatoröverlagring. 6 namnrymder (namespace)

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

LÖSNINGSFÖRSLAG TILL Tentamen i objektorienterad programmering i C++ I

Objektorienterad Programmering (OOP) Murach s: kap 12-16

Innehåll. 1 Funktionsmallar. 2 Pekare och konstanter. 3 Typomvandlingar. struct Name { string s; //... };

F12 - Collections. ID1004 Objektorienterad programmering Fredrik Kilander

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

TDIU01 - Programmering i C++, grundkurs

TDIU01 - Programmering i C++, grundkurs

Det finns många flaggor till g++,

Övning från förra gången: readword

Föreläsning 4 Innehåll

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

Repetition av OOP- och Javabegrepp

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

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

EDAf30: Programmering i C++, 7.5 hp. EDAf30: Programmering i C++, 7.5 hp Administration. EDAf30: Programmering i C++, 7.5 hp Obligatoriska moment

TDDC76 - Programmering och Datastrukturer

Symboliska konstanter const

Transkript:

*:85/ID200V C++ HT07 Föreläsning 15-17 Standardbiblioteket Introduktion till standardbiblioteket Standardbiblioteket består av följande delar: behållarklasser med stödbibliotek algoritmbiblioteket med stödbibliotek strängbiblioteket I/O-biblioteket stöd för undantagshantering och standardundantag stöd för lokal anpassning (sorteringsordning för tecken mm) matematiska och numeriska faciliteter andra stödfunktioner (numeriska gränser, dynamisk minneshantering, Run-Time-Type-Information-stöd mm) Tillgång till dessa bibliotek fås genom #include på motsvarande headerfil. Till dessa tillkommer C:s headerfiler, som också tillhör C++-standardbibliotek. C:s headerfiler finns under två namn på formen cx resp. X.h, t.ex. #include <cstring> deklarerar C:s str-och mem-funktioner i namnrymden std medan #include <string.h> definierar dessa namn i den globala namnrymden. Bild 216 1

string-klassen Typen string är egentligen en instansiering av mallen basic_string<> vars parametrar är teckentypen, dess egenskaper ( traits ) och dess minneshantering: namespace std{ template <class chart = char, // Teckentypen class traits = char_traits<chart>, // Hur tecken jämförs class allocator = allocator<chart> > // Hur man allokerar class basic_string{ ; typedef basic_string<char> string; Jag förenklar framställningen i fortsättningen genom att använda string som om den var en klass. Bild 217 string string har samma typer och medlemsfunktioner som vector<>, detta för att man skall kunna använda string som argument till de generiska algoritmerna, t.ex. void funk(char c){ cout << c; int main(){ string str("jozef"); for_each(str.begin(), str.end(), funk); Vid normal stränghantering anger man positioner inom en sträng med index och längd: string namn = Jozef Swiatycki ; string enamn = namn.substr(6, 9); // startposition, längd Vid sådana operationer måste index-värdet falla inom strängen (annars genereras undantaget std::out_of_range), längdangivelsen kan dock vara större än det återstående antalet tecken i strängen och har då betydelsen resten av strängen. Det finns en statisk medlemskonstant med namnet npos som har ett värde som är större än vad en stränglängd kan vara och som kan användas för att ange resten av strängen. Den returneras också av sökfunktioner när dessa inte hittar det sökta värdet: string resten = namn.substr(6, string::npos); string::size_type pos = namn.find( ); if (pos == string::npos) cout << Inget mellanslag! ; Obs att string::size_type är en unsigned typ, hade pos deklarerats som en int skulle kompilatorn varna för jämförelse av signed med unsigned. Bild 218 2

Varianter av medlemsfunktioner i string De flesta medlemsfunktioner i string finns i många överlagrade varianter för olika typer av argument: ett andra strängargument skall kunna vara string, char * eller char positioner skall kunna anges med index och längd eller med iteratorer om man vill operera på hela strängar skall man inte behöva ange positioner Exempel: void insert(iterator p, size_type n, char c); template<class InputIterator> void insert(iterator p, InputIterator beg, InputIterator end); string& insert(size_type pos1, const string& str); string& insert(size_type pos1, const string& str, size_type pos2, size_type n); string& insert(size_type pos, const char* s, size_type n); string& insert(size_type pos, const char* s); string& insert(size_type pos, size_type n, char c); iterator insert(iterator p, char c = char()); Exempel på användning: string knamn= Karl Gustav ; string nr= XIV XV XVI XVII ; knamn.insert(5, nr, 7, 4); //pos i egen sträng, från-sträng, pos i från-sträng, antal Bild 219 Operationer på string insert - stoppar in annan sträng eller tecken på angiven position namn.insert(6, Zbigniew ); str.insert(eve.wherex-getx(), 1, char(eve.what)); erase - tar bort tecken från angiven position (angivet antal tecken): namn.erase(6, 9); str.erase(eve.wherex-getx()); replace - ersätter angiven del av strängen med en annan sträng: namn.replace(6, 9, Bo ); append - lägger till den andra strängen eller tecknet eller ett antal tecken: str.append(char(eve.what)); namn.append( den Trötte ); operator+= push_back operator+ andra sätt att uttrycka append på - konkatenerar två strängar och returnerar resultatsträngen substr - returnerar en kopia av en delsträng: string mellan = namn.substr(6, 8); Bild 220 3

Sökning i strängar Det finns många varianter av sökfunktioner: det sökta mönstret kan uttryckas som string, char * eller char; sökningen kan göras framlänges eller baklänges och kan göras i hela strängen eller begränsas till en del av den. Index för den hittade förekomsten returneras (eller string::npos om det sökta inte hittades) find rfind - letar efter första förekomsten av angivet tecken eller sträng i angiven portion av strängen: string::size_type wstart = line.find( ); string::size_type wend = line.find(, wstart+1); string word = line.substr(wstart+1, wend-wstart); - som find men letar efter sista förekomster (söker baklänges) find_first_of find_last_of find_first_not_of find_last_not_of strängargumentet till dessa betraktas som en mängd av teckenvärden där man letar efter den första (sista) förekomsten Exempel: const string letters= abcd.zåäöabcd.zåäö ; string::size_type wstart=line.find_first_of(letters); string::size_type wend=line.find_first_not_of(letters, wstart); string word = line.substr(wstart, wend-wstart); Bild 221 Gränssnitt mot C-strängar I vissa fall kan man vilja använda C:s stränghantering på string-värden. T.ex. kan sscanf() vara bekvämt att använda i vissa situationer (C++ har motsvarande men mer begränsad funktionalitet i strängströmmar). För detta finns följande medlemsfunktioner i klassen string: c_str()- returnerar en pekare till en C-sträng som är allokerad lokalt i stringobjektet - man kan inte räkna med att den finns kvar och har korrekt värde efter nästa operation på string-objektet data() - som c_str() men utan det extra null-tecknet på slutet copy() - tar en char * som argument (skall peka ut ett allokerat och tillräckligt stort utrymme) och kopierar tecken till detta utrymme (utan null-tecken) Exempel: string line = Klockan är 14:15 ; int tim, min; sscanf(line.c_str(), Klockan är %d:%d, &tim, &min); Bild 222 4

Orientering om ström-biblioteket Ström-biblioteket utgör ett utbyggbart ramverk för att lätt kunna omfatta hantering av olika teckenkodningar, nya externa medier, regionala anpassningar o.s.v. Strömbiblioteket kan sägas bestå av två nivåer: strömklasser för formattering och parsing strömbuffertklasser för buffring, kodkonvertering och fysisk i/o program formatteringsnivån - IOStream-klasser transportnivån - Streambuffer-klasser externa medier Till detta tillkommer möjlighet att i strömklasser installera egna data och call-back - funktioner. Dessutom är alla dessa klasser mallar som instansieras med typen av tecken de skall hantera och teckentypens egenskaper. Här orienteras endast om grundläggande användning av strömklasserna instansierade för teckentypen char. Strömmar kan även ses som sekvenser av element (tecken) och det finns strömiteratorer i standardbiblioteket, dessa visas senare vid avhandling av iteratorer. Bild 223 stream-klasser basic_streambuf<> streambuf ios_base basic_ios<> ios basic_filebuf<> filebuf basic_stringbuf<> stringbuf basic_ostream<> ostream basic_istream<> istream basic_ofstream<> ofstream basic_ostringstream<> ostringstream ostrstream basic_fstream<> fstream basic_iostream<> iostream basic_ifstream<> ifstream basic_istringstream<> istringstream istrstream basic_stringstream<> stringstream Bild 224 5

Strömstatus class ios { public: typedef. iostate; static const iostate goodbit,eofbit,failbit,badbit; bool good() const; // Allt ok bool eof() const; // eof har inträffat bool fail() const; // Icke-fatalt fel har inträffat bool bad() const; // Fatalt fel har inträffat iostate rdstate() const; // Returnerar iostate void clear(iostate f=goodbit); // Sätter en viss bit void setstate(iostate f); // Sätter hela iostate operator void *() const; // Icke-null om good()==true bool operator!() const; // true om fail()==true class failure; // Undantagsklass void exceptions(iostate forwhat); // Begäran om undantagsgenerering typedef. int_type; // Teckentypen som int typedef. streamsize; // Typen för antalsangivelser. Bild 225 Output Medlemmar hos ostream: ostream& put(char c); ostream& write(const char *ptr, streamsize n); Överlagringar av operator<<: ostream& operator<<(ostream& os, int i); ostream& operator<<(ostream& os, double d); osv. Komplettering för en egendefinierad typ: class Rat{ int num, denum; friend ostream& operator<<(ostream& os, const Rat& r); ; ostream& operator<<(ostream& os, const Rat& r){ return os << r.num << / << r.denum; Bild 226 6

Input Medlemmar hos istream: int_type get(); istream& get(char& c); istream& get(char *ptr, streamsize n, char delim= \n ); istream& getline(char *p, streamsize n, char delim= \n ); istream& ignore(streamsize n=1, int_type del=eof); istream& read(char *ptr, streamsize n); istream& putback(char c); istream& unget(); int_type peek(); Överlagringar av operator>>: ostream& operator>>(ostream& os, int& i); ostream& operator>>(ostream& os, double& d); osv. operator>> kan även överlagras för egnendefinierade typer (se nästa bild) I <string> definieras även istream& getline(istream& is, string& str); istream& getline(istream& is, string& str, char delim); Bild 227 Exempel: operator>> för Rational istream& operator>>(istream& is, Rational& r){ int n, d; if (is >> n) { char sep; is >> sep >> d; if (sep!= '/') is.clear(ios_base::badbit); if (is) r=rational(n, d); return is; Exempel på användning: int main(){ Rational r1; cout << "r1: "; while (!(cin >> r1)){ cin.clear(); cin.ignore(256, '\n'); cout << "Fel, försk igen: "; Bild 228 7

Manipulatorer T.ex.: cout << setw(3) << setfill( - ) << 5; ger utskriften --5 Exempel på manipulatorer: #include <iomanip> setw(int w) // Nästa utskrift med bredden w setfill(char c) // Utfyllnad med c left // Vänsterjusterat right // Högerjusterat setprecision(int p) // p siffrors noggranhet setbase(int b) // Heltal skrivs ut i basen b (8,10 el. 16) endl // Lägg till \n och skriv ut flush // Skriv ut bufferten Bild 229 Filströmmar Filströmmar definieras i headerfilen <fstream> Klasserna heter ifstream, ofstream resp fstream. Medlemsfunktionerna är: void open(const char *, ios_base::openmode=); bool is_open(), void close(); Exempel: fstream fil; fil.open( prog.cpp ); // Argumentet är en const char * eller fil.open( prog.cpp, ios_base::in ios_base::out); Flaggor för angivande av mode kombineras alltså med och kan vara: ios_base::in öppna för läsning ios_base::out öppna för skrivning ios_base::ate positionera vid filslutet ios_base::app öppna för tillägg på slutet ios_base::trunc ta bort innehållet ios_base::binary binärfil Om mode inte anges öppnas filen i default-mode (ifstream i in-mode o.s.v.) Det finns också konstruktorer som tar samma argument som open och öppnar filen ifstream fil( minfil.txt ); Destruktorer stänger öppna filer. Bild 230 8

Strängströmmar Strängströmmar definieras i headerfilen <sstream> ostringstream: Man kan skriva till en ostringstream som om den vore en ostream: ostringstream ostr; int i=133; ostr << Summa = << i; Man kan få en kopia av den resulterande strängen genom medlemsfunktionen str(): string s=ostr.str(); istringstream: Man kan läsa från en istringstream som om den vore en istream. Den kan kopplas till en sträng i konstruktorn eller genom att få en ny sträng genom anrop av medlemsfunktionen str(string): string line= 9:41 ; int h, m; char c; istringstream istr(line); istr >> h >> c >> m; istr.str( 10:05 ); istr >> h >> c >> m; stringstream: Kan användas för att både skriva till och läsa från (även omväxlande), givetvis kan man endast läsa vad som redan finns i strängen. Bild 231 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 forskat 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 232 9

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 hashtabellimplementeringar att tillgå t.ex. på webben (det finns också en implementering i Stroustrups bok). Bild 233 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 234 10

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 235 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 236 11

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 237 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 238 12

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 239 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 240 13

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 241 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> (se nästa bild). 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) lower_bound(k) upper_bound(k) equal_range(k) count(k) - returnerar iteratorn till paret som identifieras av k - returnerar iteratorn till första paret identifierat av k - returnerar iteratorn till paret efter de som innehåller k - returnerar ett par av iteratorer som avgränsar paren som identifieras av k - returnerar antalet par som identifieras av k Bild 242 14

pair<> Mallen för en struct som omsluter två värden (något förenklad). Tillgänglig genom #include <utility>, ligger i namespace std template <class T1, class T2> struct pair { typedef T1 first_type; typedef T1 second_type; T1 first; T2 second; pair() : first(t1()), second(t2()) { pair(const T1& a, const T2& b):first(a),second(b){ ; template <class T1, class T2> inline bool operator==(const pair<t1,t2>& x, const pair<t1,t2>& y){ template <class T1, class T2> inline bool operator<(const pair<t1, T2>& x, const pair<t1, T2>& y){ return x.first < y.first (!(y.first<x.first)&& x.second < y.second); o.s.v. Hjälpfunktion för implicit instansiering: template <class T1, class T2> inline pair<t1,t2> make_pair(const T1& x,const T2& y){ return pair<t1,t2>(x, y); Obs hur medlemmarna ges default-värden! Bild 243 Ett exempel från standardbiblioteket: pair<>, forts. pair<> kan användas i många situationer, inte bara i map<>. T.ex. om man i inluppen ville ha en typ för skärmkoordinater (d.v.s. x- och y-koordinaten förpackade i en liten struct): #include <utility> namespace inlupp{ typedef pair<int, int> Koord; struct Rectangle{ bool contains(const Koord& point) const throw() { return.. ; ; class.{ void funk(const Rectangle& rect) { Event eve=term.getnextevent(); if (rect.contains(make_pair(eve.wherex, eve.wherey))) eller if (rect.contains(koord(eve.wherex, eve.wherey))) Bild 244 15

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 245 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 246 16

Kategorier av generiska algoritmer Sökning, t.ex.: find(), find_if(), count(), count_if(), binary_search() Sortering, t.ex.: sort(), stable_sort(), partial_sort(), nth_element() Borttag och ersättning, t.ex.: copy(), remove(), remove_if(), reverse() 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 247 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 248 17

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 249 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 250 18

Uppgift: hitta talet 23 i listan lint: Funktioner som predikat list<int>::iterator pos=find(lint.begin(), lint.end(), 23); if (pos!=lint.end()). Uppgift: hitta Stefan i listan lip : vi kan inte använda find, eftersom den använder operator!=, dvs. vi skulle behöva redan ha en pekare 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 251 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 252 19

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. Funktionsobjekt brukar ofta kallas funktorer. 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 253 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 254 20

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 skriv() { 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 fri 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(); // fri 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 255 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 funktor-klassen själv i headerfilen <functional> finns fördefinierade funktorer för de vanliga operationerna och villkoren i headerfilen <functional> finns även adaptorer som genererar de oftast behövliga funktor-klasser 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 256 21

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 257 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 fri unär funktion från en argumentlös medlemsfunktion eller en fri binär funktion från en unär medlemsfunktion anropad via pekare mem_fun_ref - skapar en fri unär funktion från en argumentlös medlemsfunktion eller en fri binär funk. från en unär medlemsfunktion 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 258 22

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 259 Mer om funktoradaptorer Standardbibliotekets funktoradaptorer kan göra om: fria binära funktioner till unära argumentlösa medlemsfunktioner till fria unära funktioner unära medlemsfunktioner till fria binära funktioner Har man andra behov måste man göra egna funktionsobjektklasser. Exempel: class Rectangle{ int x1, y1, x2, y2; public: Rectangle(int xx1, int yy1, int xx2, int yy2): x1(xx1), y1(yy1), x2(xx2), y2(yy2){ bool contains(int x, int y) const{ return x1 <= x && y1 <= y && x<=x2 && y<=y2; ; class Component:public Rectangle{ ; Bild 260 23

Exempel, find_if med egen funktor Vill vi använda find_if för att hitta en komponent på en viss koordinat måste vi göra en egen funktorklass eftersom contains är en binär medlemsfunktion: struct Check:public unary_function<ptr<component>, bool>{ int x, y; Check(int xx, int yy): x(xx), y(yy){ bool operator()(ptr<component> p){ Inte nödvändigt return p->contains(x, y); ;. vector<ptr<component> > comps;. Event eve = term.getnextevent(); vector<ptr<component> >::iterator pos= find_if(comps.begin(), comps.end(), Check(eve.whereX, eve.wherey)); if (pos!= comps.end()) Bild 261 Förberedelse för användning av funktor-adaptorer Vill man slippa göra egna funktorklasser kan man ofta förbereda för att kunna använda funktor-adaptorer: typedef pair<int, int> Koord; class Rectangle{ int x1, y1, x2, y2; public: Rectangle(int xx1, int yy1, int xx2, int yy2): x1(xx1), y1(yy1), x2(xx2), y2(yy2){ bool contains(int x, int y) const{ return x1 <= x && y1 <= y && x<=x2 && y<=y2; bool contains(const Koord k) const{ // Obs! Ej referens ; return contains(k.first, k.second); class Component:public Rectangle{ ; Bild 262 24

find_if med funktor-adaptorer. vector<ptr<component> > comps;. Event eve = term.getnextevent(); vector<ptr<component> >::iterator pos= find_if(comps.begin(), comps.end(), bind2nd(mem_fun(&component::in_me), Koord(eve.whereX, eve.wherey))); Bild 263 Två problem Referens-till-referens-problemet Det är förbjudet att bilda typer som blir referenser till referenser. Dessvärre inträffar detta när man kombinerar t.ex. bind2nd och mem_fun och den medlemsfunktion man gör om tar ett argument som är referens. T.ex. skulle detta inträffa om vi i tidigare exempel hade bool Rectable::contains(const Koord& k); Problemet att få mem_fun att acceptera Ptr<> istället för pekare De funktor-klasser som skapas vid användning av mem_fun förväntar sig pekare, inte klasser som beter sig som pekare. Det blir fel vid instansiering av klasserna om det inte går att implicit omvandla ett Ptr<>-objekt till en vanlig pekare. Detta är anledningen till att jag föreslog en konverteringsoperator till vanlig pekare i Ptr<>-mallen: template <class T> class Ptr{ operator T*() const throw() { return ptr; Bild 264 25

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 265 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 266 26

iostream-iteratorer Strömmar (t.ex. 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). ostream_iterator har även en konstruktor som tar som andra argument en sträng som skrivs ut mellan elementen (kan t.ex. vara mellanslag eller \n ). Exempel på nästa bild. Bild 267 Exempel på iostream-iteratorer #include <fstream> // Filströmmar, inkluderar iostream #include <vector> #include <algorithm> #include <iterator> int main(){ ifstream fil("tal.txt"); // Textfil med en massa tal 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 copy(vi.begin(),vi.end(),ostream_iterator<int>(cout, )); Bild 268 27

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()); eller i ett steg: lip.erase(remove_if(lip.begin(), lip.end(), DennaPers( Jozef ), lip.end()); Bild 269 28