Forudsætninger: Jeg går ud fra at læseren på nuværende tidspunkt har kendskab til alle simple variabeltyper (int, char og float) og arrays og pointere til samme. Det er ydermere meget smart at forstå funktioner da klasser og objekter har noget der ligner.
Hvis man ellers skal tro på alt hvad man hører er objektorienteret programmering djævlens værk, imidlertid er det nogle gange brugbart fordi det kan give MEGET simplere og væsentligt mere simpel kode. Hvis jeg ikke husker galt (og det gør jeg næsten aldrig) introducerede jeg win32 og C++ med en slags database for at vise hvordan man kunne opbevare informationer om en person, denne var meget begrænset da den kun kunne tage 1 person af gangen. Med din nuværende viden kunne du helt sikkert lave et par arrays og have flere personer i, men det ville efterhånden blive kompliceret hvis de skal have alder osv. Ind enkeltvis. Det vi har behov for er en ny type variabel, en der kan indeholde præcis de informationer vi skal bruge. Sådan en findes heldigvis i C, de kaldes structs men mere vil jeg nu ikke nævne om dem, structs har nemlig en mangel som er årsagen til C++ eksistens i C++ har man nemlig klasser og klasser har metoder i modsætning til structs i C som kun har felter (members på engelsk). Uden nærmere forklaring end det tror jeg at det vil være på sin plads at give et eksepel her:
001 #include <windows.h>
002
003 class person{
004 public:
005 char navn[256];
006 int alder;
007 bool mand; //1 for mand 0 for kvinde
008 int penge; //i ører
009 };
010
011 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nCmdShow){
012 person pers[3];
013 strcpy(pers[0].navn,"Jens");
014 pers[0].alder = 42;
015 pers[0].mand = 1;
016 pers[0].penge=1150;
017 strcpy(pers[1].navn,"Erik");
018 pers[1].alder = 25;
019 pers[1].mand = 1;
020 pers[1].penge = 125;
021 strcpy(pers[2].navn,"Karin");
022 pers[2].alder = 31;
023 pers[2].mand = 0;
024 pers[2].penge = 500;
025 }
OK, ved første øjekast ser det meget besværligt ud, men inden denne tutorial slutter, vil jeg have forkortet main delen af koden så meget ned at det næsten er utroligt. Jeg vil samtidig give alt koden en overskuelig struktur og i en senere tutorial vil jeg tilmed gøre den så magisk at man frit kan tilføje personer til listen. Men lad os se på koden:
Linie 003: Sådan starter man en klasse, man giver den et navn. Fra dette øjeblik er person en variabeltype.
Linie 004: public indikerer at vi kan benytte punktum til at skrive til de variabler der kommer bagefter. (Hvis vi havde brugt private havde alle linier i main delen været forkert fordi vi så ikke må tilgå felter på den måde mere om det senere).
Linie 5-8: forskellige variable som indgår i klassen som det man kalder felter, de indikerer de typer af data der kan være i vores nye variabel og giver dem nogle navne så man kan adskille dem. I main delen er der ikke så meget nyt udover at vi nu har en variabel der indeholder andre variable og at man kan få fat i disse med et punktum.
En mindre tilføjelse til punktummerne er at hvis det er en pointer bruger man en pil (lavet af bindestreg og større end ->) til at indikere at vi vil have indholdet. Man kan sætte Dev-cpp og VS til automatisk at vise indholdet af klasser, VS gør det vidst som standard og jeg vil ikke anbefale at slå det til i Dev-Cpp da det er yderst ustabilt i nuværende beta). I dette eksempel har jeg gjort alt public, det vil mange mene er meget dumt da det gør at der let kommer fejl hvis man er flere om at programmere på samme projekt. I stedet vil de anbefale at man bruger metoder (på engelsk methods) til at ændre indholdet af felterne. Metoder er meget ligesom funktioner, men de hører til som en del af en klasse og når man kalder dem har de adgang til de private dele af klassen, i ovenstående eksempel kunne det være man ikke var interesseret i at have negative værdier i penge, eller man ville måske have en metode til at skrive informationer om en person ind i en streng eller sådan noget. I C++ har vi derfor den mulighed at gøre informationerne sværere at tilgå på den måde kan man forhindre at de andre fra ens programmeringsprojekt ødelægger det man har lavet. Der er 2 specielle metoder nemlig constructor og destructor (jeg lader dem hedde det same på dansk). Disse er funktioner der bliver kaldt når et objekt oprettes eller slettes, en constructor skal have samme navn som klassen selv og destructor skal have et ~ foran og hedde det samme som klassen selv. Med det vi har lært nu er det tid til simplificering og forbedring af koden.
001 #include <windows.h>
002
003 #define KOEN_MAND 1
004 #define KOEN_KVINDE 0
005 #define FEJL_ALDER -1
006
007 #define ANTAL_PERSONER 3
008
009 class person{
010 public:
011 person(char *navn,int alder,bool mand,int penge);
012 void setnavn(char *navn);
013 bool setalder(int alder);
014 void setmand(bool mand);
015 void setpenge(int penge);
016 int givpenge(int ekstra);
017 char* tostring(char *output);
018 private:
019 char navn[256];
020 int alder;
021 bool mand; //1 for mand 0 for kvinde
022 int penge; //i ører
023 };
024
025 person::person(char *navn,int alder,bool mand,int penge){
026 setnavn(navn);
027 if(!setalder(alder))
028 alder = FEJL_ALDER;
029 setmand(mand);
030 setpenge(penge);
031 }
032
033 void person::setnavn(char *navn){
034 strcpy(this->navn,navn);
035 }
036
037 bool person::setalder(int alder){
038 if(alder<18)
039 return 0;
040 else
041 this->alder = alder;
042 return 1;
043 }
044
045 void person::setmand(bool mand){
046 this->mand = mand;
047 }
048
049 void person::setpenge(int penge){
050 this->penge = penge;
051 }
052
053 int person::givpenge(int ekstra){
054 return penge+=ekstra;
055 }
056
057 char* person::tostring(char *output){
058 if(alder!=FEJL_ALDER)
059 wsprintf(output,"%s er en %d år gammel %s, med %d.%02d kr. i lommen.",navn,alder,mand?"mand":"kvinde",penge/100,penge%100);
060 else
061 wsprintf(output,"%s er en %s, med %d.%02d kr. i lommen.",navn,mand?"mand":"kvinde",penge/100,penge%100);
062 return output;
063 }
064
065 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nCmdShow){
066 person *pers[ANTAL_PERSONER];
067 pers[0] = new person("Jens",42,KOEN_MAND,1150);
068 pers[1] = new person("Erik",25,KOEN_MAND,125);
069 pers[2] = new person("Karin",31,KOEN_KVINDE,50);
070 pers[1]->setmand(KOEN_KVINDE); //Erik er nu kvinde.
071 for(int i=0;i<ANTAL_PERSONER;i++){
072 char output[512];
073 pers[i]->tostring(output);
074 MessageBox(0,output,"Info:",MB_OK);
075 }
076 }
Et godt eksempel på hvordan man kan lave en simpel database, lige umiddelbart ser det ud som om det er blevet mere avanceret, men hvis vi delte det i 2 filer ville det være meget simplere, og hvis vi skule have mere end 3 personer ville det være en kort tilføjelse. Bemærk at mange af funktionerne kun benyttes i construktor, disse kunne teknisk set undværes.
Det er ret almindeligt at benytte klassens egne set funktioner i construktor, på den måde er der kun 1 metode til at sætte disse felter og hvis personer under 18 år lige pludselig er ok i databasen skal det kun skiftes et sted. Det er samme tanke der ligger til grund for #define linierne, #define er en slags variable kaldet konstanter, i C++ er der en mere fornuftig måde at erklære konstanter på, men den vil vi ikke se på lige nu. Disse defines kan så bruges steder hvor man skal have et fast tal, årsagen til at jeg bruger den her er fordi 3 skal bruges 2 gange, 1. da arrayet oprettes og 2. da alle pladser gennemløbes. De tre andre er der for at gøre det nemmere at læse, KOEN_MAND er nemmere at huske end 1 og 0, især hvis der er en situation med mange muligheder kan det være fornuftigt med defines. Bemærk at en #define sætning ikke sluttes med ’;’. Hvis du har læst tutorialen om funktioner skulle det være rimeligt tydeligt hvordan metoder virker. Jeg har valgt at lave så den ikke accepterer personer under 18, eller rettere så den ikke fortæller om deres aldrer. Ligsom med funktioner kan man returnere variable, der er en årsag til at tostring er nødt til selv at have en output array med, dette skyldes måden arrays og variabler virker på i C++, det er lidt besværligt at gå i mere detaljer end det, så bare husk at hvis noget skal returnere et array er det normalt mest fornuftigt at sende arrayet med. Det er muligt at returnere arrayet bagefter som tostring gør, dette er nyttigt hvis man vælger at bruge malloc eller new. Jeg har valgt ikke at bruge nothrow udgaven af new her, så hvis der ikke er hukommelse nok crasher programmet bare uden at sige noget til brugeren. Bemærk også at jeg har skiftet punktummet ud med en pil, og at jeg bruger noget kaldet this nogen gange men ikke andre. Årsagen til at jeg bruger this er at den medsendte variabel hedder det samme som den der hører til klassen, this er en pointer til det objekt (et objekt er en variabel lavet ud fra en klasse) som metoden er blevet kaldt på. Den er ikke nødvendig i tostring f.eks. fordi der ikke er andre variabler med samme navn. Hvis du ikke kan lide this pointeren kan du bare omdøbe variablerne i funktionskaldene. Nogle kan bedst lide det ene og andre bedst det andet. I tostring bruger jeg også % som er modulo operatoren, når man dividerer 2 int med hinanden i C og C++ bliver resultatet altid rundet ned, vil man gerne vide hvilken rest der var i en division kan man bruge modulo på den, så hvis man tager 7%3 får man 1 fordi 7/3=2 rest 1, denne operator er ofte benyttet, især i lange for løkker hvor man bare vil have et resultat en gang imellem, man kan så kigge på hvornår i%1000 giver 0 og så kun udskrive hver tusinde gang. Teknisk set burde man bruge div(7,3) hvis man vil have begge dele, dette returnerer en variabel af typen div_t dette er faktisk en struct fra C som indeholder 2 felter, quot og rem grunden til man bør benytte denne metode er at det så kan afvikles som 1 assembly instruktion, det er dog ikke et problem i nyere tid hvor compilere automatisk optimerer hvor de kan, men man ved jo aldrig om den optimerer det.div funktionen findes i stdlib.h for dem der vil lege lidt med den. Jeg tror vi slutter her for denne gang. Hvis der er nogen spørgsmål så smid mig en mail på
admin@AODASoft.net.