Løsningsforslag for eksamensoppgave, våren 2004 Simen Hagen Høgskolen i Oslo, Avdeling for Ingeniørutdanning Oppgave 1 Node.h // I oppgaven i n d i k e r e s d e t a t en s k a l kunne l a g r e t a l l, så j e g har v a l g t // å l a g r e i n t e g e r s. Vi kunne g o d t ha b r u k t noe annet ( f. e k s. f l o a t ). // Implementasjonen v i l l e vært den samme. Om en v i r k e l i g s k u l l e g j o r t d e t // b r a hadde en b r u k t t e m p l a t e s, men d e t e r i k k e d e t oppgaven s p ø r e t t e r. class Node public : Node ( int t ) ; ~Node ( ) ; int v a l u e ; Node * next ; Node * prev ; ; Node.cpp Node : : Node ( int t ) : v a l u e ( t ), next (NULL), prev (NULL) Node : : ~ Node ( ) i f ( next ) delete next ; List.h class L i s t public : L i s t ( ) ; ~ L i s t ( ) ; i n s e r t _ f i r s t ( int t ) ; 1
i n s e r t _ l a s t ( int t ) ; i n s e r t _ s o r t ( int t ) ; g e t _ f i r s t ( ) ; g e t _ l a s t ( ) ; get_next ( ) ; get_prev ( ) ; int get_ current_ value ( ) const ; s e t _ c u r r e n t _ f i r s t ( ) ; s e t _ c u r r e n t _ l a s t ( ) ; i s _ c u r r e n t ( ) const ; f i n d ( int t ) const ; remove ( int t ) ; operator ++(); operator (); private : Node * head ; Node * t a i l ; Node * c u r r e n t ; ; List.cpp // Denne oppgaven e r l a g e t f o r å t e s t e s t u d e n t e n e s kunnskaper om f o r s k j e l l i g e // a s p e k t e r ved C++. Løsningen e r o gså l a g e t f o r å være e n k e l og // f o r s t å. D e s i g n e t av L i s t k l a s s e n e r d e r f o r i k k e i d e e l, og d e t e r // f l e r e p r o b l e m e r med p r a k t i s k b r u k av denne k l a s s e n. Det v i s e s t i l // b ø k e r om a l g o r i t m e r og d a t a s t r u k t u r e r f o r i n f o r m a s j o n d e s i g n og // i m p l e m e n t a s j o n av l i s t e k l a s s e r. // Oppgave a L i s t : : L i s t ( ) : head ( NULL ), t a i l ( NULL ), c u r r e n t ( NULL ) L i s t : : ~ L i s t ( ) // Vi s l e t t e r kun head, så s l e t t e s r e s t e n a u t o m a t i s k i f ( head ) delete head ; // Oppgave b L i s t : : i n s e r t _ f i r s t ( int t ) 2
i f (! head ) head = node ; t a i l = node ; else node >next = head ; head >prev = node ; head = node ; // Opggave c L i s t : : i n s e r t _ l a s t ( int t ) i f (! head ) head = t a i l = node ; else t a i l >next = node ; node >prev = t a i l ; t a i l = node ; // Oppgave d L i s t : : i n s e r t _ s o r t ( int t ) using namespace s t d ; i f ( head ) Node * runner = head ; while ( runner && ( runner >v a l u e < t ) ) runner = runner >next ; i f ( runner == NULL) // s e t t inn b a k e r s t i n s e r t _ l a s t ( t ) ; else i f ( runner >v a l u e < t ) // t s k a l inn e t t e r runner node >next = runner >next ; runner >next = runner ; node >prev = runner ; i f ( node >next ) node >next >prev = node ; else // t s k a l inn f ø r runner i f ( runner >prev ) node >next = runner ; 3
node >prev = runner >prev ; runner >prev >next = node ; runner >prev = node ; else // runner e r d e t f ø r s t e o b j e k t e t i l i s t e n i n s e r t _ f i r s t ( t ) ; else // l i s t e n e r tom, så s e t t inn f ø r s t i n s e r t _ f i r s t ( t ) ; // Oppgave e 1 // g å r t i l d e t f ø r s t e e l e m e n t e t L i s t : : g e t _ f i r s t ( ) c u r r e n t = head ; // Oppgave e 2 // g å r t i l d e t s i s t e e l e m e n t e t L i s t : : g e t _ l a s t ( ) c u r r e n t = t a i l ; // Oppgave e 3 // g å r t i l d e t n e s t e e l e m e n t e t L i s t : : get_next ( ) i f ( c u r r e n t ) c u r r e n t = c u r r e n t >next ; return f a l s e ; // Oppgave e 4 // g å r t i l d e t f o r r i g e e l e m n e t e t L i s t : : get_prev ( ) // v i må s j e k k e om c u r r e n t! = NULL f o r å unngå f e i l. i f ( c u r r e n t ) c u r r e n t = c u r r e n t >prev ; 4
return f a l s e ; // Oppgave f L i s t : : f i n d ( int t ) const Node * runner = head ; while ( runner && runner >v a l u e! = t ) runner = runner >next ; // Hvis runner == NULL f a n t v i i k k e t a l l e t, og h v i s runner har en annen // v e r d i f a n t v i en node med k o r r e k t v e r i d. // Testen runner! = NULL v i l r e t u r n e r e t r u e h v i s v i f a n t o b j e k t e t, og f a l s e // h v i s v i i k k e f a n t d e t. return runner! = NULL; // Oppgave g L i s t : : remove ( int t ) // Finn den k o r r e k t e noden Node * node = head ; while ( node && node >v a l u e! = t ) node = node >next ; // og f j e r n dne h v i s den b l e f u n n e t. i f ( node ) i f ( node == head ) head = head >next ; node >next = NULL; delete node ; else i f ( node == t a i l ) t a i l = t a i l >prev ; t a i l >next = NULL; delete node ; else node >prev >next = node >next ; // f o r å unngå å s l e t t e a l t som kommer e t t e r node, må v i s e t t e // node >n e x t = NULL node >next = NULL; delete node ; // Oppgave h L i s t : : operator ++() return get_next ( ) ; 5
// Oppgave i L i s t : : operator () return get_prev ( ) ; Oppgave 2 A Main-funksjonen er den første funksjonen som blir kallt i programmet vårt, og er altså hvor de første linjene i programmet vårt benner seg. Alle C/C++programmer må ha en main-funkjson (eller en tilsvarende oppstartsfunkjson. Microsoft Windows bruker en funksjon som heter WinMain, den tar ere parametere enn en vanlig main-funksjon). En minimal main-funksjon kan se slik ut: int main ( ) return 0 ; B 1. Det allokeres minne. Hvis vi bruker List-klassen i fra oppgave en ovenfor, allokeres det sizeof(list) bytes. 2. Deretter initialiseres minnet med standardkonstruktøren. Det er mulig å kalle andre konstruktører enn standardkonstruktøren, f.eks. slik List* l = new List(3); C Det er alltid viktig å unngå minnetap, men det er noen situasjoner som det er spesiellt viktig å passe på å slette alt minne som en ikke lenger trenger. Her er noen eksempler på situasjoner hvor det er ekstra viktig å passe på hva en gjør: ˆ Vi lager et program som skal kjøre over lengre tid. Dette kan være ting som en service til operativsystemet, eller f.eks. en nettleser som kjøres i mange dager uten å avslutte. Siden programmene kjører så lenge blir heller ikke minne frigjort når en avlustter, og at minne som går tapt for systemet (intill en restart av programmet/operativsystemet gjøres). ˆ Vi opererer i et miljø hvor det er lite minne tilgjengelig. Spesiellt enheter som mobiltelefoner, PDAer o.l. har lite minne å ta av, og det 6
er dermed viktig at vi ikke bruker mer minne enn nødvendig til enhver tid. Slike enheter står også ofte på lenge, så det er lang tid mellom hver gang det blir ryddet opp. ˆ Vi lager programmer som bruker veldig mye minne. Det er viktig å frigjøre minne slik at systemet ikke går tomt for fritt minne. D 1. En wild pointer er en peker som peker til noe som er søppel. 2. En wild pointer kan oppstå på tre måter: (a) En uinitialisert peker som inneholder søppel. (b) En peker er (feilaktig?) blitt overskrevet med en verdi som er søppel. (c) Pekeren refererer til noe som ikke lenger eksisterer (dette er en dangling pointer). E Linje 5 En må en bruke *j = 7; for å gi j verdien 7. Linje 6 har samme problem som linje 7, en må bruke *j. Linje 7 returnerer en referanse til et lokalt objekt. Linje 8 blir aldri kalt på grunn av return statementet ovenfor. Dermed får en også minnetap i funksjonen, fordi j aldri blir slettet. 7