Programsystemkonstruktion med C++: Övning 1 Karl Palmskog palmskog@kth.se september 2010
Programuppbyggnad Klassens uppbyggnad en C++-klass består av en deklaration och en definition deklaration vanligtvis i.h (.hh) och definition i.cpp (.cc) private är förvalt för funktioner och variabler i klasser globala funktioner och variabler är OK main är en global funktion macro (inleds med #) körs av preprocessorn innan kompilering
Programuppbyggnad Exempel på deklaration: inventory item.h #i f n d e f INVENTORY ITEM H #d e f i n e INVENTORY ITEM H #i n c l u d e <c s t d d e f > c l a s s I n v e n t o r y I t e m { p r i v a t e : double c o s t ; s i z e t u n i t s ; p u b l i c : s t a t i c const double DEF COST = 1 0 ; s t a t i c const s i z e t DEF UNITS = 1 0 0 ; I n v e n t o r y I t e m ( ) : c o s t (DEF COST ), u n i t s ( DEF UNITS ) { I n v e n t o r y I t e m ( double c, s i z e t u ) ; I n v e n t o r y I t e m ( ) { double v a l u e ( ) const ; void removeunits ( s i z e t u ) ; ; #e n d i f
Programuppbyggnad Exempel på definition: inventory item.cpp #i n c l u d e i n v e n t o r y i t e m. h I n v e n t o r y I t e m : : I n v e n t o r y I t e m ( double c, s i z e t u ) { c o s t = c < 0? 0 : c ; u n i t s = u ; double I n v e n t o r y I t e m : : v a l u e ( ) const { r e t u r n c o s t u n i t s ; void I n v e n t o r y I t e m : : removeunits ( s i z e t u ) { i f ( u > u n i t s ) { u n i t s = 0 ; e l s e { u n i t s = u ;
Programuppbyggnad Exempel på användning av klassdeklaration: main.cpp #i n c l u d e i n v e n t o r y i t e m. h #i n c l u d e <i o s t r e a m > using namespace s t d ; i n t main ( ) { I n v e n t o r y I t e m d e f a u l t I t e m ; cout << d e f a u l t I t e m. v a l u e ( ) << e n d l ; I n v e n t o r y I t e m customitem ( 2 0, 2 0 0 ) ; cout << customitem. v a l u e ( ) << e n d l ; customitem. removeunits ( 5 0 ) ; cout << customitem. v a l u e ( ) << e n d l ; customitem. removeunits ( 2 5 0 ) ; cout << customitem. v a l u e ( ) << e n d l ; r e t u r n 0 ;
Kompilering och körning av program Exempel på kompilering och körning av program Kompilering med g++ direkt till program och körning: $ g++ o i i main. cpp i n v e n t o r y i t e m. cpp $. / i i 1000 4000 3000 0 Ekvivalent med objektfiler kompilerade var för sig: $ g++ c i n v e n t o r y i t e m. cpp $ g++ c main. cpp $ g++ o i i main. o i n v e n t o r y i t e m. o Med varningar, avlusningssymboler och optimering: $ g++ o i i d e b u g Wall g O1 main. cpp i n v e n t o r y i t e m. cpp
Kompilering och körning av program Programmets väg till exekvering Källkod (.c,.cpp,.h,...) Bibliotekskod (.so,.a,.dll,...) Startkod Kompilator Objektkod (.o) Länkare Exekverbar kod (.exe,...)
Kompilering och körning av program Beroendehantering om vi ändrar main.cpp räcker det att kompilera om den om vi ändrar i inventory item.cpp/h måste vi eventuellt kompilera om både inventory item.cpp och main.cpp jobbigt hålla reda på beroenden långsamt att alltid kompilera om allt intressanta fel om allt som behöver kompileras inte kompileras om en lösning: använd verktyget make
Kompilering och körning av program En enkel makefil i i : main. o i n v e n t o r y i t e m. o g++ o i i main. o i n v e n t o r y i t e m. o main. o : main. cpp i n v e n t o r y i t e m. h g++ c main. cpp i n v e n t o r y i t e m. o : i n v e n t o r y i t e m. cpp i n v e n t o r y i t e m. h g++ c i n v e n t o r y i t e m. cpp c l e a n : rm f main. o i n v e n t o r y i t e m. o i i
Kompilering och körning av program En mer avancerad makefil CC = g++ FLAGS = g I / l i b s OBJ = main. o i n v e n t o r y i t e m. o SRC = main. cpp i n v e n t o r y i t e m. cpp PROG = i i $ (PROG) : $ (OBJ) $ (CC) $ (FLAGS) o $ (PROG) $ (OBJ) %.o : %.cpp $ (CC) $ (FLAGS) c $. cpp c l e a n : rm f. o $ (PROG) depend : $ (SRC) makedepend $ ( FLAGS) $ˆ
Språkfunktionalitet Preprocessordirektiv #include slår ihop filer innan kompilering #ifndef... #define... #endif garanterar att en fil bara inkluderas en gång // may be u s e f u l #d e f i n e MAX 10000 // dangerous #d e f i n e SQR1(A) A A // l e s s dangerous #d e f i n e SQR2(A) (A) (A)
Språkfunktionalitet Funktionen main funktionen main är speciell kan inte heta något annat argc är antalet argument inkl. programmets namn argv är en array av C-strängar med argumenten som gavs till programmet argv[0] är programmets namn (tex /usr/bin/zip eller zip) main är en global funktion och inte del av någon klass main behöver inte deklareras
Språkfunktionalitet Pekare void f o o ( I n v e n t o r y I t e m i t ) { i n t ptr, i ; // p t r i s p t r to i n t, i i s i n t i n t x, y ; // x i s p t r to i n t, y i s i n t p t r = &i ; // p t r now h o l d s a d d r e s s o f i ( e. g. 0 x b f f f f f 2 4 ) p t r = 5 ; // a s s i g n 5 to i x = p t r ; // x now a l s o p o i n t s to i cout << x << e n d l ; // would p r i n t 5 ( i t ). removeunits ( 1 0 ) ; i t >removeunits ( 1 0 ) ; p t r = new i n t ; // p t r now h o l d s adr o f i n t on heap p t r = &y ; // no way to a c c e s s memory anymore
Språkfunktionalitet Pekare och arrayer void f o o ( f l o a t f ) { char a r r a y [ 4 7 1 1 ] ; // a r r a y o f 4711 u n i n i t i a l i z e d c h a r s i n t a r y [ ] = { 1, 2, 5 ; // a r r a y o f 3 i n i t i a l i z e d i n t s // same as c o n s t c h a r s t r [ ] = { h, e, j, 0 ; char s t r [ ] = h e j ; char p t r = 0 ; // c h a r p o i n t e r to n o t h i n g const f l o a t f P t r ; // memory p o i n t e d to i s r e a d o n l y f l o a t const f c P t r=f ; // p o i n t e r f i s r e a d o n l y p t r = a r r a y ; // same as p t r = &a r r a y [ 0 ] ; p t r [ 4 ] = x ; // s e t f i f t h element o f a r r a y to x ( p t r + 4) = y ; // same t h i n g, but to y p t r = new char [ 5 1 2 ] ; // d y n a m i c a l l y a l l o c a t e d a r r a y
Språkfunktionalitet Referenser alias för ett objekt som en pekare vars adress inte får ändras måste referera till ett objekt (inte NULL) void iswap ( i n t &i1, i n t &i 2 ) { i n t tmp = i 1 ; i 1 = i 2 ; i 2 = tmp ; void main ( ) { i n t i = 5 ; i n t &i R e f = i ; i n t i P t r = &i ; i R e f ++; // i, i R e f and i P t r i s now 6
Vanliga problem Vanliga slarvfel void f o o ( i n t p ) { d e l e t e p ; i n t bar ( ) { i n t x = 1 ; r e t u r n &x ; // oops! void fun ( ) { i n t p1, p2 ; // p2 i s normal i n t not p o i n t e r i n t p3 = new i n t [ 2 ] ; i n t p4 = new i n t [ 2 ] ; p3 = p4 ; // MEMORY LEAK! f o o ( p3 ) ; // d e s t r u c t i o n o f p3 ( and p4 ) i n t x = p4 [ 1 ] ; // e r r o r ( s e g f a u l t i f LUCKY)
Vanliga problem Vanliga fel vid kompilering anrop till funktioner vars headerfil inte inkluderats med include, för att använda funktionen cout måste man t ex ha #include <iostream> anrop till funktioner i namnrymden std utan att ha deklarerat using namespace std, anrop till cout måste då skrivas std::cout alla objektfiler har inte kompilerats om efter koduppdatering användning av C-kompilatorn gcc istället för g++ funktionerna som används har inte deklarerats som public (private är förvalt) klassdeklarationerna avslutas inte med semikolon
Testning Testning testning är processen att prova vissa av exekveringarna av ett system enligt ett givet kriterie. enhetstestning är processen att testa de minsta beståndsdelarna av ett system (funktioner och klasser) i en isolerad miljö. Några anledningar till att testa: evidens att koden gör vad den ska möjliggör omstrukturering med bibehållen funktionalitet snabbare lokalisering av introducerade buggar Testa tidigt. Testa ofta. Testa automatiskt.
Testning Testramverket cxxtest testramverk för C++ som bara kräver kompilator och Python tester skrivs i separat fil körbar kod genereras via skript tester organiseras i testsviter (test suites) i testfunktioner görs påstående med macron som TS ASSERT och TS ASSERT EQUALS mer information på http://cxxtest.tigris.org
Testning Exempel på enhetstester: inventory item test.cpp #i n c l u d e <c x x t e s t / T e s t S u i t e. h> #i n c l u d e i n v e n t o r y i t e m. h c l a s s I n v e n t o r y I t e m T e s t : p u b l i c CxxTest : : T e s t S u i t e { p u b l i c : void t e s t d e f a u l t c o s t a n d u n i t s ( void ) { I n v e n t o r y I t e m i t ; TS ASSERT EQUALS( i t. v a l u e ( ), I n v e n t o r y I t e m : : DEF COST I n v e n t o r y I t e m : : DEF UNITS ) ; void t e s t n e g a t i v e c o s t ( void ) { I n v e n t o r y I t e m i t ( 10, 1 0 0 ) ; TS ASSERT EQUALS( i t. v a l u e ( ), 0 ) ; ;
Testning Makefil för cxxtest CC = g++ FLAGS = g OBJ = i n v e n t o r y i t e m. o PROG = t e s t r u n n e r t e s t r u n n e r. o $ (PROG) : $ (OBJ) $ (CC) $ (FLAGS) o $ (PROG) $ (OBJ) %.o : %.cpp $ (CC) $ (FLAGS) c $. cpp t e s t r u n n e r. cpp : i n v e n t o r y i t e m t e s t. cpp i n v e n t o r y i t e m. h c x x t e s t g e n. py e r r o r p r i n t e r o t e s t r u n n e r. cpp \ i n v e n t o r y i t e m t e s t. cpp c l e a n : rm f. o $ (PROG) t e s t r u n n e r. cpp
Testning Testkörningar $ make g++ g c i n v e n t o r y i t e m. cpp c x x t e s t g e n. py e r r o r p r i n t e r o t e s t r u n n e r. cpp \ i n v e n t o r y i t e m t e s t. cpp g++ g c t e s t r u n n e r. cpp g++ g o t e s t r u n n e r i n v e n t o r y i t e m. o t e s t r u n n e r. o $. / t e s t r u n n e r Running 2 t e s t s.. OK! Om cost kan anta negativa värden: $. / t e s t r u n n e r Running 2 t e s t s. I n I n v e n t o r y I t e m T e s t : : t e s t n e g a t i v e c o s t : i n v e n t o r y i t e m t e s t. cpp : 1 5 : E r r o r : Expected ( i t. v a l u e ( ) == 0 ), found ( 1000.0000!= 0) F a i l e d 1 o f 2 t e s t s S u c c e s s r a t e : 50%