CD5250 -
Objektorienterad programutveckling med C++, 5 poäng, period 4,
2002
STL-introduktion
intro
STL är en förkortning av standard template library. Det är ett antal klasser som erbjuder funktionalitet
för att lösa vanliga problem som programmerare ställs inför. Dessa klasser underlättar implementationsdelen vid skapande
av program. Istället för att implementera länkade listor, stackar m.m. för varje enskilt program, så har skaparna av
STL gjort ett antal klasser som oftast är tillräckligt generella för att kunna återanvändas i alla program
som använder sig av de vanligaste algoritmerna. Dessa klasser har dessutom fördelen att vara effektiva och
väl testade. STL gör att programmeraren kan koncentrera sig på att välja bra datastrukturer istället för att
bekymra sig om hur han/hon skall implementera dessa.
Som namnet antyder så är STL baserat på templates. Templates tas upp grundligare först en bit in på kursen,
därför kommer vi inte att gå in på så mycket detaljer om de olika delarna i STL. Vi kommer här att
gå igenom ett antal exempel på hur man använder STL i enklare fall. Där STL skall användas i labbarna kommer
dess deklarationer finnas beskrivna i labbuppgiften. Det krävs dock en del studerande för att få en förståelse
för hur STL fungerar och skall användas och det är därför viktigt att förstå de nedan följande exemplen.
A.
string
Stränghantering är väldigt krångligt i C, man måste allokera minne, avallokera dessa korrekt, krångligt att
skapa sammanfogade strängar, skicka char -pekare o.s.v. I STL finns klassen string som hanterar
alla möjliga önskvärda funktioner som rör strängar.
Det är mycket enkelt att hantera strängar med STL.
Här nedan följer ett utdrag av delar av gränssnittet för string-klassen, efteråt följer några enkla strängexempel.
I dessa exempel så är s, x och y string-objekt, dessutom är k och n heltal (int) |
s = x |
Kopierar innehållet i sträng x till sträng s |
s += x |
Lägger till x i slutet av s |
s = x + y |
Tilldelning av den sammanslagna strängen x+y. Varken x eller y påverkas av denna operation |
s == x, s != x |
Likhetsjämförelser. Jämför sträng s med sträng x |
s.append(x) |
Lägger till x i slutet av s (samma som "+=") |
s[k] |
Returnerar den bokstav (char) som finns på position k i strängen s (samma som "at").
Fungerar precis som vanlig array-indexering |
s.at(k) |
Returnerar den bokstav (char) som finns på position k i strängen s |
s.c_str() |
Returnerar en "char * " motsvarande strängen i s |
s.find(x) |
Letar efter en förekomst av x i strängen s. Returnerar positionen för första bokstaven |
s.length() |
Returnerar längden på s (int) |
s.substr(k,n) |
Returnerar ett nytt string-objekt som är en delsträng av s, med början i position k och längden n |
cout << s |
Skriver ut s till skärmen |
cin >> s |
Läser in tecken från tangentbordet, slutar vid vitt tecken
(mellanslag, nyrad och tab kallas för whitespace) |
getline(cin, s) |
Läser in en hel rad till s från cin.
Observera att getline är en vanlig funktion och inte en medlemsfunktion |
Rutan ovanför visar bara upp en liten del av vad som kan göras med string-objekten och är därför
långt ifrån en komplett sammanställning.
#include <string>
#include <iostream>
using namespace std;
void main()
{
string str;
str = "En STL-sträng";
cout << str;
}
|
Exempel 1.Enkel hantering av STL-strängar. |
Detta exempel skriver inte helt oväntat ut strängen "En STL-sträng". Vi utökar denna till att skriva ut ett
sammansatt meddelande.
#include <string>
#include <iostream>
using namespace std;
void main()
{
string str("En STL-sträng. ");
str.append("En till.");
cout << str;
}
|
Exempel 2.Nästan lika enkel hantering av STL-strängar. |
string-klassen sammanfogar strängarna till en enda sträng. Medlemsfunktionen append har
även en synonym. Följande rader är således ekvivalenta
str.append("En till.");
och
str += "En till.";
#include <string>
#include <iostream>
using namespace std;
void func(string);
void main()
{
string str("En STL-sträng. ");
func(str);
}
void func(string param1)
{
cout << param1;
}
|
Exempel 3.Skicka strängar som parametrar |
Det är lätt att skicka strängar som parametrar till en funktion. Strängklassen tar automatiskt hand om
eventuell kopiering av strängar m.m.
B.
vector
Behållaren vector är en motsvarighet till en array i C. Fördelen med vector är att
den kan växa dynamiskt. Det innebär att ingen bestämd storlek behöver sättas och att vektorn inte kan bli full.
Den hanterar automatiskt minnesallokering för de dataelement som läggs in i vektorn.
Här nedan följer en kort sammanställning av några av de vanligast använda medlemsfunktionerna i vector.
I dessa exempel så är v ett vektor-objekt, x ett element (motsvarande int i vector<int>, eller float i vector<float>)
string-objekt, k är ett heltal (int) |
v[k] |
Returnerar det element som finns på plats k i vektorn v (samma som medlemsfunktionen "at") |
v.at(k) |
Returnerar det element som finns på plats k i vektorn v |
v.begin() |
Returnerar en iterator till det första elementet i vektorn.
Detta beskrivs noggrannare längre fram i kursen |
v.clear() |
Tömmer vektor v på alla element |
v.push_back(x) |
Lägger till ett element sist i vektorn |
v.reserve(k) |
Reserverar ett antal platser i vektorn,
vilket gör att man kan använda vektorn som en vanlig array med "[]" indexering |
v.size() |
Returnerar antalet element i vektorn |
För att illustrera när en vector ger uppenbara fördelar framför arrayer med fasta storlekar skall
vi läsa in en fil med ett okänt antal datavärden och lagra dessa i minnet.
#include <vector> // Denna fil behövs för att kunna använda vector-klassen
#include <iostream> // Deklarationer för streams (cin, cout m.m.)
#include <fstream> // Deklarationer för filhantering
// detta gör att vi kan använda STL-klasserna
using namespace std;
void main()
{
// öppna en fil från c:\
ifstream data("c:\\values.dat");
// skapa en vektor som skall innehålla double-värden.
vector<double> vektor;
// fortsätt så länge 'data' inte har nått slutet på dataströmmen
while(!data.eof())
{
// variabler kan deklareras där de behövs i C++
double value;
// läs in en double från 'data' till 'value'
data >> value;
// lägg till det inlästa talet 'value' på sista plats i vektorn
vektor.push_back(value);
}
}
|
Exempel 4.Inläsning av en fil med okänt antal datavärden till en vector. |
Om vi nu i efterhand vill ändra på programmet så att det läser in sina datavärden från tangentbordet istället
för från en fil så behövs endast en liten justering. Byt ut raden
ifstream data("c:\\values.dat");
mot
istream & data = cin;
Observera skillnaden mellan ifstream och istream! Tryck Ctrl-Z för att avsluta inmatningen från tangentbordet
(Ctrl-Z innebär samma sak som 'end-of-file' vilket får while-satsen att avsluta). cin är ett globalt objekt för
inläsning från tangentbordet.
Programmet har nu gjort en inläsning av alla tal som fanns i filen values.dat eller som matats in från
tangenbordet utan att vi har behövt bekymra oss om hur många som lästs in. För att kontrollera att alla tal
verkligen finns i vektorn, kan vi skriva ett program som skriver ned alla tal i vektorn på en fil.
#include <vector>
using namespace std;
void main()
{
ifstream data("c:\\values.dat");
vector<double> vektor;
while(!data.eof())
{
double value;
data >> value;
vektor.push_back(value);
}
ofstream ut("c:\\values2.dat");
for(int i=0; i<vektor.size; i++)
{
ut << vektor.at(i);
}
}
|
Exempel 5.Inläsning av en fil med okänt antal datavärden till en vektor, samt nedskrivning av dessa till en ny fil. |
Om vi här vill ändra utskriften av värdena till att skriva ut dem på skärmen istället för på en fil så kan vi även här
åstadkomma detta med en väldigt enkel förändring av programmet. But ut raden
ofstream ut("c:\\values2.dat");
mot
ostream & ut = cout;
Observera även här skillnaden mellan ofstream och ostream!
Mer information om STL och vektorer hittar ni exempelvis i STL-referensen i Visual C++.
|