9. Funktionen II Wiederholung Parameterübergabe Funktionsparameter Statische Variablen Inline Funktionen Überladen von Funktionen Templates in Funktionen Rekursion in Funktionen Testen von Funktionen Grundlagen der Informatik (Alex Rempel) 1
Wiederholung Parameterübergabe Wertübergabe (by value) type funcname(type param); 'type param' ist eine neue Variable in der Funktion neuer Speicherplatz wird angelegt Wert des Aufrufparameters wird in neue Variable kopiert Referenzübergabe (by reference) type funcname(type& param); 'type& param' ist keine neue Variable in der Funktion kein neuer Speicherplatz wird angelegt 'param' ist ein neuer Name desselben Aufrufparameters Grundlagen der Informatik (Alex Rempel) 2
Wiederholung Parameterübergabe #include <string> // call by value, 'str' is new variable with value copy void writestringbyvalue(string str) { str = "Text from writestringbyvalue(string str)"; // call by reference, 'str' is just another name, no new variable void writestringbyreference(string& str) { str = "Text from writestringbyreference(string str)"; // main function string strmain1 = "Text from main()"; string strmain2 = "Text from main()"; cout << "...before function calls:" << endl << strmain1 << endl << strmain2 << endl; writestringbyvalue(strmain1);...before function calls: Text from main() Text from main()...after function calls: Text from main() Text from writestringbyreference(string& str) // value from strmain1 is copied writestringbyreference(strmain2); // strmain2 itself is taken to function cout << "...after function calls:" << endl << strmain1 << endl << strmain2 << endl; Grundlagen der Informatik (Alex Rempel) 3
Array als Parameter Funktionsparameter Array als Funktionsparameter immer Referenzübergabe Wegen C als Ursprung von C++ sind Arrays Sonderfälle Es wird kein & verwendet, sondern die Adresse des ersten Elements im Array wird implizit übergeben Funktion hat also nicht die lokale Kopie, sondern das Original Funktion kennt aber die Größe des Arrays nicht Array darf nicht als Rückgabewert angegeben werden Darf kein type[] sein Implizite Referenzübergabe type name(type param[]); Arraygröße unbekannt d.h. sizeof(param) funktioniert nicht Grundlagen der Informatik (Alex Rempel) 4
Funktionsparameter Beispiel: Vektor skalieren sizeof(svec) in der Funktion würde 4 Bytes ergeben (Größe der Adresse) #include <iomanip> 3-2 34-6 4-68 void scalevector(short svec[], int ilen, short sscalar) { for (int i=0; i < ilen; ++i) svec[i] *= sscalar; short svector[] = {3,-2,34; int ilen = sizeof(svector)/sizeof(short); for (int i=0; i < ilen; ++i) cout << setw(5) << svector[i]; scalevector(svector, ilen, -2); cout << endl; for (int i=0; i < ilen; ++i) cout << setw(5) << svector[i]; cout << endl; Grundlagen der Informatik (Alex Rempel) 5
Funktionsparameter Mehrdimensionale Arrays als Parameter Bei mehrdimensionalen Arrays darf im formalen Parameter nur die Größe der ersten Dimension undefiniert bleiben, alle weiteren müssen fest vorgegeben werden #include <iomanip> 1 4 2 5 3 6 void printmatrix(short smx[][3], int ilen) { for (int i=0; i < 3; ++i) { for (int j=0; j < ilen; ++j) cout << setw(2) << smx[j][i]; cout << endl; cout << endl; short smatrix[2][3] = {{1,2,3,{4,5,6; printmatrix(smatrix, 2); Dadurch wird die direkte Übergabe von mehrdimensionalen Arrays in Funktionen sehr unkomfortabel Grundlagen der Informatik (Alex Rempel) 6
Funktionsparameter Konstante Parameter type name(const type param); Referenzübergabe ist effizient: Speicher wird gespart (keine neue Variable) Performance wird gespart (nur eine Adresse wird kopiert) Wenn Referenzübergabe nur aus Effizienz benutzt wird, muss der Parameter vor Änderungen geschützt sein Dazu verwendet man den const Modifikator Compiler kontrolliert beim Übersetzen, dass der Parameter in der Funktion nicht verändert wird (read-only Parameter) Grundlagen der Informatik (Alex Rempel) 7
Funktionsparameter Beispiel: Summe im Array berechnen Werte aus dem Array müssen also nur gelesen werden #include <iomanip> z.b. wäre jetzt uiarr[0] = 3; verboten const kann auch bei Wertübergabe genutzt werden 19 2 55 12 5 Sum = 93 unsigned long getsum(const unsigned int uiarr[], const int ilen) { unsigned long ulresult = 0; for (int i=0; i < ilen; ++i) ulresult += uiarr[i]; return ulresult; Für Länge des Arrays wurde keine Referenzübergabe genutzt, um auch einen Ausdruck übergeben zu können unsigned int uinumbers[] = {19,2,55,12,5; unsigned long ulsum = getsum( uinumbers, sizeof(uinumbers)/sizeof(unsigned int) ); for (int i=0; i < sizeof(uinumbers)/sizeof(unsigned int); ++i) cout << uinumbers[i] << " "; cout << endl << "Sum = " << ulsum << endl; Grundlagen der Informatik (Alex Rempel) 8
Funktionsparameter Variable Parameteranzahl durch Defaultwerte Formale Parameter der Funktion können mit Defaultwerten versehen werden Funktionen können dann mit weniger Parametern aufgerufen werden als sie haben Die fehlenden Parameter bekommen automatisch Defaultwerte Alte Funktionen können so erweitert werden, ohne dass alle alten Funktionsaufrufe geändert werden müssen Normaler Aufruf name(param); Alternativer Aufruf name(); type name(type param = value); Grundlagen der Informatik (Alex Rempel) 9
Funktionsparameter Beispiel: Kreisfläche mit variabler Pi Genauigkeit #include <iomanip> R=5 Area = 78.5 Area = 78.5398 double getcirculararea(const double dradius, const double dpi = 3.14) { return dpi*dradius*dradius; double dradius = 5.0; cout << "R=" << dradius << endl; cout << "Area = " << getcirculararea(dradius) << endl; cout << "Area = " << getcirculararea(dradius, 3.14159265) << endl; Grundlagen der Informatik (Alex Rempel) 10
Statische Variablen static Variablen in Funktionen Statische Variablen innerhalb einer Funktion... Werden beim ersten Aufruf initialisiert Bleiben nach Funktionsaufruf erhalten Der Wert nach dem letzten Aufruf bleibt ebenfalls erhalten Dadurch erhält die Funktion "Gedächtnisvariablen" Statt globaler Variablen sollten statische verwendet werden Grundlagen der Informatik (Alex Rempel) 11
Funktionsparameter Beispiel: Tausch von Elementen im Array #include <iomanip> void swapelements(int iarr[], int ifirst, int isecond) { static int iswaps = 0; int itemp = iarr[ifirst]; iarr[ifirst] = iarr[isecond]; iarr[isecond] = itemp; ++iswaps; cout << "Swaps until now: " << iswaps << endl; 8 9 4 2 3 6 Swaps until now: 1 Swaps until now: 2 Swaps until now: 3 Swaps until now: 4 8 4 2 3 6 9 int inumbers[] = {8,9,4,2,3,6; int ilen = sizeof(inumbers)/sizeof(int); for (int i=0; i < ilen; ++i) cout << inumbers[i] << " "; cout << endl; for (int i=0; i < ilen-1; ++i) if (inumbers[i] > inumbers[i+1]) swapelements(inumbers, i, i+1); for (int i=0; i < ilen; ++i) cout << inumbers[i] << " "; cout << endl; Grundlagen der Informatik (Alex Rempel) 12
inline Funktionen Inline Funktionen Manche Funktionen sind so klein, dass der Aufwand beim Kopieren der Parameter größer ist als der Aufwand in der Funktion selbst Trotzdem ist es besser, den Code in Funktionen zu kapseln Bei einem inline Modifikator kopiert der Compiler beim Übersetzen den ganzen Funktionsblock an die Aufrufstelle inline double quad(double d) { return d*d; quad(3.14) = 9.8596 cout << "quad(3.14) = " << quad(3.14) << endl; Grundlagen der Informatik (Alex Rempel) 13
Überladen von Funktionen Überladen (engl. overloading) Funktionen können überladen werden, um mit dem gleichen Funktionsnamen gleichartige Operationen mit unterschiedlichen Datentypen durchzuführen Es werden eigentlich mehrere Funktionen erstellt, welche zwar den gleichen Funktionsnamen haben, aber unterschiedliche Parametertypen/Parameterreihenfolge Signatur in C++: Funktionsname, Parameterreihenfolge/-typen int getmax(int i1, int i2); float getmax(float f1, float f2); Signaturen getmax(int, int); getmax(float, float); Grundlagen der Informatik (Alex Rempel) 14
Überladen von Funktionen Beispiel: Maximum bestimmen Der Aufruf bleibt gleich, korrekte Funktion wird automatisch erkannt #include <string> int getmax(int a, int b) { return (a > b)? a : b; ; //long getmax(int a, int b) { return (a > b)? a : b; ; double getmax(double a, double b) { return (a > b)? a : b; ; string getmax(string a, string b) { return (a > b)? a : b; ; cout << "max(4,-5) = " << getmax(4,-5) << endl; cout << "max(0.4,0.8) = " << getmax(0.4,0.8) << endl; cout << "max(\"abc\",\"abd\") = " << getmax("abc","abd") << endl; max(4-5) = 4 max(0.4,0.8) = 0.8 max( abs, abd ) = abd Unterschiedlicher Rückgabewert reicht nicht aus Grundlagen der Informatik (Alex Rempel) 15
Überladen von Funktionen Beispiel: Maximum bestimmen Der Aufruf bleibt gleich, korrekte Funktion wird automatisch erkannt Wenn der Code in der Funktion gleich abläuft, kann explizite Typumwandlung genutzt werden, um Code zu sparen #include <string> double getmax(double a, double b) { return (a > b)? a : b; ; string getmax(string a, string b) { return (a > b)? a : b; ; int getmax(int a, int b) { return (int)getmax((double)a,(double)b); ; cout << "max(4,-5) = " << getmax(4,-5) << endl; cout << "max(0.4,0.8) = " << getmax(0.4,0.8) << endl; cout << "max(\"abc\",\"abd\") = " << getmax("abc","abd") << endl; max(4-5) = 4 max(0.4,0.8) = 0.8 max( abs, abd ) = abd Grundlagen der Informatik (Alex Rempel) 16
Templates in Funktionen Funktionen in C++ können auch typlose Parameter haben, wenn Templates verwendet werden (generisches Programmieren) So kann ein einzelner Funktionsblock für unterschiedliche Typen erstellt werden Compiler erstellt beim Übersetzen automatisch überladene Funktionen für jede Typvariation, die im Programm auf das Funktionstemplate aufgerufen wurde template <class T> T getsum(t param); template <class T> type getsum(t param); template <class T> T getsum(type param);... Grundlagen der Informatik (Alex Rempel) 17
Templates in Funktionen Beispiel: Summe aller Werte im Array berechnen template <class LOLCAT> LOLCAT getsum(lolcat arr[], int ilen) { LOLCAT sum = 0; for (int i=0; i < ilen; ++i) sum = sum + arr[i]; return sum; short sarray[] = {3, 6, 22, 2, 7, 9; int iarray[] = {45, 326, 7, 5; double darray[] = {0.45, 123.4, 0.0; sum(short) = 49 sum(int) = 383 sum(double) = 123.85 Beim Übersetzen merkt der Compiler, dass die Funktion mit short, int und double aufgerufen wird und erstellt automatisch 3 Versionen der Funktion, jeweils mit dem Typ statt LOLCAT cout << "sum(short) = " << getsum(sarray,sizeof(sarray)/sizeof(short)) << endl << "sum(int) = " << getsum(iarray,sizeof(iarray)/sizeof(int)) << endl << "sum(double) = " << getsum(darray,sizeof(darray)/sizeof(double)) << endl; Grundlagen der Informatik (Alex Rempel) 18
Rekursion in Funktionen Rekursionen in C++ Direkte Rekursion Funktion A ruft selbst Funktion A nochmal auf Indirekte Rekursion Funktion A ruft Funktion B auf, welche Funktion A aufruft Rekursionen werden verwendet, wenn ein rekursiver Algorithmus (z.b. Fakultät) oder eine rekursive Datenstruktur realisiert werden sollen Grundlagen der Informatik (Alex Rempel) 19
Rekursion in Funktionen Beispiel: Bogen zeichnen Abbruchbedingung Rekursion void printbow(int isize, int iblanks = 0) { if (isize < 0) return; for (int i=0; i < iblanks; ++i) cout << " "; cout << isize << endl; printbow(isize-1,iblanks+1); // recursion for (int i=0; i < iblanks; ++i) cout << " "; cout << isize << endl; printbow(5); 5 4 3 2 1 0 0 1 2 3 4 5 Grundlagen der Informatik (Alex Rempel) 20
Rekursion in Funktionen Beispiel: Bogen zeichnen printbow(5,0) printbow(4,1) printbow(3,2) printbow(2,3) printbow(1,4) printbow(0,5) printbow(-1,6) void printbow(int isize, int iblanks = 0) { if (isize < 0) return; for (int i=0; i < iblanks; ++i) cout << " "; cout << isize << endl; printbow(isize-1,iblanks+1); // recursion for (int i=0; i < iblanks; ++i) cout << " "; cout << isize << endl; printbow(5); 5 4 3 2 1 0 0 1 2 3 4 5 Grundlagen der Informatik (Alex Rempel) 21
Rekursion in Funktionen Beispiel: Berechnung Fakultät Algorithmus: fac(0)=fac(1)=1, fac(n)=n*fac(n-1) factorial(5) = 120 factorial(13) = 1932053504 unsigned long factorial(unsigned int uinumber) { if (uinumber == 0 uinumber == 1) return 1; return uinumber * factorial(uinumber-1); cout << "factorial(5) = " << factorial(5) << endl; cout << "factorial(13) = " << factorial(13) << endl; Grundlagen der Informatik (Alex Rempel) 22
Top Down Grundlagen der Informatik Testen von Funktionen main() wird bereits programmiert und soll getestet werden, Funktionen stehen aber noch nicht zur Verfügung Lösung: "Stubs" erstellen, welche die Schnittstellen der Funktionen (Deklarationen) implementieren, aber das Verhalten nur simulieren Beispiel: "int factorial(short)" gibt erstmal immer 1 zurück Vorteil: Vorführung und Einbeziehung der Anwender sehr früh Nachteil: Funktionen sind noch nicht universell einsetzbar Grundlagen der Informatik (Alex Rempel) 23
Bottom Up Grundlagen der Informatik Testen von Funktionen Funktion wird bereits entwickelt, wird aber noch nirgendwo aufgerufen Lösung: "Treiber" erstellen, welche die Funktion testweise aufrufen Beispiel: "int factorial(short)" gibt es bereits Treiber wird realisiert, welcher die Funktion mit unterschiedlichen Zahlen aufruft und Ergebnisse zeigt Vorteil: universeller ausführlicher Funktionstest Nachteil: spätes Erkennen von Entwurfsfehlern Grundlagen der Informatik (Alex Rempel) 24
Testen von Funktionen Testplan für Funktion Eingabedaten sind immer Parametervariationen Vorteil: keine unzulässigen Eingaben wie char statt int Beispiel: "double calculatesquareroot(double d);" Fall Beschreibung d Ergebnis 1 Normalfall 2.0 1.4142 2 Sonderfall 1 1.0 1.0 3 Sonderfall 0 0.0 0.0 4 Fehlerfall -1.0 0.0 (?) Grundlagen der Informatik (Alex Rempel) 25