Programsystemkonstruktion med C++ Övning 1 Daniel Aarno bishop@kth.se
Översikt övning 1 Kompilering och länkning Makefile Preprocessordirektiv Funktioner Funktionen main() Datatyper Minneshantering Pekare och arrayer Vanliga slarvfel Referenser Statiska variabler Kommentarer
Ett första program En klass behöver både deklaration och definition Deklaration i.h (.hh) och definition i.cpp (.cc) Private är förvalt Student::learn() Student::Student() Student::~Student() Globala funktioner och variabler är OK. main() är en global funktion Macro (inleds med #) körs av preprocessorn
Ett första program (cont'd) student.h #ifndef _STUDENT_ #define _STUDENT_ #include <iostream> #include <string> using std::string; class Student { private: string knowledge; public: Student(); ~Student() {; void learn (string info); string speak(); ; #endif student.cpp #include "student.h" Student::Student() { knowledge=""; void Student::learn(string info) { knowledge += info; string Student::speak() { return knowledge;
Ett första program (cont'd) main.cpp #include "student.h" int main() { Student s; s.learn("bla bla bla"); std::cout << s.speak() << std::endl; return 0; För att köra programmet: > g++ o ex1 main.cpp student.cpp >./ex1 bla bla bla
Kompilator och länkare
Kompilator och länkare (cont'd) Filer kan kompileras var för sig till objektfiler (.o) > g++ c file.cpp > g++ c main.cpp > ls... file.o main.o Flera objektfiler kan länkas samman till ett program (endast om main() är definerad) > g++ o ex1 main.o file.o... > g++ o ex1_debug g O1 main.o file.o debug optimeringsnivå 1
Beroenden (dependencies) Om vi ändrar main.cpp räcker det att kompilera om den Men om vi ändrar i student.cpp/h måste vi eventuellt kompilera om både student.cpp och main.cpp (main.cpp beror på student.h) Jobbigt hålla reda på beroenden Långsamt att alltid kompilera om allt Intressanta fel om allt som behöver kompileras inte kompileras om
Makefiler #variables CC = g++ FLAGS = g I~/c++kurs/student OBJ = main.o student.o #rules ex1: $(OBJ) $(CC) $(FLAGS) o ex1 $(OBJ) #don't forget tab! main.o: main.cpp student.h $(CC) $(FLAGS) c main.cpp student.o: student.h $(CC) $(FLAGS) c student.cpp clean: rm *.o ex1
Makefiler (cont'd) #variables CC = g++ FLAGS = g I~/c++kurs/student OBJ = main.o student.o SRC = main.cpp student.cpp #rules ex1: $(OBJ) $(CC) $(FLAGS) o ex1 $(OBJ) %.o: %.cpp $(CC) $(FLAGS) c $*.cpp depend: makedepend $(FLAGS) $(SRC) clean: rm *.o ex1 http://www.gnu.org/manual/make3.79.1/html_chapter/make_toc.html
Preprocessorn #include slår ihop filer innan kompilering #ifndef... #define... #endif garanterar att en fil bara inkluderas en gång // may be useful #define MAX 10000 // dangerous #define SQR1(A) A*A // less dangerous #define SQR2(A) (A)*(A)
Funktioner En funktion Behöver inte defineras där den deklareras Behöver deklareras innan den används int foo(int x); // declaration of foo int bar(int x) { return foo(x); // call to foo int foo(int x) { return x; // definition of foo Kan överlagras int foo(int x) { // code here... int foo(float x) { // code here...
En funktion Kan ha argument med förvalt värde int foo(float f, int x = 0); //< OK int foo(float f = 0, Kan vara inline Funktioner (cont'd) int x, int y = 0); //< NOT OK inline int sqr(int x) { return x*x;
Funktioner (cont'd) Fördelar med funktionen sqr() Mer läsbar kod (samma anledning som macrot SQR) Ett ställe att ändra på Alla anrop till sqr funkar likadant Löser problemet med macrot SQR Det går slött att använda funktioner. Argument måste kopieras och programmets exekvering måste styras om.
Funktioner (cont'd) inline är användbart för korta funktioner som används ofta Overhead vid funktionsanrop är störst för små funktioner Inline gör att funktionsanropet ersätts med koden det innehåller vid kompileringstilfället För mycket inlining kan ge långsammare program och större filer För mycket inlining ger långsammare kompilering
Funktioner (cont'd) 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 således inte del av någon klass main() behöver inte deklareras
Funktioner (cont'd) main.cpp #include <iostream> int main(int argc, char* argv[]) { for(int i = 0; i < argc; i++) { std::cout << argv[i] << std::endl; > g++ main.cc o main >./main hej 12./main hej 12 >
Datatyper Inbyggda datatyper bool (true/false (1 byte)) char (1 byte) int (plattformsberoende storlek) short long float (4 byte) double (8 byte) long double void (ingenting)
Datatyper (cont'd) Egna datatyper typedef char* charptr; enum Fruit { apple, banana, orange ; struct { int x; int y; double d; ; class {int x; public: void foo(); ; union {int x, bool ok, double d ; Testa sizeof(t); för olika typer T (int, double, charptr osv)
Automatiska objekt Städas automatiskt upp när scopet är slut Snabb allokering på stacken Mindre risk för slarvfel Minneshantering void foo(student s) { //< begin scope int x; for(int i = 0; i < 5; i++) { //< begin scope int y = 2*i; x = 3 + y; //< end scope (i,y) //< end scope (x,s)
Dynamiska objekt Minneshantering Heap större än stack Arrayers längd kan bestämmas i run-time Objekt kan leva utanför scope där de skapas void foo(int nmemb) { int *xptr; // Allocate array of nmemb ints xptr = new int[nmemb]; // do stuff, then // delete memory allocated by new[] delete[] xptr;
Minneshantering CODE/TEXT Själva programkoden och konstanta variabler DATA Statiska (static) initialiserade variabler BSS Statiska icke initialiserade variabler STACK Automatiskt allokerade variabler vid körning Returvärden och returadresser 4kB på Linux, kan växa till 2MB (default)
Pekare och arrayer void foo(student *studptr) { int *ptr, i; //< ptr is ptr to int, i is int int* x, y; //< x is ptr to int, y is int ptr = &i; //< ptr now holds address //< of i (e.g. 0xbfffff24) *ptr = 5; //< assign 5 to i x = ptr; //< x now also points to i; cout << *x << endl; //< would print 5 (*studptr).learn( something ); studptr >learn( more ); ptr = new int; //< ptr now holds adr //< of an int on the heap ptr = &y; //< error no way to access //< memory on heap anymore
Pekare och arrayer (cont'd) void foo(float f) { char array[4711]; //< array of 4711 uninitialized chars int ary[] = {1,2,5; //< array of 3 initialized ints // same as const char str[] = {'h','e','j',0; char str[] = hej ; char* ptr = 0; //< char pointer to nothing const float *fptr; //< The memory pointed to by f is //< read only float * const fcptr=f; //< The pointer f is read only ptr = array; //< same as ptr = &array[0]; ptr[4] = 'x'; //< set fifth element of array to x; *(ptr + 4) = 'y'; //< same thing, but to y ptr = new char[512]; //< dynamically allocated array
Vanliga slarvfel void foo(int *p) { delete p; int* bar() { int x=1; return &x; // oops! void fun() { int* p1, p2; // p2 is normal int not pointer int *p3 = new int[2]; int *p4 = new int[2]; p3 = p4; // MEMORY LEAK! foo(p3); // destruction of p3 (and p4) int x = p4[1]; // error (segfault if LUCKY)
Referenser Alias för ett objekt Som en pekare vars adress inte får ändras Måste referera till ett objekt (inte NULL) Snabbare skicka referens än objekt void iswap(int &i1, int &i2) { int tmp = i1; i1 = i2; i2 = tmp; void main() { int i = 5; int &iref = i; int *iptr = &i; iref++; // i, iref and // *iptr is now 6
static.cpp void foo() { static int i = 5; cout << i++ << endl; int main() { foo(); foo(); foo(); >./static 5 6 7 > Statiska variabler
Funktionskommentarer förklarar användning (.h) Kommentarer /** * Send a signal to the task. * * @param signal The signal to send (i.e. SIGINT) * @return true on success, false on failiure. */ bool sigsend(int signal); Procedurkommentarer förklarar implementationen (.cpp) /* getline reads one line from stdin and stores it in * the given buffer. The newline is stored. * The given buffer must be big enough to store the * trailing \0 (i.e. it must be at least one bigger * than nmax). */ void getline (char buf[], int nmax, int *nread) { //function implementation...