rand (randomize) genererar slumptal
Ex: a =rand % 10 (ger slumptal inom 0 till 10
#include <cstdlib>
srand ger variation #include
<ctime> Ex: (srand(time(0));
#include <iostream>
Man kan läsa in flera variabler med cin >> på samma rad. cin läser in talet,
men inte blanktecken. När man gör inmatning kan man antingen trycka enter
eller göra ett mellanslag.
variabelnamn = uttryck (variabelnamn- bokstäverna a- z + siffror, men man
får inte börja med en siffra. Man skiljer mellan
versaler och gemener.)
Uttrycket till höger måste ha samma typ som variabeln eller automatiskt
kunna omvandlas till denna typ. Automatisk typomvandling finns för bl. a. aritmetiska typer.
Genom att använda manipulatorer kan redigera utskriften. Detta kan göras
med filen #include <iomanip>.
cout << setiosflags(ios::fixed) <<
setprecision(2); //2 decimaler
Man kan också använda bara fixed som är enklare att använd:
cout << fixed << setprecision(2); //2 decimaler
Standardvärdet för setprecision är 6 decimaler. Man behöver bara ange det en gång
sedan kommer efterföljande värden att skrivas ut med samma precision om inte
annat anges exv. cout << resetiosflags(ios::fixed);
setw() = utskriftspositionering
Några
vanliga manipulatorer i utskrifter #include <iomanip>
endl radframmatning
setprecision(n) utskift av tal på formen fixed skall ske med n st decimaler
n=0 betyder standard
fixed eller
setiosflags(ios::fixed) reella tal skall skrivas ut med fast decimalpunkt och decimaler
resetiosflags(ios::fixed) återgång till standard utskrift
setw(n) nästa utskrift skall ske med n st positioner
setfill(’x’) i nästa utskrift skall utfyllnad ske med tecknet ’x’ i stället
för blanka.
Typer som används för att skriva vanliga tal kallas aritmetiska typer. int, double, long
etc ’är sådana typer. När man använder aritmetiska typer kan man använda de vanliga mate-
matiska operationerna. De två operanderna får vara av olika typer. Resultatet av ett uttryck
bestäms av de ingående operandernas typer. Har båda operanderna samma typ blir det den typen som resultat. Om en av operanderna är ett reellt tal och den andra ett heltal blir resultatet ett reellt tal. Vid division med heltal huggs decimalbiten bort.
Det finns unära
varianter av operatorerna + och -. Ex k = -1;
k * (-3);
Arithmetic operators ( +, -, *, /, % )
+ addition
- subtraction
* multiplication
/ division
% module
Tilldelnings operatorer (+=, -=, *=, /=, %=, >>=, <<=,
&=, ^=, |=)
a++
eller a-- (postfix) ++a
eller --a (prefix) ökar a:s värde
med 1 resp minskar med 1
TYPKONVERTERING
Implicit
innebär att något utförs utan att man bett om det. Ibland kan det vara bra att
man
själv kan bestämma när typkonvertering skall ske.
Detta görs med så kallad explicit
typkonvertering.
Avrunding, heltal till character, float till int etc.
Alla funktioner returnerar ett reellt tal. Argumenten x och y skall också vara rella tal.
exp(x) ger e upphöjt till x
log(x) ger den naturliga logaritmen (ln) av x
log10(x) ger den vanliga logaritmen (basen 10) av x
sqrt(x) roten ur x
ceil(x) ger det minsta hela tal som är >= x
floor(x) ger det största hela tal som är <= x
fabs(x) absolutvärdet av x
pow(x,y) x upphöjt med y (om x är neg måste y vara ett helt tal)
fmod(x,y) ger resten vid division x/y
sin(x), cos(x), tan(x) x anges i radianer
asin(x), acos(x), atan(x) ger arcsin etc
epsilon skrivs 1e-6 t.ex. och betyder 1
x 10-6
Aritmetiska funktioner i biblioteket cstdlib
abs(n) n har typen int. Ger absolutvärdet av n
labs(n) n har typen long int. Ger absolutvärdet av n
It is used to execute an instruction or block
of instructions only if a condition is fulfilled. Its form is:
if (condition) statement
where condition is the
expression that is being evaluated. If this condition is true, statement is executed. If it is
false, statement is ignored (not executed) and the program continues on
the next instruction after the conditional structure.
For example, the following code
fragment prints out x is
100 only if the
value stored in variable x is indeed 100:
if (x == 100)
cout <<
"x is 100";
If we want more than a single
instruction to be executed in case that condition is true we can specify a block of instructions
using curly brackets { }:
if (x == 100)
{
cout <<
"x is ";
cout << x;
}
We can additionally specify what we
want that happens if the condition is not fulfilled by using the keyword else.
Its form used in conjunction with if is:
if (condition) statement1 else statement2
For example:
if (x == 100)
cout <<
"x is 100";
else
cout <<
"x is not 100";
prints out on the screen x is 100 if indeed x is worth 100, but if it is not
-and only if not- it prints out x is not 100.
The if + else structures
can be concatenated with the intention of verifying a range of values. The
following example shows its use telling if the present value stored in x is positive, negative or none of the previous,
that is to say, equal to zero.
if (x > 0)
cout <<
"x is positive";
else if (x < 0)
cout <<
"x is negative";
else
cout <<
"x is 0";
Remember that in case we want more
than a single instruction to be executed, we must group them in a block of
instructions by using curly brackets { }.
Relational operators ( ==, !=, >,
<, >=, <= ) (Jämförelse operatorer)
== Equal
!= Different (not equal)
> Greater than
< Less than
>= Greater or equal than
<= Less or equal than
Logic operators ( !, &&, || ).
!(5 == 5) returns false because
the expression at its right (5 == 5) would be true.
!(6 <= 4) returns true
because (6 <= 4) would be false.
!true returns false.
!false returns true.
AND &&
OR ||
( (5 == 5) && (3 >
6) ) returns false ( true && false ).
( (5 == 5) || (3 > 6)) returns
true ( true || false ).
Conditional operator ( ? ).
condition ? result1 : result2
if condition is true the
expression will return result1, if not it will return result2.
7==5 ? 4 : 3 returns 3 since 7 is not equal to 5.
7==5+2 ? 4 : 3 returns 4 since 7 is equal to 5+2.
5>3 ? a : b returns a, since 5 is greater than 3.
a>b ? a : b returns the greater one, a or b.
Bitwise Operators ( &, |, ^, ~, <<, >>
).
Bitwise operators modify
variables considering the bits that represent the values that they store,
that means, their binary
representation.
operand asm
Description
&
AND Logical AND
| OR Logical OR
^ XOR Logical exclusive OR
~ NOT Complement to one (bit inversion)
<< SHL Shift Left
>> SHR Shift Right
a = sizeof (char);
while(uttryck) while(uttryck)
sats; {
en eller
flera satser
}
do
{
en eller
flera satser
} while
(uttryck);
The for loop.
Its format is:
for (initialization; condition; increase) statement;
and its main function is to repeat statement
while condition remains true, like the while loop. But in
addition, for provides places to specify an initialization
instruction and an increase instruction. So this loop is specially
designed to perform a repetitive action with a counter.
It works the following way:
1, initialization is
executed. Generally it is an initial value setting for a counter varible. This
is executed only once.
2, condition is checked, if it is true the
loop continues, otherwise the loop finishes and statement is skipped.
3, statement is executed. As usual, it can be either a single
instruction or a block of instructions enclosed within curly brackets { }.
4, finally, whatever is specified in the increase field is executed and
the loop gets back to step 2.
Man kan
initiera fler variabler ex for(int g, int i; i<=n;
i++)
r*= x;
samma som:
for(int g, int i; i<=n; i++, r*=x)
; //tom sats
FÄLT:
int f[5];
//ett fält med 5 variabler av typen int
int f[5] = {0 , 1, 2, 3, 4}; //ett fält med 5 int som getts värde
int[] = {i+1, 5, k-3,4,7}; //ett fält med 5
variabler
Sekvenser – list, vector och deque
I C++ finns ett antal standrdklasser, egentligen mallar som man kan använda.Sådana
klasser kalla containerclasser. Fördelen med detta är att man slipper skriva all kod
själv utan man kan använda väl utprövade rutiner eller program kod.
#include <vector> #inlcude
<list> #include <deque>
Ex: vector <double> v; //en vector av
typen double
vector <int> i(3); //ett fält av
integer som initieras med 0 på varje element
Man kan också kopiera en vector till en
annan. Ex vector <double> v4(v3);
Här blir v4 en kopia av v3.
sekv <typ> s; skapar en sekvens med längden noll.
sekv <typ> s(n); skapar en sekvens med längden n. Initiering
med nollor
sekv <typ> s(n,e); skapar en sekvens med längden n.
Initiering med e
sekv <typ> s(t); skapar en sekvens som blir en kopia av
sekvensen t
s=t; tilldelning. s blir lika med t
s.assign(n, e); tilldelning. s får längden n: Alla elementen
får värdet e
s.clear(); tar bort alla
elementen i s. Längden blir noll
s.size(); ger antalet
element i s.
s.empty(); ger true om antalet
element i s är 0, annars false
s.push_back(e); lägger till element e sist i sekvensen s
s.pop_back(); tar bort det sista elementet i s
s.push_front(e); lägger till elementet e först i s (FINNS EJ
FÖR VECTOR)
s.front(); ger det första
elementet i s
s.back(); ger det sista
elementet i s
s.resize(n); ändrar s:s längd
till n. Fyller ut med nollor om den nya
längden är större, annars kapar på slutet
s.resize(n,e); som ovan men fyller ut
med e i stället för nollor
s==t ; jämförelse enligt alfabetiska
principer
s != t; samma som
ovan
s < t osv samma som ovan
v[i] indexering utan indexkontroll (EJ FÖR LIST)
v.at(i); indexering
med indexkontroll (EJ FÖR LIST)
v.capacity(n); ger det interna fältets storlek (BARA FÖR
VECTOR)
v.reserve(n); anger om man kommer att
behöva plats för n st element
(BARA FÖR VECTOR)
\n new line ny rad
\a alert ger
ljudsignal
\b backspace ett
steg åt vänster
\r return ny rad
\f form feed nästa sida
\t tab till
nästa tabulatorstopp
\v vertical tab
\nnn tecknet med octal kod (nnn)
\xnn tecknet
med hexadecimalt (nn)
teck1 = '\'' ;/använd
backslash för att skriva enkelt och dubbelt
teck2 = '\\' ;//citations
tecken + backslash
teck3 = '\"';
cout << c; skriver ut c
cout.put(c) skriver ut c
cin >> c läser in c tills
blanktecken
cin.get(c) läser in nästa
tecken även blanka
cin.peek() tjuvtittar på nästa
tecken
cin.ignore()
cin.ignore(n,c) läser och hoppar över n st tecken
cin >>ws hoppar över vita
tecken
cin.getline(a,n) läser in a längd n (lägger själv till 0
tecknet)
cin >> setw(n) inläsning med högst n-1 tecken
while(cin.get(teck)) läser in ett tecken även blanka
cin.peek() tjuvtittar
på nästa tecken som matas in
cin.ignore(100,'x') hoppar över 100 st x bokstäver
cin >> ws enklare
att använda = hoppar över alla blanka tecken
while(cin.peek() == ' ' ||
cin.peek() == 't' || cin.peek() == '\n')
cin.ignore (); //hoppar över
alla vita tecken, tab och radmatning
getline(cin,text) läser in en hel rad
Vid initiering av sträng string s1
=("Peter Pan") eller string s2(s1);
string s3(s1, 6, 3) s2 får värdet
"Pan" = 3 teck långt, börja med pos 6 start med pos 0
string s4(5, 'x'); //skriver ut xxxxx
string s5("Peter Pan", 5);
//skriver ut Peter
När man inte initierar en sträng
//man
kan använda s2.assign(s1, 6, 3);
s1.assign(s4,2,3);
s2.append("Hejsan",
4 ,2);
cout << s2+s1 << endl;
s2.erase(2, 10) //raderar från 2 och 10 bokstäver
string
name = ”Levi Johansson”;
cout
<< name.size(); // ger svaret 14
string s1 = "subklasser och
superklasser";
string s2 = "klass";
s1.find(s2); //ger värdet 3 string::npos saknas likhet
s1.find("ser"); //ger värdet 7
s1.find('a'); //ger värdet 5
s1.find("som"); // ger
string::npos
string s; deklaration
av text – noll längd
string s=t deklaration
med initiering s blir kopia av t
string s(t); deklaration
med initiering s blir kopia av t
string(s(s2, p,n); dekl,
initierar s med n tecken från pos p i s2
string s(x,n); dekl,
initierar s med de n första tecken från x
string s(n,c); dekl
med initiering s tilldelas n tecken från
c
s=t tilldelning
s.assign(t) tilldelar
t till s
s.assign(s2, p, n) tilldelar
s n tecken från pos p i s2
s.assign( x,n) tilldelar
s de n första tecknen från x
s.assign(n,c) tilldelar
n st c:n till s
s[k] indexering
utan indexeringskontroll
s.at(k) indexering
med indexkontroll
s.substr(k,n) ger
del av s med början i pos k och längden n
cout << s skriver
ut s
cin >> s läser
in s (slutar vid blanktecken)
getline(cin,s) läser
in en hel rad till s, ger true om det gick bra
s.erase(k,n) tar
bort n tecken ur s med början i pos k
s.clear() tar
bort alla tecken ur s
s.size() ger
längden av s
s.resize(n) ändrar s längd till n, fyller ut med nollor om längre annars kapas s
s.resize(n,c) som
ovan men fyller ut med c i stället för nollor
s.capacity(n) ger
det interna fältets storlek
s.reserve(n) anger
att man behöver plats för n st tecken
s.c_str() ger
en pekare till en textsträng med variabelns text
s<t s>t
t==t + jämförelser, ej alfabetiskt korrekta
s.append(t) lägger
till t sist i s
s.append(s2,p,n) lägger
till n tecken från pos p i s2 till s
s.append(x,n) lägger
till de n första tecknen från x till s
s.append(n,c) lägger
till de n st c:n till s
s.insert(k,t) skjuter
in t i pos k i s
s.insert(k,s2,p,n) skjuter
in n tecken från pos p i s2 till pos k i s
s.insert(k,x, n) skjuter
in de n första tecken från x i pos k i s
s.insert(k,n, c) skjuter
in de n st c:n i pos k i s
s.replace(k,m,t)
ersätter tecken k till k+m-1 i s med t
s.replace(k,m,s2,p,n
ersätter tecken nr k till k+m-1 i s
s.replace(k,m,x,t)
ersätter tecken nr k till k+m-1 i s
med
de första tecken från x
s.replace(k,m,n,c)
ersätter tecken k till k+m-1 i s med n st c:n
s.find(t ) söker
i s efter text t, annars string::npos
s.find(t,p) söker
i s efter text t, i pos p
s.find(c,n) söker
i s efter n st c:n
s.refind() som
fin men söker bakifrån
s.find_first_of(t) söker
efter första förekomsten av något tecken i
t
s.find_first_of(t,p)
som ovan men börjar i pos p
s.find_last_of(t) som find_first_of(t) men söker bakifrån
s.find_first_not_of(t) söker
i s efter första tecken som inte finns i t
Ger
samma resultatvärde som find()
s.find_first_not_of(t,p) som
ovan men börjar i pos p
s.find_last_not_of(t,p) som
ovan men börjar bakifrån
#include <cstring>
strcpy(s1,s2) kopierar s2 till s1 –
ingen kontroll om plats finns
strncpy(s1,s2,n) kopierar s2 till s1 – max n stycken tecken
inkl 0 teck
strcat(s1,s2) lägger kopia av s2 sist
i s1 – ingen kontroll om plats finns
strncat(s1,s2) lägger kopia av s2 sist i s1 – max n
tecken, s1 avslutas med 0
tecken
strcmp(s1,s2) jämför 2 textsträngar – resultat <0 om
s1<s2, 0 om s1=s2
och >0 om s1>s2
strncmp(s1,s2,n) som ovan – jämför högst n tecken
strlen(s) ger längden av
textsträngen s (exklusive nolltecken)
atoi() omvandlar
från const tecken till int
Ett funktionsanrop betraktas som ett uttryck. Call by value eller värdeanrop innebär att funktionen inte ändrar de parametrar som stoppas in. Argumenten eller parametrarna kopieras och behåller sina värden när funktionen har avslutats.
Ett program kan bestå av flera filer. Varje fil är ett par bestående av definitionsfil och inkluderingsfil. En definitionsfil innehåller funktionsdefinitioner. En inkluderingsfil innehåller funktionsdefinitioner. Inkluderingsfilen inkluderas i motsvarande definitionsfil samt i andra filer där de aktuella funktionerna skall anropas.
funktionsnamn(typ& p);
//deklaration
funktionsnamn(a); //anrop
Parametern p blir en referens till argumentet a. Man kan säga en alias för p.
En funktion som direkt eller indirekt anropar sig själv. Vid exekveringen finns det lika många upplagor av funktionen som antalet gjorda oavslutade anrop. Varje upplaga av funktionen har egna unika värden på formella parametrar och loka variabler.
float, double long double
#include <cfloat> innehåller diverse info om de olika standardtyperna för reella tal. Vill man ha reda på största och minsta tal som kan lagras och nogrannhet finns konstanterna DBL_DIG, DBL_MAX,DBL_MIN FLT_DIG, FLT_MAX, FLT_MIN för float och LDBL_DIG, LDBL_MAX,LDBL_MIN för long double
sizeof
sizeof(long double)
long double ldbl=334.89878;
Man kan också skriva cout << "variabeln ldbl är " << sizeof ldbl <<"bytes" << endl;
För att beräkna antalet element i ett fält kan man göra sizeof a/ sizeof a[0];
Man dividerar hela fältet med längden på ett element
typ* p; p får typen pekare
p = &a; p tilldelas adressen till a
*p betyder det p pekar på
p1 = p2 p1 kommer att peka på samma som p2
*p = *p det p1 pekar på kommer att ändras
Pekare och fält
float f[4];
float * pd;
pd = f[0]; pekaren pd pekar på första elementet
pd = f; enklare sätt – samma sak
cout << *pd; skriver ut första elementet
cout<< *(pd+3); skriver ut element nr 3
cout<< pd[3]; samma som ovanstående
f[3] samma som *(pd+3) samma som 3[f] samma som
*(3+f)
typ a[..]
Fältnamnet a omvandlas
automatiskt till typen * I uttryck, och värdet blir en pekare till fältets
första element. Gäller tex tilldelningar och funktionsanrop.
Fält som parametrar kan
deklareras antingen som typ a[] eller typ *a
Pekararimetik: p+n betyder
pekarvärdet p plus n steg
*(p+n) är samma sak som p[n]
*(a+n) är samma sak som a[n]
Text lagras i tecken fält som har typen char. Vi kan göra char text[50] eller char namn[] =”Sara”; Variabeln namn kommer aoutomatiskt att få längden 5 och avslutas med nolltecken. Precis som för andra fält sker typomvandling till pekare. Här blir det pekare till char, dvs char *
Vi kan därför skriva char *p; p=namn;
Man kan enklare skriva char * namn=”Sara;
Exempel:
char namn[] = "Sara";
char *p;
p =namn;
cout << namn << " " << p << endl;
char *pnamn = " Svensson"; //kan man också göra
cout << namn << pnamn << endl;
//vill man skriva ut enskilda tecken måste man avreferera pekaren
cout << *(p) << pnamn << endl; //skriver ut S Svensson
//man kan också ha konstant pekare till char
const char *q ="Hanna"; //går inte att ändra det pekaren pekar på
//Det är vanligt att man använder pekare i stället för indexering
//i text strängar
void skriv(const char *pp[], int i) //pp är
const pekare till pekare eller char **pp
{
cout << pp[i] << endl;
}
//ett fält med pekare (array) eller vektor
const
char *medd[4] = {"Meddelande 1", "Detta är meddelande
nummer 2",
"detta är ännu ett
meddelande", "NU ÄR MEDDELANDEN SLUT"};
for(int i =0; i<4; i++)
skriv(medd ,i); //skriver alla meddelanden
skriv (medd,3); //skriver sista meddelandet
double *pd; //en pekare av typen double
pd = new double; //den variabel man
allokerar minne för saknar namn
//enda sättet att komma åt variablen är
via pekaren
//för att tilldela värde till pekaren kan
vi skriva
*pd = 2.5; //initiera det allokerade
minnesutrymmet
int n=10;
int *pi = new int[n]; //fält med pekare
till int
//för att initera fältet kan vi skriva
for(int i=0; i<n; i++)
{
pi[i]=0;
cout << pi[i]<< endl;
}
//för att radera pekare som skapats med
new
delete [] pd; //obs skrivsättet
//för att radera en enskild pekare delete
p;
p = new typ; allokerar
plats för en variabel av typen typ
p = new typ[n]; allokerar
plats för ett fält med n st element av typen typ
p
pekar på fältet
delete p frigör
det som p pekar på (inte fält)
delete [] p; frigör
det fält som p pekar på
int a = 10;
int &ra = a; ra är en alias till a
enklare att arbeta med referenser än pekare. Referenser fungerar inte
med fält.
float (*pf) (int); Att det är en pekare till en funktion ser
man på parentesen.
Har vi en funktion float g(int);
pf=g; Tilldelning av funktionen g till pekaren pf. OBS pf får bara peka
funktioner som har en float till returvärde och en int som parameter.
void (*pf2) (double, char *, int); = pf2 är en pekare till en funktion
som har tre parametrar – en double, en pekare till en char och en int och som
inte ger något returvärde.
Man kan äver bilda fält med funktionspekare. float (*apf[10]) (int);
typedef unsigned long
int ulong; skriv bara sedan ulong a;
typedef unsigned short
int ushort; ushort b;
typedef falt[12]; falt a;
atypedef float (*funk_pek)
(int); funk_pek
apf;
Automatiska typomvandlingar
Vid aritmetiska uttryck
Omvandling till och från
bool
Fält och funktioner –
automatiskt till fältets första element resp pekare till funktionen
Tom pekare
Pekare till void
Klasser
Explicita typomvandlingar
Antingen (typnamn) uttryck
eller formen typnamn(uttryck)
Den första formen är från C
och kallas cast
Exempel:
char *p = (char*) 07650;
//heltal till pekare
long int i = (long int) p;//pekare till heltal
double *q = (double *) p;
//pekare till annan pekare
void *v;
q = (double *) v; //void
pekare till annan pekare
Nyare formen
cout << int(p);
//pekare till heltal
typedef char *text;
cout << text(v);
//void pekare till annan pekare
Bättre igentligen att
använda
dynamic_cast, static_cast,
reinterpret_cast och const_cast
enum farg{gul, rod, bla,gron};
enum veckodag{mandag, tisdag, onsdag, torsdag, fredag, lordag, sondag};
För att skriva ut veckodag kan man använda sig av en tabell.
char *enum_txt[] = {”Måndag”, ”Tisdag”, ”Onsdag”, ”Torsdag”, ”Fredag”, ”Lördag”, ”Söndag”};
veckodag idag;
cout << ”I dag är det ” << enum_text[idag];
I en matris kan man ha olika antal rader men fast antal kolumner.
Grundläggande
begreppet i C++ standard bibliotek för läsning och skrivning är strömmar
istream
ifstream, istringstream, istrstream
Klassen
ios_base iostream fstream, stringsream, strstream
ostream ofstream, ostringstream, ostrstream
cin objektet är från klassen istream
cout är från klassen ostream
cerr och clog
används för felutskrift till skärmen respektive programloggar
cin, cout, cerr och clog är definerade i inkluderingsfilen <ios> och deklarerade i filen <iostream> Basklassen är ios_base
istream
och iostream är def i filen <istream>
och klassen ostream i filen <ostream>
Klasserna
ifstream, ofstream och fstream är def i inkluderingsfilen <fstream>
Inkluderingsfilen
<sstream> innehåller def av
klasserna istringstream, ostringstrem och stringstream. <strstream> är inkluderingsfil för istrstream, ostream och strstream
I
klassen ios finns s.k tre flaggor som anger en ströms tillstånd.
Flagga Indikerar
failbit senaste inläsningen misslyckades
eofbit ett filslut inträffade vid ett tidigare
läsförsök
badbit ett allvarligare fel som har med strömmens
interna buffert att göra
I
klassen ios finns också medlemsfunktioner för att ändra flaggorna
void
clear(); slår
av alla tillståndsflaggorna
bool
good(); ger
true om ingen flagga är satt
bool
fail(); ger
true om failbit eller badbit är satt
bool
eof(); ger
true om eof flagga satt
bool
bad(); ger
true om badbit är satt
operator bool(); ger resultat not fail()
void main()
{
char a;
cin >> a;
if(clog.good()) //bool good() ingen av tillståndsflaggorna är satt
cerr << "Strömmen clog är
OK";
if(cin.fail()) //eller if(!cin)
{
cerr << "Inläsningen
misslyckades";
cin.clear();
}
int n=0;
while(!cin.eof()) //OBS Fel
{
cin.get(a);
n++;
}
cout << "Du skrev "
<< n << " tecken" << endl;
n=0;
while(true) //Korrekt
{
cin.get(a);
if(cin.eof())
break;
n++;
}
cout << "Du skrev "
<< n << " tecken" << endl;
n=0;
while(cin.get(a)) //Enklast och korrekt
n++;
cout << "Du skrev "
<< n << " tecken" << endl;
}
Man kan också använda exceptions
Ex. cin.exceptions(ios::eofbit);
clog.exceptions(ios::badbit|ios::failbit);
Om man användaer exceptions blir de
av klassen ios::failure som är en underklass till exception
Formaterad inmatning görs med >>
Oformaterad med olika medlemsfunktioner
Tänk på att vid formaterad inmatning hoppas blanktecken
över. Matar man in fel kommer in det heller med. Om man läser in text strängar
läggs nolltecken auto-
matiskt in.
Vid inmatning finns manipulatorer. En
del finns i filen <iomanip>
setw(n) anger hur många tecken som ryms i teckenfältet
ws hoppa
fram till nästa icke vita tecken
skipws hoppa alltid över inledande vita tecken när
operatorn >> används
noskipws hoppa inte över inledande vita tecken vid >>
dec indata
av heltalstyp skall tolkas som decimala tal
oct indata
av heltalstyp skall tolkas som oktala tal
hex indata
av heltalstyp skall tolkas koms hexadecimalt
boolalpha indata av typen bool skall
anges som true eller false
formaterad inmatning - hoppar aldrig över vita tecken
gcount() returnerar
antal tecken som lästes av den senaste funktionen för oformaterad inmatning
get() läser
och returnerar nästa tecken i strömmen,
ger EOF vid filslut påträffas
get(c) läser
in nästa tecken i strömmen till variabeln c
getline(s, n, t) Parametern
t har default värdet ‘\n’. Läser in ett antal tecken till teckensträngen s
tills nästa tecken står i tur att läsas är lika med stopptecknet t. Detta läses
men placeas inte i textsträngen s. Inläsningen avbryts tidigare om filslut
påträffaseller om n-1 tecken har lästs. Ett nolltecken placeras automatiskt
sist i s.
read(s, n) Läser
in n st tecken till det teckenfält s pekar på.Om filslut påträffas innan n
tecken kunnat läsas sätts flaggorna eofbit och failbit
readsom(s, n) Som read,
men antalet lästa tecken ges som retur. failbit sätts inte om detta antal är
mindre än n.
ignore(n, t) Parametern
n har default värdet 1 och t default EOF. Hoppar över n st teck i strömmen.
Stannar tidigare om nästa tecken som står i tur att läsas är lika med t eller
filslut påträffas
peek() Returnerar nästa tecken
som står i tur att läsas, men teckent finns kvar som nästa tecken i strömmen.
putback(c) Lägger
tillbaka tecknet c i strömmen.
unget() Lägger tillbaka det
senaste lästa tecknet i strömmen
Utskift till strömmar – formaterad utmatning
left Anger att det utskrivna
värdet skall vänsterjusteras – ev utfyllnads tecken skall läggas till efter utskriften
right Anger att det utskrivna
värdet skall högerjusteras – ev utfyllnads tecken skall läggas till före utskriften
internal Anger
att ev utfyllnadstecken skall läggas till mellan talets tecken och första
siffran
dec Anger
att utskrift av heltal skall ske decimalt
oct Anger
att utskrift av heltal skall vara oktalt
hex Anger
att utskrift sker hexadecimalt
scientific Anger
att utskrift av reella tal skall ske på den flytande formen 999.999e99 Antalet
decimaler ges av precision
fixed Anger
att utskrift av flyttal skall ske på fast form med decimalpunkt.
Antalet
decimaler anges med precision
uppercase Anger
att stora bokstäver skall användas vid flytande utskrifter av reella tal och
vid hexadecimala utskrifter av heltal
boolalpha Anger
att värden av typen bool skall skrivas på formen false, true (ej 0,1)
showbase Anger
att oktala tal skall skrivas ut med inledande nolla och hexadecimala
tal
med ox först
showpoint anger
att decimalpunkt och avslutande decimaler med värdet noll alltid skall
skrivas ut för flyttal
showpos Anger
att positiva tal skrivs ut med + först
unitbuf Anger
att alla tecken skall skrivas ut direkt och inte buffras
width Anger
minsta antalet urskriftspositioner i nästa utskrift
fill Det
tecken som används för utfyllnad om utskriften kräver färre än width st
positioner. Standardvärde är blanktecken
precision Används
vid utskrift av flyttal. Om formen är fixed eller scientific, så anger
precision antalet decimaler, annars anger precision det totala antalet
signifikanta siffror. Standard är 6 st.
left sätter
flaggan ios::left slår av ios::right och ios::internal
right sätter
flaggan ios::right slår av ios::left och ios::internal
internal sätter
flaggan ios::internal slår av ios::right och ios::left
oct sätter
flaggan ios::oct slår av ios::hex och ios::dec
hex sätter
flaggan ios::hex slår av ios::oct och ios::dec
dec sätter
flaggan ios::dec slår av ios::hex och ios::oct
scientific sätter
flaggan ios:: scientific slår av ios::fixed
fixed sätter
flaggan ios::fixed slår av ios:: scientific
uppercase slår på
nouppercase slår av
boolalpha slår
på
noboolalpha slår av
showbase slår på
noshowbase slår av
showpoint slår
på
noshowpoint slår av
showpos slår
på
noshowpos slår av
Manipulatorerna setiosflags och resetiosflags kan användas för att sätta eller slå av godtycklig flagga.
Manipulator
setw(n) Sätter
värdet ios::width till n
setfill(c) Sätter
värdet ios::fill till tecknet c
setprecision(n) Sätter
värdet ios::precision till n
setbase(n) setbase(10)
samma som manipulatorn dec
setbase(8)
samma som manipulatorn oct
setbase(16)
samma som manipulatorn hex
flush Tömmer
utskriftsbufferten
endl Lägger
in radslutstecken i strömmen och tömmer
bufferten
ends Lägger
in nolltecken i strömmen
put(c) Skriver
ut tecknet c till strömmen
write(s, n) Skriver
ut n st tecken till strömmen från det tecken fält s pekar på
flush() Tömmer
utskriftsbufferten
#include <fstream> både läser och skriver <ifstream> läser en fil <ofstream> skriver en fil
ifstream f1("Min fil.txt");
while(f1.get(c))
{
cout.put(c);
if(c == '\n')
n++;
}
eller
ifstream f1;
f1.open ("Min fil.txt");
while(f1.get(c))
{
cout.put(c);
if(c == '\n')
n++;
}
Man kan stänga filen genom att skriva f1.close , men det behövs inte egentligen
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
void main()
{
char namn[100];
cout << "Vilken fil skall öppnas" << endl;
cin >> setw(100) >> namn;
ifstream f1(namn);
if(!f1)
{
cout << "Filen kan ej öppnas " << endl;
exit(EXIT_FAILURE); //EXIT_SUCCESS är motsvarande
}
//man kan använda rdbuf() som ger en pekare till filbufferten
//hela bufferten skrivs ut
cout << f1.rdbuf();
kopiera till en annan fil
//f2 <<f1.rdbuf ();
}
Man kan också deklarera vad man skall göra i början.
ostream fil2(namn, ios::app) //lägger till i slutet på filen
in det skall gå att läsa från filen. Filen skall existera
out Det skall gå att skriva till filen. Om filen inte existerar
skall en ny skapas.
Om filen existerar skrivs den över
app Det skall gå att skriva till filen. Om filen inte existerar
skapas en ny.
Om filen existerar, så skall utskriften läggas sist
trunc Om filen existerar skall dne skirvas över.
ate Flytta till filens slut direkt när den har öppnats
binary Filen skall hanteras som en
binärfil
Om man inte anger någon
extra parameter vid open blir default ios::in för klassen istream och ios::out
för ostream. Man kan kombinera flaggorna ex (vertikalt streck mellan värdena)
fil.open(namn, ios::in |
ios::out | ios::binary)
Deklarera ströobjekt för
varje fil. Använd klassen ifstream för filer som skall läsas och ofstream för
filer som skall skrivas.
ifstream(filnamn, mode); //
mode = ios::filflagga|ios::filflagga etc
Alternativt använd default konstruktor
och anropa open senare.
ifstream fil_1;
………
fil1.open(filnamn,mode);
En fil stängs automatiskt
när ströobj destructor anropas. Kan också stängas genom explicit anrop av
close. fil_1.close();
#<cstdio> innehåller
olika funktioner för filhantering. Dessa funktioner skall man normalt inte
använda samtidigt med c++ standarfiler för läsning och skrivning. Funktionen
remove går dock bra.
remove(”tmpfil”)
rename(const char *oldname,
const char *newname) byter namn på en
fil
Vill man ha ett unikt filnamn
kan man använda funktionen tmpnam. Returnerar en pekare till en textsträng
innehållande ett filnamn som är unikt.
main(int argc, char *argv[])
main har två argument.
Argument count och pekare till en textsträng
is.tellg() Ger aktuell pos i inströmmen is
os.tellp() Ger aktuell pos i utströmmen
os Resultat av typen streampos
is.seekg(pos) Sätter aktuell pos i inströmmen
os.seekp(pos) Sätter aktuell pos i utströmmen Resultat av typen streampos
is.seekg(off,dir) Flyttar till pos. off ett heltal, även
negativt. dir =
os.seekp(off,dir) ios::beg =början, ios::end, ios::cur = aktuell
pos i filen
1.
Finna de
objekt som skall ingå i modellen
2.
Beskriva
objektens olika attribut
3.
Fastställa relationerna
mellan de olika objekten
4.
Gruppera
objekten
UML – metoden
·
Känner
till
·
Har
·
Är
Känner till – Association – enkelriktad eller dubbelriktad
Aggregat – typ kundregister
Har relation – ett obj är uppbyggt av ett annat obj – kallas ibland för
komposition
Bil – motor – cylindrar etc
Känner till ä r lätt att för växla med har relationen. I program
implementerar man känner till relationer med hjälp av pekare. För att beskriva
att en bil har en ägare, låter man klassen bil innehålla attribut som är en
pekare till ett objekt av klassen Person.
Är relationen används för att beskriva att en klass har vissa allmänna
egenskaper som kan vara gemensamma med andra klasser. T ex en ekorre är ett
däggdjur, en hund är ett däggdjur. De gemensamma egenskaperna beskrivs då i en
separat överordnad klass, här klassen Däggdjur. Man kan ha är relationer i flera led. Däggdjur – djur –
levande ting.
FORDON
hastighet
vikt
ändra hastighet
Bil Båt Tåg Cykel
motoreffekt dödvikt antal vagnar antal växlar
växla sväng koppla vagna trampa
För att beskriva är relationer
använder man inom objoorintering ett
begrepp som kallas arv
Man använder en befintlig klass och ärver dess attribut och kan sedan lägga till egna eller ändra den andra klassens attribut. Den gamla klassen kallas för superklass och den nya subklass
Gränsen mellan analysfasen och designfasen är ganska flytande. Rent allmänt kan man säga att i analysfasen gör man en idealiserad modell som man i design fasen gör mera konkret.
Designfasen kan man dela upp i två delar:
· systemdesign
· objektdesign
Ett bra program skall vara:
· korrekt
· effektivt
· återanvändbart
· ändringsbart
Klass definition
class namn {
public:
deklarationer av synliga medlemmar och funktioner
private:
deklarationer av gömda medlemsfunktioner och medlemmar
}
Medlemmars tillgänglighet.
Utanför klassen: kommer bara åte synliga medlemmar (som dekl som public)
Man använder då punktoperatorn eller piloperatorn.
Objektnamn.medlemsnamn
pekare_till_obj ->
medlemsnamn
Inne i en medlemsfunktion kan man komma åt alla medlemmar utan punktnotation
Obs Deklaration av klass = class NyKlass
Definition = när man skriver in alla funktioner och medlemmar
Bör placeras separat, utanför klassdefinitionen
Ex
void minklass::minfunk(parametrar)
{
lokala dekl;
}
Mycket korta medlemsfunktioner kan definieras i klassdefinitionen och blir då aoutomatiskt inline funktioner.
Man kan även skriva direkt en funktion som inline.
inline void kortfn(parametrar) //fungerar som slags macro
{
cout << ”Hej” ;
}
Man bör ha parvisa filer Class1.cpp och Class1.h (definitionsfil och inkluderingsfil)
Inkluderingssfilen innehåller klassdefinitionen samt definitioner av ev medlemsfunktioner som är inline.
för att undvika dubbel definitioner använd:
#ifndef CLASS_H
#define CLASS_H - sist I filen skriver man #endif
Initierar ett objekt. Anropas aoutomatiskt när ett objekt skapas. Har samma namn som klassen.
Kan finnas flera överlagrade.
En konstruktor som kan anropas utan argument kallas default-konstruktor.
En default konstruktor definieras aoutomatiskt om man inte skriver någon själv. Kompileringsfel om klassen innehåller datamedlemmar, konstanter eller referenser eller objekt för vilka default konstruktor saknas, eller om klassen har en basklass utan defualt konstruktor
Den Automatiskt definerade defaultkonstruktorn gör ingenting:
Ex på definition av konstruktor:
Klocka() { t = m = s = 0;} // i klass definitionen
eller i funktionsdefinitionen:
Klocka::Klocka ()
{ t = m = s = 0;}
Den andra konstruktorn med parametrar: (i klassdefinitionen)
Klocka::Klocka (int tim, int min, int sek)
{
t = tim;
m= min;
s = sek;
}
Eller med
initieringslista: (OBS bara med klass
konstruktorer)
Klocka::Klocka (int tim, int min, int sek)
: t(tim), m(min), s(sek) {}
Eller med både
initieringslista och vanlig tillddelning:
Klocka::Klocka (int tim, int min, int sek)
: t(tim), m(min)
{
s=sek)
}
Referenser och konstanter måste initieras med initieringslista. Detta sätt är bättre och kan ge effektivare kod.
Klassnamn::Klassnamn(parameterlista) initieringslista {sfunktionskropp}
Initieringslistan kan utelämnas. Har formen:
:d1(uttryck), d2(uttryck), …. d1 och d2 … är namn på datamedlemmar
Referenser och konstanter
måste initieras med initieringslista
Klocka k1; default konstruktorn anropas – medlemmarna nollställs
Klocka k2(8, 25, 30) konstruktorn med 3 arg anropas
Klocka *p1, *p2;
p1 = new Klocka default konstruktorn anropas via pekare
p2 = new Klocka(3, 45, 0) konstruktorn med 3 argument anropas
k1.skriv(); blir 00:00:00
k2.skriv(); 08:25:30
p1->skriv(); 00:00:00
p2->skriv(); 03:45:00
p1 = new Klocka[5]; Defaultkonstruktorn anropas
Klocka a[] = {Klocka(2,3,0), Klocka(1,0,0)};
#include <string>
#include <iomanip>
#include <iostream>
#include "iodos.h"
#define PR(X) cout << #X << "
= " << X << endl;
using namespace std;
class Klocka{
public:
Klocka(); // går även att initiera här{t = min; m= min; s = sek;}
Klocka(int tim, int min, int sek);
~Klocka(){};
void stall (int tim, int min, int sek);
int avlas_tim(){return t;}
int avlas_min(){return m;}
int avlas_sek(){return s;}
void skriv(bool skriv_sek = true);
void ticka();
private:
int t, m, s;
};
Klocka::Klocka()
{t = m = s = 0;}
//Klocka::Klocka(int tim, int min, int sek)
// {t = min; m= min; s = sek;}
Klocka::Klocka(int tim, int min, int sek)
: t (min), m(min), s(sek) {} //BÄST MED INITIERINGSLISTA
void Klocka::stall(int tim, int min, int sek)
{
t=tim; m=min; s=sek;
}
void Klocka::skriv(bool skriv_sek)
{
cout<<setw(2)<<setfill('0')<<t<<':'<<setw(2)<<setfill('0')<<m;
if(skriv_sek)
cout << ':' << setw(2) << setfill('0') << s;
}
void Klocka::ticka()
{
s =
(s+1) %60;
if(s
== 0)
{
m
= (m+1) % 60;
if(m == 0)
t = (t+1) % 24;
}
}
class
Flight{
public:
Flight(){};
Flight(string flight_no, int dep_h, int dep_m, int arr_h, int arr_m);
void init (string flight_no, int dep_h, int dep_m, int arr_h,
int arr_m);
void info();
void delay(int min);
private:
string no;
Klocka dep, arr;
}; //initiering med explicita anrop av Klocka
Flight::Flight(string flight_no, int dep_h, int
dep_m,int arr_h, int arr_m)
:
no(flight_no), dep(Klocka(dep_h ,
dep_m, 0)),
arr(Klocka(arr_h, arr_m,0)){}
void Flight::init(string flight_no , int dep_h,
int dep_m,
int arr_h, int arr_m)
{
no =
flight_no;
dep.stall(dep_h, dep_m, 0);
arr.stall(arr_h, arr_m,0);
}
void Flight::info()
{
cout
<<"Flight no "<<no;
cout<<", Dep "; dep.skriv(false); //false = skriv inte sekunder
cout <<", Arr "; arr.skriv(false);
cout << endl;
}
void Flight::delay(int min)
{
for(int i=1; i<= min*60; i++)
dep.ticka();
for(int j=1; j<= min*6 ; j++)
arr.ticka();
}
void main()
{
dos_console();
Klocka k;
int tt, mm, ss;
cout << "Ställ klockan!" << endl
<< "ange tim, min och sekunder" << endl;
cin >> tt >> mm >> ss;
k.stall(tt,mm,ss);
cout << "Hur många tick?" ; cin >> ss;
for(int i=0; i<ss; i++)
k.ticka();
cout << "Klockan är nu " ;
Klocka k1;
Klocka k2(8,25,30);
Klocka k3 = Klocka(22,15,10);
Klocka *p1, *p2;
p1 = new Klocka;
p2 = new Klocka(14,5,40);
k1.skriv(); cout << endl; //00:00:00
k2.skriv(); cout << endl; //08:25:30
k3.skriv(); cout << endl; //22:15:10
p1->skriv(); cout << endl; //00:00:00
p2->skriv(); cout << endl; //14:05:40
Klocka kf[3]; //ett fält med tre klockor skapas
p1 = new Klocka[5]; //allokerar fält dynamiskt. default konstruktorn
//anropas 5 gånger
p1[0].stall(12,12,0);
Klocka aa[] = {Klocka(9,4,45), Klocka(14,35,20)};
aa[0].skriv(); cout << endl; //skriver 09:04:45
aa[1].skriv(); cout << endl; //skriver 14:35.20
kf[2].skriv(); cout<< endl; //skriver
p1->skriv(); cout << endl; //skriver 12:12:00
Flight f2("TPO255", 11, 25, 13, 05);
f2.info() ;
Klocka ka(7,35,30);
Klocka kb = ka;
kb.skriv(); cout << endl; //Skriver 07:35:30
ka.skriv(); cout << endl; //Skriver 07:35:30
kb.stall(0,0,0);
ka = kb; //Tilldelning
kb.skriv(); cout << endl; //Skriver 07:35:30
ka.skriv(); cout << endl; //Skriver 07:35:30
}
klassnamn (const klassnamn &);
Har en parameter som är en
referens till ett annat objekt av samma klass.
Anropas automatiskt om man
har deklarationerna:
X x1 = x2;
X x1(x2); x1
och x2 måste tillhöra klass X
anropas bara vid
DEKLARATIONER inte vanlig TILLDELNING
För det mesta behöver man inte definiera en kopieringskonstruktor, men när man arbetar med klasser som allokerar minnesutrymme dynamiskt måste man definiera en egen kopieringskonstruktor.
//Array Main.cpp
//#define NDEBUG //onm du inte vill ha assert med skriv #define NDEBUG
#include <cassert>
#include <iostream>
using namespace std;
class Array{
public:
Array(int forsta_index =1,int antal =0);
Array(const Array & v); //kopieringskonstruktor
int forsta(){return i1;} //första elementet
int sista(){return i1 + ant -1;} //sista elementet
int langd(){return ant;} //längden på array
int avlas(int index); //avläsa ett visst element
void andra(int index, int varde); //ändra ett element
private:
int *p; //pekare till int
const int i1; //första tillåtna index
int ant; //antalet
};
Array::Array(int forsta_index, int antal )
: i1(forsta_index), ant(antal) //i1 är en konstant kan bara använda
//initieringslista
{
assert(ant>=0); //assert för att kolla ant ej blir negativt
p = new int[ant];
}
//kopieringskonstruktor anropas vid Array vb(va); eller Array vc = va;
//anropas bara vid initieringar ej tilldelningar
Array::Array(const Array & v )
: i1(v.i1), ant(v.ant ) //i1 och ant kopieras först
{
p = new int[ant]; //allokera ett fält int[ant]
for(int i = 0;i< ant; i++)
p[i] = v.p[i]; //kopiera en och en - det går
} //inte att kopiera p = v.p för då kommer v.p objektet att dela
//på samma minnes område. Vi måste kopiera hlea fältet.
//kallas för DJUP KOPIERING till skillnad från den aoutomatiska konstruktorn
//som gör en GRUND KOPIERING
Array::avlas(int index)
{
assert(index >= i1 && index <= sista());
return p[index-1];
}
void Array::andra(int index, int varde)
{
assert(index >= i1 && index <= sista());
p[index - i1] = varde;
}
int sum(Array& v)
{
int s =0;
for(int i=v.forsta(); i<= v.sista(); i++)
s += v.avlas(i);
return s;
}
void kvadrera(Array& v)
{
for(int i=v.forsta(); i<= v.sista(); i++)
{
int x= v.avlas(i);
v.andra(i, x*x);
}
}
void main()
{
int n, x;
cout << "Antal mätvärden?"; cin >> n;
Array v1(1,n);
cout << "Ange mätvärden" << endl;
for(int i = 1; i<= n; i++)
{
cin >> x;
v1.andra(i,x);
}
Array v2 = v1; //här används vår egen definerade kopieringskonstruktor
kvadrera(v2);
cout << "Summan: " << sum(v1) << endl;
cout << "Kvadrat summan: " << sum(v2) << endl;
}
En konstruktor som kan anropas med bara ett argument. Finns fler måste de ha default värden.
#include <iostream>
using namespace std;
class RatTal{
public:
RatTal() :talj(0), namn(1) {}
RatTal(int i) : talj(i), namn(1){} //har en parameter TYPOMVANDLINGS
void skriv(){cout << talj << " " << namn << endl;} //KONSTRUKTOR
private:
int talj, namn;
};
class Text{
public:
Text(int n = 0);
Text(char * s); //en parameter TYPOMVANDLINGSKONSTRUKTOR
void skriv(){cout << p << endl;}
private:
char *p;
int langd;
};
Text::Text(int n ){
langd
= n;
p =
new char [langd +1];
for(int i=0; i<langd; i++)
p[i] = ' ';
p[langd] ='\0';
}
Text::Text(char *s ){
langd
= strlen(s);
p =
new char [langd +1];
strcpy(p,s);
}
void main()
{
RatTal r1, r2(5);
r1.skriv();
r2.skriv ();
r1 = 9; //typomvandling från int till RatTal
r2=RatTal(9);
r1.skriv();
r2.skriv();
Text t1(5), t2("typomvandlingskonstruktor");
t1.skriv() ; //skriver: 5 blanktecken
t2.skriv() ; //skriver: typomvandlingskonstruktor
}
~Klassnamn();
Används för att städa efter ett objekt. anropas automatiskt nr ett objekt upphör att existera.
Är alltid parameterlös. Kan ej överlagras.
Om man har allokerat minne ”on the heap” alltså med new kan man sätta delete [] pek; här.
Medlemsfunktioner som inte gör
några ändringar i det aktuella objektet anges med const i
funktionsdefinitionen. resultattyp funktionsnamn(parameterlista)
const;
Obs gör det för alla funktioner
som inte ändras!
Ex:
class Klocka{
public:
void stall (int tim, int min, int sek);
int avlas_tim() const {return t;}
int avlas_min() const {return m;}
int avlas_sek()
const {return
s;}
void skriv(bool skriv_sek = true) const;
void ticka();
private:
int t, m, s;
};
Alla medlemsfunktioner får en
dold parameter. Denna parameter heter this och det
är en pekare till det aktuella objektet. När man är inne i en medlemsfunktion
skriver namnet på en medlem m så tolkar kompilatorn det som om man hade skrivit (this->m)
I vanliga fall behöver man
inte tänka på pekaren this men det finns två fall när man explicit måste
använda sig av pekaren this:
När man inifrån en medlemsfunktion behöver anropa en annan funktion som skall ha ett objekt av den aktuella klassen som parameter.
När man vill lämna det aktuella objektet – eller referens eller pekare till det som resultat från en medlemsfunktion. Detta behövs om man vill skriva medlemsfunktionen som skall kunna anropas i en enda sekvens.
Ex:
Klocka& Klocka::stall(int tim, int min, int sek)
{
t = tim;
m = min;
s = sek;
return *this; //en referens till Klocka
}
Klocka& Klocka::ticka()
{
s = (s+1) %60;
if(s == 0)
{
m
= (m+1) % 60;
if(m == 0)
t = (t+1) % 24;
}
return *this; //this = det som this pekar på
}
Man kanske tycker att det borde stå return this, men det är fel: Hade det däremot varit en retur typ från funktionen som pekare till Klocka, dvs Klocka* skulle det stå reurn this
I en klass funktion kan man ange att en viss funktion eller en annan klass skall vara ens vän.
class C{
friend typ f(parametrar);
friend class C2;
……
};
Funktionen f och klassen C2 får då tillgång till de private medlemmarna I C
Ex:
class Array{
public:
friend void kvadrera(Array& v); //kvadrera tillgång till
Array(int forsta_index =1,int antal =0); //privata medlemmar
Array(const Array & v); //kopieringskonstruktor
int forsta(){return i1;} //första elementet
int sista(){return i1 + ant -1;} //sista elementet
int langd(){return ant;} //längden på array
int avlas(int index); //avläsa ett visst element
void andra(int index, int varde); //ändra ett element
private:
int *p; //pekare till int
const int i1; //första tillåtna index
int ant; //antalet
};
Nu kan vi effektivare göra en kod för kvadrera
void kvadrera(Array& v)
{
for(int i=0 i<= v.ant i++)
v.p[i] *= v.p[i];
}
Funktioner med namn operatorx,
där x är operator symbol. De flesta fördefinerade operatorer får överlagras för
klasstyper. Kan ha en operand (unära) eller två operander (binära).
Antal operander och prioritet
samma som för motsvarande fördefinierade operator. Kan konstrueras som medlemsfunktioner
eller vänfunktioner (friend).
Unära operatorer har saknar parameter, binära operatorer har en parameter.
Binära operatorer:
Skall ha en parameter om den
är en medlemsfunktion
aktuellt objekt == vänster
operand
Parametern == höger operand
Varnoga med att skilja på
operatorer där aktuellt objekt ändras eller inte
operator==
//referens för att undvika kopiera hela objektet (constant - inget får ändras)
bool Array::operator== (const Array& v) const //binär operator
{ //första parametern undeförstådd
if (ant != v.ant) //samma längd för att bli lika
return false;
for (int i=0; i<ant; i++)
if (p[i] != v.p[i]) //samma innehåll
return false;
return
true;
}
operator+=
//vänstra operanden underförstådd. Referens till en konstant
//resultatet skall vara en kopia av det nya värdet
const Array& Array::operator+= (const Array& v)
{
assert(ant==v.ant);
for (int i=0; i<ant; i++)
p[i] += v.p[i];
return *this; //this = detta objektet, returnera som
referens
}
const Array& Array::operator+= (int d) // Heltalsoperator
{
for (int i=0; i<ant; i++)
p[i] += d;
return *this;
}
operator+
Array Array::operator+ (const Array& v)
const
{
Array temp(*this); // Skapa kopia av detta Array-objekt
temp += v;
return temp; //går inte att resultattypen skall vara en referens
} //temp existerar bara här och vi skulle få en kvardröjande pekare
Array Array::operator+ (int d) const
// Heltalsoperator
{
Array
temp(*this);
temp
+= d;
return temp;
}
Sammanslagningsoperator
(lägg till nytt element)
operator&=
const Array& Array::operator&= (const
Array& v)
{
int *q = new int[ant+v.ant]; // Skapa nytt utrymme
for (int i=0; i<ant; i++)
q[i] = p[i]; // Kopiera detta Array-objekt
for (int j=0; j<v.ant; j++)
q[j+ant] = v.p[j]; // Kopiera v
delete[] p; // Frisläpp gammalt utrymme
p=q; // Peka på den nya utrymmet
ant += v.ant; // Öka längden
return *this;
}
operator&
(ex
V3 = V1 & V2)
Array Array::operator& (const Array& v)
const
{
Array temp(*this); // Skapa kopia av detta Array-objekt
temp &= v; //använd vår tidigare operator&=
return temp; //returnerar kopian (ej en pekare)
}
operator!=
bool Array::operator!= (const Array& v)
const
{
return !((*this) == v); //använder vår == operator
}
Har man definierat en == operator är det lätt att !=, precis som < lätt blir > osv
I filen utility finns en uppsättning färdigdefinierade mönster för
relationsoperatorer och man kan tillämpa dem på vilken klass som helst. Vad
som krävs är att man själv definierar == och <
operator<
bool Array::operator< (const Array& v)
const
{
int n; // skall innehålla längden av den kortaste
if (ant < v.ant)
n =
ant;
else
n =
v.ant;
for
(int i=0; i<n; i++)
if
(p[i] < v.p[i])
return true;
else if (p[i] > v.p[i])
return false;
return ant < v.ant; // lika långa
}
Skalll inte ha några parameterar
om den är medlemsfunktion
aktuellt objekt == operanden
const Array&
operator++(); //prefix ++v
Array operator++(int); //postfix v++
Eftersom båda operatorerna är lika har man gjort ett speciellt skrivsätt.
Heltalsparametern är med bara för att markera att det är postfix varianten och
man låtsas att operatorn är binär.
const Array& Array::operator++ () // prefix ++v
{
return
(*this) += 1;
// Returnera nya värdet
}
Array
Array::operator++ (int) //
postfix v++
{
Array temp(*this); // Skapa kopia av detta Array-objekt
(*this) += 1; // Öka detta detta Array-objekt
return temp; // Returnera gamla värdet
}
operator-
Array Array::operator- () const
{
Array temp(*this); // Skapa kopia av detta Array-objekt
for (int i=0; i<ant; i++)
temp.p[i] = -temp.p[i]; // Negera varje element
return temp;
}
Tilldelningsoperatorn:
I de flesta fall fungerar den
automatiska tilldelningsoperatorn, men om man i en klass har någon data medlem
som är en konstant eller referens fungerar det inte. Om man för att göra en
djup kopiering använder en egen kopieringskonstruktor måste man och göra en
tilldelningskonstruktor. vid initieringar kommer
dock kopieringskonstruktorn att anropas.
operator=
const Array& Array::operator= (const
Array& v)
{
if (this != &v) // kopiering till sig själv?
{ // nej
delete [] p; // frigör gammalt utrymme
ant = v.ant;
p = new int[ant]; // allokera nytt utrymme
for (int i=0; i<ant; i++)
p[i] = v.p[i]; // kopiera elementen
}
return *this;
}
operator[] //lämligt att ha två – en med
och en utan const
int& Array::operator[] (int i)
{
assert(i >= i1 && i <= sista());
return p[i-i1]; // returnera en referens
}
int Array::operator[] (int i) const //med const
{
assert(i >= i1 && i <= sista());
return p[i-i1]; // returnera en kopia
}
operator() //funktionsoperatorn kan ha godtyckligt
antal parametrar
const Array Array::operator() (int fran, int till) const
{
assert(fran<=till && fran>=i1 &&
till<=sista());
Array temp(fran, till-fran+1); // Skapa en skiva
for (int i=0; i < temp.ant; i++)
temp.p[i] = p[fran-i1+i]; // Kopiera elementen
return temp;
}
friend funktioner som
operand
skall ha lika många parametrar som antalet operander. Måste användas när vänsteroperanden inte tillhör den aktuella klassen
Array operator+ (int d, const Array& v)
{
return
v+d;
}
std::ostream& operator<<
(std::ostream& o, const Array& v)
{
o << '[';
if (v.ant>0)
o << v.p[0];
for (int i=1; i<v.ant; i++)
o
<< ", " << v.p[i];
o << ']';
return o;
}
std::istream& operator>>
(std::istream &in, Array &v)
{
for (int i=0; i<v.ant; i++)
in >> v.p[i];
return
in;
}
typomvandlingsoperator
operator typ()const;
Medlemsfunktion. Inga
parametrar eller resultattyp får anges. Omvandlar från den aktuella klassen
till typen typ. Anropas automatiskt när typomvanling behövs
Ex från RatTal (rationella tal)
RatTal::operator double() const
{
return double(talj)/namn;
}
//DynArray.h
#ifndef DYNARRAY_H
#define DYNARRAY_H
//
// Operatorerna tolkas som elementvis addition, multiplikation, etc.
//
// Operatorerna antar att arrayernas längd är lika. Detta kollas INTE
// eftersom huvudsyftet med detta klass är att demonstrera överlagring av
// operatorer. Vissa operatorer kan skrivas om och fungera bra även för
// oliklånga arrayer (== och < t.ex.) medan andra (+,-,etc) nog bör generera
// fel (kasta s.k. undantag) om arrayerlängderna inte stämmer överrens.
//
#include <iostream>
using namespace std;
#include <utility> // för automatiska jämförelseoperatorer
using namespace std::rel_ops; // för automatiska jämförelseoperatorer
class DynArray
{
public:
DynArray();
DynArray(int size);
DynArray(double *arr, int size );
DynArray(const DynArray & arr);
~DynArray();
int
getSize() const { return bufSize; }
void add(
double d );
void
add( int num, double d = 0.0);
void
remove( int index );
//
Tilldelningsoperatorn
const
DynArray & operator=(const DynArray & arr);
//
Indexeringsoperatorn
double
& operator[](int index) {return array[index];}
double
operator[](int index) const {return array[index];}
//
Binära operatorer
DynArray& operator+=(const DynArray & v);
DynArray& operator-=(const DynArray & v);
DynArray operator+(const DynArray & v) const;
DynArray operator-(const DynArray & v) const;
DynArray operator*(const double scale) const;
DynArray operator*(const DynArray & arr) const;
const
DynArray & operator*=(const double scale);
const
DynArray & operator*=(const DynArray
& arr);
bool
operator==(const DynArray & arr) const;
bool
operator<(const DynArray & arr) const;
// operatorerna !=, <=, > och >= genereras av <utility>
// Unära operatorer
DynArray operator-() const;
operator bool() { return array != 0; } // returnerar status
bool operator
!() { return array == 0; } //
returnerar status
//
Funktionsanropsoperatorn
DynArray operator() (const int lower, const int upper) const;
//
Vänner som operatorer
friend
DynArray operator*(const double scale, const DynArray & arr);
friend
ostream & operator<<(ostream & os, const DynArray & arr);
friend
istream & operator>>(istream & is, DynArray & arr);
private:
double
* array;
int
bufSize;
};
#endif
//DynArray.cpp
#include "DynArray.h"
#include <iostream>
using namespace std;
DynArray::DynArray() : bufSize(0), array(0)
{
cout<<"DynArray constructor"<<endl;
}
DynArray::DynArray(int size) : bufSize(size)
{
cout<<"DynArray constructor: size =
"<<bufSize<<endl;
array =
new double[bufSize];
for
(int i=0;i<bufSize;i++)
array[i] = 0.0;
}
DynArray::DynArray(double *arr, int size ) :
bufSize(size)
{
array =
new double[bufSize];
for
(int i=0;i<bufSize;i++)
array[i] = arr[i];
}
// Kopieringskonstruktor
DynArray::DynArray(const DynArray & arr) :
bufSize(arr.bufSize)
{
cout<<"DynArray copy constructor"<<endl;
array =
new double[bufSize];
for(int
i = 0; i < bufSize; i++)
array[i] = arr.array[i];
}
DynArray::~DynArray()
{
cout<<"DynArray destructor"<<endl;
delete[] array;
}
// Lägg till ett element sist
void DynArray::add( double d )
{
double
*temp = new double[bufSize+1];
for
(int i=0; i < bufSize; i++)
temp[i] = array[i];
temp[bufSize++] = d;
delete[] array;
array =
temp;
}
void DynArray::add( int num, double d )
{
double
*temp = new double[bufSize+num];
for
(int i=0; i < bufSize; i++)
temp[i] = array[i];
for (
i=bufSize; i < bufSize+num; i++)
temp[i] = d;
delete[]
array;
array =
temp;
}
// Ta bort elementet i position index
void DynArray::remove( int index )
{
double
*temp = new double[bufSize-1];
for
(int src=0,dst=0; src < bufSize; src++)
if
(src!=index)
temp[dst++] = array[src];
delete[] array;
array =
temp;
}
// Tilldelningsoperator
const DynArray & DynArray::operator=(const
DynArray & arr)
{
cout<<"DynArray assignment"<<endl;
if
(this != &arr)
{
bufSize = arr.bufSize;
delete[] array;
array = new double[bufSize];
for(int i = 0; i < bufSize; i++)
array[i]
= arr.array[i];
}
return
*this;
}
DynArray& DynArray::operator+=(const
DynArray & arr)
{
for (int i=0; i < getSize(); i++)
operator[](i) += arr[i]; // använder indexeringoperatorn i båda arrayerna
// Alternativa, ekvivalenta skrivsätt:
// array[i] = arr[i]; // indexeringsoperatorn bara i arr
// array[i] = arr.array[i]; // använder ej indexeringsoperatorn
return *this;
}
DynArray& DynArray::operator-=(const
DynArray & arr)
{
for
(int i=0; i < getSize(); i++)
array[i]
-= arr[i];
return
*this;
}
DynArray DynArray::operator+(const DynArray
& arr) const
{
DynArray result(*this); // använder kopieringskonstruktorn
return result += arr; // använder operatorn +=
}
DynArray DynArray::operator-(const DynArray
& arr) const
{
DynArray result(*this); // använder kopieringskonstruktorn
return result -= arr; // använder operatorn -=
}
/* Alternativa versioner av + och - utan användning av += och -=
DynArray DynArray::operator+(const DynArray
& arr) const
{
DynArray result(getSize());
for(int
i = 0; i < getSize(); ++i)
result[i] = operator[](i) + arr[i];
return result;
}
DynArray DynArray::operator-(const DynArray
& arr) const
{
DynArray result(getSize());
for(int
i = 0; i < getSize(); ++i)
result[i] = operator[](i) - arr[i];
return result;
}
*/
const DynArray & DynArray::operator*=(const
double scale)
{
for(int
i = 0; i < getSize(); ++i)
array[i] *= scale;
return
*this;
}
const DynArray & DynArray::operator*=(const
DynArray & arr)
{
for(int
i = 0; i < getSize(); ++i)
array[i] *= arr[i];
return
*this;
}
DynArray DynArray::operator*(const double
scale) const
{
DynArray result(getSize());
for(int
i = 0; i < getSize(); ++i)
result[i]
= operator[](i) * scale; // eller
result[i] = array[i]*scale;
return
result;
}
DynArray DynArray::operator*(const DynArray
& arr) const
{
DynArray result(getSize());
for(int
i = 0; i < getSize(); ++i)
result[i] = operator[](i) * arr[i]; // eller result[i] = array[i]*arr[i];
return result;
}
bool DynArray::operator==(const DynArray &
arr) const
{
for(int
i = 0; i < bufSize; i++)
if(array[i] != arr.array[i])
return false;
return true;
}
// Mindre än definieras här som att det första elementet som skiljer
// sig åt är mindre.
bool DynArray::operator<(const DynArray & arr) const
{
for(int i = 0; i < bufSize; i++)
if(array[i] < arr.array[i])
return true;
return false;
}
/* om vi inte inkluderade <utility>:
bool DynArray::operator!=(const DynArray &
arr) const
{
return
! ((*this) == arr);
}
*/
DynArray DynArray::operator-() const
{
DynArray result(getSize());
for(int
i = 0; i < getSize(); ++i)
result[i] = -operator[](i);
return
result;
}
DynArray DynArray::operator() (const int lower,
const int upper) const
{
// OBS! vi tolkar upper som elementet efter det sista som skall kopieras
DynArray result(upper -
lower);
for(int
i = 0; i < upper - lower; ++i)
result[i] = operator[](lower + i);
return
result;
}
// *** Friends ***
DynArray operator*(const double scale, const
DynArray & arr)
{
return
arr * scale;
}
ostream & operator << (ostream &
os, const DynArray & arr)
{
if(arr.getSize() > 0)
{
os<<"<"<<arr[0];
for(int i = 1; i < arr.bufSize; ++i)
{
os<<" "<<arr[i];
}
os<<">";
}
return os;
}
// Läsning är alltid litet krångligare än skrivning...
// Inläsningsmetoden använder en tillfällig array för läsning
// Denna kan växa efter behov.
istream & operator >> (istream &
is, DynArray & arr)
{
// Här ser vi ett exempel på en kanske inte alltid önskvärd
// typkonvertering från int till DynArray
// 8 "förvandlas" till en temporär DynArray av längden 8
// som sedan med hjälp av DynArrays tilldelningsoperator tildelas arr
// arr = 8;
// Bättre att explicit tala om vad man gör:
arr = DynArray(8);
char c;
is
>> c; // läs bort '<'
double
value;
int
realSize = 0;
while (
is >> value)
{
if (realSize== arr.bufSize) // om slut på plats
arr.add(arr.bufSize); // fördubbla storleken!
arr[realSize] = value;
realSize++;
}
arr.bufSize = realSize; // vi har iofs troligen allokerat mer men...
return is;
}
static typ namn;
//deklaration
Definitionen läggs utanför
klassdefinitionen
typ klassnamn::namn =
initieringsvärde
Finns bara i en enda
instans. Delas av klassens objekt.
class C {
static returtyp namn(parametrar);
};
Definieras som andra medlemsfunktioner. Får endast
använda statiska datamedlemmar. Anropas inte för något speciellt objekt.
I C++ finns möjligheten att
använda generiska klasser. Man skriver då en mall (template) för
klassen, en så kallad klassmall och kompilatorn genererar aoutomatiskt utifrån
denna mall. Man kan också konstruera generiska funktioner och
man skriver då en funktionsmall.
Exempel – skriv först en vanlig klass:
class Matris {
public:
Matris(int i=0, int j=0) //default parametrar
: r(i), k(j), a(new double[r*k]){} //initieringslista
Matris(const Matris& m) //kopieringskonstruktor
: a(0) {*this = m;}
~Matris() {delete []a;}
int ant_rad() {return r;} //antal rader
int ant_kol() {return k;} //antal kolumner
Matris& operator= (const Matris&); //tilldelningskonstruktor
double& operator() (int i, int j); //funktionsoperator - gör
private: //det möjligt med indexeringar
double *a; //pekare till fältet
int r,k ; //antal rader resp kolumn
};
Matris& Matris::operator= (const Matris& m)
{
if(this != &m) //om det inte är tilldelning till sig själv
{
r = m.r;
k = m.k;
delete []a; //avallokera minnet
a = new double[r*k]; //allokera nytt minne
for(int i= 0; i< r*k; i++)
a[i] = m.a[i]; //kopiera medlem för medlem
}
return *this;
}
double& Matris::operator () (int i, int j){
if(i<1 || i>r || j<1 || j>k)
throw out_of_range("Matris::operator()");
return a[(i-1)*k + j-1];
}
template <class T>
class Matris {
public:
Matris(int i=0, int j=0) //default parametrar
: r(i), k(j), a(new T[r*k]){} //initieringslista
Matris(const Matris& m) //kopieringskonstruktor
: a(0) {*this = m;}
~Matris() {delete []a;}
int ant_rad() {return r;} //antal rader
int ant_kol() {return k;} //antal kolumner
Matris& operator= (const Matris&); //tilldelningskonstruktor
T& operator() (int i, int j); //funktionsoperator - gör
private: //det möjligt med indexeringar
T *a; //pekare till fältet
int r,k ; //antal rader resp kolumn
};
template<class T>
Matris<T>& Matris<T>::operator= (const Matris& m)
{
if(this != &m) //om det inte är tilldelning till sig själv
{
r = m.r;
k = m.k;
delete []a; //avallokera minnet
a = new T[r*k]; //allokera nytt minne
for(int i= 0; i< r*k; i++)
a[i] = m.a[i]; //kopiera medlem för medlem
}
return *this;
}
template<class T>
T& Matris<T>::operator () (int i, int j){
if(i<1 || i>r || j<1 || j>k)
throw out_of_range("Matris::operator()");
return a[(i-1)*k + j-1];
}
void main()
{
Matris<int> mi(5,8);
Matris<person> mp(3,4);
}
template<class T> //klassdefinition
class C{
returtyp f(parametrar);
.
. .
};
template<class T> //definition av medlemsfunktion
returtyp C>T>::f(parametrar)
{ … }
T är en typparameter och används som en vanlig typ inne I klassen. Både klassdefinitionen och definitioner av medlemsfunktionerna bör placeras i inkluderingsfilen.
Instanser av klassen skapas när man skriver C<typnamn>
då erästts T med typnamn överallt i denna instans. Uttrycket C<typnamn> kan användas som ett vanlig klassnamn.
// Simple Windows Template
// Copyright (c) William H. Murray and Chris H. Pappas, 1999
.cpp =
Den fil du arbgetar med och skriver (source file eller källfil)
.h
= Header fil där du deklarer dina
funktioner
.dsp
= Projekt filen
.dsw
= Arbetsminnet (Workspace)
.ncb = This is the No compile Browser file. It
contains information
generated by the
parser that is used by Visual Studio utilities
such as ClassView,
izardBar, and the Component Gallery. If
the file is
accidentally or deliberately deleted, it is
automatically
regenerated.
.opt =
Denna fil innehåller de inställningar du har gjort för
projektet (option)
.plg
= Logg fil
************* I MAPPEN DEBUG FINNS ***********
.exe
= Executable file
.ilk
= Länkfil
.obj
= Objektkod i maskinkod och som
används till länkningen
.pch
= Förkompilerad header fil (jmf
stdafx.obj)
.pdb
= Program Data Bas fil - länkning
av debug program version
vc60.idb
= Kompilerar bara det som har ändrats (vc50 kompilering utan
projekt)
vc60.pdb
= Samma som ovan
*/
#include <windows.h>
//Fördeklaration av windows
CALLBACK rutin LRESULT 32 bitars typ
//som
kan delas upp i så kallad LOW eller
HIGH
LRESULT CALLBACK
WndProc(HWND,UINT,WPARAM,LPARAM); //WndProc
finns här
char szProgName[]="ProgName";
//WinMain = själva
huvudrutinen
int WINAPI WinMain(HINSTANCE
hInst,HINSTANCE hPreInst,
LPSTR
lpszCmdLine,int nCmdShow)
// The WinMain() function is
called by Windows
{//Ska
initiera, skapa, registrera, hantera meddelande loop
//och terminera WM_QUIT
HWND hWnd; //handle till
fönstret
MSG lpMsg; //instans
meddelande loopen
WNDCLASS wcApp; //skapa
applikationen
//wcApp - hur den ska se ut
wcApp.lpszClassName=szProgName;
wcApp.hInstance =hInst;
wcApp.lpfnWndProc =WndProc;
wcApp.hCursor
=LoadCursor(NULL,IDC_ARROW);
wcApp.hIcon =0;
wcApp.lpszMenuName =0;
wcApp.hbrBackground =(HBRUSH) GetStockObject(LTGRAY_BRUSH);
wcApp.style
=CS_HREDRAW|CS_VREDRAW;
wcApp.cbClsExtra =0;
wcApp.cbWndExtra =0;
if (!RegisterClass
(&wcApp)) //om det inte gick att registrera
return 0; //återvänd till
windows
//Skapa fönstret
hWnd=CreateWindow(szProgName,"Simple Windows Template",
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,(HWND)NULL,(HMENU)NULL,
hInst,(LPSTR)NULL);
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
//Hantera meddelande kön
while (GetMessage(&lpMsg,0,0,0)) { //0,0,0
är filter till GetMessage
TranslateMessage(&lpMsg);//Inmatning
från tangentbord WM_CHAR mm
DispatchMessage(&lpMsg);//Meddelande
till olika ställen
}
return(lpMsg.wParam);//avslutar
om WM_QUIT sänds
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT messg,WPARAM wParam,LPARAM
lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (messg)
{
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
MoveToEx(hdc,50,60,NULL);
LineTo(hdc,500,400);
TextOut(hdc,200,100,"Draw a line",11);
ValidateRect(hWnd,NULL);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return(DefWindowProc(hWnd,messg,wParam,lParam));
break;
}
return(0);
}