08. december 1999 - 18:46Der er
27 kommentarer og 1 løsning
Eksempel HURTIGT!!!!!
Er der en haj til C++, der kan lave et hurtigt eksempel til mig ?? Mit problem er at jeg har en eksame i C++ på Tirsdag og skal have styr på disse ting inden da...
Jeg skal bruge et eksempel der demonstrerer: Klasser Constructors Destructors Pointere til klasser Structures (Struct) Og filhåndtering...
Umiddelbart overvejede jeg et hurtigt eksempel i stil med følgende: En klasse med tilhørende contructors og destuctors, samt en struct(ure) der skrives/læses til/fra disk...
(Eks. class film struct whatever (indhold: titel + tid) Funktion der opretter en fil til indholdet Funktion der skriver til filen Funktion der læser fra filen Contructor til klassen/funktionerne Destructor til klassen/funktionerne og en eller anden pointer til klassen...)
Eksemplet skal enten være MEGET pædagoisk opbygget (mht. variabelnavne osv.) eller have nogle beskrivende kommentarer!
Nogen der HURTIGT kan klare dette ?? (da jeg som sagt har en eksame på Tirsdag)
Jeg har lige lavet dette lille eksempel, prøv at se om det kan bruges.
Der mangler selvfølgelig meget hvis det skal kunne bruges til noget som helst, men her kan du da i hvert fald få lidt inspiration, og se lidt grundlæggende struktur for brug af klasser, structs, pointere og læse/skrive i en fil.
Jeg har prøvet at køre koden, og den ser umiddelbart ud til at virke, men jeg har ikke lige gennemtestet den, så der kan godt skjule sig et par fejl.
Spørg hvis der noget du er i tvivl om, eller hvis jeg har misforstået det hele...
Til DMK: Det er et flot, illustrativt og godt eksempel du der har fabrikeret, omend jeg nok ville byde på at en novice vil have lidt problemer med at tygge et par af metoderne...
Til Tigerdyret: pøj pøj, der er vist ingen grund til at tilføje noget til det svar.
Til privaten >> Du har ret, hvis dette havde været til en novice, havde jeg nok valgt et lidt mere simplificerede eksempel, og måske med nogle flere kommentarer. Men jeg tog mig den frihed at kigge lidt på hvad tigerdyret ellers har deltaget i her på siten, og ud fra det konkluderede jeg, at han ikke røg under definitionen "novice". Derfor mente jeg nok han ville kunne forstå størsteparten af dette.
Hvis det ikke er tilfældet, så må tigerdyret meget gerne skrive det, så skal jeg nok forenkle mit eksempel lidt. Det skulle bare gå lidt stærkt da jeg lavede det, så jeg havde ikke lige tid til en masse kommentarer, og en simplere struktur.
Hej Tigerdyr, jeg formoder at du læser til Datamatiker eller lignende og skal til eksamen i faget..... Jeg er selv lige blevet færdig som Datamatiker og har sikkert skrevet nogle noter om emnet.... Jeg prøver lige at tjekke i morgen.... Mail mig evt. på pt@everest.dk
Til DMK >> Helt enig, jeg var egentlig også bare glad for at et super godt eksempel. Det er det man har brug for i den slags situationer. Desuden er jeg sikker på at et Tigerdyr sagtens kan tygge sig igennem, det er vel ikke en tandløs tiger? :-)
Jeg skal lige se om jeg kan finde rundt i det... Anyway...2 hurtige spørgsmål ang. eksemplet... Jeg går ud fra at DataIO(); ~DataIO(); er hhv. Contructor og Destructor.... ?? Men jeg havde en ide om at man brugte constructoren til at oprette et object af klassen (for at få adgang til klassen funktioner) og nedlagde objectet efter brug med en destructor... Er det helt ved siden af ?? (spørger kun fordi de står lige efter hinanden i det jeg hurtigt har set)
Jeg har vist brug for nogle kommentarer i koden...
Bortset fra det er der nogle småting der generer mig ved koden... Min lære brugte kun new/delete istedet for malloc osv. (er malloc ikke også C ?) Desuden har jeg lært at skrive pointere som eks. int pointer* og forstår derfor ikke helt princippet i dine eksempler, specielt linien: List=(Data**)malloc(sizeof(Data*)*AllocSize); driller mig... Data er en pointer til en pointer, men hvad peger den på ?? Også sizeof(Data*)*AllocSize); er lidt tågede for mig :o((
Anyway... har jeg ellers forstået eksemplet rigtigt, hvis jeg tror at constructor og destructor kun skal defineres i klassen, men først oprettes længere nede i programmet ??
Desuden linien: DataIO::DataIO() er en constructor til funktionen DataIO() i klassen DataIO ikke ??... mens linien: DataIO::~DataIO() er en destructor til selvsamme funktion ??
Men men men... hvordan fanden kan: void DataIO::AddToList(Data* NewElement) virke ??, det er jo en privat funktion i klassen du vil have adgang til her...troede kun at man kunne skabe kontakt til private: sektionen, hvis man oprettede kontakten fra public: sektionen...
De sidste ting, må så blive... under en compile får jeg nu 3 fejl... Call to undefined function 'malloc' -||- 'free' -||- 'realloc' Det er vel bare en headerfil mere der skal inkluderes...(løste strcpy osv. med string.h)
Og jeg savner en main(), så jeg kan se det i brug, med nogle indtastninger... Kan du klare alle de spørgsmål ?? :o)
Bortset fra det, ser det kanon godt ud... Det er et godt eksempel (jeg er sikkert bare for stresset til at tygge mig gennem koden selv... har trods alt kun ganske få dage tilbage til eksamen og læser samtidigt med C++ også både Java (hvor jeg personligt mener at klasser er nemmere) og Delpji, hvilket ikke gør det nemmere)
Ja du har ret, DataIO() og ~DataIO() er constructor/destructor. Det er også rigtigt, at en constructor bruges til at oprette et objekt med om man vil, og at destructoren brugges til at nedlægge det. Men her skal "oprette" nok lige forstået rigtigt. En constructor bliver kaldet når objektet bliver allokeret, og hvorefter det følgende kan tilgåes. Constructoren kan man bruge til at initialisere interne variabler osv, men man behøver ikke have en constructor. I mit eksempel fra før vil det dog gå grueligt galt, hvis ikke jeg havde skrevet en constructor. Uden den, ville mine Size-variabler ikke blive sat, og deres værdi vil være ren garbage, og listen vil heller ikke blive allokeret. Det vil betyde at man vil komme til at tilgå hukommelse der ikke er allokeret, og man vil sandsynligvis få sig en Access Violation/segmentation fault. Destructoren bruges ligeledes til at ryde op efter klassen, og i dette tilfælde frigive noget hukommelse. Hvis ikke det bliver gjort, vil man få en masse memory leaks, og ens program æder lige så stille computerens hukommelse. Men du må ikke begå den klassiske fejl og tro, at constructor/destructor har noget som helst med pointere at gøre. Jeg har mødt mange der har troet, at en constructor KUN bliver kaldt når der skrives " DataIO* Obj = new DataIO; ", og at destructoren bliver KUN bliver kaldt når man skriver " delete Obj; ". Statisk allokeret objekter bliver også oprettet og nedlagt med constructor/destructor, så følgende to programstumper fungerer fuldstænig ens hmt hvornår constructor og destructor bliver kaldt:
Eksempel 1: int main() { printf("starting...\n"); . . DataIO* Obj = new DataIO; . . delete Obj; return 0; }
Eksempel 2: int main() { printf("starting...\n"); . . DataIO Obj; . . return 0; }
I begge tilfælde bliver constructoren kaldt der hvor Obj bliver erklæret, og destructoren bliver kaldt lige før man returnerer ud af Main funktionen. Der er dog en lille forskel på de to eksemplers destructor kald. Den første kalder destructoren og laver deallokering af objektet når delete bliver kaldt. Den anden smider retur-værdien på stakken, og kalder derefter destructoren for alle statisk allokerede objekter i omvendt rækkefølge af allokeringen. Da dette bliver gjort automatisk, er det garanteret at det ALTID bliver gjort. Men i det første eksempel kan man risikere, at destructoren aldrig bliver kaldt, og at man derfor får memory-leak. Det kan optræde hvis der bliver kastet en exception inde i koden, og den ikke bliver fanget og behandlet inden for funktionen. Men selv hvis der bliver kastet en exception vil det statisk allokeret objekt altid få kørt sin destructor, og hukommelsen for selve objektet vil blive frigivet.
Jeg håber det hjalp lidt??? Spørg endelig videre hvis der er noget du ikke forstår.
>List=(Data**)malloc(sizeof(Data*)*AllocSize); >driller mig... Data er en pointer til en pointer, men hvad peger den på ?? >Også sizeof(Data*)*AllocSize); er lidt tågede for mig :o((
Denne er lidt kryptisk. Det første du skal forstå er, at en pointere kan bruges til to ting. Den kan bruges til at oprette et objekt dynamisk, og den kan benyttes til at oprette et array dynamisk. Det vil sige, at hvis jeg laver en int* Tal, kan jeg skriver Tal=new int[200];, og derved få et array på 200 integers. Det jeg benytter mig af i min kode er, at lave et array af pointere til Data. Det vil sige:
Data** PointerArray = new Data*[Size];
Jeg vil gerne have at jeg kan udvide dette array når jeg tilføjer objekter til det, og der ikke er mere plads i det. Det kan jeg gøre sådan:
Data** TempArray = new Data*[NewSize]; for (unsigned int i=0; i<Size; i++) TempArray[i]=PointerArray[i]; delete[] PointerArray; PointerArray=TempArray;
Dette giver dog en kopiering af hvert element i arrayet hver gang jeg skal ændre størrelsen, og det vil jeg gerne være fri for. Derfor vil jeg gerne bruge realloc, som kan udvide et array, uden at kopiere det til en anden fysisk position i hukommelsen, hvis der er plads til udvidelsen der hvor det er. Men realloc er en standard c ting, og fungerer kun sammen med andre standard c ting. Derfor bliver jeg nødt til helt at undgå new og delete, og kun bruge malloc og free. Malloc og realloc tager som parameter hvor meget memory man vil have allokeret. Tallet er i bytes, så for at få allkeret nok, skal man have antallet af elementer gange størrelsen pr. element. Sizeof() giver størrelsen af elementet, så sizeof(Data*) giver størrelsen i bytes på en pointer til structen Data.
>DataIO::DataIO() er en constructor til funktionen DataIO() i klassen DataIO >ikke ??... >mens linien: >DataIO::~DataIO() er en destructor til selvsamme funktion ??
Du laver ikke constructor og destructor til funktioner. Du laver constructor og destructer til klasser, så DataIO(); er erklæring af en constructor til klassen DataIO, og DataIO::DataIO() er implementation af constructoren til klasen DataIO. Lige som alle andre funktioner til klassen, så behøver man ikke ødelægge overskueligheden af erklæringen af klassen ved at lave implementationen i klasse erklæringen. Man kan flytte implementationen ud af klassen ved at skrive klassens navn samt :: foran funktionens navn der hvor man skrier implementationen.
>Men men men... hvordan fanden kan: >void DataIO::AddToList(Data* NewElement) virke ??, det er jo en privat funktion i >klassen du vil have adgang til her...troede kun at man kunne skabe kontakt til private: >sektionen, hvis man oprettede kontakten fra public: sektionen...
Du har NÆSTEN forstået private og public rigtigt. Private betyder, at kun klassens egne funktioner (herunder også constructor og destructor) kan tilgå det, og det er både tilfældet for funktioner og variabler. Det vil sige, at når AddToList er en private funktion, så kan kun klassens egne funktioner tilgå den. Det er også tilfældet i min kode. Det er kun klassens egne funktioner der tilgår den.
>Call to undefined function 'malloc' >-||- 'free' >-||- 'realloc' >Det er vel bare en headerfil mere der skal inkluderes...(løste strcpy osv. med string.h)
Sorry, den smuttede. Du skal skrive #include "malloc.h" for at den kender de 3 funktioner.
Tester lige igen... (jeg er vist ikke så hurtig til C++ som jeg havde håbet)
Anyway... dit svar gav mig lige lidt mere... void DataIO::AddToList(Data* NewElement) virker kun fordi du har lavet constructoren DataIO::DataIO() ?? Og derfor kan du via den constructor bruge alt fra klassen ?
Constructoren har intet med AddToList at gøre. Constructoren sørger bare for at initialisere de interne variabler, så AddToList ikke læser og skriver i u-initialiseret hukommelse. AddToList kan kaldes fra alle klassens andre funktioner, og det kunne den også selv om jeg ikke havde lavet en constructor.
Går dit spørgsmål på private/public tilgang til AdToList funktionen, eller går det på selve koden i AddToList funktionen ?
Okay dit sidste svar, hjalp mig en del videre... (rettede et par småfejl i din main...eg. int (ID) skal ikke stå i ")
Men jeg bliver lige nødt til at stille et par spørgsmål mere!
Hvordan er det lige at -> operatoren virker ?? (jeg har læst lidt om den, men ikke lavet nogle øvelser med den) Hvad bruges Data* DataIO::operator [] (unsigned int idx) til ?? I main() skriver du: DataIO Object2; Object2.Read("TestFile.dat"); for ( int i=0; i<Object2.GetSize(); i++) printf("Name: %s Id: %d\n", Object2[i]->Name, Object2[i]->Id);
Hvordan pokker kan samme objekt bruges til at hente 2 forskellige ting ?? (Eg. Name & ID)...Hvor jeg går udfra at DataIO Object2 opretter et objekt at klassen... Desuden fatter jeg intet af filen, åbnes filen i eks. Notepad, kan jeg fint se navnene, men ikke ID...
void DataIO::AddToList(Data* NewElement) det er denne funktion du udvider arrayet med, eller hvad ?
Og sidst men ikke mindst, constructoren og destructoren... Constructoren starter du med at oprette til 10 byte ?? og udvider hvis den kaldes igen, er det sådan den virker ?? Destructoren "nedlægger" de enkelte elemeter af arrayet, en af gangen og slutter af med at fjerne selve arrayet ??
Det jeg mente var at hvis du ikke havde lavet den constructor, så havde man ikke kunne tilgå funktionen AddToList, som så vidt jeg forstår tilgås via constructoren (DataIO:: = Constructor AddToList... = Funktion) Jeg udtrykker mig sikkert ret uklart her...
Operator -> bruges når man vil tilgå indholdet af en pointer til en klasse eller struct på samme måde som . bruges hvis det er et statisk objekt.
DataIO Obj1; Obj1.Write("test");
Sådan tilgår du Write funktionen for Obj1, når den er statisk allokeret.
DataIO* Obj1=new DataIO; Obj1->Write("test");
Sådan tilgår du Write hvis den er dynamisk allokeret.
operator [] kan man bruge til at lave en indeksering. hvis jeg nu havde skrevet:
int* Array=new int[100];
Så kunne jeg tilgå element nummer 4 ved at skrive:
Array[3]; // Husk arrays starter i 0
Hvis jeg nu laver et array af min egen klasse:
Data* Array=new Data[100];
kan jeg på samme måde tilgå element nummer 4 ved at skrive:
Array[3];
Men nu indeholder elementet nummer 4 jo både et navn og en id, så derfor kan jeg tilgå de enkelte ved at skrive:
Array[3].Id; Array[3].Name;
Men nu laver jeg et array af pointere:
Data** Array=new Data*[100];
Nu har jeg 100 pointere, som jeg så kan tilgå med indeksering. Jeg skal så oprette elementer til hver pointer:
for (int i=0; i<100; i++) Array[i]=new Data;
For at tilgå elementerne nu skal jeg skrive:
Array[3]->Id; Array[3]->Name;
Nu har jeg så lavet min egen klasse til at indeholde arrayet, og skal derfor selv definiere hvad jeg mener, når jeg skriver [] . Derfor laver jeg en operator [], som returnerer en Data* . Det vil sige, at når jeg skriver Obj[i] så er det det samme som da jeg før skrev Array[3] . Og da det jo er en pointer jeg får tilbage, så skal jeg skrive Obj[i]->Id for at tilgå dataene for den struct min pointer peger på.
Hvis du bedre forstår det, kunne du også skrive:
Data* TempPtr;
for ( int i=0; i<Object2.GetSize(); i++) { TempPtr=Object2[i]; printf("Name: %s Id: %d\n", TempPtr->Name, TempPtr->Id); }
Grunden til at du ikke kan se integeren når du bruger notepad er, at jeg ikke skriver integeren i filen som tekst, men som rå data. Du kan derfor ikke læse den som en tekststreng, når det i virkeligheden er en integer.
Hvis du vil have at man kan læse filen i notepad, skal du konvertere integeren til tekst når du skriver den, og konvertere den tilbage til en integer når du læser den. Funktionerne til at gøre det hedder ltoa ( for at konvertere integer til tekst) og atol (for at konvertere tekst til integer).
AddToList bruger jeg til at udvide arrayet med, det er korrekt. Men jeg udvider kun arrayet hvis der ikke er plads. Jeg har to tællere, den ene fortæller hvor meget der er plads til, og den anden fortæller hvor mange elementer der er fyldt i. Hvis de to tal er ens, ja så er der ikke plads til mere. Så skal der udvides.
Constructoren opretter et array på 10 pointere. Constructoren bliver dog IKKE kaldt igen, AddToList tager sig af yderligere allokeringer. Destructoren har du vist forstået korrekt...
AddToList har intet med constructoren at gøre. AddToList tilgåes ikke via constructoren. Constructoren bruges til at initialisere variabler med, så de ikke er uinitialiseret når AddToList kaldes. Jeg forstår ikke helt din sammenligning af AddToList og constructoren ? Det er rigtigt at AddToList ikke vil virke, hvis ikke constructoren er kaldt først. Man havde godt kunnet tilgå funktionen, men den vil nok resultere i en access violation.
Du har gjort et stort stykke arbejde her og skal selvfølgelig have dine point... (har dog tilsyneladende stadig nogle problemer med klasser...og structs!) Kan jeg få dig til at skrive kommentarer i din oprindelige kode, så jeg har lidt at gå ud fra ??
Jeg troede jeg havde fanget det hele, men men men... så er der den her grimme historie: #include <iostream.h>
Jeg tror grunden til at jeg kæder constructoren og AddToList sammen er fordi det står som: void DataIO::AddToList(Data* NewElement) Hvor jeg betragter DataIO som et kald til constructoren...Men det er vist mig, der er på et sidespor der ikke ?? DataIO fortæller bare hvilken klasse funktionen hentes fra, eller hvad ? En anden grund til det er nok at AddToList jo er en privat funktion i klassen, og derfor skal tilgås fra klassen selv, så vidt jeg har forstået enten via et object af klassen eller en constructor til klassen... Ret mig endelig hvis jeg tager fejl! :o))
Bortset fra det, er det en utrolig stor hjælp for mig...Tusind Tak!
Som tidligere nævnt, så kan en pointer laves til et array:
int* Tal=new Tal[200];
Derfor kan vi konkludere, at en pointer og et array er (**NÆSTEN**) det samme. Det vil sige, at hvis jeg laver det på denne måde:
int Tal[200];
Så er Tal stadig en pointer, men nu er arrayet statisk allokeret, og Tal-pointeren kan ikke laves om.
På samme måde er det med dine strenge. Du har defineret nogle strenge således:
char Streng[20];
Det vil være det samme som at skrive:
char* Streng=new char[20];
I begge tilfælde er Streng en char* . Så det du prøver at gøre er følgende: Du prøver at sætte en char* lig med en anden. Det vil også kunne lade sig gøre, hvis begge var "rigtigt" pointere, selv om du nok ikke ville bryde dig om resultatet.
Var[ID].Fornavn = Navn1;
Fornavn er her en char*, og det samme er Navn1. Men fornavn er statisk allokeret pointer, og kan derfor ikke sættes til at pege på noget andet. Det du i virkeligheden vil er, at du vil kopiere indholdet af Navn1-pointeren over i indholdet af Fornavn-pointeren. Det gør du ved:
strcpy(Var[ID].Fornavn, Navn1);
Så skulle dine problemer være løst.
Jeg håber det hjalp? Har du tidligere kodet Turbo Pascal? Så kan jeg nemlig godt forstå at du er mystificeret af syntaksen, for i TP kan man godt sætte to strenge lig med hinanden. Jeg kan godt huske dengang jeg skiftede fra TP til C++... Det var et helvede, men når man lærer C++ ordentligt at kende, så er det så meget bedre og nemmere at kode i. Man skal bare vide hvad man gør.
Men HELD OK LYKKE med din eksamen! Jeg håber jeg har været i stand til at hjælpe! Du må gerne stille flere spørgsmål, men så skulle du måske oprette et nyt spørgsmål, og sætte nogle flere point på spil ;-) Jeg syntes i hvert fald snart jeg har fortjent de 58 ...
Jeps, jeg havde 6 måneders Turbo Pascal sidste semester... (Grundlæggende prog.) og ca. 3 måneder Videregående prog. i C++ dette semester + Java og Delphi :o))
Jeg tror du er ved at forstå hvad det med constructoren og AddToList funktionen. Men jeg vil da godt lige give det et forsøg mere:
En constructor og en destructor på en klasse er bare helt almindelige funktioner, og kan betragtes som sådanne. Den eneste forskel på de to i forhold til resten er, at de bliver kaldt automatisk 1 gang hver.
Gruden til DataIO:: foran funktionerne samt constructor/destructor har du vist også fanget. Se følgende eksempel:
class a1 { void Func(); };
class b1 { void Func(); };
void Func() { printf("hej"); }
I implementationen af Func har compileren ingen mulighed for at vide, hvad det er jeg er i gang med. Er jeg ved at implementere en enkeltstående funktion, er jeg ved at implementere Func for a1, eller er jeg ved at implementere Func for b1 ? Compileren vil gå ud fra, at eftersom jeg ikke har sagt andet, så har jeg lavet en enkeltstående funktion, som intet har med hverken a1 eller b1 at gøre. Derfor bliver man nødt til at FORTÆLLE compileren, hvis funktionen hører til en klasse:
class a1 { void Func(); };
class b1 { void Func(); };
void a1::Func() { printf("hej"); }
void b1::Func() { printf("hej igen"); }
Her kan compileren se hvilken funktion der hører til hvilken klasse. Alternativet er at skrive alt koden inden for selve klassen:
class a1 { void Func() { printf("Hej"); } };
class b1 { void Func() { printf("hej igen"); } };
Så er der ikke noget at tage fejl af for compileren. Men dette ødelægger fuldstændig overskueligheden i definitionen af klassen, især hvis man har mange og lange funktioner. Derfor er det en god ide altid at flytte koden ud af klassen.
Hej, kan jeg få ligeså god en behandling, selvom jeg er pige og på begynderstadiet? Jeg skal nu til at afslutte 1. semester af datamatiker uddannelsen? Så vil jeg være dybt taknemmelig. Mine problemer starter på tirsdag. Jeg skal aflevere om fredagen. Emnet bliver iterative og rekursive algoritmer samt algoritmebeskrivelser.
Men jeg er faktisk ret sikker på at du kender mere til det end mig *G* Læser du på NielsBrock ?? Anyway... der er nogle downloads af noter her: http://194.182.110.224/ (DaPunk)
Jeg kan sgu ikke lige finde de andre, jeg fandt et sted med noter til specielt algoritmer osv.
Hej Tigerdyr, Ja, jeg læser på Niels Brock, hvor læser du?? Jeg misunder dig, at du er nået så langt, jeg synes programmering går for stærkt, man når ingen rutine i noget af det, før man er igang med noget nyt. Jeg er ikke sikker i noget som helst. Tak for adressen, jeg kigger på det.
Ja, selvfølgelig kan du få lige så god behandling som Tigerdyr! Du skal bare stille et spørgsmål, så skal jeg nok gøre hvad jeg kan for at svare på det. Iterative og rekursive algoritmer arbejder jeg med til daglig, men algoritmebeskrivelser har jeg nok ikke rigtig kigget på, siden jeg blev færdig med min uddannelse for 2 år siden, men jeg skal da gøre hvad jeg kan.
Tigerdyr: Er du færdig med at forberede dig til din eksamen? Hvis du har flere lignende spørgsmål, så kom endelig med dem.
>> DMK ...Nej jeg knokler med min bog :o)), men takket være din hjælp er jeg kommet et godt stykke videre... >>Blackie Hillerød Erhvervsakademi.... Og sådan har vi andre det også med programmeringen *S*, hæng i!! :o) Og som DMK siger, selvfølgelig kan du få lige så god behandling... jeg hjælper også gerne andre Datamatiker studerende (og andre) med det jeg kan!
/Tigerdyr
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.