Programmering i C++ Eksamen høsten 2005 Simen Hagen Høgskolen i Oslo, Avdeling for Ingeniørutdanning 7. desember 2005 Generelt Denne eksamensoppgaven består av tre oppgaver, pluss en ekstraoppgave. Det er ikke nødvendig å gjøre ekstraoppgaven, men den kan gi ekstrapoeng. Hvis du ikke gjør ekstraoppgaven, eller hvis du svarer feil på den, gjør det ikke noe. Den kan kun telle positivt. For de vanlige oppgavene gjelder det at alle deloppgavene teller likt. 1 Pekere og arrays 1a) Forklar kort hvordan funksjonen a() virker, og hva den returnerer. 1 i n t a ( c o n s t char c ) 2 { 3 i n t i = 0 ; 4 while ( c [ i ] ) 5 ++ i ; 6 return i ; 7 } Funksjonen a() teller antall tegn i en NULL-terminert streng. While-løkken kjører så lenge uttrykket c[i] er forskjellig fra NULL ( \0 ). Variabelen i fungerer som en teller, og økes med en for hvert tegn som er forskjellig fra NULL. Når en kommer til slutten av strengen stopper løkken og verdien til i returneres. Funksjonen returnerer med andre ord lengden på en NULL-terminert streng, ikke inkludert NULL-terminatoren. 1
1b) Lag en funksjon som finner det største elementet i et array. Funksjonen skal kunne akseptere et array av en vilkårlig type (både innebygde og egendefinerte) og lengde, og returnere det elementet som er størst. Du kan gå ut i fra at typen du skal sjekke på har en operator<. 1 template <typename T> 2 T find_max ( c o n s t T a r r a y, i n t n ) 3 { 4 / / S j e k k om v i har m i n s t t o e l e m e n t e r i a r r a y e t 5 i f ( n < 2) 6 return a r r a y [ 0 ] ; 7 8 T c u r r e n t _ m a x = a r r a y [ 0 ] ; 9 f o r ( i n t i = 1 ; i < n ; ++ i ) { 10 i f ( c u r r e n t _ m a x < a r r a y [ i ] ) 11 c u r r e n t _ m a x = a r r a y [ i ] ; 12 } 13 return c u r r e n t _ m a x ; 14 } 1c) Forklar kort hva funksjon f() gjør, og hva den returnerer. 1 f l o a t f ( f l o a t g, i n t h ) 2 { 3 f l o a t i = 0 ; 4 f o r ( i n t j = 0 ; j < h ; ++ j ) { 5 i += ( g+ j ) / h ; 6 } 7 return i ; 8 } Argumentet g er et array med floats, og h er antall elementer i arrayet. Funksjonen henter ut hvert element i arrayet ( (g+j)), og deler på antall elementer (h). Funksjonen returnerer dermed et snitt av alle tallene i arrayet. 2
For å få ekstra poeng kan studenten nevne at det vil være mer effektivt å først legge sammen tallene i loopen, og dele på antallet etter at loopen er ferdig. En slipper da unna med kun en divisjon i stedet for h. 2 Skrive ut en fil Denne oppgaven går ut på å lage et lite program som leser inn en tekstfil og skriver denne ut til skjermen. Avhengig av argumentene til programmet skal hver linje i teksten skrives ut med eller uten linjenummer før innholdet på linjen. Oppgaven skal oppfylle følgende krav: 1. Det skal leses inn argumenter fra kommandolinjen (som et argument til programmet, ikke interaktivt under kjøring). Argumentene skal være: Navnet på filen som skal leses inn -n hvis det skal skrives ut linjenummer Filnavn må være med, men det skal ikke være nødvendig å skrive ut linjenummer.formatet på kommandoen kan være noe slikt som: skrivut [-n] <filename> 2. Filen skal åpnes, leses inn og lukkes. Du må håndtere muligheten for at filen ikke finnes. 3. Utskrift av tekstfilen. Her vil du eventuelt også sette inn linjenummeret, som skal skrives ut først på linjen, om det skal skrives ut. For denne delen er det viktig at du kan lese inn uendelig lange linjer. Du er fri til å bruke de klasser og funksjoner som C++ tilbyr når du skal løse denne oppgaven. Skriv programmet slik at det er lett å lese. Bruk gjerne kommentarer. 1 # i n c l u d e < i o s t r e a m > 2 # i n c l u d e < f s t r e a m > 3 # i n c l u d e < s t r i n g > 4 5 using namespace s t d ; 6 7 void 8 usage ( ) 9 { 10 c o u t << " Bruk : s k r i v u t [ n ] < f i l n a v n >" << e n d l ; 3
11 e x i t ( 0 ) ; 12 } 13 14 i n t main ( i n t argc, char argv [ ] ) 15 { 16 char f i l e n a m e = " " ; 17 i n t l i n j e n r = 0 ; 18 19 i f ( a r g c > 1) { 20 i f ( ( argv [ 1 ] [ 0 ] == ) && ( argv [ 1 ] [ 1 ] == n ) ) { 21 l i n j e n r = 1 ; 22 i f ( a r g c > 2) 23 f i l e n a m e = a rgv [ 2 ] ; 24 e l s e { 25 usage ( ) ; 26 } 27 } e l s e { 28 f i l e n a m e = a rgv [ 1 ] ; 29 } 30 } 31 32 i f s t r e a m i n ( f i l e n a m e ) ; 33 i f (! i n ) 34 c o u t << " c o u l d n o t open f i l e " << f i l e n a m e << e n d l ; 35 36 s t r i n g s t r ; 37 while ( i n ) { 38 g e t l i n e ( in, s t r ) ; 39 40 i f ( l i n j e n r ) 41 c o u t << l i n j e n r ++ << " " ; 42 43 c o u t << s t r << e n d l ; 44 } 45 i n. c l o s e ( ) ; 46 } 3 En Stack klasse I denne oppgaven skal du lag en Stack-klasse. Du skal bruke en node-peker (liste) struktur for å lagre dataene i stacken. Stacken skal være template-basert. Du skal gjøre følgende oppgaver: 4
1. Lag klassedeklarasjonene med alle funksjoner og variabler som trengs (alt det som vanligvis finnes i en header-fil). 2. Lag det som trengs av konstruktører og destruktører. 3. Lag en funksjon push() som legger nye elementer inn på stacken. 4. Lag en funksjon pop() som fjerner et element fra stacken. Etter pop() skal det finnes et element mindre på stacken. 5. Lag en funksjon empty() som returnerer true hvis det ikke finnes noen elementer på stacken, og false hvis det finnes elementer på stacken. Du kan anta at elementene som skal lagres på stacken kan kopieres. Stack.h 1 template <typename T> 2 c l a s s Node 3 { 4 p u b l i c : 5 T d a t a ; 6 Node n e x t ; 7 8 Node ( T d, Node n = NULL) 9 : d a t a ( d ), 10 n e x t ( n ) 11 {} 12 13 } ; 14 15 template <typename T> 16 c l a s s S t a c k 17 { 18 p r i v a t e : 19 Node<T> head ; 20 21 p u b l i c : 22 S t a c k ( ) ; 23 ~ S t a c k ( ) ; 24 25 void push ( T i tem ) ; 26 T pop ( ) ; 27 bool empty ( ) c o n s t ; 5
28 } ; Stack.cpp 1 # i n c l u d e < i o s t r e a m > 2 using namespace s t d ; 3 4 # i n c l u d e " S t a c k. hpp " 5 6 template <typename T> 7 Stack <T > : : S t a c k ( ) 8 : head (NULL) 9 { 10 } 11 12 template <typename T> 13 Stack <T > : : ~ S t a c k ( ) 14 { 15 while (! empty ( ) ) { 16 pop ( ) ; 17 } 18 } 19 20 template <typename T> 21 void 22 Stack <T > : : push ( T item ) 23 { 24 head = new Node<T>( item, head ) ; 25 } 26 27 template <typename T> 28 T 29 Stack <T > : : pop ( ) 30 { 31 T d a t a = head >d a t a ; 32 Node<T> n = head ; 33 head = head >n e x t ; 34 d e l e t e n ; 35 return d a t a ; 36 } 37 38 template <typename T> 39 bool 6
40 Stack <T > : : empty ( ) c o n s t 41 { 42 return head == NULL; 43 } 44 45 i n t main ( ) 46 { 47 Stack < i n t > s t a c k ; 48 49 f o r ( i n t i = 1 ; i < 1 0 ; ++ i ) { 50 s t a c k. push ( i ) ; 51 } 52 53 Stack < i n t > s t = s t a c k ; 54 55 c o u t << " P r i n t i n g s t a c k \ n " ; 56 while (! s t a c k. empty ( ) ) { 57 c o u t << s t a c k. pop ( ) << e n d l ; 58 } 59 60 c o u t << " \ n \ n P r i n t i n g s t \ n " ; 61 while (! s t. empty ( ) ) { 62 c o u t << s t. pop ( ) << e n d l ; 63 } 64 65 return 0 ; 66 } 4 Deklarasjoner 4a) Forklar hva disse deklarasjonene betyr: 1. int f ; 2. int f = 0; 3. int f (); 4. int f () = 0; 5. int f [] = {0}; 7
4b) 1. Deklarering av integer-variabelen f. f får en udefinert verdi. 2. Deklarering av integer-variabelen f, som får initialverdien 0. 3. Deklarering av funksjonen f, som returnerer en int og som ikke tar noen argumenter. 4. Deklarering av pure virtual funksjonen f, som returnerer en int og som ikke tar noen argumenter. Denne deklarasjonen må være en del av en klasse for å fungere, og vil da gjøre klassen til en abstract base class, som vil si at det ikke kan lages instanser av denne klassen. Funksjonen f må implementeres i klasser som arver denne klassen. 5. Deklarering av arrayet f, av type int. Arrayet får her kun et element med initialverdien 0. Gitt følgende kode: 1 char c [ ] = " H e l l o World! " ; 2 char pc = &c [ 2 ] ; Hva skriver de følgende uttrykkene til skjermen? 1. cout << c; 2. cout << c [3]; 3. cout << pc; 4. cout << (pc 2); 5. cout << pc 2; 1. cout << c; Hello World! 2. cout << c [3]; l 3. cout << pc; llo World! 4. cout << (pc 2); H 5. cout << pc 2; 106 8
5 Ekstraoppgave Denne oppgaven er en ekstraoppgave. Det er ikke nødvendig å gjøre den, men det gir ekstra poeng hvis du gjør det. Hvis du svarer feil på oppgaven vil det ikke telle negativt. Nedenfor er det et enkelt lite program. Programmet kompilerer og kjører helt fint, men fungerer kanskje ikke helt som planlagt. Hva går galt? Hvorfor? Forklar så godt du kan hva som skjer. 1 # i n c l u d e < i o s t r e a m > 2 3 c l a s s P e r s o n 4 { 5 p u b l i c : 6 ~ P e r s o n ( ) { } 7 } ; 8 9 c l a s s Englishman 10 : p u b l i c P e r s o n 11 { 12 p u b l i c : 13 v i r t u a l ~ Englishman ( ) { 14 s t d : : c o u t << ( " Goodbye \ n " ) ; 15 } 16 } ; 17 18 c l a s s J a p a n e s e 19 : p u b l i c P e r s o n 20 { 21 p u b l i c : 22 v i r t u a l ~ J a p a n e s e ( ) 23 { 24 s t d : : c o u t << ( " Sayonara \ n " ) ; 25 } 26 } ; 27 28 c l a s s German 29 : p u b l i c P e r s o n 30 { 31 p u b l i c : 32 v i r t u a l ~German ( ) 33 { 34 s t d : : c o u t << ( " Auf Wiedersehen \ n " ) ; 35 } 9
36 } ; 37 38 i n t main ( ) 39 { 40 P e r s o n p = new J a p a n e s e ; 41 d e l e t e p ; 42 p = new German ; 43 d e l e t e p ; 44 return 0 ; 45 } Her ser koden grei ut, men til tross for det vi skulle tro, blir det ikke skervet ut noe til skjermen. Grunnen til dette er at vi ikke har deklarert destructoren i Person som virtuell. I main funksjonen lager vi en peker til Person, men en instans av for eksempel Japanese. Siden destructoren i Person ikke er virtuell, bruker kompliatoren static linkage, det vil si at den linker direkte til Persons destruktør. Denne destruktøren skriver ikke ut noe, og det gjør heller ikke programmet. For at programmet skulle fungeret slik det sansynligvis var tenkt, må også destruktøren i Person gjøres virtuell. Da vil kompilatoren bruke dynamic linkage, og hvilken destruktør som skal kalles blir bestemt runtime, avhengig av typen til objektet det blir pekt til. 10