C++ Programmering V
|
|
|
- Sidsel Aagaard
- 9 år siden
- Visninger:
Transkript
1 Indholdsfortegnelse 1. Indledning Forudsætninger: Udeståender Start med C Det første C++ program Formatering af output Kommentarer Funktions prototyper Konstanter Inline funktioner Rækkevidde operatoren enumerations Link specifikation Overload Referencer for løkken i C Opgaver Klasser og objekter Objektets liv Overførsel af data til et objekt Konstant erklæringer Medlemmer defineret som static Friends Array af objekter Pointere This pointeren Afrunding Opgaver Nedarvning Den førstefødte Opgaver Multibel nedarvning Polymorfi Pointere og nedarvning Virtuelle funktioner Abstrakte klasser Hvorfor abstrakte klasser? Absktrakte destructorer Opgaver Introduktion til objektorienteret design Indkapsling Klassehirakiet Opgaver Sidst men ikke mindst Exceptions Operator overstyring...80 Sponsor: Ingeniørfirmaet Synkro Side 1 af 107
2 4.3 Et eksempel Opgaver...92 Appendix A Operatorer...93 A.1 Aretmetiske operatorer...93 A.2 Relations- og betingelsesoperatorer...93 A.3 Bitvise operatorer...93 A.4 Præcendens...94 Appendix C Standardbiblioteker for C Sponsor: Ingeniørfirmaet Synkro Side 2 af 107
3 1. Indledning Denne bog er en videreudvikling/tilpasning af den jeg udgav i 2001 med titlen C/C++, men her er kun C++ delen. Bogen udgives on-line som pdf fil, og kan frit benyttes og kopieres af alle som de ønsker det, delvis eller i uddrag. Ved kopiering skal der dog mindst kopieres en fuld side med header og footer, og der må ikke fjernes noget fra siderne. 1.2 Forudsætninger: Kendskab og færdigheder der svarer til on-line bogen (pdf-filen) C-programmering, der kan findes på Kapitel 1 er en kortfattet introduktion til en række nye måder som C++ bruger, de skal kunnes inden man går videre. Kapitel 2. Her dykker vi ned i hvad en klasse og et objekt er og hvordan vi kan bruge dem. Kapitel 3 handler om begrebet nedafvning Kapitel 4 Her afrundes med de ting man måske er lidt for doven til at få lært sig. Sidst i bogen er et appendix, hvor der findes praktiske informationer i form af tabeller og C++ standardbiblioteker. Sponsor: Ingeniørfirmaet Synkro Side 3 af 107
4 1.3 Udeståender Der udestår først og fremmest en tilpasning til de nye dele der kom lige efter den oprindelige udgievlse, derefter udestår en del kontrol af kapitel og afsnitsnumre, og et grundigt tjek af kildeteksterne passer til den lidt mere moderne tekst. Så skal jeg have ændret outputtet så det passer med UNIX verdenen, altså, Linux, Apple og Android's måde at se verden på. Er du i MS verdenen er du velkommen til at bruge alting, men jeg gider ikke bruge mere tid på den verden. Har været der, har prøvet det, har tabt lysten, nu har jeg så valgt UNIX. Har du valgt noget andet er du i din bedste ret til det, men kræv ikke at jeg også er der. Der udestår en gennemgang af layout, så det hele bliver meget nemmere at læse. Som altid er kritik velkommen, men det er ikke ensbetydende med jeg altid er enig i den :) Henrik Kressner [email protected] Sponsor: Ingeniørfirmaet Synkro Side 4 af 107
5 1 Start med C++ Hvis vi kigger lidt på navnet på programmeringssproget, og tænker lidt på det vi har lavet hidtil, så kan vi opfatte C som en variabel, og ++ som en operator. Derved fremkommer, at C++ er C og så lidt mere, hvad der faktisk er sandt. Alt hvad der kan laves i C, kan også laves i C++, men ikke omvendt. C++ er C udviddet med objektorienteret programmering og nogle andre små godter. Dette kapitel vil gennemgå de små godter der kan siges at være en udvidelse af C, de resterende kapitler vil udelukkende beskæftige sig med den objektorienterede del af C Det første C++ program Det første program i ethvert programmeringssprog går ud på at skrive noget på skærmen. I C++ kan det se således ud. /* Filnavn = prg1.cpp */ #include <iostream> int main() std::cout << "Jeg tænker, ergo er jeg\n"; return 0; Figur $ cpp prg1.cpp $ prg1 Jeg tænker, ergo er jeg $ For at dette program skal kunne kompileres forlanger de fleste kompilere, at kildeteksten gemmes i en fil med ekstendet CPP, i dette tilfælde har vi valgt at kalde filen prg1.cpp. Kompileringsprocessen er den samme som for C, find ud navnet på din c++ kompiler, og eksekver den med kildeteksten som argument. I forhold til C er der nogle ændringer. For det første inkluderes headerfilen iostream. i stedet for stdio.h. Derudover virker den nye syntaks std::cout << "Jeg tænker, ergo er jeg\n" noget anderledes. std::cout er hvad der i C++ kaldes, en standard output stream, de 2 "mindre end" tegn betyder i denne sammenhæng, at data flyttes, fra strengen, til standard output, det er skærmen. For at være mere korrekt så er cout en standard outputstrøm, som tilhører objektet ved nevn std, men meget mere om det senere. Vores første program skriver altså "Jeg tænker, ergo er jeg" på skærmen, og slutter af med et linieskifte. Sponsor: Ingeniørfirmaet Synkro Side 5 af 107
6 Vi kan også udskrive tal i C++, det gøres på følgende måde: /* Filnavn = prg2.cpp */ #include <iostream> $ cpp prg2 123$ int main() int vaerdi = 123; std::cout << vaerdi; return 0; Figur Programmet udskriver 123 uden linieskift. Bemærk, at C's formateringsparametre ikke bliver benyttet. Vi kan udvide programmet til både at udskrive tekst og tal, det kan gøres således: /* Filnavn = prg3.cpp */ #include <iostream> int main() int vaerdi = 123; $ cpp prg3 Indholdet af vaerdi er 123 $ std::cout << "Indholdet af vaerde er " << vaerdi << '\n'; return 0; Figur Programmet sender 3 datatyper til standard output. Først en streng (Indholdet af vaerdi er ), derefter sendes et heltal (123), og til sidst en tegnkonstant nemlig '\n' Bemærk at der er trykket på enter lige efter det sidste gåseøje i linien: std::cout << "Indholdet af vaerdi er " Vi kan dele en linie blot vi gør det lige før, eller lige efter << operatoren. Sponsor: Ingeniørfirmaet Synkro Side 6 af 107
7 Programmet kan udviddes med en input funktion: /* Filnavn = prg4.cpp */ #include <iostream> int main() int vaerdi; cpp prg4 Indtast en værdi : 246 Den indtastede værdi er : 246 $ std::cout << "Indtast en vaerdi : "; std::cin >> vaerdi; std::cout << "Den indtastede værdi er : " << vaerdi << '\n'; return 0; Figur Dette program beder først om at få indtastet en værdi, derefter udføres et linieskift, og til sidst udskrives resultatet, det er unægtelig nemmere end i C. >> operatoren betyder, at data hentes fra cin, der er standard input, og placerer det i variablen ved navn vaerdi. Det er muligt at bede om flere input i den samme linie, men det er ikke smart når vi arbejder emd standard input, da det ikke er muligt at prompte brugeren. Opgave Skriv et program der udskriver dit fornavn og dit efternavn på en linie. Opgave Skriv et program der udskriver dit fornavn på en linie, og dit efternavn på efterfølgende linie. Opgave Skriv et program der først beder om dit fornævn, derefter om dit efternavn, og så til sidst udskriver dit efternavn og fornavn, i den rækkefølge, på en linie. Sponsor: Ingeniørfirmaet Synkro Side 7 af 107
8 1.2 Formatering af output. I C skulle vi bruge lidt kryptiske metoder for at formatere vores output, det er en del mere forståeligt i C++. I C++ er der 3 manipulatorer, der kan benyttes til at ændre udskriftformattet. De 3 manipulatorer er dec, oct og hex. De kan benyttes på følgende måde: /* Filnavn = prg5.cpp */ #include <iostream> int main() int vaerdi = 15; std::cout << dec << vaerdi << '\t' << oct << vaerdi << '\t' << hex << vaerdi; std::cout << '\n'; return 0; Figur $cpp prg f $ Dette program udskriver først decimalværdigen af variablen vaerdi, derefter udskrives den octale værdig, og til sidst udskrives den hexadecimale værdig på skærnen. Vi benytter tabulring mellem udskrifterne for at skille dem lidt ad, og gøre output mere læselig. Opgave Skriv et program der beder om et tal fra brugeren. Derefter skal tallet udskrives som hex, decimal og octaltal på hver sin linie. Sponsor: Ingeniørfirmaet Synkro Side 8 af 107
9 1.3 Kommentarer Kommentarer i C++ kan udføres med /* for start og */ for slut kommentar, ganske som i C. Dette kan være en ganske upraktisk teknik, hvilket enhver der har ledt efter en fejl der skyldes et manglende */ kan skrive under på. Derfor er der udviklet en ny måde at kommentere på i C++. En Kommentar starter med // og afsluttes automatisk ved linieskift. Brug af kommentarer kan se således ud: /* Filnavn = prg6.cpp Demoprogram kan ikke kompileres */ Kan ikke kompileres. // Program til demo af kommentarer #include <iostream> /* Dette er også en lovlig kommentar i C++ */ int main() char biltype[10]; // Erklær en streng std::cout << "Indtast biltypen : "; // Bed om input std::cin >> biltype; // Indlæs biltypen... // O.S.V return 0; Figur Som det fremgår af eksemplet, kan variabler i C++ erklæres på samme måde som i C 1.4 Funktions prototyper I C kan man undlade at erklære en funktion, før den benyttes. Der sker blot det, at selve funktionen også fungerer som erklæring. Dette er ikke tilladt i C++. Følgende vil give en kompilerfejl i C++. /* Filnavn = prg7.cpp */ // ****** FEJL testfunction HAR INGEN PROTOTYPE ****** #include <iostream> int main() testfunction(); return 0; void testfunction()... Figur Sponsor: Ingeniørfirmaet Synkro Side 9 af 107
10 Vi kan få programmet kompileret ved at indføre prototyperne: /* Filnavn = prg8.cpp */ // ****** RIGTIGT testfunction har en prototype****** #include <iostream> void testfunction() int main() testfunction(); return 0; void testfunction()... Figur En anden løsning kan være at placere testfunction() før den kaldes, så er det nemlig ikke nødvendigt at have en prototype. Hvis en function er defineret i en anden fil, skal den have en prototype, i den fil hvorfra den kaldes. Prototypen skal stå i kildeteksten, før functionen kaldes. I C++ kan en funktions prototype initialisere de argumenter der er i funktionen. Dette betyder, at når man kalder funktionen, kan man undlade at overføre parametre, herved vil funktionen benytte sine initialiseringsparametre. /* Filnavn = prg9.cpp */ #include <iostream> void display(int a = 1, char b = 'a'); int main() display(); display(3); display(5,'c'); return 0; $ spp prg5 1 : a 3 : a 5 : c $ void display(int a, char b) std::cout << a << " : " << b << '\n'; Figur Ikke alle argumenter behøves udfyldes, men initering af argumenter skal altid foregå fra venstre mod højree. Sponsor: Ingeniørfirmaet Synkro Side 10 af 107
11 Følgende er ikke lovligt. Da det andet argument ikke er initieret må det 3. argument ikke forsøges initieret. void display(int a = 1, char b, int c = 4); Men det er fuldt tilladt kun at initiere det første argument således. void display(int a = 1, char b = 'a', int c); 1.5 Konstanter Variabler kan defineres som konstanter ved at benytte det reserverede ord const. Når en konstant erklæres skal den tilskrives, da en konstant i sagens natur kun kan tilskrives én gang. // KAN IKKE KOMPILERES #include <iostream> int main() const int testvar = 100; // Dette er i orden testvar = 25; Return 0; // Kompiler melder fejl, forsøg på at ændre konstant. Figur En pointer kan også defineres som en konstant. Dette medfører, at det pointeren peger på kan ændres, men selve pointeren kan ikke ændres. Pointeren vil hele tiden pege på samme plads i hukommelsen. // Filnavn = konst1.cpp #include <iostream> int main() int tal = 10; int *const pegepind = &tal; $ spp konst1 *pegepind = 12 $ *pegepind = 12; std::cout << "*pegepind = " << *pegepind << '\n'; return 0; Figur Dette program har defineret en konstant pointer, der peger på et hukommelseselement. I dette element står heltallet 10. Linien *pegepind = 12; tilskriver det pegepind peger på, med tallet 12. Dette er legalt, da pegepind ikke er forsøgt at pege på noget andet. Sponsor: Ingeniørfirmaet Synkro Side 11 af 107
12 Det vil derimod fremkalde en kompilerfejl, hvis pointeren forsøges at pege et andet sted hen end defineret. // Filnavn = konst2.cpp // KAN IKKE KOMPILERES Kan ikke kompileres #include <iostream> int main() int a = 10; int b = 12; int *const pegepind = &a; pegepind = &b; // FEJL! std::cout << "Indholdet af pegepind er : " << *pegepind; return 0; Figur Hvis en pointer erklæres til at pege på en konstant, vil det ikke være muligt at ændre indholdet af konstanten, heller ikke ved at pege på den med pegepinden. Der er altså lukket for snedig tilskrivning. Et eksempel kan illustrere dette.!!!! EKSEMPEL SKAL TJEKKES!!!! // Filnavn = konst3.cpp #include <iostream> int main() char minstreng[] = "A"; char dinstreng[] = "B"; const char *pegepind = minstreng; $ cpp konst1 $ pegepind peger på : B pegepind = dinstreng; std::cout << "pegepind peger på : " << *pegepind << '\n'; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 12 af 107
13 En pointer der peger på en konstant, må ikke ændre indholdet af den konstant den peger på, følgende er ikke tilladt:!!!! EKSEMPEL SKAL TJEKKES!!!! // Filnavn = konst4.cpp // Programmet kan ikke kompileres Kan ikke kompileres #include <iostream> int main() char minstreng[] = "a"; char dinstreng[] = "b"; const char *pegepind = minstreng; *pegepind = 'c'; std::cout << "pegepind peger på : " << *pegepind; return 0; Figur Man kan altså sige, at en pointer der peger på en konstant er en skrivebeskyttet pointer, fordi det ikke er muligt at ændre det pointeren peger på. Sponsor: Ingeniørfirmaet Synkro Side 13 af 107
14 1.6 Inline funktioner C++ supporterer en udviddelse af C makroer. Disse makroer, kaldet inline og er lidt mere sofistikerede end C makroer, i det makroer blot er en simpel genkendelse af tekst, mens inline blive tjekket ved kompilering, således at en klassisk fejl undgåes. // Filnavn = inline.cpp #include <iostream> #define MAX(A,B) ((A) > (B)? (A) : (B)) $ cpp inline1 1. x = y = x = y = 21 $ inline int max(int a,int b) return (a > b)? a : b; int main() int i,x,y; x = 10; y = 20; i = MAX(x++,y++); std::cout << "1. x = " << x << "1. y = " << y << '\n'; x = 10; y = 20; std::cout << "2. x = " << x << "2. y = " << y << '\n'; return 0; Figur Den højeste værdi inkrementeres 2 gange i en makro, men udføres som forventet af en inline. Bortset fra dette fungerer inline funktioner som makroer. Sponsor: Ingeniørfirmaet Synkro Side 14 af 107
15 1.7 Rækkevidde operatoren I C kan man få fat i en global variabel ved at erklære den med det reserverede ord extern. C++ tilbyder en mulighed mere, nemlig rækkevidde operatoren ::. Med denne kan man definere at man vil benytte den globale variabel. // Filnavn = scope2.cpp #include <iostream> int alder = 18; int main() int alder = 67; variabel // Global variabel // Lokal $ cpp inline1 alder er = 67 men ::alder er = 18 $ std::cout << "alder er = " << alder << '\n'; std::cout << "men ::alder er = " << ::alder << '\n'; return 0; Figur Rækkevidde operatoren :: henviser altid til den globale variabel. Hvis variablen alder var defineret flere steder, evt. nestet, så ville ::alder stadig henvise til den globale variabel alder. Sponsor: Ingeniørfirmaet Synkro Side 15 af 107
16 1.8 enumerations Enumerations fungerer helt som i C, med den udviddelse, at man ikke behøves at bruge det reserverede ord enum, når først enumerationen er erklæret. // Filnavn = enum2.cpp #include <iostream> enum farve hjerter, kloer, ruder, spar ; $inline1 hjerter = 0 spar = 3 $ int main() farve kortfarve; kortfarve = hjerter; std::cout << "hjerter = " << kortfarve << '\n'; kortfarve = spar; std::cout << "spar = " << kortfarve << '\n'; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 16 af 107
17 1.9 Link specifikation Selv om C++ skal kunne kompile en C kildetekst, er det ikke sikkert, at alle header filer kan kompiles. For at sikre dette, kan et include kald defineres som extern "C". Dette medfører at den inkluderede fil, vil blive kompilet af en C kompiler. // Filnavn = externc.cpp // KAN IKKE KOMPILES #include <iostream> extern "C" #include <stdio.h> extern "C" #include <mitlib.h> #include <ditlib.h> void main() printf("hallo verden \n"); Figur Flere C headerfiler kan inkluderes ved at sætte dem ind i en blok, som vist med mitlib.h og ditlib.h. Hvis du får mærkelige problemer når du kompilerer et C program med en C++ kompiler, så skal du bruge extern C i forbindelse med alle dine C inkludelinier. Sponsor: Ingeniørfirmaet Synkro Side 17 af 107
18 1.10 Overload Ved at "overloade" en function, kan man opnå flere funktioner med samme navn. Rigtigt brugt kan dette gøre det nemmere at programmere, og nemmere at læse et program. Forkert brugt, kan overloadning gøre et program ganske ulæseligt. Lad os se på et eksempel hvor overloadning virker naturlig. Vi ønsker at skrive en funktion max(a,b). Functionen skal returnere det højeste af de 2 tal a og b. Problemmet er, vi ved ikke om a og b er int eller float, derfor: // Filnavn = overload1.cpp #include <iostream> int max(int a, int b); float max(float a, float b); $ cpp inline1 Integer sammenligning 2 Float sammenligning 3.23 $ int main() int t1,t2; float t3,t4; t1 = 1; t2 = 2; t3 = 1.23; t4 = 3.23; std::cout << "Integer sammenligning! " << max(t1, t2) << '\n'; std::cout << "Float sammenligning " << max(t3, t4) << '\n'; return 0; int max(int a, int b) return (a > b)? a : b; float max(float a, float b) return (a > b)? a : b; Figur C++ kan se forskel, blot parameterlisten for funktionen er forskellige, enten i antallet, eller i typen. 2 Funktioner må ikke have samme navn, hvis deres eneste forskel er den type der returneres. C++ ser kun på parameterlisten, ikke på returværdier. Sponsor: Ingeniørfirmaet Synkro Side 18 af 107
19 1.11 Referencer Referencer benyttes til at overføre parametre til funktioner, samt til at returnere data fra funktioner. En reference kan betragtes som et alias for en variabel. Sådan at forstå, at alt hvad der sker med et alias, sker med den variabel aliaset refererer til. Et lille eksempel kan belyse sagen. // Filnavn = reference1.cpp #include <iostream> int main() int mittal = 1; int &aliastal = mittal; std::cout << "mittal = " << mittal << '\n'; std::cout << "aliastal = " << aliastal << '\n'; mittal++; std::cout << "mittal = " << mittal << '\n'; std::cout << "aliastal = " << aliastal << '\n'; aliastal++; std::cout << "mittal = " << mittal << '\n'; std::cout << "aliasttal = " << aliastal << '\n'; return 0; Figur $ cpp reference1 mittal = 1 aliastal = 1 mittal == 2 aliastal = 2 mittal = 3 aliastal = 3 $ Vi kan se, at alt hvad der sker med mittal, sker samtidig og identisk på aliastal, dette skyldes at aliastal er defineret som en reference. Denne defenition sker i linien: int &aliastal = mittal; En reference erklæring kan altså erkendes, når et & starter navnet på en variabel, og der umiddelbart inden er erklæret en type. Navnet på variablen skal efterfølges med navnet på den variabel der refereres til. Det er nærligende at mene, at en reference kan forveksles med en addresse. Dette er dog ikke muligt hvis man holder sig følgende for øje. int *pind; // Erlærer en pointer til int int &alias = i; // Erklærer alias som refernce til i. pind = &i; // pointeren pind peger nu på i's addresse. I det ovenstående eksempel er det nødvendigt at initialisere referencen når den erklæres. Dette gøres ved at tilskrive referencen aliastal med indholdet af variablen mittal. Sponsor: Ingeniørfirmaet Synkro Side 19 af 107
20 En reference skal initieres, med mindre: - Den er medlem af en klasse. I så fald må man formode, at klassens constructer sørger for initiering. - Den er erklæret som parameter i en functions erklæring. I det referencen så vil blive initieret nå functionen kaldes. - Den er en erklæret som en return type for en function. I så fald sørger functionen selv for at initiere reference. - Den er erklæret med extern, i så fald vil referencen være initieret et andet sted. Når en reference benyttes som parameter i en function, fungerer det på nogenlunde samme måde, som når et PASCAL program benytter VAR erklæringer i en Procedure's parameterliste. Funktionen (Proceduren) arbejder på selve variablen. Dette betyder at en funktion kan returnere et resultat, i en ændret parameter. Dette kan være uhensigtsmæssigt, da det kan gøre koden sværere at læse. Det må dog være op til den enkelte programmør at vurdere, hvorledes man ønsker at benytte referencer. Her er et eksempel. På næste side er programmet reference2.cpp der viser hvordan structurer og funktioner kan bruge referencer. Programmet starter med at erklære struct'en record. I samme arbejdsgang oprettes et tilfælde ved navn datablok, og data initialiseres. I starten af main udskrives indholdet af datablok, og test1 kaldes. Funktionen udskriver data uden at ændre dem, og fungerer helt forudsigeligt. Derefter kaldes test2 der lægger en til tal og udskriver indholdet på skærmen. Her er der heller ikke de store overaskelser. Nå programmet returnerer til main, udskriver main indholdet af datablok, og vi kan nu se, at test2's manipulation også har virkning her, som om vi havde overført en pointer til funktionen. Slutteligt kaldes test3, der lægger en til tal efter at have skrevet værdien ud på skærmen, og returnerer struct'en til main. Som det ses af den sidste linie i main får vi også på denne måde fat i de manipulerede data. Sponsor: Ingeniørfirmaet Synkro Side 20 af 107
21 // Filnavn = reference2.cpp #include <iostream> struct record int tal; char navn[12]; datablok = 10, " Petersen" ; C:\reference2 10 Petersen 10 Petersen Petersen Petersen 12 C:\ void test1(struct record &minrec) std::cout << minrec.tal << minrec.navn << '\n'; void test2(struct record &minrec) minrec.tal++; std::cout << minrec.tal << minrec.navn << '\n'; struct record &test3(struct record &minrec) std::cout << minrec.tal << minrec.navn << '\n'; minrec.tal++; return minrec; int main() std::cout << datablok.tal << datablok.navn << '\n'; test1(datablok); std::cout << datablok.tal << '\n'; test2(datablok); std::cout << datablok.tal << '\n'; datablok = test3(datablok); std::cout << datablok.tal << '\n'; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 21 af 107
22 Opgave Skriv en funktion findop(char *s). Når den kaldes skal den søge den streng *s peger på gennem, indtil den finder en operator for en af de 4 regnearter. Når operatoren er fundet, skal den returnes til den kaldende function, via en reference til en char. Opgave Skriv en function filter(char *s). Når den kaldes, skal den søge den streng gennem som *s peger på. Funktionen skal filtrere det første heltal i strengen ud, og returnere det via en reference af typen int. Opgave Udvid opgave og opgave , således at du har et program, der beder om et input. Dette input skal være et regnestykke, baseret på de 4 regnearter. Når der trykker på enter, skal programmet filtrere operator, samt de 2 argumenter fra, og udføre den aktuelle beregning. Sponsor: Ingeniørfirmaet Synkro Side 22 af 107
23 6.12 for løkken i C++ I C++ er det gjort muligt at erklære variabler inde i en for løkke således. for (int i = 0; i < MAX; i++)... Argumentet for at tillade denne teknik er, at det kan være en fordel at erklære variablen der hvor den skal bruges, så kan man se variabelerklæringen mens man arbejder med den. Det kan dog også være en svaghed, da man risikerer at bruge samme variabelnavn flere gange, og dermed tabe overblikket. I ovenstående tilfælde er rækkevidden af variablen i selve løkken, og intet andet. Det må være op til personligt behag om man vil bruge denne teknik eller ej. I denne bog bruges teknikken når det er praktisk, og ellers ikke, men det er en af de fornøjelige småting der åbner mulighed for lidt personlighed i kildeteksterne. Sponsor: Ingeniørfirmaet Synkro Side 23 af 107
24 1.13 Opgaver Opgave Skriv en et program, der henter indtastede data fra standard input, indlæser det i en variabel, og derefter udskriver variablen til standard output. Opgave Skriv en function, der har 2 konstanter a og b. Functionen skal udskrive forskellen på de 2 konstanter i hexadecimalt format. Opgave Skriv et program der skal have 3 strengkonstanter. Programmet skal ved hjælp af en pointer udskrive de 3 strengkonstanter, én linie ad gangen. Opgave Skriv en function alder(a,b). Funktionen skal aflevere aldersforkellen (b - a). Hvis a ikke bliver overført skal a = 18. Hvis b ikke overføres, skal b = 67. Opgave Skriv et program speed. Programmet skal have en global konstant mspeed = 110. Programmet skal have en function ved navn nufart(int mspeed). Functionen skal trække den lokale mspeed fra den globale. Hvis resultattet er over nul, skal functionen aflevere en pointer til sætningen: "Advarsel, du kører for stærkt.", ellers skal functionen aflevere en pointer til ordet OK. Hovedprogrammet skal kalde mspeed, og udskrive den tekst pointeren peger til. Opgave Skriv de funktioner der skal til for at udføre de fire regnearter (+-*/). Functionens navn er regn(int a,int b, char operator). a og b skal kunne være int eller float. Functionen skal aflevere resultattet af beregningen til den kaldende function. Opgave Skriv en funktion beregn(char a[]). Funktionen modtager strengen a, (faktisk en pointer til strengen) i denne skal et regnestykke modtages defineret på følgende form. <tal1>operator<tal2> Funktionen skal udskille tal1 til et int, operatoren til en char, og tal2 til et integer. Derefter skal functionen lade regn(a,b,operator) udføre beregningen, og udskrive resultatet. Opgave Udvid programmet fra opgave således at det også kan håndtere kommatal. Sponsor: Ingeniørfirmaet Synkro Side 24 af 107
25 2 Klasser og objekter iterator const_iterator explicit #ifndef TEXTFINDER_H #define TEXTFINDER_H #include <QWidget> namespace Ui class TextFinder; class TextFinder : public QWidget Q_OBJECT public: explicit TextFinder(QWidget *parent = 0); ~TextFinder(); private: Ui::TextFinder *ui; ; #endif // TEXTFINDER_H copy constructor!!! namespace!!! Lad os starte med defenitionen: En klasse er en definerbar type Sponsor: Ingeniørfirmaet Synkro Side 25 af 107
26 Et objekt er et tilfælde af en klasse. Klasser er en datatype, der ikke findes i C. Klasser kan og vil normalt indeholde både data og kode. En klasse er skabelonen for et objekt, på samme måde som en struct erklæring kan bruges til at skabe mange arkivkort. En klasse bruges til at samle alt beslægtet under et sæt vinger. Herfra kan man så føde andre klasser, der har til opgave, at løse definerede delopgaver inden for hovedopgaven. 2.1 En simpel klasse For at hugge hul på isen, så lad os skabe en klasse der kan omregne temperaturen fra Fahrenheit til Celcius, i intervalet Vi starter med at lave en headerfil der skal indeholde erklæringer. // Filnavn = f_til_c1.h Headerfil class celsius ; public: celsius(int fahr); void showtemp(); // Constructor ~celsius(); // Destructor private: int resultat; Figur Det ser dette jo temmelig anderledes ud hvis man sammenligner med C. Til at starte med har vi en klasseerklæring, klassens navn er celsius, hvilket jo straks burde give accosiationer. Det handler altså om temperatur. celsius er en type vi netop har defineret. Den efterfølgende blok indeholder et par nye begreber såsom Constructer, Destructor, public og private. Lad os starte med public og private. public er et reserveret ord der betyder, at det efterfølgende kan ses af hele verden. Hvis intet defineres er private default (underforstået). private kan altså udelades, men det hjælper på læseligheden, hvis det skrives. private er et reserveret ord der betyder, at det efterfølgende er privat. Det betyder at private variabler eller funktioner kun kan ses af klassen og dens medlemmer. Man kan bruge public og private så ofte man ønsker inde i en klasse erklæring, men det er god orden, og hjælper på læseligheden af programmet, hvis man kun benytter hvert ord en gang per klasse. Constructor: Dette er det sted hvor klassens elementer initialiseres. Alle klasser har en construktor, hvis programmøren undlader den vil compilereren lave sin egen constructor. Constructoren er en Sponsor: Ingeniørfirmaet Synkro Side 26 af 107
27 funktion der kaldes hver gang klassen erklæres og ellers aldrig. Ved rigtig brug af constructoren for en klasse kan man sikre sig, at alle elementer er korrekt initialiseret. Constructoren skal have samme navn som klassen. Der kan defineres flere constructorer, blot de har forskellige parametre. Constructore kan med andre ord overloades. En constructor kan ikke returnere værdier, den skaber et object. Destructor: Denne har den modsatte opgave af Constructoren. Hvor constructoren bygger op, river destructoren ned. Det kan lyde destruktivt, men er faktisk bare en måde at sikre sig mod forurening, i det destructoren ryder op efter klassen, frigiver hukommelse m.m. Der kan kun være en destructor per class. En destructor kan ikke modtage og aflevere data. Destructoren kan ikke overloades. En klasse definition afsluttes med et semikolon. Man kan sammenligne klassen med en samling af prototyper. I C++ er det ikke noget man må gøre, men noget man skal gøre. Hele klassens indhold skal erklæres i klasseerklæringen, senere skal klassen implementeres, det vil ske i filen f_til_c1.cpp. Det første vi lægger mærke til i den fil er gode gamle main. Her er intet ændret, main skal være der. (I nogle kompilermiljøer er main erstatet af en funktion med beslægtet navn, den kan eksempelvis hedde winmain(), det er ikke lige efter C++ standarden, men funktionaliteten er den samme.) Sponsor: Ingeniørfirmaet Synkro Side 27 af 107
28 // Filnavn = f_til_c1.cpp #include <iostream> #include "f_til_c1.h" $ cpp f_til_c1 celcius = 93 $ celsius::celsius(int fahr) // Constructor fahr = (fahr > 0)? fahr : 0; fahr = (fahr < 300)? fahr : 300; resultat = 5*(fahr 32)/9; void celsius::showtemp() std::cout << "celcius = " << resultat << '\n'; celsius::~celsius() // Intet // Destructor int main() celsius mintemp(200); mintemp.showtemp(); return 0; Figur Når klassen er defineret, skal de enkelte medlemmers funktion beskrives. Vi starter med constructoren: En klasses constructor implementeres ved klassens navn, efterfulgt af to koloner,m (rækkeviddeoperatoren) efterfulgt af klassens navn igen. Herefter følger en evt. parameterliste I dette eksempel vil konstruktoren sikre, at ingen værdier af variablen fahr er større end 300, og ingen er mindre end 0. Derefter udfører construktoren beregningen 5*(fahr-32)/9 og tilskriver resultatet af beregningen til den private variabel resultat. Det er ikke den normale måde at bruge en construktor på, men det belyser en dens funktion. Sponsor: Ingeniørfirmaet Synkro Side 28 af 107
29 Variablen resultat, kan kun ses af klassen celcius'es medlemmer, main kan altså ikke se hvad der står i resultat, det kan showtemp derimod. Og det er også showtemp der udskriver resultatet af den beregning der blev foretaget i constructoren. Til sidst har vi destructoren. Dens navn begynder med ~(tilde). Destructoren erklæres på næsten samme måde som en klasses constructor, blot med den forskel, at destructoren skal starte med et tilde. Alle medlemsfunktioner der defineres i klassen, skal fremgå af den følgende tekst. En klasses medlemmer, behøves ikke at fremkomme i nogen speciel orden, men det hjælper på programmets læselighed hvis de gør. At en senere defineret funktion hører til klassen celsius, kan ses ved at dens navn skal begynde med celsius::. I main erklæres et object af typen celcius. Dette gøres i linien celcius mintemp(200); herved oprettes et objekt af typen celcius. Objektet bliver initialisret med værdien 200. Linien kan opfattes som et kald til konstruktøren, der skaber objektet ud fra klassen. Objektets navn er mintemp, klassens navn er celsius. Med linien: mintemp.showtemp(); kaldes celcius'es medlemsfunktion showtemp. Da showtemp ikke modtager variabler, kan man ikke overføre variable i dette kald. showtemp udfører en beregning på de initialiserede variabler, med det resultat, at fahrenheit 200 grader, bliver udskrevet som celcius. Dette var lidt mere omfattende, end vores første program, men egentlig ikke så indviklet endda. I næste afsnit vil vi bore lidt mere i hvad constructoren og destructoren foretager sig. Opgave Omskriv programmet celsius, så det i stedet omregner fra selsius til fahrenheit. Opgave Omskriv programmet fra opgave således, at programmet regner med kommatal. Opgave Omskriv programmet fra opgave således at der skrives en omregningstabel på skærmen for selsuis = 0 til selcius = 25 i step på 1. Sponsor: Ingeniørfirmaet Synkro Side 29 af 107
30 2.2 Objektets liv For at beskrive et objekts livssyklus lidt mere udtømmende har vi lavet programmet showoff1.cpp. Programmet viser objektets livscyklus, ved at vise hvornår constructor og destructor kaldes. Programmet fungerer på den måde, at før, efter og mens constructor og destructor kaldes, så udskrives en besked på skærmen om, hvor i programmet dette foregår. // Filnavn = showoff1.h class showoff ; public: private: showoff(char *s); ~showoff(); char besked[25]; Figur Som det ses af klasseerklæringerne, er programmet så simpelt som det kan være. Ud over Constructeren og destructoren er der en streng. Den er lokal for hvert objekt der oprettes af denne klasse. Den bruger vi til at gemme informationer om, hvem der kaldte constructoren. I filen showoff1.cpp bliver arven fra C meget synlig, i det vi frækt blander ordinær C kode ind i vores objektorienterede program, ved at indføre en testfunktion. Her er det gjort for at demonstrere en pointe, men det viser også hvor nært beslægtet C og C++ er med hindanden. Før main, ude midt i kildeteksten opretter vi et globalt objekt af typen showoff. I main opretter vi to objekter af typen (klassen) showoff, det ene objekt kalder vi for mainoff, som erklæres på normal vis, det andet objekt kalder vi statisk, og det erklæres som static. Senere i main kaldes testfunktionen, der også opretter et objekt af showoff klassen. Sponsor: Ingeniørfirmaet Synkro Side 30 af 107
31 // Filnavn = showoff11.cpp #include <iostream> #include "showoff1.h" showoff::showoff(char *s) strcpy(besked,s); std::cout << "Constructor kaldt af " << besked << '\n'; C:\showoff1 Constructor kaldt af Globalobject Constructor kaldt af Netop ankommet til main Constructor kaldt af Erklærer statisk Inde i main før kald til test Constructor kaldt af Testfunction Inde i testfunctionen Destructor kaldt af Testfunction Inde i main, efter kald til test Destructor kaldt af Netop ankommet til main Destructor kaldt af Erklær statisk Destructor kaldt af Globalobject C:\ showoff::~showoff() std::cout << "Destructor kaldt af " << besked << '\n'; void test() showoff testoff("testfunktion"); std::cout << "Inde i tesfunctionen \n"; showoff globaloff("globalobject"); int main() showoff mainoff("netop ankommet til main"); static showoff statisk("erklærer statisk"); std::cout << "Inde i main før kald til test\n"; test(); std::cout << "Inde i main, efter kald til test\n"; return 0; Figur Når vi eksekverer programmet kan vi se, at for lokale objekter gælder, at når objektet erklæres (fødes) udføres constructoren. Objektet nedlægges (dør) og kalder destructoren når funktionsblokken forlades. Programmøren kan ikke kalde destructoren, det sker automatisk under programafviklingen, og det er en fejl at forsøge at kalde destructoren. For globale objekter gælder, at dets constructor kaldes når programmet startes. Dets destructor kaldes når programmet forlades. Sponsor: Ingeniørfirmaet Synkro Side 31 af 107
32 For static objecter kaldes constructoren når objectet første gang erklæres. Destructoren kaldes når programmet forlades. 2.3 Overførsel af data til et objekt Op til nu har vi demonstreret en klasse ved at bruge nogle atypiske teknikker. Programmet distance.cpp, indeholder en klasse der har til opgave, at beregne en kørt distance. Dette udføres af funktionen getdistance(). Denne funktion er medlem af klassen distance. Klassens constructor sikrer, at hvis getdistance() kaldes før nogle data er initialiseret, så vil de private variabler have værdien nul. Dette sikrer igen mod uønskede programafviklingsfejl, som funktion af hvad der tilfældigvis stod i de aktuelle dataelementer, da programmet blev startet. Programmet udskriver resultatet af en multiplikation. Multiplikationen udføres i klassen på klassens private variabler. Main programmet har ingen muligheder for at manipulere variablerne lokaltid, og lokaldistance. Produktet af de 2 variabler kan dog hentes, i det funktionen getdistance()afleverer produktet af de 2 variabler. // Filnavn = distance1.h class distance ; public: private: distance(); int getdistance(); void setafstand(int afstand); void settid(int tid); ~distance(); int lokaltid, lokaldistance; Figur Blot ved at kigge på klasseerklæringen i headerfilen kan man få et godt indtryk af hvad klassen kan, og hvordan den bærer sig ad med at gøre det. Klasseerklæringen kan, hvis man tænker sig lidt om når man skriver den, virke meget i retning af en pseodokode, og dermed i sig selv være forklarende. Sponsor: Ingeniørfirmaet Synkro Side 32 af 107
33 Sponsor: Ingeniørfirmaet Synkro Side 33 af 107
34 // Filnavn = distance1.cpp #include <iostream> #include "distance1.h" $ cpp distance1 distance før tilskrivning fra main 0 distance efter tilskrivning af settid 0 distance efter tilskrivning af setafstand 8 $ distance::distance() // Constructor lokaltid = lokaldistance = 0; int distance::getdistance() return lokaltid*lokaldistance; void distance::setafstand(int afstand) lokaldistance = afstand; void distance::settid(int tid) lokaltid = tid; distance::~distance() // Destructor int main() distance beregner; std::cout << "distance før tilskrivning fra main " << beregner.getdistance() << '\n'; beregner.settid(2); std::cout << "distance efter tilskrivning af settid " << beregner.getdistance() << '\n'; beregner.setafstand(4); std::cout << "distance efter tilskrivning af setafstand " << beregner.getdistance() << '\n'; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 34 af 107
35 Programmet starter med at oprette et objekt af typen distance, dette objekt kaldes beregner, herefter skal enhver adgang til funktioner og variabler i objektetet beregner indledes med beregner.(beregner punktum), på samme måde som når vi vil tilgå variabler i en struct. I programmet har vi lavet hele implementeringen af destructoren på en linie, hvilket er ganske legalt. Hvis vi ikke ønsker at implementere noget i destructoren, kan den helt udelades. Opgave Skriv en klasse. Klassen skal indeholde medlemsfunctionen givmig(char *s). Denne function skal modtage en pointer til en streng. Klassen skal også indeholde medlemsfunktionen skrivmig(). Denne function skal udskrive det der blev modtaget i givmig(char *s). Hvis givmig(char *s) ikke har været kaldt, skal skrivmig() udskrive "Intet defineret" på skærmen. Opgave Skriv et program der kan udskrive et vandret histogram på skærmen. Programmet skal indeholde en klasse, histoclass. Denne klasse skal garentere, at ingen værdier over 79, og ingen under 0, vil blive udskrevet. Klassen skal have en medlemsfunktion modtag(int a). Dette medlem skal modtage den værdi der skal symboliseres i histogrammet. Klassen skal også have en medlemsfunction udskriv(). Dette medlem skal udskrive selve histogrammet, med de værdier der er givet i modtag(int a). Hvis data er større eller mindre end de tilladte, skal udskriv() ikke skrive på skærmen, men aflevere en pointer til en fejlmeddelse. Hovedprogrammet skal kontrollere for fejl, og udskrive evt. fejlmeddelelser. Sponsor: Ingeniørfirmaet Synkro Side 35 af 107
36 2.4 Konstant erklæringer const funktioner, classer og objekter!!! Et objekt kan defineres som værende en konstant. Det medfører at ingen funktioner kan kaldes, da kompileren ikke kan se hvilke funtioner der har lov til hvad. Variabler der er public kan læses udefra. Medlemsfunktioner i en klasse kan erklæres som konstanter, med det reserverede ord const. Når et medlem af en klasse defineres som konstant, kan funktionen ikke ændre på data i det objekt det bliver en del af. Der kan derfor ej heller overføres parametre til medlemmet. Det kan være praktisk hvis en programmør ikke ønsker andre programmører skal kunne ændre i data, men have adgang til at læse data. class indud public: indud(int n1, int n2); int skriv() const; void input(int tal); private: int temp1, temp2; ; Figur Programmet demonstrerer brugen af en konstantfunktion. Funktionen skriv er erklæret som en konstantfunktion, men det hindrer ikke at funktionen kan kaldes, da funktionen ikke tilskriver nogen af objektets variabler. Funktionen input kan derimod ikke kaldes, det skyldes vi har oprettet objektet som et konstantobjekt. Sponsor: Ingeniørfirmaet Synkro Side 36 af 107
37 // Filnavn = indud1.cpp #include <iostream> #include "indud1.h" indud::indud(int n1, int n2) temp1 = n1; temp2 = n2; C:\indud1 2 skriv = 2 C:\ Retur fra 1 : int indud::skriv() const std::cout << temp1 << " : " << temp2 << '\n'; return temp2; void indud::input(int tal) temp1 = tal; int main() const indud test(1,2); std::cout << "Retur fra skriv = " << test.skriv() << '\n'; // test.input(5); // Ulovligt, test er en konstant return 0; Figur Hvis vi ønkser at bruge input funktionen, så skal linien: Ændres til: const indud test(1,2); indud test(1,2); Et konstant objekt kan initieres. Det gælder jo for alle konstanter, at de initieres når de bliver defineret. Et medlem der er defineret som konstant, kan ikke skrive i klassens variabler. Forsøg herpå vil give fejl ved kompilering. Opgave Tilpas programmet indud1.cpp således at input funktionen erklæres const. Undersøg om programmet kan kompileres. Sponsor: Ingeniørfirmaet Synkro Side 37 af 107
38 2.5 Medlemmer defineret som static Når en variabel defineres som static i en klasse, så kan den kun initieres én gang. Dette sker første gang der bliver erklæret et objekt af klassen. Det betyder, at variablen herefter har den samme værdi i alle de objekter der oprettes af klassen. Variabler der erklæres som static i en klasse kaldes klassevariabler, fordi det vil være den samme variabel i alle de objekter der oprettes af klassen. Sagt på en anden måde, en klassevariabels rækkevidde er hele klassen, en objektvariabel (ikke static) rækkevidde er objektet. // Filnavn = ibrug1.h class ibrug ; public: private: ibrug() antal++; void netopnu(); ~ibrug() antal ; static int antal; Figur I headerfilen sker der noget nyt, vi implementerer contructoren og destrutctoren i headerfilen, men ikke netopnu() funktionen. Det er ganske legalt at gøre det på denne måde, men bør kun gøres når implementeringen er så lille som her. Bemærk i øvrigt at antal erklæres som static, det er det der gør den til en klassevariabel. Funktionen i constructoren er at lægge en til antal hver gang constructoren kaldes, og det er hver gang der fødes et nyt objekt af klassen, destructoren gør det modsatte. Det betyder, at antal til enhver tid indeholder antallet af objekter der er skabt af klassen. Sponsor: Ingeniørfirmaet Synkro Side 38 af 107
39 // Filnavn = indud1.cpp // Tæller antal af klassen // der er i brug netop nu. #include <iostream> #include "ibrug1.h" $ cpp ibrug Der er 1 af denne klasse i brug nu Der er 2 af denne klasse i brug nu Der er 1 af denne klasse i brug $ int ibrug::antal = 0; void ibrug::netopnu() std::cout << "Der er " << antal << " af denne klasse i brug nu\n"; void testfunc() ibrug no2; no2.netopnu(); void main() ibrug no1; no1.netopnu(); testfunc(); no1.netopnu(); Figur Bemærk linien: int ibrug::antal = 0; Den står helt for sig selv, i det globale område af kildetekten. Det er vigtigt at en variabel der er erklæret som static, kun bliver erklæret én gang, ellers vil der fremkomme en fejlmeddelelse under eksekveringen. Dette er årsagen til den bliver tilskrevet før det første objekt af klassen skabes. En Sponsor: Ingeniørfirmaet Synkro Side 39 af 107
40 ting der bør springe i øjnende er, at variablen antal tilskrives uden for klassen, selv om det er et private medlem. Dette kan kun foretages på medlemmer der er erklæret static. Dette eksempel skulle også gerne give lidt forståelse for destructorens virkemåde. Årsagen til at den 3. linie i vores output skriver, "Der er 1 af denne klasse i brug nu" er, at destructoren kaldes når testfunc() forlades. Herved slettes de objecter der blev skabt af funktionen, med mindre de er erklæret som static. En klassevariabel er global for alle tilfælde af den klasse, og er derfor velegnet til kommunikation mellem objekter. Det er ikke altid en god ide at kommunikere mellem objekter på den måde, da variablerne jo netop virker som en global variabel, og kanmedfører mistet overblik. Opgave Erklær no2 som static i testfunktionen i programmet indud1.cpp. Undersøg og forklar hvad der sker. 2.6 Friends Klasser der har meget brug for at kommunkiere kan oprettes som værende venner. Dette gøres med de reserverede ord friend class, efterfulgt af den klasse der skal være en ven til denne klasse. Når en klasse defineres som værende ven med en anden, betyder det, at vennen kan se alt der er eklæret i den klasse der erklærer en ven. Omvendt kan klassen der har defineret en anden som sin ven, ikke se noget inde i den klasse, med mindre den også er erklæret som ven. Venskabet går altså kun en vej. // Filnavn = friend1.h class ven1 ; friend class ven2; ven1() a = 1; private: int a; Figur I filen friend1.h erklæres ven2 som værende ven til ven1, det betyder, at alle objekter der skabes af ven2, kan se variablerne i ven1, men ikke omvendt. Klassen ven2 erklærer ikke ven1 som sin ven, hvorfor ven1 ikke kan se noget inde i ven2. Sponsor: Ingeniørfirmaet Synkro Side 40 af 107
41 // Filnavn = friend2.h class ven2 public: ven2() void display(); ; Figur // Filnavn = friend1.cpp // Demonstration af friend class #include <iostream> #include "friend1.h" #include "friend2.h" $ cpp friend1 ven1.a = 1 $ void ven2::display() ven1 minven; std::cout << "ven1.a = " << minven.a << '\n'; int main() ven2 vennetest; vennetest.display(); return 0; Figur Når ven2 erklærer minven i sin display() funktion, så vil ven1's constructor blive kaldt. Her tilskrives variablen a værdien 1. Så når display() lidt efter sender minven.a til standard output, er det ganske legalt. Man kan også definere en enkelt funktion som værende en ven, ved at bruge det reserverede ord friend. Det gøres på denne måde. Sponsor: Ingeniørfirmaet Synkro Side 41 af 107
42 class ven1 public: friend void testfunktion(); private: int hemmeligt; Herved gives funktionen testfunction adgang til ven1's variabler. Opgave Omskriv programmet friend1.cpp således at ven1 kan se ven2's variabler og funktioner. 2.7 Array af objekter Da en klasse er en datatype kan den også erklæres som en array. Det følgende program erklærer en array med 10 elementer, af typen flere. Derefter løber den alle 10 elementer igennem, og udskriver sit gennemløbsnummer. // Filnavn = flere1.h class flere public: flere() void fler(int i); ~flere() ; Figur Tilgang til variabler og funktioner er som hvis det var en struct. Sponsor: Ingeniørfirmaet Synkro Side 42 af 107
43 // Filnavn = flere1.cpp #include <iostream.h> #include "flere1.h" void flere::fler(int nr) std::cout << "Jeg er nr " << nr << '\n'; int main() int i; flere mange[10]; $ cpp flere Jeg er nr 0 Jeg er nr 1 Jeg er nr 2 Jeg er nr 3 Jeg er nr 4 Jeg er nr 5 Jeg er nr 6 Jeg er nr 7 Jeg er nr 8 Jeg er nr 9 $ for (i = 0; i < 10; i++) return 0; mange[i].fler(i); Figur Der er ikke meget behov for at arbejde videre med objekt array's, da der ikke rigtigt er vundet noget ved det. Skal vi bruge en samling af objekter må det varmt anbefales at benytte kædede lister af objekter i stedet, som vi vil se i næste afsnit, er det langt mere smidigt. 2.8 Pointere I C skal man benytte funktionen malloc, hvis man ønsker at reservere plads i hukommelsen. malloc funktionen har et par ulemper. Først og fremmest returnerer den en void pointer, der hvis man ikke benytter en cast, genererer en advarsel fra kompileren. Herudover skal malloc have at vide hvor meget plads der skal alloceres. Dette udføres typisk med sizeof operatoren. Set i relation til C++, har malloc nogle ulemper. Hvis man reserverer plads til et objekt med malloc, så vil objektet blive skabt, men constructoren vil ikke blive kaldt. For at imødegå dette, findes new operatoren i C++. New operatoren har en anden fordel over for malloc. New operatoren afleverer en pointer, af rette type. Hvis programmøren har taget fejl af typerne, leverer kompileren en kompilerfejl. Alt i alt er new noget nemmere at bruge end malloc. New returnerer 0, hvis der ikke kunne allokeres hukkommelse, på samme måde som malloc. Sponsor: Ingeniørfirmaet Synkro Side 43 af 107
44 I C findes funktionen free, til at frigøre benyttet hukommelse. I C++ hedder den tilsvarende operator delete. Når delete kaldes frigives hukommelse, en evt. destructor kaldes, og den ledige plads returneres til operativsystemet. Vi vil nu skrive et program der demonstrerer et objekts lyvscyklus når objektet oprettet som et dynamisk objekt, altså på samme måde som når vi ville lave en kædet liste. // Filnavn = prttest1.h Headerfil class testclass public: testclass(); ~testclass(); ; Figur Der er ikke noget nyt i headerfilen, hele klasseerklæringen er upåvirket af vi ønsker objektet oprettet som et dynamisk objekt. Sponsor: Ingeniørfirmaet Synkro Side 44 af 107
45 // Filnavn = ptrtest1.cpp #include <iostream> #include "ptrtest1.h" testclass::testclass() std::cout << "i constructor\n"; ; $ cpp ptrtest1 I main før new I constructor I main efter new I destructor I main efter destructor $ testclass::~testclass() std::cout << "i destructor\n"; ; void main() class testclass *minklasse; std::cout << "I main før new \n"; minklasse = new testclass(); std::cout << "I main efter new\n"; delete minklasse; std::cout << "I main efter delete\n"; Figur I selve implementeringen er der til gengæld en del nyt, for det første er der linien: class testclass *minclass; Her eklæres en pointer til et objekt af typen testclass. Bemærk at constructoren bliver ikke kaldt i denne linie, det skyldes at der ikke oprettes et objekt, men en pointer til et objekt. Efter pointeren er blevet oprettet, udskrives der: I main før new for at demonstrere, at der er endnu ikke oprettet noget objekt. Objektet oprettes i linien: Sponsor: Ingeniørfirmaet Synkro Side 45 af 107
46 minklasse = new testclass(); og der udskrives I constructor på skærmen. Umiddelbart derefter udskrives der I main efter new på skærmen, og linien: delete minklasse; kaldes for at nedlægge objektet, hvilket også udskrives på skærmen. Efter denne lile demonstration af hvordan vi opretter og nedlægger dynamiske objekter, vil vi vise hvordan et program der udnytter destructoren, kan gøre livet nemmere for programmøren. Vi vil lave et prorgram der laver en kædet liste af objekter, det smarte er at vi kan indkapsle opgaverne således at de løses af objektet. // Filnavn = prttest2.h Headerfil class testclass public: testclass(); void nytobjekt(); ~testclass(); private: int lokalnr; static int nr; testclass *next, *tmp; ; Figur Ved at kigge i headerfilen begynder vi at ane det som objektorienteret programmering er god til, at samle alt relateret på en overskuelig måde. Vi har en funktion ved navn nytobjekt(), der som navnet siger skal kunne oprette et nyt objekt. Vi kan også se vi har en nextpointer, hvis vi er lidt smarte lader vi nytobjekt() funktionen oprette nye objekter, og indsætte dem i listen, derved er hele den opgave isoleret til en overskuelig opgave. Bemærk at next og tmp er erklæret som private, det betyder, at ingen uden for objektet kan pille ved dem. Sponsor: Ingeniørfirmaet Synkro Side 46 af 107
47 // Filnavn = ptrtest2.cpp #include <iostream.h> #include "prttest2.h" testclass::testclass() lokalnr = nr++; std::cout << "Opretter # " << lokalnr << '\n'; next = NULL; void testclass::nytobjekt() if (next == NULL) next = new testclass(); else tmp = next; while (tmp >next!= NULL) tmp = tmp >next; tmp >next = new testclass(); testclass::~testclass() delete next; std::cout << "Nedlægger # " << lokalnr << '\n'; int testclass::nr = 0; $ cpp prttest2 Opretter # 0 Opretter # 1 Opretter # 2 Opretter # 3 Opretter # 4 Opretter # 5 Opretter # 6 Opretter # 7 Opretter # 8 Opretter # 9 Nedlægger # 9 Nedlægger # 8 Nedlægger # 7 Nedlægger # 6 Nedlægger # 5 Nedlægger # 4 Nedlægger # 3 Nedlægger # 2 Nedlægger # 1 Nedlægger # 0 $ int main() testclass *start; start = new testclass(); for (int i = 1; i < 10; i++) start >nytobjekt(); delete start; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 47 af 107
48 Constructoren opretter ikke bare objektet for os, men vi har skrevet programmet således, at constructoren også sætter nextpointeren til at pege på NULLpointeren. Det betyder, at hver gang vi opretter et nyt objekt, så er det passende initieret, og hvis objektet skal indsættes sidst i listen, så behøves der ikke blive gjort mere end at oprette det. Det er netop det funktionen nytobjekt() udnytter. Når programmøren ønsker at oprette et nyt objekt, skriver han blot linien: start->nytobjekt(); hvorefter det objekt som *start peger på, helt af sig selv finder enden af den kædede liste, opretter et nyt objekt, og indsætter det der. Det er umuligt for programmøren ude i main på nogen måde at påvirke det interne af den kædede liste, hvis han prøver at skrive: start->next = tmp; kommer der en fejlmeddelelse fra kompileren, fordi både nect og tmp er erklæret private. Denne teknik har den store fordel, at man kan løse alle opgaver omrking oprettelse, nedlæggelse, udtagning og maeget andet inde i klassen. Når den er færdig og testet, så kan man bruge klasen igen og igen i alle mulige sammenhæng, blot ved at oprette et dynamisk objekt. At ryde op efter klassen, vil altså betyde, at destructoren frigiver den hukommelse klassen har brugt. Hvis hukommelsen ikke bliver frigivet når classen "dør", vil der være en pæn riciko for, at programmet før eller siden løber tør for hukommelse. Det er dårlig programmeringsteknik, ikke at ryde op efter sig. Opgave undersøg hvad der sker hvis delete fjernes fra destructoren. 2.9 This pointeren This pointeren er en pointer til det objektet selv. For at demonstrere dette, vil vi udvidde programmet prttest2.cpp med en funktion der kan finde et bestemt nr i listen. void testclass::udskriv(int a) if (a >= nr) cout << "Så mange er der ikke i listen\n"; else tmp = this; if (a!= 0) for (int i = 0; i < a; i++) tmp = tmp->next; cout << "# " << tmp->lokalnr << " fundet\n"; Sponsor: Ingeniørfirmaet Synkro Side 48 af 107
49 Her sættes tmp lig med this som er thispointeren, hvilket er objektet selv, og i dette tilfælde det objekt som *start også peger på. Det er praktisk i denne sammenhæng, fordi det objekt som *start peger på er det første i listen, og derfor der hvor man skal begynde at søge. Bemærk at vi fastholder indkapslingen ved at lade søgningen foregå inde i selve objektet. Der er ingen this pointer til statiske funktioner Afrunding Ved første øjekast kan den objektorinterede programmeringsteknik virke som om programmeringen blot er udviddet med en masse regler, der skaber flere linier med kode. Dette er korrekt så længe vi taler om små programmer, men når et program vokser i omfang, og det har programmer tendens til, så er der behov for et værktøj til at organisere sine koder, og det er C++ faktisk rigtig god til. En anden styrke ved objektorienteret programmering er indkapslingen. Ved at udføre en praktisk indkapsling undgår man rodet programmering, og man kan undgå at gentage sig selv igen og igen. Inden du går videre bør du prøve at løse nogle af de opgaver der følger herefter. Det er temmelig store opgaver, men det er valgt for at vise den objektorienterede teknik. Sponsor: Ingeniørfirmaet Synkro Side 49 af 107
50 2.11 Opgaver Opgave Skriv en funktion analyser(). Funktionen skal indeholde en løkke, der 10 gange skal hente en linie fra standard input. Når den løkke er gennemløbet, skal de 10 strenge sendes til en funktion modtag(), der skal være medlem af Klassen haandter. Derefter skal analyser() starte funktionen haandter::genkend(). Denne function skal gemme alle linier hvor ordet "hallo" forekommer. Til sidst skal analyser benytte funktionen haandter::aflever(). Denne function skal udskrive de linier som gendkend() fandt. Opgave Skriv et program sammenlign. Programmet skal først bede om et navn. Dette navn skal gemmes i et objekt ved navn navn1. Derefter skal programmet ved hjælp af en anden klasse, navn2, bede om 10 nye navne. For hver gang et navn indtastes i navn2, skal det kontrolleres om det er det samme navn som blev indtastet til at starte med, i navn1. Hvis navn2 modtager et navn det genkender, skal navn2 udskrive en besked. Opgave Omskriv programmet sammenlign, fra opgave , således at der kun defineres et objekt, der erklæres som en variabel med 11 elementer. I det første element skal det første navn gemmes. De efterfølgende 10 elementer skal for hver indtastning sammenligne med element 1. Opgave Skriv en klasse, der ved hjælp af array med 52 elementer, kan oprette et spil kort. Ingen elementer må kunne tilgåes uden for klassen. Hint: Arrayet skal erklæres private. Opgave Udvid programmet fra opgave således at klassen får et medlem der kan udskrive alle kort i en enkelt farve. Den farve der ønskes udskrevet, skal tilføres som parameter. Opgave Udvid programmet fra opgave med et medlem der kan udskrive alle kort i spillet, én farve ad gangen Opgave Udvid programmet fra opgave med en funktion, der kan søge efter et defineret kort, og udskrive det, hvis det bliver fundet. Opgave Udvid programmet fra opgave således at søgefunktionen kan udskrive alle kort der har ét søgekriterie til fælles. Opgave Skriv en klasse, der i function er identisk med til Klassen må ikke længere gemme kortende i et array. I stedet skal kortende indsættes i en kædet liste. Programmets interface må ikke ændres. Hint: Hvis du ikke kan få sammenfaldend einterface, kan det blive nødvendigt at skrive arrayløsningen om. Sponsor: Ingeniørfirmaet Synkro Side 50 af 107
51 3 Nedarvning En ganske praktisk ting i C++ er, at man kan skabe nye klasser, der ganske automatisk er en udvidelse af eksisterende klasser. Dette kaldes nedarvning, og er meget praktisk når man skal operere på store mængder af data der ligner hinanden, men ikke er helt ens. 3.1 Den førstefødte For at demonstrere princippet i nedarvning vil vi skrive et program, der skal kunne håndtere forskellige biltyper. Alle biltyper vil have en form for data til fælles, nemlig nummerpladen. Det kan derfor være praktisk at lade vores datastruktur bygge op over dette faktum. I dette eksempel skelner vi mellem 2 forskellige typer af biler. Personbiler og busser. For personbiler er de mest interesante data vel tophastigheden. For busser er hastighed ikke så vigtig, til gengæld vil en vognmand se på hvor mange passager en bus kan medtage. Vi står nu over for følgende problem: Skal vi designe med 2 structurer? en for personbiler og en for busser. I C kunne det gøres som vist på figur struct personbil char[10] regnr; int hastighed; struct bus char[10] regnr; int passager; Figur Som det fremgår kræves den dobbelte plads af hvad der er strengt nødvendigt, og programmøren skal taste en masse data ind der ligner hindanden, hvilket har en tendens til at medføre fejlindtastninger. Hvis vi nu kunne finde en måde at skabe en fællesnævner for registreringsnummeret, så kan vi spare plads, og indtastning. Netop det kan lade sig gøre i C++ og kaldes nedavning. I praksis kan det se ud som klasserklæringerne på figur Sponsor: Ingeniørfirmaet Synkro Side 51 af 107
52 // Filnavn = arv1.h // Disse klasseerklæringer // burde være placeret i 3 // forskellige headerfiler // men for overskuelighedens // skyld er de samlet her. Headerfil class bil protected: char regnr[10]; private: char hemmeligt[10]; ; class person : public bil public: void setbil(char s[], int a); void printbil(); private: int hastighed; ; class bus : public bil public: void setbil(char s[], int a); void printbil(); private: int passager; ; Figur Der er et par nye begreber i headerfilen, for det første benyttes begrebet protected: i bil klassen. Det betyder, at alt hvad der kommer efter kun kan ses af klassen, og klasser der arver fra denne klasse. I dette tilfælde betyder det, at person og bus kan se det der er eklæret som protected i bil klassen. (Bemærk, regnr er ikke erklæret static, og er derfor ikke en klassevariabel, så det at 3 klasser kan se variablen, er ikke ensbetydende med det er den samme variabel) Klasseerklæringen til person ser lidt anderledes ud end vi plejer at erklære klasser. Det skyldes tilføjelsen : public bil, som betyder, at personklassen arver alle de af bil's egenskaber der ikke er defineret som private. Det samme gælder for bus, der også erklærer bil som værende en klasse den vil arve fra. I dette tilfælde er bil baseklassen for både personklassen og busklassen, da begge klasser arver direkte fra bilklassen. Sponsor: Ingeniørfirmaet Synkro Side 52 af 107
53 // Filnavn = arv1.cpp #include <iostream> #include "arv1.h" using namespace std; C:\arv1 Regnr. : ab hastighed 140 Regnr. : bc passager 38 C:\ void person::setbil(char s[], int a) for (int i=0; regnr[i] = s[i];i++) ; hastighed = a; void person::printbil() cout << "Regnr. : " << regnr << " hastighed " << hastighed << '\n'; void bus::setbil(char s[], int a) for (int i=0; regnr[i] = s[i];i++) ; passager = a; void bus::printbil() cout << "Regnr. : " << regnr << " Passager " << passager << '\n'; int main() person racer; bus bigbil; racer.setbil("ab ", 140); racer.printbil(); bigbil.setbil("bc ", 38); bigbil.printbil(); return 0; Figur Vi har skabt én klasse der er fælles, samt 2 klasser der er forskellige. Klassen bil siges at være baseklassen. Klasserne person og bus siges at være afledte klasser. I dette tilfælde er de afledt af den samme base klasse, nemlig klassen bil. Afledte klasser kan ikke benytte deres base klasses private medlemer. Medlemer fra person og bus kan altså ikke udskrive eller tilgå arrayet hemmeligt. Afledte klasser kan ikke se hinanden eller hinandens medlemer, med mindre man lader dem være Sponsor: Ingeniørfirmaet Synkro Side 53 af 107
54 venner. Medlemmer der er afledt af personklassen, kan altså ikke udskrive indholdet af objekter der er skabt af bus. En afledt klasse defineres som andre klasser, blot med den tilføjelse, at efter klassens navn følger et kolon, en adgangsrettighed og derefter navnet på base klassen. En afledt klasse kan også defineres private, i stedet for public. Dette vil medføre, at både baseklassens private og public medlemer, vil blive private. Det betyder altså, at den afledte klasses medlemer kan se base klassens medlemer, andre kan ikke. Når vi arbejder med nedarvning er det interessant at finde ud af, hvordan det hænger sammen med constructoren. Som vi husker fra tidligere, så er det constructoren der opretter et objekt, men hvordan fungerer det ved nedarvning. Programmet arv2.cpp er skrevet for at belyse det problem. // Filnavn = arv2.h // Disse klasseerklæringer // burde være placeret i 2 // forskellige headerfiler // men for overskuelighedens // skyld er de samlet her. Headerfil class bil public: bil(); ~bil(); ; class person : public bil public: person(); ~person(); ; Figur Headerfilen erklærer en baseklasse og en nedarvet klasse, begge klasser har en constructor og en destructor. I arv2.cpp er constructorer og destructorer implementeret således, at de udskriver hvornår de kaldes. Sponsor: Ingeniørfirmaet Synkro Side 54 af 107
55 // Filnavn = arv2.cpp #include <iostream> #include "arv2.h" using namespace std; bil::bil() cout << "inde i bil cunstructor\n"; C:\arv2 inde i bil cunstructor inde i person cunstructor inde i person destructor inde i bil destructor C:\ bil::~bil() cout << "inde i bil destructor\n"; person::person() cout << "inde i person cunstructor\n"; person::~person() cout << "inde i person destructor\n"; int main() person racer; return 0; Figur Ved at kigge på output fra programmet kan det ses, at først kaldes baseklassens constructor, hvilket må betyde, at der oprettes et tilfælde af baseklassen. Derefter oprettes der et tilfælde af selve klassen. En klasse der arver inderholder altså både sig selv, og den (de) klasser den arver fra. Når klassen nedlægges kaldes først klassens egen destructor, og derefter baseklassens destructor. Sponsor: Ingeniørfirmaet Synkro Side 55 af 107
56 Opgave Udvid programmet arv1.cpp med en klasse for lastbiler, der arver direkte fra bil. Lastbilens vægt målt i tons skal indgå. Opgave Udvid programmet arv2.cpp med en klasse der nedarver fra person. Undersøg i hvilken rækkefølge constructorer og destructorer kaldes. Efter at have set på princippet i nedarvning, vil vi nu prøve at skrive et program der kan fungere som database for flere forskellige dyrearter. Alle dyrearter har i dette eksempel det til fælles, at de har et navn, en vægt og et køn. For at kunne indtaste disse data har vi også brug for en indtastningsfunktion, der jo kan hedde hentdata(). I dette eksempel ønsker vi at opsamle data om to dyrearter, fugle og fisk. For fisk vil vi gemme antallet af finner, for fugle ønsker vi at gemme vingefanget. For begge dyr har vi brug for en funktion der henter data fra tastaturet. Det kan opsumeres således: // Filnavn dyr.h // Filnavn = dyr.cpp class dyr public: void hentdata(); protected: int vaegt; char koen; // Køn = M/K/X char navn[20]; ; Klasseerklæring for dyr #include <iostream> #include "dyr.h" using namespace std; void dyr::hentdata() cout << "Indtast artens navn : "; cin >> navn; cout << "Indtast vægt : "; cin >> vaegt; cout << "Indtast køn (M/K/X) : "; cin >> koen; Klasseimplementering for dyr Figur Det er nemmest at begynde med baseklassen, og gøre den færdig, da den jo skal bruges af de andre klasser. Det ville være passende at indsætte nogle kompilerdirektiver, for at undgå filerne skal blive kompileret mere end en gang, men det overlades til læseren at foretage den øvelse. Der er ikke så meget at sige om baseklassen, så vi går videre til den første afledte klasse, fisk. Sponsor: Ingeniørfirmaet Synkro Side 56 af 107
57 // Filnavn fisk.h // Filnavn = fisk.cpp class fisk : public dyr public: void hentfinner(); protected: int finner; ; Klasseerklæring for fisk #include <iostream> #include "fisk.h" using namespace std; void fisk::hentfinner() cout << "Indtast antal finner : "; cin >> finner; Klasseimplementering for fisk Figur Selve fisk klassen er stort set lige ud af landevejen, så vi fortsætter med fugl klassen. // Filnavn fugl.h // Filnavn = fugl.cpp class fugl : public dyr public: void hentvingefang(); protected: int vingefang; ; Klasseerklæring for fugl #include <iostream> #include "fugl.h" using namespace std; void fugl::hentvingefang() cout << "Indtast vingefang : "; cin >> vingefang; Klasseimplementering for fugl Figur I fuglklassen er der heller ikke noget nyt. Sponsor: Ingeniørfirmaet Synkro Side 57 af 107
58 // Filnavn = dyreprogram1.cpp #include <iostream> #include "dyr.cpp" #include "fisk.cpp" #include "fugl.cpp" using namespace std; int main() fisk minfisk; fugl minfugl; minfisk.hentdata(); minfisk.hentfinner(); minfugl.hentdata(); minfugl.hentvingefang(); return 0; $ dyreprogram Indtast artens navn : Torsk Indtast vægt : 5 Indtast køn (M/K/X) : M Indtast antal finner : 5 Indtast artens navn : Ondulat Indtast vægt : 1 Indtast køn (M/K/X) : K Indtast vingefang : 27 $ Figur Til sidst lader vi vores hovedprogram includere alle relevandte cpp filer, og foretage en lille test. På nuværende tidspunkt burde det virke enkelt, men hvis det er første gang du arbejder med objektorienteret programmering, kan man godt blive forvirret af alle de filer der er at holde check på. Prøv at løse de følgende opgaver for at træne lidt. Sponsor: Ingeniørfirmaet Synkro Side 58 af 107
59 3.2 Opgaver Opgave Udvid fugl klassen med en funktion der kan udskrive data. Opgave Udvid fisk klassen med en funktion der kan udskrive data. Opgave Kald begge udskrivningsfunktioner efter hindanden, for at vise der ikke er nogen sammenblanding af data, selv om variablerne har samme navne. Opgave Udvid dyreprgram.cpp med en klasse for pattedyr. Sponsor: Ingeniørfirmaet Synkro Side 59 af 107
60 3.3 Multibel nedarvning I C++ er det muligt at en klasse kan nedarve fra flere klasser samtidig. Hvis en klasse arver direkte fra mere end en klasse kaldes det multibel nedavning. Lad os forestile os, at vi vil udvidde vores dyreporgram med håndtering af en ny dyreart, lad os kalde den en flyvefisk, det er jo nok en blanding mellem fugl og fisk, så den har nok både vinger og finner. Det kan se således ud: // Filnavn flyvefisk.h #include "fisk.cpp" #include "fugl.cpp" class flyvefisk : public fisk, public fugl ; Klasseerklæring for flyvefisk // Filnavn = flyvefisk.cpp #include <iostream> #include "flyvefisk.h" using namespace std; Klasseimplementering for flyvefisk Figur Som det ses af filernes indhold, er det en ganske overkommelig opgave at skabe en flyvefisk i dette tilfælde. Det er ikke altid man er så heldig, men eksemplet bør give inspiration når man programmerer. // Filnavn = dyreprogram2.cpp #include <iostream> #include "dyr.cpp" #include "flyvefisk.cpp" using namespace std; int main() flyvefisk minflyvefisk; C:\dyreprogram Indtast artens navn : Flyver Indtast vægt : 2 Indtast køn (M/K/X) : X Indtast antal finner : 5 Indtast vingefang : 36 C:\ minflyvefisk.fugl::hentdata(); minflyvefisk.hentfinner(); minflyvefisk.hentvingefang(); return 0; Figur Selve dyreprogram2.cpp er der ikke mange ændringer i, men vi begynder at kunne se styrken i objektorienteret programmering. Når man bruger multipel nedarvning kan man komme ud for den situation, at man ønsker at kalde en funktion, eller benytte en variabel der er defineret i to eller flere af de klasser man arver fra. Problemet kan skitseres således: Sponsor: Ingeniørfirmaet Synkro Side 60 af 107
61 class klasse1a public: int nr; void hentnr(); class klasse1b public: int nr; void hentnr(); class klasse2 : public klasse1a, public klasse1b Figur Hvis man ønsker at tilgå variablen nr fra en funktion i klasse2, kan man ikke bare skrive: int mitnr = nr; Det skyldes kompileren ikke kan se om det er nr fra klasse1a eller fra klasse1b der skal bruges. Programmøren skal definere hvilken udgave af variablen han ønsker at bruge, hvis han vælger variablen fra klassse1a skal det se således ud: int mitnr = klasse1a::nr; hvis han i stedet vil bruge variablen fra klasse1b skal det se således ud: int mitnr = klasse1b::nr; Vi skal altså bruge klassens navn, efterfulgt af rækkevidde operatoren, hvis der kan være tvivl om hvilken af variablerne der ønskes benyttet. Det samme gælder hvis vi ønsker at kalde hentnr() funktionen fra klasse2, det vil se således ud: klasse1b::hentnr(); Opgave Skriv et program med følgende funktionalitet. Programmet skal kunne håndtere en ansat. En ansat har en ansættelsesdato, og et personnr. En funktionær har månedsløn, en sælger har provisionsløn. En chef har både månedsløn og provision. Sponsor: Ingeniørfirmaet Synkro Side 61 af 107
62 3.4 Polymorfi Polymorfi betyder evnen til at antage forskellige former, C++ har indbygget denne evne. I praksis betyder det, at programmøren bruger forskellige afledte klasser, til at skabe en samlet helhed af ting der har mere eller mindre til fælles. Ved at benytte polymorfi teknikken, kan man øge abstraktionsniveauet for klasser placeret højt i klassehirakiet, og samtidig tvinge brugere (programmører) af afledte klasser til at implementere definerede tekniker Pointere og nedarvning Ved at bruge pointere som vi hidtil har gjort, kan er pointer fra en baseklasse pege på en nedarvet klasse, det omvendte er ikke tilladt. Figur viser et par måder at benytte pointere fra en baseklasse. // polymorfi1.h class baseklasse public: baseklasse *next; ; class subklasse1 : public baseklasse ; class subklasse2 : public baseklasse ; Figur a Sponsor: Ingeniørfirmaet Synkro Side 62 af 107
63 // polymorfi1.cpp // Dette program kan kompileres // Men ved kørsel er der intet // output. #include "polymorfi1.h" Koden i main viser, at en baseklassepointer (her p1) kan pege på en nedarvet klasse. Koden viser også, at baseklassens nextpointer (p1->next) kan pege på fubnktioner der kan nedarves fra baseklassen. int main() subklasse1 objekt1; subklasse2 objekt2; baseklasse *p1; p1 = &objekt1; p1 = &objekt2; p1 = new baseklasse(); p1->next = &objekt1; p1->next = &objekt2; return 0; p->next kan ikke pege på objekter der ikke endarver fra baseklassen. Et forsøg på at lade en subklasse1 pointer pege på et objekt af typen subklasse2 vil medføre en kompilerfejl, da typerne er forskellige, selv om de arver fra samme baseklasse. En pointer fra subklasse1 kan pege på objekter af typen subklasse1, og pointere fra subklasse2 kan pege på objekter af subklasse2. Figur b En pointer af typen pointer fra klasser af subklasse1 kan ikke pege på objekter af baseklasse2, eller evt. andre søskende klasser. Følgende udviddelse til polymorfi1.cpp viser begrænsningerne. Subklasse1 *p; p = p1; p = &objekt2 p = &objekt1 // Ulovlig // Ulovlig // Lovlig Virtuelle funktioner En virtuelle funktion er en funktion der ikke er implementeret, men som skal implementeres før helheden virker. Når man skriver større programmer er det en fordel at abstrahere fra detaljer når man starter, den slags detaljer kan man gøre virtuelle, hvilket betyder man laver en meget kort beskrivelse af dem nu, men venter med den praktiske implementering til et senere tidspunkt. Det minder en del om pseodokode. Figur viser et eksempel på brugen af virtuelle funktioner. I baseklassen er udskriv() erklæret som værende virtual hvilket betyder, at funktionen i de nedarvede klasser kan antage forskellige former. I dette eksempel udskrives forskellige kommetarer, alt efter hvilken funktion der Sponsor: Ingeniørfirmaet Synkro Side 63 af 107
64 er benyttet. // polymorfi2.h class baseklasse public: virtual void udskriv() cout << "Hallo fra basen\n"; ; class subklasse1 : public baseklasse void udskriv() cout << "Hallo fra 1\n"; ; class subklasse2 : public baseklasse void udskriv() cout << "Hallo fra 2\n"; ; Figur a Sponsor: Ingeniørfirmaet Synkro Side 64 af 107
65 // polymorfi2.cpp #include <iostream.h> #include "polymorfi2.h" int main() subklasse1 objekt1; subklasse2 objekt2; baseklasse *p1; C:\polymorfi1 Hallo fra basen Hallo fra 1 Hallo fra 2 C:\ p1 = new baseklasse(); p1->udskriv(); p1 = &objekt1; p1->udskriv(); p1 = &objekt2; p1->udskriv(); return 0; Figur b Den interessante del i polymorfi2.cpp er de to gange udskriv() kaldes i main. Som det ses af udskriften til højre medfører det, at to forskellige funktioner, fra to forskellige objekter bliver kaldt, med præcis den samme programmeringslinie. Funktionen udskriv() har i dette tilfælde antaget 3 foreskellige former. Da vi skal benytte pointere for at opnå polymorfien, må det nu være mere end antydet, at polymorfy er specielt anvendeligt i forbindelse med kædede lister af objekter Abstrakte klasser I de tilfælde hvor det ikke giver nogen mening at implementere en virtual funktion i baseklassen, kan den gøres til en ren virtual funktion. Dette gøres ved at sætte funktionen lig med nul i baseklassens erklæringsdel, som vist på Figur hvor linien: virtual void udskriv() = 0; erklærer en ren virtual funktion. En klasse hvor der er en eller flere rne virtuale funktioner kaldes også en abstrakt klasse. En sådan klasse kan der ikke oprettes et objekt af. Objekter kan kun oprettes af nedarvede klasser der implementerer den ren virtuale funktion. Sponsor: Ingeniørfirmaet Synkro Side 65 af 107
66 // polymorfi3.h class baseklasse public: virtual void udskriv() = 0; ; class subklasse1 : public baseklasse void udskriv() cout << "Hallo fra 1\n"; ; class subklasse2 : public baseklasse void udskriv() cout << "Hallo fra 2\n"; ; Figur a // polymorfi3.cpp #include <iostream.h> #include "polymorfi3.h" int main() subklasse1 objekt1; subklasse2 objekt2; baseklasse *p1; C:\polymorfi3 Hallo fra basen Hallo fra 1 Hallo fra 2 C:\ p1 = &objekt1; p1->udskriv(); p1 = &objekt2; p1->udskriv(); return 0; Figur b Bortset fra at udskriv er en ren virtual funktion, og at der ikke oprettes et objekt af baseklassen, er Sponsor: Ingeniørfirmaet Synkro Side 66 af 107
67 der ingen forskel på polymorfi2.cpp og polymorfi3.cpp programmerne. I polymorfy3.cpp er baseklassen blevet til en abstrakt klasse. I programmet polymorfi4.cpp anskueliggøres hvordan virtuale funktioner og abstrakte klasser gør det muligt at benytte polymorfi. // polymorfi4.h class baseklasse public: virtual void udskriv() = 0; baseklasse *next; ; class subklasse1 : public baseklasse void udskriv() cout << "Hallo fra 1\n"; ; class subklasse2 : public baseklasse void udskriv() cout << "Hallo fra 2\n"; ; class beholder : protected baseklasse public: beholder(); void indsaet(baseklasse *); void udskrivliste(); void udskriv(); protected: baseklasse *start; ; Figur a Det interessante ved programmet er, at den programmør der skal skrive hovedprogrammet, ikke behøves vide noget som helst om hvordan den kædede liste er implementeret. Brugeren Sponsor: Ingeniørfirmaet Synkro Side 67 af 107
68 (programmøren af main) skal blot kende nogle funktioner i beholderklassen for at kunne håndtere vores kædede liste. Dette kaldes at øge abstraktionsniveauet. I dette tilfælde er abstraktionsniveauet lavt for den programmør der implementerer beholderklassen, men det er højt for den der skal programmere main. Faktisk behøves brugeren ikke vide noget som helst om pointere. Sponsor: Ingeniørfirmaet Synkro Side 68 af 107
69 // polymorfi4.cpp #include <iostream.h> #include "polymorfi4.h" beholder::beholder() start = next = NULL; C:\polymorfi4 Hallo fra 1 Hallo fra 2 Hallo fra 2 Hallo fra 1 C:\ void beholder::indsaet(baseklasse *p) p->next = start; start = p; void beholder::udskrivliste() baseklasse *tmp = start; while (tmp) tmp->udskriv(); tmp = tmp->next; void beholder::udskriv() // Da denne funktion er erklæret virtual // skal den implementeres, selv om den er // tom. int main() subklasse1 objekt1; subklasse2 objekt2; beholder liste; liste.indsaet(new subklasse1); liste.indsaet(new subklasse2); liste.indsaet(new subklasse2); liste.indsaet(new subklasse1); liste.udskrivliste(); return 0; Figur b Sponsor: Ingeniørfirmaet Synkro Side 69 af 107
70 3.4.4 Hvorfor abstrakte klasser? Forestil dig et tegneprogram der skal kunne tegne firkanter, cirkler og trekanter på en computerskærm. Programmet kan have en abstrakt klasse på denne form: class form public: virtual udskriv() = =; Hermed har vi tvunget programmøren der vil nedarve fra form klassen, til at implementere en funktion udskriv(). Udskrivningsfunktionen i klassen firkant kunne eksempel komme til at se således ud: void firkant::udskriv() tegnstreg(x1,y1,x2,y2); tegnstreg(x2,y2,x3,y3); tegnstreg(x3,y3,x4,y4); tegnstreg(x4,y4,x1,y1); Udskrivningsfunktionen for en trekant kunne se således ud: void firkant::udskriv() tegnstreg(x1,y1,x2,y2); tegnstreg(x2,y2,x3,y3); tegnstreg(x3,y3,x1,y1); Det er underforstået at hver eneste geometriske figur i et sådan tegneprogram er et objekt. Hvis et antal af disse sættes ind i en kædet liste har vi et program der kan tegne en tegning, med følgende elementere programstump: tmp = start; while (tmp) tmp->udskriv(); tmp = tmp->next; Det må siges at være nemt at have med at gøre. Alle problemer er isoleret omkring deres opgave. Hvis en trekant ser forkert ud på skærmen, så er det i trekant klassen fejlen skal søges, og har vi programmeret ordentligt, så er fejlen begrænset til to filer, trekant.cpp og trekant.h. Sponsor: Ingeniørfirmaet Synkro Side 70 af 107
71 3.4.5 Absktrakte destructorer Når man benytter virtuelle funktioner, og destruerer dem med delete operatoren, kan man komme til at delete noget forkert. Dette kan undgås ved at erklære destructoren i baseklassen for virtual, herved vil den rigtige destructor altid blive kaldt, og misforståelser kan undgås. En constructor kan ikke gøres virtual. 3.5 Opgaver Opgave Skriv et program der på en gang kan være en database for en møntsamler og en frimærkesamler. For mønter skal værdi, vægt og årstal gemmes, for frimærker skal værdi, årstal og antal takker gemmes. Opgave Udvid programmet fra således at en samler af rationeringsmærker også kan bruge det. Et rationeringsmærke har en værdi, et årstal, og et antal takker. Opgave Udvid programmet fra med en funktion til at gemme og hente databasen fra en fil. Opgave Udvid programmet fra med en funktion der udskriver en liste over databasens indhold. Opgave Udvid programmet fra med en funktion til at søge og udskrive et bestemt element i databasen, uanset elementets art. Af udskriften skal det fremgår hvilken type element der er tale om. (mønt, frimærke, rationeringsmærke) Hint: strcmp() Opgave Udvid programmet fra med en funktion der udskriver en liste over databasens indhold, opdelt på elementtype. Opgave Udvid programmet fra med en funktion der udskriver en liste over databasens indhold af et bestemt elementtype, brugeren skal angive hvilken af de tre typer der ønskes listet. Sponsor: Ingeniørfirmaet Synkro Side 71 af 107
72 3.6 Introduktion til objektorienteret design Et design er en teknik til opnåelse af et mål. Inden for objektorienteret design er målet, at får skrevet et program ud fra nogle givne specifikationer, til en fastsat tid. For at bryde det overordnede mål ned i mindre overkommelige, og konytolerbare opgaver (delmål) benyttes designteknik. Man kan vælge et se det overordnede mål som ét objekt. Designteknikken skal altså definere de delobjekter der danner det endelige objekt. Det første man bør søge at klarlægge er, hvilke klasser skal vi sætte vores mål sammen af. Sagt på en anden måde, hvilke klasser vil, når de er sat rigtigt sammen, løse den stillede opgave. På dette sted er det passende at slå fast. Løs den definerede opgave, ikke andre. Alt for ofte ser man, at projekter ikke kan overholde en tidsplan, udelukkende fordi en programmør har brugt tid på noget der er vældig smart, eller kunne være rart at have. Hvis der bliver tid til overs i projektet, så kan man da godt bruge tid (og penge) på alt det sjove, men hvis man starter med det sjove, så bliver man sjældent færdig til tiden. Når man definerer sine klasser og den måde de skal arbejde sammen på, så gælder det som i alle andre forhold. Komplekse systemer laver komplekse fejl, enkle systemer derimod laver enkle fejl. Derfor kan det ikke siges tit nok. Keep it simple. En nem kontrol er, prøv at forklare hvad det er du vil lave. Hvis du vikler dig ud i for mange forbehold og rettelser, har du ikke overblik over opgaven, det betyder du skal arbejde mere med selv at forstå opgaven Indkapsling Når man har valgt sine klasser, så skal man vælge hvordan de skal kommunikere. I den forbindelse er det passende at nævne begrebet indkapsling. Herved forståes, at en klasse har de nødvendige data til at udføre sit arbejde, når den først er sat i gang med at løse sin opgave. Hvis et objekt efter at have modtaget parametre og initialiseret sine variabler, skal til at kommunikere med omverdenen, så er indkapslingen dårlig. Des mere en klasse benytter globale variabler, des dårligere er indkapslingen. Målet er altså, at et objekt skal opføre sig som et system der modtager et input, tygger lidt på det, og leverer et output, uden flere gange undervejs at bede om mere input. Des flere gange et objekt beder om (eller henter det rundt omkring), input des dårligere er indkapslingen. Ideelt set bør indkapslingen være 100%, det vil dog næppe være opnåeligt i praksis. Men hvis man Sponsor: Ingeniørfirmaet Synkro Side 72 af 107
73 slet ikke forsøger at pakke sine klasser ind, så ender man med spaghettikode. C++ har et værktøj der kan hjælpe med indpakning, nemlig friend klasser og funktioner. Klasser der kommunikerer meget, kan med fordel være venner, måske skal venskabet endda kun gå den ene vej. Det er op til designeren at bestemme det Klassehirakiet Når man har bestemt hvilke klasse der er nødvendige, så er det på tide at beslutte hvordan de enkelte klasser skal fungere. Ofte vil man benytte nedarvning, hvilket vil skabe et klassehiraki. Klassehirakiet er organiseret således, at alt hvad der er fælles findes øverst. Des længere man går ned i hirakiet des mere specialiseret er det. Man kan også sige, at fællesnævneren står øverst. Et hiraki kan visualiseres, i det følgende skitseres klasse definitionerne i kildetekst format, derefter vises dette som et visuelt klasse hiraki. class person ; class arme : public person ; class fingre : public arme ; class ben : public person ; class foeder : public ben ; class hoved : public person ; class oejne : public hoved ; class naese : public hoved ; clas mund : public hoved ; Figur Et klassehiraki kan også bruges til at demonstrere designet når der er benyttet multibel nedarvning. Lad os forestille os vi udvider vores program med en negle klasse. Både fingre og foeder har negle, Sponsor: Ingeniørfirmaet Synkro Side 73 af 107
74 så det kunne måske være praktisk at have en klasse ved navn negle, der arver fra foeder og arme. Klasseerklæringen kunne se således ud: class negle : public fingre, public foeder Klassehirakiet ville herefter se således ud: Figur Med disse få midler er man rigtigt godt hjulpet. Sponsor: Ingeniørfirmaet Synkro Side 74 af 107
75 8.7 Opgaver Opgave Skriv et program der kan udskrive en rentetabel på standard output. Programmet skal bede om en rentefod. Derefter skal programmet udskrive tabellen for den indtastede rentefod, og for de efterfølgende 8, i step på 0,25%. Programmet skal beregne 10 terminer. Hint (1 + r) n Opgave Skriv et program der kan udskrive histogram for 10 værdier. Værdierne skal udskrives sorteret, med den nederste værdi først, og den øverste værdi sidst. Inden udprint skal der vælges en scalering, således at det højeste tal kan repræsenteres. Scalerings faktoren skal udskrives. Opgave Skriv en klasse ved navn krypto. Klassen skal kunne kryptograffere og dekryprograffere ud fra cæsar koden. (a = b, b = c,... z = a). Opgave Skriv en klasse ved navn filklasse, den skal kunne gemme og hente data fra en fil. Opgave Udvid filklassen fra opgave med funktionene fra Opgave Opgave Udvid filklassen fra opgave med funktionene fra Opgave Opgave Udvid krypto fra opgave således, at koden ikke flytter sig 1 bogstav i koden, men n bogstaver. Hvis intet opgives skal n = 13. (n = 2 => a = c, b = d,... z = b). Opgave Udvid ved at bruge således at de krypterede data kan gemmes på en fil. Opgave Implementer opgave i kryptoklassen fra opgave Opgave Skriv en klasse ved navn kortklasse. Klassen skal kunne genere et spil kort som en kædet liste. Alle variabler der benyttes i klassen skal være private, et korts værdi skal oplyses af kortet selv. Lav en udskrivningsfunktion for at teste. Opgave Tilpas programmerne fra opgave og således at den samlede funktionalitet ligger i en klasse. Opgave Implemeter programmer fra opgave så det bliver objektorienteret. Sponsor: Ingeniørfirmaet Synkro Side 75 af 107
76 Opgave Design et database program. Programmet skal danne en database over køretøjer. For alle køretøjer skal stelnummer indtastes. - For indregistrerede køretøjer, skal brændstoftype indtastes. - For cykler og knallerter skal vægt indtastes. - For knallerter skal mærke indtastes. - For alle indregistrerede køretøjer skal nummerpladen indtastes. - For motercykler og scooterer skal antal hjul indtastes. - For biler busser og lastbiler skal totalvægt indtastes. - For busser skal antal passager indtastes. Opgave Tegn klassehirakiet for opgave Opgave Indtast koden til programmet i opgave Databasen skal kunne rumme mindst 10 køretøjer. Opgave Udvid programmet i , således at programmet får en søgefunktion, hvor en bruger kan kalde et køretøj frem, ved at søge på dets stelnummer. Opgave Tegn klassehirakiet for opgave Opgave Skriv et program database. Programmet skal håndtere en persondatabase. For hver person er følgende data fælles. CPR nr. Navn, og Adresse. Hvis en person er pensionist, skal der yderligere være et datafelt for pensionens størrelse. Hvis personen har en uddannelse, skal det fremgå af et datafelt. Hvis personen er umyndig (under 18) skal der være datafelter for personens forældres navne. Databasen skal kunne indeholde 10 personer. Hint: Start med at tegen klassehirkaiet. Sponsor: Ingeniørfirmaet Synkro Side 76 af 107
77 4 Sidst men ikke mindst I dette sidste kapitel vil vi kigge på de sidste par hjørner af C++, og til sidst vil vi trække trådende sammen ved at gennemgå et større eksempel. 4.1 Exceptions På dansk betyder exception undtagelse. I C++ benyttes exception til at fange den undtagelse hvor der ellers ville have opstået en programfejl. En programfejl vil typisk kunne opstå hvis man i sit program forsøger at dividere med nul. Teknikken i exceptions er således: Prøv om dette virker Fang fejlen, hvis der var en Fortsæt Det betyder vi laver noget koder hvor vi mistænker der kan opstå fejl, eksempelvis kan en bruger komme til at taste nul ved et input der senere skal bruges til en division. Hvis fejlen opstår, så har vi også lavet noget kode der fanger fejlen, så vi undgår en programafbrydelse. Fordelen ved exception er ikke så meget at vi kan fange en fejl, det kan vi sagtens finde andre metoder til. Det smarte er at vi kan skrive en fejlhåndteringsklasse, således at alle fejl af samme type får samme behandling, ud over lighedsprincippet har det den store fordel, at vi slipper for at skrive den samme kode igen og igen. Sponsor: Ingeniørfirmaet Synkro Side 77 af 107
78 // Filnavn = throw1.cpp #include <iostream.h> class fejlfanger public: void skriv(); ; void fejlfanger::skriv() cout << "fejl fanget\n"; C:\throw1 Programstart -5: -4: -3: -2: -1: fejl fanget Programslut C:\ int main() cout << "Programstart\n"; try for (int i = -5; i < 10; i++) if (i == 0) throw fejlfanger(); cout << i << ":" << endl; catch (fejlfanger objekt) objekt.skriv(); cout << "Programslut\n"; return 0; Figur I programmet throw1.cpp er der lavet en løkke der tæller fra minus 5 til 10. Men da vi har en grim mistanke om, at vi lige pludselig kan stå med værdien nul i variablen i, så sikrer vi os mod en (tænkt) fejl ved at kaste en fejlmeddelelse. Det sker i linien: throw fejlfanger(); Hvis denne linie eksekveres, og det gør den i dette tilfælde når i == 0, så vil al normal programeksekvering ophøre, try blokken forlades, uanset hvor vi er i den, og catch blokken der skal stå umiddelbart efter try blokkens slutmærke udføres. Bemærk linien: Sponsor: Ingeniørfirmaet Synkro Side 78 af 107
79 catch (fejlfanger objekt) Det henviser til vi har en klasse ved navn fejlfanger, og af den vil vi nu oprette et objekt ved navn objekt. I den efterfølgende linie kaldes skriv() funktionen i fejlfanger, uden at vi i øvrigt behøves gøre mere. I C++ er der en del standard fejlhåndterings klasser, og man kan komme ud for at skulle kaste flere forskellige exceptions inde i en try blok. Det er helt legalt blot man husker, at catch blokken skal starte lige efter try blokken. Hvis der er flere catch blokke skal de følge umiddelbart efter hindanen således: try throw fejltype1() throw fejltype2() throw fejltype3() catch(fejltype1 a) catch(fejltype2 a) catch(fejltype2 a) Fortsæt Der er intet teknisk forkert ved at bruge navnet a til fejlhåndteringsobjektet i alle tre catch, da rækkevidden af hvert objekt er catch blokken, og intet mere. Men hvis det giver uoverskuelighed bør man undlade en sådan ens navngivning. Vi kan også skrive en kode der kan fange alle typer af fejl: try throw fejltype1() throw fejltype2() throw fejltype3() catch(...) Fortsæt En catch med tre punktumer i parentes betyder, at catch blokken fanger fejl af alle typer. Det er en fejl at foretage noget som helst mellem en try blok og en catch blok. Følgende skal give en kompilerfejl: try throw fejltype1() Sponsor: Ingeniørfirmaet Synkro Side 79 af 107
80 throw fejltype2() throw fejltype3() cout << Går den så går den\n ; catch(...) Fortsæt // Fejl, den går ikke Hvis en fejl bliver kastet fra en funktion, og der ikke er en try blok i funktionen, så skal der et eller andet sted før den funktion blev kaldt være en try blok. Dette er vist i programmet throw2.cpp // Filnavn = throw2.cpp #include <iostream.h> #include <stdexcept> void testerbare() throw runtime_error("kørselsfejl"); C:\throw1 Programstart Kørselsfejl Programslut C:\ int main() cout << "Programstart\n"; try testerbare(); catch (runtime_error objekt) cout << objekt.what() << endl; cout << "Programslut\n"; return 0; Figur Der er et par nye kneb i programmet. For det første er der linien: throw runtime_error("kørselsfejl"); i testerbare() funktionen. Umiddelbart ser det ud til at skulle give en kompileringsfejl, men det gør det ikke. Årsagen er, at der i C++ er et helt klassehiraki af exceptions, og runtime_error er en af dem, som findes i stdexcept. Som det fremgår af koden må constructoren kunne modtage en streng, som vi benytter til senere at udskrive i linien: Sponsor: Ingeniørfirmaet Synkro Side 80 af 107
81 cout << objekt.what() << endl; Med vores efterhånden udemærkede kendskab til objektorienteret programmering må vi kunne deducere. At der i runtime_error klassen må være en streng der kan opbevare det input vi gav constructoren, og what() må være en funktion der udskriver indholdet af denne streng. Det afsluttende endl må være identisk med \n, og så er der ikke så meget mere at bore i her. Men der er flere faciliteter omkring exceptions, hvis du ønsker at vide mere om dem, bør du anskaffe dig en bog om avanceret programmeringsteknik. Opgave Skriv et program der beder brugeren om to heltal. Hvis det sidst indtastede tal er nul, skal der kastes en undtagelse der medeler brugeren at det ikke var et lovligt input. 4.2 Operator overstyring Det kan være praktisk at kunne lægge to objekter sammen, på samme måde som man kan lægge to tal sammen, for at opnå dette har C++ operatorer overstyring. // Filnavn = opoverstyr1.h class tidklasse public: tidklasse(int, int, int); tidklasse operator+(tidklasse); void test(); int tim, min, sek; ; Figur Programmet opoverstyr1.cpp viser hvad det kan bruges til i praksis. Vi har tre objekter (a, b og c) der hver indeholder en tid målt i sekunder, minutter og timer. Vi kan ikke umiddelbart adderer to tidspunkter, da tid benytter 24 og 60 som grundtal, modsat 10 som vi normalt bruger. For at håndtere dette er funktionen: tidklasse tidklasse::operator+(tidklasse operand2) skrevet. Denne funktion er en overstyring af + (plus) operatoren, og selv om overstyringen kun gælder inden for klassen, er det ganske effektivt. Sponsor: Ingeniørfirmaet Synkro Side 81 af 107
82 // Filnavn = opoverstyr1.cpp #include <iostream.h> #include "opoverstyr1.h" tidklasse::tidklasse(int x, int y, int z) tim = x; min = y; sek = z; C:\opoverstyr1 0:0:0 1:1:0 C:\ tidklasse tidklasse::operator+(tidklasse operand2) tim = tim + operand2.tim; min = min + operand2.min; sek = sek + operand2.sek; if (sek > 59) sek -= 60; min++; if (min > 59) min -= 60; tim++; if (tim > 23) tim -= 24; return tidklasse(tim, min, sek); void tidklasse::test() cout << tim << ":" << min << ":" << sek << endl; int main() tidklasse a(0,0,1),b(1,1,1),c(23,59,59); a = a + c; a.test(); a = b + c; a.test(); return 0; Sponsor: Ingeniørfirmaet Synkro Side 82 af 107
83 Figur For at forstå overskrivningen vil vi gå ned i main og kigge på linien: a = b + c; Udtrykket implementeres som a = b.operator+(c) hvilket betyder, at objekt b's operator+ funktionen kaldes med objekt c som argument. Det første argument i en overstyret funktion skal altid være et objekt, men det andet argument kan godt være en anden type: tidklasse tidklasse::operator+(int sekunder) return tidklasse (tim, min, sek + sekunder); Det medfører man kan skrive: int main() tidklasse a(0,0,0), b(1,1,1); a = b + 123; Det ville til gengæld ikke være lovligt at skrive: a = b; Årsagen er, der kan ikke oprettes et objekt af typen 123, og da det er angivet som første operand skal det være af datoklasse typen. Følgende operatorer kan overstyres: + - * / % ^ & ~!, = < > <= >= << >> ==!= && += -= *= /* %= ^= &= = <<= >>= [] () -> ->* new delete Sponsor: Ingeniørfirmaet Synkro Side 83 af 107
84 Når operatorer overstyres skal følgende regler overholdes: - Kun eksisterende operatorer kan overstyres. Der kan ikke opfindes nye operatorer. - Antallet af operander kan ikke ændres. - Regler for en operators precedense kan ikke ændres. - Assosieringsregler kan ikke ændres. (Regler for den rækkefølger et udtryk evalueres) - Der kan ikke ændres i operator for indbyggede datatyper som int og char. - Følgende operatorer kan ikke overstyres. Klassemedlems operatoren.* Pointer til medlems operator :: Rækkevidde operatoren?: Betinget udtryk operatoren Opgave Omskriv datoklassen så minusoperatoren bliver overstyret. 4.3 Et eksempel her til sidst vil vi gennemgå et lidt større eksempel, for at vise begreberne i en relevandt samenhæng, og for at give læseren mulighed for at se en smalet opgave. Vi ønsker at skrive et program der kan fungere som database for husejere, husbådejere og færgeejere. De tre kategorier er ret forskellige, men har alle en ejer, hvorfor den kan være nærliggende at have en baseklasse ved navn ejer. Ejerklassen skal have nogle funktioner og nogle variabler. Der er nok brug for funktioner til at hente og udskrive data, og der er nok brug for et par pointere, da pointere fra en baseklasse kan pege på alt der arver fra den. Ud fra ovenstående kan man mene det vil være logisk, at have en klasse der hedder hus og en der hedder baad. Begge klasse skal arve egenskaberne fra ejer. Valget kan begrundes med, at en færge jo er en slags båd, og det er en husbåd også. Blot med denne navngivning er det antydet, at husbåd skal benytte multibel nedarvning, fra hus og båd. For at undgå problemer med for høj specialisering af en klasse, vil vi også skabe en klasse ved navn villa, i stedet for at satse på alt kan rumes af husklassen, og samtidig bruges af husbådklassen. Sponsor: Ingeniørfirmaet Synkro Side 84 af 107
85 Figur Til sidst er der brug for et menusystem til at styre det hele, og det kan jo være oplagt at lave denne funktionalitet i en klasse ved navn menu. Alt dette kan opsumeres til at klassehiraki der er vist på Figur Vi har udviddet klassehirakiet med navne på public funktioner og variabler, for bedre at kunne overskue opgaven. // Filnavn = ejer.h class ejer public: ejer(); void hentejerdata(); void skrivejerdata(); virtual void indtastdata() = 0; virtual void skrivdata() = 0; int alder; char fnavn[20]; char enavn[20]; ejer *next, *start; ; Figur Figur viser erklæring for baseklassen ejer. Bemærk at funktionerne indtastdata() og skrivdata() er erklæret rent virtuelt, hvorfor hele klassen per defenition er en abstrakt klasse. De to funktioner er gjort virtuelle fordi vi hermed tvinger programmøren til at implementere et fast deifineret interface til sine data. Det betyder i sidste ende, at vi kan benytte polymorfi til at gøre opgaven nemmere for os selv. Sponsor: Ingeniørfirmaet Synkro Side 85 af 107
86 // Filnavn = hus.h class hus : public ejer public: void hentdata(); void udskrivdata(); char matrikelnr[20]; ; Figur erklæring for husklassen // Filnavn = baad.h class baad : public ejer public: void hentdata(); void udskrivdata(); char vaegt[20]; ; Figur erklæring for baadklassen De to klasser hus og baad er søskende klasser, i det begge arver direkte fra ejerklassen. Det er tilstræbt at klassernes interface, (programmørens syn på klassen set uderfra som bruger af klassen) er så ens som muligt. I dette eksempel er det valgt at gøre alt public, men det er relevandt at diskuttere, om ikke variablerne matrikelnr og vaegt bør være protected eller private, for at undgå en ivrig programmør begynder at pille i dem udefra. // Filnavn = villa.h // Filnavn = husbaad.h // Filnavn = faerge.h class villa : public hus public: void indtastdata(); void skrivdata(); char antal[20]; ; class husbaad : public virtual hus, public virtual baad public: void indtastdata(); void skrivdata(); char laengde[20]; ; class faerge : public baad public: void indtastdata(); void skrivdata(); char antal[20]; ; Figur erklæring for villaklassen Figur erklæring for husbaadklassen Figur erklæring for faergeklassen De samme principper som vi begrundede i hus og baadklassen går igen på dette nivau. Der er en væsentlig nyhed, nemlig linien: class husbaad : public virtual hus, public virtual baad Her vælges det at nedarve virtuelt. Det er nødvendigt i denne sammenhæng, da det ellers vil opstå tvivl om hvilken af de to ejerklasser der nedarves gennem hus og baad der er baseklasse for husbaabklasse. Ved at erklære begge som virtuel vælger kompileren en og kun en udgave af ejer. // Filnavn = menu.h class menu : public villa, public husbaad, public faerge public: menu(); void ny(ejer *); void udskrivliste(); void indtastdata(); void skrivdata(); ejer *start; ; Figur Til sidst har vi erklæringen af menuklassen. I dette tilfælde har vi valgt at lade menuklassen arve fra villa, husbaad og faergeklassen. Vi kunne i stedet have erklære en pointer til et objekt af hver type i stedet, og fået adgang til de tre typer på den måde. Selv om det ville ændre koden en del, kan det Sponsor: Ingeniørfirmaet Synkro Side 86 af 107
87 være lige så rigtigt som den teknik der er valgt her. Med den valgte teknik har vi direkte adgang til alle public og protected varibler i det hiraki vi har opbygget, og menuklassen virker som en container (beholder) klasse, da alting sker inde i klassen. Bemærk den store sammenhæng der er mellem Figur og klasseerkæringerne. Springet fra klassehiraki til implementation skal ikke være større. Nu kommer så det sidste skridt i vores kodning, implementering af de enkelte klasser. Vi starter fra oven med ejerklassen. // Filnavn = ejer.cpp #if!defined(ejer) #define EJER #include <iostream.h> #include <stdlib.h> #include "ejer.h" ejer::ejer() next = start = NULL; void ejer::hentejerdata() char tmp[20]; cout << "Indtast ejers fornavn : "; cin >> fnavn; cout << "Indtast ejers efternavn : "; cin >> enavn; cout << "Indtast ejers alder : "; cin >> tmp; alder = atoi(tmp); void ejer::skrivejerdata() cout << fnavn << '\t' << enavn << '\t' << alder << '\t'; #endif Figur Klasseimplementeringen starter med kompilerdirektiver, for at undgå at klassen skal blive kompileret mere end en gang. Med mange klasser der nedarver fra ejer, direkte eller indirekte, kan der nemt opstå problemer. Hvis du gerne vil vide hvilken slags problemer der kan opstå bør du prøve at udkommentere kompilerdirektiverne, og så kompilere menklassen. (Det er faktisk en god øvelse) I ejerklassen erklærer vi pointerne start og next, som vi sætter lig med NULLpointeren i klassens constructor. Herudover har klassen to funktioner, hvor der ikke må være de store overraskelser for en læser der er kommet så langt. Sponsor: Ingeniørfirmaet Synkro Side 87 af 107
88 // Filnavn = hus.cpp #if!defined(hus) #define HUS #include <iostream.h> #include "ejer.cpp" #include "hus.h" void hus::hentdata() hentejerdata(); cout << "Indtast husets matrikelnr : "; cin >> matrikelnr; void hus::udskrivdata() skrivejerdata(); cout << matrikelnr << '\t'; #endif Figur Også husklassen kan blive kompileret flere gange, i det både hus og husaad arver fra den, så der er også gjort brug af kompilerdirektiver her. HentData() funktionen kalder hentejerdata() funktionen for at indlæse data der kun relaterer til ejerklassen. Dette er valgt for at opnå en god indkapsling, og dermed et godt overblik. Vi kunne godt have valgt at skrive direkte i fnavn, enavn og alder, men det ville øge chancen for forvirring hvis vi i denne klasse pillede ved baseklassens variabler. // Filnavn = baad.cpp #if!defined(baad) #define BAAD #include <iostream.h> #include "ejer.cpp" #include "baad.h" void baad::hentdata() hentejerdata(); cout << "Indtast bådens vægt : "; cin >> vaegt; void baad::udskrivdata() skrivejerdata(); cout << vaegt << '\t'; #endif Figur Der gælder de samme betragtninger for baadklassen som for husklassen. Sponsor: Ingeniørfirmaet Synkro Side 88 af 107
89 // Filnavn = villa.cpp #include <iostream.h> #include "hus.cpp" #include "villa.h" void villa::indtastdata() cout << "Indtast villa\n"; hentdata(); cout << "Indtast antal værelser : "; cin >> antal; void villa::skrivdata() udskrivdata(); cout << antal << '\n'; Figur Der er ikke de store bemærkninger til villaklassen, den blot en endelig specialisering i forhold til den del af opgaven der går ud på at kunne registrere et hus. // Filnavn = husbaad.cpp #include <iostream.h> #include "hus.cpp" #include "baad.cpp" #include "husbaad.h" void husbaad::indtastdata() cout << "Indtast husbåd\n"; baad::hentdata(); cout << "Indtast længde : "; cin >> laengde; void husbaad::skrivdata() baad::udskrivdata(); cout << laengde << '\n'; Figur Klassen husbaad er nok den mest specielle i dette projekt. Det skyldes den benytter multibel nedarvning, for at opnå sine egenskaber. Det medførte i klasseerklæringen, at vi skulle erklære baseklasserne for virtual, og det betyder her at vi skal specificere hvilken udgave af hentdata() og udskrivdata() vi ønsker at benytte. I dette eksempel har vi valgt at benytte funktionerne fra baadklassen, da de passer bedst til opgaven. Hvis man glemmer denne specifikation i en klasse der benytter multiobel nedarvning, så kommer der mange fejl under kompileringen. (Det er en god øvelse at prøve det) Sponsor: Ingeniørfirmaet Synkro Side 89 af 107
90 // Filnavn = faerge.cpp #include <iostream.h> #include "baad.cpp" #include "faerge.h" void faerge::indtastdata() cout << "Indtast færge\n"; hentdata(); cout << "Indtast antal passager : "; cin >> antal; void faerge::skrivdata() udskrivdata(); cout << antal << '\n'; Figur Ligesom villaklassen er faergeklassen lige ud af landevejen. Det er værd at bemærke, at vi indtil nu ikke har gjort os nogen overvejelser om hvordan en liste med disse objekter skal styres, vi har blot sørget for der er pointere til stede, resten må vente til vi kaster os over den del af projektet der omhandler listestyringen. Ved at skille opgaverne ad i logiske klumper på denne måde, blive de mere overskuelige, og nemmere at vedligehole bagefter. Sponsor: Ingeniørfirmaet Synkro Side 90 af 107
91 // Filnavn = menu.cpp #include <iostream.h> #include "villa.cpp" #include "husbaad.cpp" #include "faerge.cpp" #include "menu.h" // Disse to funktioner skal implementeres // fordi de er erklæret virtual i baseklassen void menu::indtastdata() void menu::skrivdata() void menu::ny(ejer *tmp) if (tmp!= NULL) tmp->next = start; start = tmp; tmp->indtastdata(); else cout << "Ikke mere RAM!\n"; void menu::udskrivliste() ejer *tmp = start; while (tmp) tmp->skrivdata(); tmp = tmp->next; menu::menu() char ind = 'x'; start = NULL; for (int i = 0; i < 24; i++) // Slet skærmen cout << '\n'; while (ind!= '0') cout << " Menu \n\n"; cout << "1 = Indtast villa" << '\t' << "2 = Udskriv liste\n\n"; cout << "3 = Indtast husbåd" << '\t' << "4 = \n\n"; cout << "5 = Indtast færge" << '\t' << "6 = \n\n"; cout << "0 = Forlad programmet\n\n"; cout << " \n"; cout << "Vælg et nr: "; cin >> ind; switch (ind) case '1': ny(new villa()); break; case '2': udskrivliste(); break; case '3': ny(new husbaad()); break; case '4': cout << "Du valgte 4\n"; break; case '5': ny(new faerge()); break; case '6': cout << "Du valgte 6\n"; break; case '0': break; default: cout << "Fy! Det må du ikke\n"; cout << "Tak for denne gang\n"; int main() menu minmenu; return 0; Figur Sponsor: Ingeniørfirmaet Synkro Side 91 af 107
92 Selve menuklassen tager sig af alt det sjove med at holde fast i pointerne, men er ellers først og fremmest et menusystem. Selve menuen er lige ud af landevejen, med en switch der træffer et valg ud fra hvad brugeren beder om. Selv fejlindtastninger bliver håndteret med en høflig uforskammethed. Hvis brugeren vælger et af punkterne 1, 3 eller 5, så skal der oprettes et objekt, og det skal sættes ind i den kædede liste. Det er på dette sted vi rigtigt kan udnytte det smarte ved polymorfien. Bemærk at vi kalder ny() funktionen med tre forskellige argumenter: ny(new villa()) ny(new husbaad()) ny(new faerge()) Funktionen ny() modtager altså en pointer til tre forskellige objekter. (new afleverer en pointer til et objekt) Det kan kun lade sig gøre, fordi vi modtager dem med en pointer fra fællesnævneren, som er en pointer til ejerklassen. Herved har vi sørget for kun at have en funktion der skal håndtere pointere, i stedet for tre funktioner, og en masse cast med pointere. I ny() funktionen starter vi med at tjekke om vi har modtaget NULLpointeren. Hvis det er tilfældet skyldes det, at der ikke kunne allokeres plads til objektet, og så vil et hvert forsøg på at tilgå det i de efterfølgende linier kunne medføre, at programmet går ned. Hvis den situation skulle opstå forsøger vi ikke at indsætte objektet, men skriver i stedet en fejlmeddelse ud på skærmen til brugeren. Selve udskrivliste() funktionen bør være temmelig banal at se på, men det gør ikke noget at huske på, at der faktisk ligger en del know-how bagved det enkle udseende. Havde vi ikke haft polymorfien, havde dette program ikke været så enkelt at skrive. Selve mainprogrammet kan ikke være nemmere, men der ligger faktisk også en del know-how bag de få programmeringslinier. Sponsor: Ingeniørfirmaet Synkro Side 92 af 107
93 4.4 Opgaver I de følgende opgaver skal du udvidde/tilpasse programmet menu med følgende faciliteter. Opgave Skriv type ud ved listning (hus, husbaad, færge) Opgave Udskriv listen (punkt 2) baglæns. Opgave Fremstil et menupunkt hvor en bruger kan finde et datasæt, og rette i det. Opgave Fremstil et menupunkt hvor en bruger kan fjerne (slette) fra listen Opgave Fremstil et menupunkt hvor en bruger kan slette hele listen Opgave Fremstil et menupunkt hvor en bruger kan gemme de indtastede data i en fil. Hint: Husk at specificere hvilken type der er gemt hvor i filen, ellers ved programmet ikke hvilken type objekt der skal oprettes når data senere skal indlæses. Opgave Fremstil et menupunkt hvor en bruger kan hente de data der blev gemt under opgave Opgave Tilpas opgave således, at data krypteres med Cæcarkoden inden der gemmes til fil. Opgave Tilpas opgave således, at data dekrypteres med Cæcarkoden inden der hentes fra fil. Opgave Fremstil et menupunkt for statistik. Når dette kaldes skal gennemsnits værdier for alder, vægt og længde udskrives. Opgave Fremstil et menupunkt hvor en bruger kan bede om at liste en af de tre typer. Sponsor: Ingeniørfirmaet Synkro Side 93 af 107
94 Appendix A Operatorer A.1 Aretmetiske operatorer Operator Anvendelse Beskrivelse + op1 + op2 Adder op1 og op2 - op1 - op2 Subtraher op2 fra op1 * op1 * op2 Multipliser op1 og op2 / op1 / op2 Divider op1 med op2 % op1 % op2 Beregn restværdien af divisionen op1/op2 + +operand Indikerer positiv værdi (Fortegn) - -operand Indikerer negativ værdi (Fortegn) ++ op++ Adder 1 til op: Evaluer før addering ++ ++op Adder 1 til op: Evaluer efter addering -- op-- Træk 1 fra op: Evaluer før addering -- --op Træk 1 fra op: Evaluer efter addering A.2 Relations- og betingelsesoperatorer Operator Anvendelse Returnerer sand hvis: > op1 > op2 op1 er større end op2 >= op1 >=op2 op1 er større end eller lig med op2 < op1 < op2 op1 er mindre end op2 <= op1 <= op2 op1 er mindre end eller lig med op2 == op1 == op2 op1 er lig med op2!= op1!= op2 op1 er forskellig fra op2 && op1 && op2 op1 og op2 er sande op1 op2 op1 eller op2 er sand!!op op1 er false A.3 Bitvise operatorer Operator Anvendelse Operation >> op1 >> op2 Skift bits i op1 til højre op2 gange << op1 << op2 Skift bits i op1 til venstre op2 gange & op1 & op2 AND binært op1 op2 OR binært ^ op1 ^ op2 XOR binært ~ ~op1 Komplementært binært Sponsor: Ingeniørfirmaet Synkro Side 94 af 107
95 A.4 Præcendens Når et udtryk skal evalueres sker det altid fra venstre mod højre, operator for operator. Hvilken operation der skal udføres næste gang afhænger af operatorenes præsedens. Et klassisk eksempel er: * 4 Er det lig med (2 + 3) * 4 eller 2 + (3 * 4)? Det afhænger af operatorenes præcedens. En operator med højere præcedens udføres før en operator med lavere præcedens. Vi ved fra vores skolelærdom, at gange går forud for plus og minus. Computeren har brug for også at have nogle regler for præcedens, disse er givet i tabel A.4.1. Operatorer på samme linie har samme præcedens, og udtryk bestående af dem udføres fra venstre mod højre. Operatorer har faldende præcedens, des længere de kommer ned i tabellen. Operator præcedens () [] ->.! ~ * & (type) sizeof * / % + - (binære) << >> < <= > >=!= == & (Binær AND) ^ (Binær XOR) (Binær OR) && (Logisk AND) (Logisk OR)?: += -= /= *= %= &= ^= = <<= =>> =, Tabel A.4.1 Sponsor: Ingeniørfirmaet Synkro Side 95 af 107
96 Appendix C Standardbiblioteker for C++ C.1 Klassehiraki for streamklasser. Sammenhæng mellem klasserog headerfiler: Klasse Headerfil filebuf fstream fstream_withassign ifstream ios iostream_withassign istream istrstream istream_withassign iostream ofstream ostream ostream_withassign fstream.h fstream.h fstream.h fstream.h iostream.h iostream.h iostream.h strstream.h iostream.h iostream.h fstream.h iostream.h iostream.h Sponsor: Ingeniørfirmaet Synkro Side 96 af 107
97 Klasse Headerfil ostrstream streambuf stdiobuf stdiostream strstream strstreambuf strstream.h iostream.h stdiostrem.h stdiostream.h iostream.h strstream.h C.2 <iostream.h> Generelle stream klasser class ios Filplacering: iostream.h Dette er baseklassen for C++ Input og Output stream hiraki. Den er baseklasse for istream og ostream. class ios Konstanter ios::in ios::out ios::trunc ios::nocreate ios::noreplace ios::binary ios::ate ios::app ios::skipws ios::left ios::right ios::internal ios::dec ios::oct ios::hex ios::showbase ios::showpoint ios::uppercase in hex and scientific formats). ios::showpos ios::scientific ios::fixed ios::unitbuf ios::stdio static const long ios::adjustfield static const long ios::basefield ios::goodbit ios::eofbit ios::badbit Sponsor: Ingeniørfirmaet Synkro Side 97 af 107
98 Medlemsfunktioner ios::beg ios::cur ios::end ios(streambuf* sb) ~ios() long flags(long flags) long flags() const long setf(long flags) long setf(long flags, long mask) long unsetf(long flags); int width(int w=0) int width() const char fill(char ch) char fill() const int precision(int prec) int precision() const int rdstate() const int bad() const int good() const int eof() const int fail() const void clear(int flags=0) class istream Filplacering: iostream.h Klassen istream nedarver fra klassen ios. Klassen er skabt til håndtering af Input strømme. class istream public ios Sponsor: Ingeniørfirmaet Synkro Side 98 af 107
99 Medlemsfunktioner istream(streambuf* sb) ~istream() int gcount() const int get() istream& get(char* buf, int count, char delim='\n') istream& get(signed char* buf, int count, char delim='\n') istream& get(unsigned char* buf, int count, char delim='\n') istream& get(char& ch) istream& get(signed char& ch) istream& get(unsigned char &ch) istream& getline(char* buf, int count, char delim='\n') istream& getline(signed char* buf, int count, char delim='\n') istream& getline(unsigned char* buf, int count, char delim='\n') istream& ignore(int count=1, int delim=eof) int peek() const istream& putback(char ch) istream& read(char *buf, int count) istream& read(signed char *buf, int count) istream& read(unsigned char *buf, int count) istream& seekg(long pos) istream& seekg(long offset, long location) long tellg() class ostream Filplacering: iostream.h Klassen ostream nedarver fra klassen ios. Klassen er skabt til håndtering af Output strømme. class ostream public ios Medlemsfunktioner ostream(streambuf* psb) ~ostream() ostream& flush() ostream& put(char ch) ostream& seekp(long pos) ostream& seekp(long offset, long location) long tellp() ostream& write(char* psb, int count) ostream& write(signed char* psb, int count) ostream& write(unsigned char* psb, int count) operatorer &operator<<(const char ) &operator<<(char) &operator<<(short) &operator<<(int) Sponsor: Ingeniørfirmaet Synkro Side 99 af 107
100 &operator<<(long) &operator<<(float) &operator<<(double) &operator<<(const unsigned char *) &operator<<(unsigned char) &operator<<(unsigned short) &operator<<(unsigned int) &operator<<(unsigned long) &operator<<(void *) &operator<<(streambuf *) &operator<<(ostream &(*f)(ostream &)) &operator<<(ios &(*f)(ios &)) class iostream Filplacering: iostream.h Denne klasse nedarver multibelt fra istream og ostream, for at danne baseklasse for en række klasser der muliggør input og output fra en og samme datastrøm. Klassen har ingen medlemsfunktioner. class streambuf: Filplacering: iostream.h Klassen håndterer den rå dataflow, mellem dit program og mediet class streambuf Medlemsfunktioner streambuf(char* buf, int length) ~streambuf() virtual streambuf *setbuf(char *buf, int length) int sbumpc() int sgetc() int sgetn(char *buf, int count) int snextc() int sputbackc(char ch) void stossc() int sputc(char ch) int sputn(char* buf, int count) Sponsor: Ingeniørfirmaet Synkro Side 100 af 107
101 class istream_withassign Filplacering: iostream.h Tilføjer tilskrivelsesoperator og en constructor uden argumenter. class istream_withassign public istream Medlemsfunktioner istream_withassign() virtual ~istream_withassign() istream_withassign &operator = (istream &s) istream_withassign &operator = (streambuf *sb) istrstream public istream Medlemsfunktioner istrstream(char *cp) istrstream(char *cp, int len) strstreambuf *rdbuf() iostream_withassign Filplacering: iostream.h Tilføjer tilskrivelsesoperator og en constructor uden argumenter. class iostream_withassign public iostream Medlemsfunktioner iostream_withassign() virtual ~iostream_withassign() iostream_withassign &operator = (iostream &) iostream_withassign &operator = (streambuf *) ostream_withassign Filplacering; iostream.h Tilføjer tilskrivelsesoperator og en constructor uden argumenter. Sponsor: Ingeniørfirmaet Synkro Side 101 af 107
102 class ostream_withassign public ostream Medlemsfunktioner Operatorer ostream_withassign() virtual ~ostream_withassign() ostream_withassign &operator = (ostream &s) ostream_withassign &operator = (streambuf *sb) class ostrstream Filplacering: strstream.h Tilføjer funktioner til at indsætte og udtage fra en sekvens af tegn. class ostrstream public ostream Medlemsfunktioner ostrstream::ostrstream(char *cp, int n, int mode) ~ostrstream() int pcount() strstreambuf *rdbuf() char *str() C.2 <fstream.h> Fil stream klasser class fstream Filplacering: fstream.h Klassen understøtter formatteret og uformatteret input og uotput til filer. class fstream public iostream Medlemsfunktioner fstream() fstream(const char* name, int mode, int prot=filebuf::openprot) fstream(filedesc fd, char* buf, int length) fstream(filedesc fd) ~fstream() void close() void open(const char *name, int mode, int prot=filebuf::openprot) filebuf* rdbuf() const streambuf* setbuf(char *buf, int length) void attach(filedesc fd) Sponsor: Ingeniørfirmaet Synkro Side 102 af 107
103 class ifstream Filplacering: fstream.h Understøtter formatteret og uformatteret input fra fil. class ifstream public iostream Medlemsfunktioner ifstream() ifstream(int fd) ifstream(int fd, char *p, int len) ifstream(const char *name, int mode, int prot) ~ifstream() void attach(int fd) void close() void open(const char *name, int mode, int prot) filebuf *rdbuf() void setbuf(char *p, int len) class ofstream Filplacering: fstream.h Klassen understøtter output til fil. class ofstream: public ostream Medlemsfunktioner ofstream() ofstream(const char *name, int mode = ios::out, int prot = filebuf::openprot) ofstream(int fd) ofstream(int fd, char *p, int len) ~ofstream() void attach(int fd) void close() void open(const char *name, int mode = ios::out, int prot = filebuf::openprot) filebuf *rdbuf() void setbuf(char *p, int len) class filebuf Filplacering: fstream.h Klassen understøtter fil -input og -output faciliteter. class filebuf public streambuf Sponsor: Ingeniørfirmaet Synkro Side 103 af 107
104 Konstanter Medlemsfunktioner filebuf::sh_compat filebuf::sh_none filebuf::sh_read filebuf::sh_write filebuf::openprot filebuf() filebuf(filedesc fd) filebuf(filedesc fd, char* buf, int length) ~filebuf() filebuf* attach(filedesc fd) filebuf *close() filedesc fd() const filebuf* open(const char *name, int mode, int prot=filebuf::openprot); virtual int overflow(int ch=eof) streampos seekoff(long offset, long location, int mode=ios::in ios::out) streampos seekpos(long pos, int mode=ios::in ios::out) virtual streambuf *setbuf(char* buf, int length) virtual int synch() virtual int underflow() C. 4 strstream.h class strstream Filplacering: strstream.h Klassen er specialiseret til at håndtere bytearryes direkte i hukommelsen. class strstream: public iostream Konstanter Medlemsfunktioner strstream(); strstream(char *, int, int); strstreambuf *rdbuf(); char *str(); class ostrstream Filplacering: strstream.h Klassen er specialiseret til at håndtere udtagelse af bytearryes direkte i hukommelsen. class ostrstream: public istream Konstanter Medlemsfunktioner istrstream(char *) istrstream(char *, int) Sponsor: Ingeniørfirmaet Synkro Side 104 af 107
105 class ostrstream: public istream strstreambuf *rdbuf() class istrstream Filplacering: strstream.h Klassen er specialiseret til at håndtere indsættelse af bytearryes direkte i hukommelsen. class itrstream: public istream Konstanter Medlemsfunktioner istrstream(char *) istrstream(char *, int) strstreambuf *rdbuf() class strstreambuf Filplacering: strstream.h class strstreambuf: public streambuf Konstanter Medlemsfunktioner strstreambuf() strstreambuf(char *, int, char *) strstreambuf(int) strstreambuf(unsigned char *, int, unsigned char *) strstreambuf(void *(*a)(long), void (*f)(void *)) void freeze(int n = 1) virtual int overflow(int) virtual streambuf *setbuf(char *, int) char *str() virtual int underflow() C. 4 stdiostream.h class stdiostream Filplacering: stdiostream.h Specialiserer iostream klassen til FILE. class stdiostream: public iostream Konstanter Medlemsfunktioner stdiostream(file *f) Sponsor: Ingeniørfirmaet Synkro Side 105 af 107
106 class stdiostream: public iostream ~stdiostream() stdiobuf *rdbuf() class stdiobuf Filplacering: stdiostream.h Skaber input og output facilteter til FILE. class stdiobuf: public streambuf Konstanter Medlemsfunktioner stdiobuf(file *f); virtual int overflow(int = ); virtual streampos seekoff(streamoff, seek_dir, int mode); FILE *stdiofile(); virtual int sync(); virtual int underflow() Sponsor: Ingeniørfirmaet Synkro Side 106 af 107
107 Stikordsregister abstrahere...59 abstrakt klasse...61 afledte klasser...50 baseklassen...53 catch...74 const...34 constructor...26, 29, 67 constructorer...51 delete...40, 67 destructor...29, 67 destructoren...26 destructorer...51 enum...16 exception...73 extern C...17 indkapsling...68 inline...14 klasse...25 klassehiraki...69 klassevariabel...49 konstant...11, 34 makro...14 multibel nedavning...56 nedarvning...48 new...40 objekt...25 overload...18, 26 pointer...11 Polymorfi...58 private...25 protected...49 prototype...10 public...25 ren virtual funktion...61 rækkevidde...15 Rækkevidde operatoren...81 rækkevidde operatoren...57 throw...74 try...74 virtual...59 virtuelle funktion...59 Sponsor: Ingeniørfirmaet Synkro Side 107 af 107
C++ Programmering V. 0.99
Indholdsfortegnelse 1. Indledning...3 1.2 Forudsætninger:...3 1.3 Udeståender...4 6 Start med C++...5 6.1 Det første C++ program...5 6.2 Formatering af output...8 6.3 Kommentarer...9 6.4 Funktions prototyper...9
Videregående Programmering for Diplom-E Noter
Videregående Programmering for Diplom-E Noter 1. Uddelegering Ét af de væsentlige principper i objektorienteret programmering er, at enhver klasse selv skal kunne "klare ærterne". Enhver klasse skal altså
Programmering i C. Lektion 4. 5. december 2008
Programmering i C Lektion 4 5. december 2008 Funktioner Eksempel Fra sidst 1 Funktioner 2 Eksempel Funktioner Eksempel Eksempel: 1 / f u n k t i o n s p r o t o t y p e r / i n t i n d l a e s ( void )
Java Programmering. En bog for begyndere. Skrevet af Henrik Kressner
Java Programmering En bog for begyndere Skrevet af Henrik Kressner Indholdsfortegnelse Introduktion...3 1 Introduktion til Java...4 1.1 Javakoden...4 1.2 Det første program...6 1.2 Skriv til skærmen...6
Abstrakte datatyper C#-version
Note til Programmeringsteknologi Akademiuddannelsen i Informationsteknologi Abstrakte datatyper C#-version Finn Nordbjerg 1/9 Abstrakte Datatyper Denne note introducerer kort begrebet abstrakt datatype
Ugeseddel 4 1. marts - 8. marts
Ugeseddel 4 1. marts - 8. marts Læs følgende sider i kapitel 6 i lærebogen: s. 233 258 og s. 291 317 (afsnit 6.3 overspringes). Begynd at overveje, hvad afleveringsopgaven skal omhandle. Læs vejledningen,
METODER ARV KLASSER. Grundlæggende programmering Lektion 5
METODER KLASSER ARV Grundlæggende programmering Lektion 5 1 METODER Sekvenser af kode om samme emne 2 REPETITION Række af statements der udfører en handling Mindst én metode der hedder main Forskellen
C Programmering V1.37
Indholdsfortegnelse Indledning...3 1 I gang med C...5 1.2 Variabler...12 1.3 Intelligens...17 1.6 Afrunding...38 1.7 Opgaver...41 2 Grundbegreber...42 2.1 Blokke...42 2.2 Datatyper...47 2.3 Typekonvertering...49
AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007
AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007 Opgavebesvarelsen skal afleveres som enten en printerudskrift eller som et passende dokument sendt via email til [email protected]. Besvarelsen skal
Kursusarbejde 3 Grundlæggende Programmering
Kursusarbejde 3 Grundlæggende Programmering Arne Jørgensen, 300473-2919 klasse dm032-1a 21. november 2003 Indhold 1. Kode 2 1.1. forestillinger.h............................................. 2 1.2. forestillinger.cc.............................................
Kursusarbejde 2 Grundlæggende Programmering
Kursusarbejde 2 Grundlæggende Programmering Arne Jørgensen, 300473-2919 klasse dm032-1a 31. oktober 2003 Indhold 1. Kode 2 1.1. hotel.h.................................................... 2 1.2. hotel.cc...................................................
Programmering i C Intro og grundlæggende C 5. marts 2007
Programmering i C Intro og grundlæggende C 5. marts 2007 Mads Pedersen, OZ6HR [email protected] Plan for kurset Ma. 5/3: Ma. 19/3: Ma. 2/4: To. 12/4: Formål, intro, grundlæggende Videre, sprogkonstruktioner
JavaScript. nedarvning.
JavaScript er et sprog, der kan give en hjemmeside mere funktionalitet og gøre den interaktiv, så den reagerer på læsernes handlinger. CGI (Common Gateway Interface) har hidtil været de protokoller, man
Skriftlig eksamen i Datalogi
Roskilde Universitetscenter side 1 af 9 sider Skriftlig eksamen i Datalogi Modul 1 Vinter 1999/2000 Opgavesættet består af 6 opgaver, der ved bedømmelsen tillægges følgende vægte: Opgave 1 5% Opgave 2
DM536. Rapport og debug
DM536 Rapport og debug Kilder Vigtig.it (Felix Palludan Hargreaves) http://vigtig.it/dm502/howto_report.pdf http://vigtig.it/blog/teaching/#toc-relevant-tips Peter Schneider-Kamp http://imada.sdu.dk/~petersk/dm536/project2.pdf
Kursus navn: Indledende programmering Kursus nr. 02101
Danmarks Tekniske Universitet Side 1 af 8 sider Skriftlig prøve, den 15. december 2007 Kursus navn: Indledende programmering Kursus nr. 02101 Tilladte hjælpemidler: Alle skriftlige hjælpemidler Vægtning
Flettebreve og Doc2mail
Flettebreve og Doc2mail Denne vejledning beskriver hvordan du kan sende flettebreve via Doc2mail. I vejledningen er der vedlagt en række skabeloner du kan benytte til dette. Vejledningen er rettet mod
Objektorientering. Programkvalitet
1 PROSA-Bladet nr. 4 1993 Objektorientering = Programkvalitet? Af Finn Nordbjerg, adjunkt ved Datamatikeruddannelsen, Aalborg Handelskole 1. Indledning Objektorientering er blevet et edb-fagets mest udbredte
C++ Gratis PDF-udgave Forlaget Libris 1996-2004
C++ Gratis PDF-udgave Forlaget Libris 1996-2004 Gratis PDF-udgave af C++ I 1996 udgav vi for første gang C++ af Kris Jamsa. Første udgave var på bogform, som dengang kostede kr. 228,-. I 1999 udgav vi
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2019 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 27. februar, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2016 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 29. februar, 2016 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Hvad er Objekter - Programmering
Denne guide er oprindeligt udgivet på Eksperten.dk Hvad er Objekter - Programmering En rigtig god gennemgang af hvad objekter er! Hvordan de oprettes og anvendes! Det er helt klart til nybegyndere, som
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { int wmid, wmevent; programmering med
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) int wmid, wmevent; PAINTSTRUCT Introduktion ps; til HDC hdc; programmering med switch (message) case WM_COMMAND: wmid = LOWORD(wParam);
Anvendelse af metoder - Programmering
Denne guide er oprindeligt udgivet på Eksperten.dk Anvendelse af metoder - Programmering En forhåbentlig rigtig god forklaring på hvad metoder er og hvordan de anvendes. Lidt om private og public, retur
Systemkald DM14. 1. Obligatoriske opgave. Antal sider: 7 inkl. 2 bilag Afleveret: d. 18/3-2004 Afleveret af: Jacob Christiansen, 130282-2111
DM14 1. Obligatoriske opgave Systemkald Antal sider: 7 inkl. 2 bilag Afleveret: d. 18/3-2004 Afleveret af: Jacob Christiansen, 130282-2111 Side 1 af 5 Intro: Formålet med opgaven at et lave en system kald
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 20. marts, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
Python programmering. Per Tøfting. MacFest
Python programmering MacFest 2005 Per Tøfting http://pertoefting.dk/macfest/ Indhold Måder at afvikle Python program på Variabler Data typer Tal Sekvenser Strenge Tupler Lister Dictionaries Kontrolstrukturer
Specifikation Abstrakt OO OS-API Rev. 1.7. Specifikation. Abstrakt, objektorienteret operativsystem-api
Specifikation Abstrakt, objektorienteret operativsystem-api Indhold 1 Indledning... 3 1.1 Introduktion... 3 1.2 Formål... 3 1.3 Overordnede krav... 3 2 Ressourcer i OS-API et... 4 2.1 Tråde... 4 2.2 Timere...
Brugervejledning VFT-Reservedelsstyring
Brugervejledning VFT-Reservedelsstyring Forord Online-systemet Online-systemet er et integreret administrationsprogram til brug for blandt andet administration af køb og salg af biler køb og salg af reservedele
IT Support Guide. Installation af netværksprinter (direkte IP print)
IT Support Guide Denne guide er hentet på www.spelling.dk Program: Microsoft Windows Vista Program sprog version: ENG (US) Guide emne: Installation af netværksprinter (direkte IP print) Publikationsnr.:
Sitecore - basisvejledning Version 2. September 2010
Sitecore - basisvejledning Version. September 00 Sådan opretter du en ny artikelside... Sådan omdøber du et artikelnavn så du får vist æ,ø og å... Sådan udgiver (publiserer) du nyt eller redigeret indhold...4
Indholdsfortegnelse resultat- & kritikprogrammet.
Indholdsfortegnelse resultat- & kritikprogrammet. Ringsekretærers indtastning af resultater og kritikker... 2 Kom i gang Opstart af programmet... 2 En anden bruger er i gang med ringen... 3 Dommer ændringer
DM507 Algoritmer og datastrukturer
DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 13. marts, 2018 Dette projekt udleveres i tre dele. Hver del har sin deadline, således
dcomnet-nr. 6 Talrepræsentation Computere og Netværk (dcomnet)
dcomnet-nr. 6 Talrepræsentation Computere og Netværk (dcomnet) Efterår 2009 1 Talrepræsentation På maskinkodeniveau (Instruction Set Architecture Level) repræsenteres ordrer og operander ved bitfølger
//Udskriver System.out.println("Hej " + ditfornavn + " " + ditefternavn + "."); System.out.println("Du er " + dinalder + " aar gammel!
Denne guide er oprindeligt udgivet på Eksperten.dk Brugerinput i Java Denne her artikel gennemgår diverse ting ved brug af brugerinput i Java. Den starter med det simple og fortæller derefter skridt for
Guide. Administration af FDF.dk/Nyborg. 1. Udgave 2008. Ide og layout Christoffer S. Rasmussen
Guide Administration af FDF.dk/Nyborg 1. Udgave 2008 Ide og layout Christoffer S. Rasmussen FDF.Dk/NyboRG Den nye hjemmeside for FDF Nyborg er baseret på et bloksystem. Det vil sige at det er super nemt
Regneark II Calc Open Office
Side 1 af 10 Gangetabel... 2 Udfyldning... 2 Opbygning af gangetabellen... 3 Cellestørrelser... 4 Øveark... 4 Facitliste... 6 Sideopsætning... 7 Flytte celler... 7 Højrejustering... 7 Kalender... 8 Dage
Med TI-89 / TI-92 Plus kan du også sammenligne eller manipulere binære tal bit for bit.
Kapitel 20: Talsystemer 20 Resumé af talsystemer... 344 Indtastning og omregning af talsystemer... 345 Udførelse af matematiske beregninger med hexadecimale og binære tal... 346 Sammenligning eller manipulation
Automatisering Af Hverdagen
Automatisering Af Hverdagen Programmering - Eksamensopgave 10-05-2011 Roskilde Tekniske Gymnasium (Kl. 3,3m) Mads Christiansen & Tobias Hjelholt Svendsen 2 Automatisering Af Hverdagen Indhold Introduktion:...
Import-vejledning Fra Dansk Skoledata til UNI Login
Import-vejledning Fra Dansk Skoledata til UNI Login - For UNI Login brugeradministratorer 1. udgave, april 2007 UNI C 2007 Vermundsgade 5 2100 København Ø Tlf: 35 87 88 89 1 Eksporter fra Dansk Skoledata...
Indledning. Hvorfor det forholder sig sådan har jeg en masse idéer om, men det bliver for meget at komme ind på her. God fornøjelse med læsningen.
Indledning...2 Variabler...13 Eksempel: 1...13 Eksempel 2:...13 Eksempel 3:...15 Eksempel 4:...16 Metoder...17 Metode (intet ind og intet ud)...17 Metode (tekst ind)...18 Metode (tekst ind og tekst ud)...19
Anklagemyndighedens Vidensbase
Anklagemyndighedens Vidensbase Indhold 1 OM DENNE VEJLEDNING... 2 2 LOGIN... 3 3 SØGNINGER... 4 3.1 SØG EFTER DOKUMENTER... 4 3.2 NAVIGÉR DIG FREM... 5 3.3 KOMBINÉR SØGNING OG NAVIGATION... 6 3.4 VISNING
Wipigo Galleri. Brugsforvirring. Venstre side af startbillede efter der er logget ind (Højre side viser det/de gallerier der er oprettet).
Wipigo Galleri. Brugsforvirring Venstre side af startbillede efter der er logget ind (Højre side viser det/de gallerier der er oprettet). Kategorier/Gallerier/Albums, flere benævnelser for den samme ting.
DATALOGI 1E. Skriftlig eksamen torsdag den 3. juni 2004
Københavns Universitet Naturvidenskabelig Embedseksamen DATALOGI 1E Skriftlig eksamen torsdag den 3. juni 2004 Opgaverne vægtes i forhold til tidsangivelsen herunder, og hver opgaves besvarelse bedømmes
Alt elektronik heri er købt via http://dx.com, og arduino udviklingssoftware er hentet fra http://arduino.cc.
Få-tiden-til-at-gå-[DIGITAL]-ur =============================== Copyright 2013, Richard Jørgensen. Alle ophavsretlige rettigheder frafaldet 2015. (Kopier og brug som du har lyst.) Forord: ===== Denne vejledning
Sådan vedligeholder du UNI Login med data fra KMD Elev
Sådan vedligeholder du UNI Login med data fra KMD Elev 1 Indhold 1 Indledning... 3 2 Importer brugerdata... 4 2.1 Automatisk import... 4 2.2 Manuel import... 5 3 Når du har importeret, sker der følgende...
Undtagelseshåndtering i C#
Denne guide er oprindeligt udgivet på Eksperten.dk Undtagelseshåndtering i C# I modsætning til C++ kan man i C# ikke skrive et program uden undtagelseshåndtering, så derfor har jeg skrevet denne guide
INDLEDNING 2. Design og layout 3
INDLEDNING 2 Design og layout 3 Skyd genvej... 3 Omdøb skabelonsamling 4 Omdøb brevskabelon 5 Layout 6 Indsæt/Redigér logo 6 Indsæt/Redigér tekst 8 Redigér kolonner 11 Ved vejs ende... 14 Tips & Tricks...
Dokumentation af programmering i Python 2.75
Dokumentation af programmering i Python 2.75 Af: Alexander Bergendorff Jeg vil i dette dokument, dokumentere det arbejde jeg har lavet i løbet opstarts forløbet i Programmering C. Jeg vil forsøge, så vidt
Lectio. Overgang til Lectio Eksamensmodul. MaCom A/S Vesterbrogade 48, 1. 1620 København V Telefon: 33 79 79 00
Lectio Overgang til Lectio Eksamensmodul 1992-2008 MaCom A/S MaCom A/S Vesterbrogade 48, 1. 1620 København V Telefon: 33 79 79 00 Telefax: 33 79 79 84 E-mail: [email protected] Internet: www.macom.dk Forord
OPRET OG REDIGER FORMULARER I DYNAMICWEB
OPRET OG REDIGER FORMULARER I DYNAMICWEB Modulet formularer giver dig mulighed for at oprette dynamiske formularer, som enten kan anvendes til kontakt, brugerundersøgelser, quiz eller tilmeldinger. Du
Sproget Six. Til brug i rapportopgaven på kurset Oversættere. Vinter 2006. Abstract
Sproget Six Til brug i rapportopgaven på kurset Oversættere Vinter 2006 Abstract Six er baseret på det sprog, der vises i figur 6.2 og 6.4 i Basics of Compiler Design. Den herværende tekst beskriver basissproget
Egenskaber ved Krydsproduktet
Egenskaber ved Krydsproduktet Frank Nasser 12. april 2011 c 2008-2011. Dette dokument må kun anvendes til undervisning i klasser som abonnerer på MatBog.dk. Se yderligere betingelser for brug her. Bemærk:
Lær Python dag 1 - modul 1
Lær Python dag 1 - modul 1 Introduktion, basis python Steffen Berg Klenow Jonas Bamse Andersen Syddansk Universitet Indhold 1. Velkommen 2. Programmering i python 3. Typer, variabler og udtryk 1 Velkommen
Bits, bit operationer, integers og floating point
Denne guide er oprindeligt udgivet på Eksperten.dk Bits, bit operationer, integers og floating point Denne artikel beskriver hvordan data gemmes som bits og hvordan man kan manipulere med bits. Den forudsætter
Vejledning til. DUI-LEG og VIRKEs
Vejledning til DUI-LEG og VIRKEs Medlemssystem version 1.0 Opdateret 1. november 2009 Indholdsfortegnelse Sådan får du en kode til systemet...3 Sådan logger du ind på systemet...3 Forsiden og ændring af
Har du ikke fået oprettet et afdelings-id og PIN-kode til udskrivning på husets printere bedes du tage kontakt til receptionen først:
SOHO/NOHO Printer Installation til Windows (PC) - Version 2.0 Vigtig før du går i gang: Har du ikke fået oprettet et afdelings-id og PIN-kode til udskrivning på husets printere bedes du tage kontakt til
Grundlæggende Programmering ITU, Efterår 1999. Skriftlig eksamen i Grundlæggende Programmering
Skriftlig eksamen i Grundlæggende Programmering ITU, 20. januar 2000 Alle hjælpemidler tilladt, dog ikke datamat. Eksamen er skriftlig, fire timer, og bedømmes efter 13-skalaen. Opgavesættet består af
SWC eksamens-spørgsmål. Oversigt
SWC eksamens-spørgsmål Oversigt #1 Typer og variable #2 Aritmetik og logik #3 Klasser (definition, objekter) #4 Klasser (metoder) #5 Klasser (nedarvning, polymorfi) #6 Conditional statements #7 Repetition
Tastevejledning Windows XP
Tastevejledning Windows XP Tastevejledningen dækker den danske udgave af Windows XP. Der er taget udgangspunkt i en standard installation, hvor der ikke er foretaget tilpasninger i skærmopsætning, valg
Indholdsfortegnelse. 1. Installation af LØN... 1. 2. Introduktion til LØN... 2. 3. Indtastning af lønseddel... 7. 4. Udskrifter...
Løn til Windows Indholdsfortegnelse 1. Installation af LØN... 1 2. Introduktion til LØN... 2 2.1. Første start af LØN...2 2.1.1. Ét eller flere distrikter...2 2.1.2. Lønperioder...3 2.1.3. Kartoteker...4
Sammenlign og byt. Et eksempel på dokumentering af et program
Sammenlign og byt Et eksempel på dokumentering af et program Sammenlign og byt Jeg har valgt, som et eksempel, at dokumentere et meget enkelt program som indlæser to tal, sammenligner dem og udskriver
Klasser og Objekter i Python. Uge 46 Learning Python: kap 15-16, 19-22.
Klasser og Objekter i Python Uge 46 Learning Python: kap 15-16, 19-22. Klasser og objekter En klasse beskriver en klump af samhørende funktioner og variable En klasse er en beskrivelse. En kage form Klassens
Indhold. Maskinstruktur... 3. Kapitel 1. Assemblersprog...3. 1.1 Indledning...3 1.2 Hop-instruktioner... 7 1.3 Input og output...
Indhold Maskinstruktur... 3 Kapitel 1. Assemblersprog...3 1.1 Indledning...3 1.2 Hop-instruktioner... 7 1.3 Input og output... 9 Kapitel 2. Maskinkode... 13 2.1 Den fysiske maskine... 13 2.2 Assemblerens
Cecilie Maria Nielsen, Mathias Fornitz Eriksen og Martin Arnetoft klasse 1.6 07-05-2010
ROSKILDE TEKNISKE GYMNASIUM Eksamensopgave Kommunikation/it Cecilie Maria Nielsen, Mathias Fornitz Eriksen og Martin Arnetoft klasse 1.6 07-05-2010 Vi har valgt at beskæftige os med opgave 1 fra oplæget.
På de følgende sider kan du læse om nogle af de overvejelser du bør gøre dig, hvis du påtænker at skifte din bolig ud.
Wennemoes Bolig På de følgende sider kan du læse om nogle af de overvejelser du bør gøre dig, hvis du påtænker at skifte din bolig ud. Ingen bolig passer til vores liv, hele livet. Vi bor alene, vi flytter
Design og funktionel prototype
Design og funktionel prototype 2.1) Minus scenarie Der bliver sendt nye billeder til rammen og Hans ønsker at se billederne, men billederne rotere for langsomt så Hans går op og bruger touch funktionen
WebGT 3.0 - Graveansøgning. Brugervejledning. 25. september 2012. Udgave 1.0
WebGT 3.0 - Graveansøgning Brugervejledning 25. september 2012 Udgave 1.0 Indholdsfortegnelse 1 INDLEDNING... 3 1.1 OPRETTELSE SOM BRUGER... 3 1.2 NOTIFICERINGSMAILS... 4 2 OPBYGNING OG SAGSGANG... 5 2.1
//--------------------------------- Definition af porte og funktioner -------------------------
Temeraturmåler (C-program).txt // Initialiserings-sekvens #include #pragma model=medium #pragma code=0x0000 #pragma xdata=0x4000 #pragma asm=on #pragma parameters=register //#define display P4
Applikationen Klip (dansk)
Applikationen Klip (dansk) PMH Version 3.0-0315 Indhold 1 Manual 2 1.1 Vejledning................................. 2 1.1.1 Starten.............................. 8 1.1.2 Strækkene mellem posterne...................
Vejledning: Anvendelse af kuber på SLS-data fra LDV i Excel 2007. Målgruppe: Slutbruger
Vejledning: Anvendelse af kuber på SLS-data fra LDV i Excel 2007. Målgruppe: Slutbruger April 2015 Indholdsfortegnelse Indholdsfortegnelse... 2 1 Indledning... 3 1.1 Metode til anvendelse af kuber med
Athena DIMENSION Varmeanlæg 4
Athena DIMENSION Varmeanlæg 4 Juni 2001 Indhold 1 Introduktion.................................. 2 2 Programmets opbygning........................... 2 3 Fremgangsmåde................................ 3
Import-vejledning Fra KMD Elev til UNI Login
Import-vejledning Fra KMD Elev til UNI Login - For UNI Login brugeradministratorer 6. udgave, juli 2008 UNI C 2008 Vermundsgade 5 2100 København Ø Tlf: 35 87 88 89 1 Bestil data fra KMD Elev... 2 2 Indlæsning
Få navn på analysenr. i excel-fil og ind i pivottabel med data fra qlikview
Få navn på analysenr. i excel-fil og ind i pivottabel med data fra qlikview Opret en excel-fil med analysenr. og navn. Gemt som dataliste_til_pivottabeller Analysenr. skal stå i nr. orden, og cellen skal
Procedurer og funktioner - iteration og rekursion
Procedurer og funktioner - iteration og rekursion Procedurer De første procedurer vi så på var knyttet til handlinger, der skulle udføres, fx at klikke på en knap for at lukke en form eller afslutte et
fotografisk kommunikation
XDANMARKS MEDIE- OG JOURNALISTHØJSKOLE CAMPUS KØBENHAVN Forprøve 2016 fotografisk kommunikation 1/2 Professionsbacheloruddannelsen i Visuel Kommunikation Studieretning: Fotografisk Kommunikation Del 1:
Kursus i OOP og Java. Kursus i Objektorienteret programmering i Java
Kursus i OOP og Java Kursus i Objektorienteret programmering i Java Åben Dokumentlicens Dette foredragsmateriale er under Åben Dokumentlicens (ÅDL) Du har derfor lov til frit at kopiere dette værk Bruger
Eksamensadministration, EUD, udtrækning af elever Sidst opdateret 16-03-2010/version 1.3 /UNI C/Steen Eske Christensen
Eksamensadministration, EUD, udtrækning af elever Sidst opdateret 16-03-2010/version 1.3 /UNI C/Steen Eske Christensen Indhold Ændringer Centrale begreber Generelt Arbejdsgange mv. Vejledningen består
Fig. 1 Billede af de 60 terninger på mit skrivebord
Simulation af χ 2 - fordeling John Andersen Introduktion En dag kastede jeg 60 terninger Fig. 1 Billede af de 60 terninger på mit skrivebord For at danne mig et billede af hyppighederne flyttede jeg rundt
CANSAT & ARDUINO step by step
CANSAT & ARDUINO step by step Jens Dalsgaard Nielsen SATLAB Aalborg Universitet Danmark [email protected] 1/51 Arduino CANSAT - MÅL At måle ved hjælp af sensor temperatur, tryk, acceleration, CO2, lys,...
