Ein paar Kleinigkeiten zum Einstieg Includes und Namensräume Ausgabe in in C++ Ausgabeformatierung mit mit Manipulatoren Eingabe in in C++ Defaultargumente in in C++ Überladene Funktionen Typisierte Konstanten Inlinefunktionen Referenzen 1
Defaultargumente Jede Funktion muss in in C++ vor vor ihrer Verwendung bekannt (definiert oder deklariert sein) Parametern können im im Funktionskopf Werte zugeordnet werden, sogenannte Defaultargumente. Dies muss aber unbedingt von rechts nach links geschehen. 2
Defaultargumente Beim Funktionsaufruf können nun Parameter von rechts beginnend weggelasssen werden. Es Es ergeben sich dadurch unterschiedliche Aufrufmöglichkeiten für für eine Funktion. Gibt es es zu zu einer Funktion eine Deklaration (Funktionsprototyp) und die die Funktionsdefinition, so so dürfen die die Defaultargumente nur nur im im Prototyp angegeben werden. 3
#include #include <iostream> using using namespace std; std; Defaultargumente Defaultargument void void myfunc(int i=5, i=5, double double d=1.234 d=1.234 ) { cout<< cout<< i<<'\n' i<<'\n' << << d << << '\n'; '\n'; } Defaultargument int int main() main() { myfunc(10,999.99); myfunc(10); myfunc(); return return 0; 0; } 2. 2. Parameter weggelassen - wird durch Defaultargument ersetzt! beide Parameter weggelassen - durch Defaultargumente ersetzt! 4
Defaultargumente 10 10 999.99 999.99 10 10 1.234 1.234 5 1.234 1.234 10 10 und und 999.99 sind sind Ergebnis des des ersten ersten Aufrufs mit mit 2 Parametern myfunc(10,999.99); 10 10 und und 1.234 1.234 sind sind Ergebnis des des zweiten Aufrufs mit mit 1 Parameter myfunc(10); 5 und und 1.234 1.234 sind sind Ergebnis des des dritten dritten Aufrufs mit mit ohne ohne Parametern myfunc(10,999.99); Aber: Weglassen von von Aufrufparametern nur nur von von rechts nach links! 5
Funktionsüberladung Wichtiger Begriff merken: Überladene Funktionen oder Funktionsüberladung In In objektorientierten Sprachen, nicht nur nur in in c++ kann man mehrere Funktionen mit gleichem Namen aber verschiedenen Parameterlisten definieren, bzw. deklarieren. Man spricht von überladenen Funktionen 6
Funktionsüberladung In In c konnte in in einem Gültigkeitsbereich immer nur nur eine Funktion mit mit einem Funktionsnamen vereinbart werden. In In c++ gilt gilt das nicht mehr. Intern werden dem Funktionsnamen Kürzel für für die die einzelnen Parameter angefügt, so so dass sich wieder ein ein eindeutiger Name am am Ende ergibt. int int calc(int,int) könnte damit etwas vereinfacht unter unter dem Namen calc_ii geführt werden. 7
Funktionsüberladung Beispiel 1 #include #include <iostream> <iostream> #include #include <cstdlib> <cstdlib> #include #include <cmath> <cmath> using using namespace namespace std; std; int int myabs myabs (int (int z) z) {return {return abs(z);} abs(z);} long long myabs myabs (long (long z) z) {return {return labs(z);} labs(z);} double double myabs myabs (double (double z){return z){return fabs(z);} fabs(z);} int int main() main() { double double d= 123.5; d= 123.5; cout cout << << "Double:" "Double:" <<myabs(d)<<endl; <<myabs(d)<<endl; } Aufruf von myabs mit Parameter double 8
Funktionsüberladung Im Im Beispiel gibt es es drei Funktionen myabs mit mit je je einem Parameter unterschiedlichen Typs. Grundbedingung bei bei Funktionsüberladung ist, ist, dass die die Parameterlisten der der gleichnamigen Funktionen in in Typ und/oder Anzahl der der Parameter voneinander verschieden sind. Auf Grund der der Aufrufparameter findet der der Compiler die die richtige Funktion. 9
Funktionsüberladung Beispiel 2 #include #include <iostream> <iostream> #include #include <cstdlib> <cstdlib> #include #include <cmath> <cmath> using using namespace namespace std; std; Aufruf der Standardfunktion std::abs Kein rekursiver Aufruf!!! namespace namespace beck beck { int int abs abs (int (int z) z) {return {return abs(z);} abs(z);} long long abs abs (long (long z) z) {return {return labs(z);} labs(z);} double double abs abs (double (double z){return z){return fabs(z);} fabs(z);} } int int main() main() { double double d= 123.5; d= 123.5; using using namespace namespace beck; beck; cout cout << << "Double:" "Double:" <<beck::abs(d)<<endl; <<beck::abs(d)<<endl; } 10
Funktionsüberladung Da Da es es in in diesem Beispiel den Namensraum beck gibt, dürfen unsere Funktionen den Namen abs tragen, den sonst die die Standardfunktion hat. Die Standardfunktion abs können wir wir dennoch verwenden. Wir müssen nun nur nur den Namen abs unserer Funktionen voll qualifiziert verwenden beck::abs Nachfolgendes Beispiel dreht den Spieß um 11
Funktionsüberladung Beispiel 3 #include <iostream> #include <cstdlib> #include <cmath> namespace beck { int abs (int z) {return std::abs(z);} long abs (long z) {return std::labs(z);} double abs (double z){return std::fabs(z);} } using namespace beck; int main() { double d= 123.5; using namespace beck; std::cout << "Double:" <<abs(d)<<std::endl; } 12
Funktionsüberladung An An dem Beispiel hat hat sich in in puncto Überladung nichts geändert. Wir benutzen hier nicht using namespace std; statt dessen benutzen wir using namespace beck; In In der Folge müssen wir alle Elemente von std voll qualifiziert ansprechen, können aber unsere abs-funktionen ohne beck:: aufrufen. 13
Funktionsüberladung Bisher haben sich die Datentypen der Funktionen unterschieden um Funktionsüberladung zu zu realisieren. Im Im folgenden Beispiel unterscheiden sich die Funktionen durch die Anzahl der Parameter Wichtig: Bei der kombinierten Anwendung von Überladung und Defaultargumenten darf es es nicht zu zu Mehrdeutigen Situationen kommen, dies hätte Compilerfehler zur Folge. 14
Funktionsüberladung Beispiel 4 #include <iostream> using namespace std; int Max (int a, a, int b) b) { return (a>b?a:b); } int Max (int a, a, int b, b, int c) c) { return Max(Max(a,b),c); } int main() { cout << << Max(47,11) <<endl; cout << << Max(5, 9, 9, 2)<< endl; return 0; 0; }./a.out./a.out 47 47 9 15
Funktionsüberladung Die Funktion Max ermittelt aus zwei bzw. drei int-werten den jeweils größten. Auch hier wird aus der Parameterliste des Aufrufs die richtige Funktion ermittelt und aufgerufen. Eine Funktion int Max (int a, a, int b, b, int c=99) mit Defaultargument führt unweigerlich zum Compilerfehler!...... call call of of overloaded overloaded Max(int, Max(int, int) int) is is ambiguous ambiguous 16
Inlinefunktionen Eine Funktion kann in in c++ mit mit dem Arrtibut inline versehen werden. Das Attribut bewirkt, dass die Funktion nicht über den üblichen Funktionsaufrufmechnismus aufgerufen wird, sondern es es wir ihr ihr Code direkt in in die Aufrufstelle kopiert. Dies spart den Overhead, den sonst der Funktionsaufruf mit sich bringt. 17
Inlinefunktionen Inlinefunktionen sind mit expandierten Macros vergleichbar, nur dass hier eine vollständige Typprüfung der Parameter erfolgt. Inline sollte nur für für kleine Funktionen, die aus nur wenigen Anweisungen bestehen, verwendet werden. 18
Typisierte Konstanten C++ gestattet mit Hilfe des Attributes const, typisierte Konstanten zu zu zu zu definieren. Die Syntax entspricht der einer Variablendefinition mit Initialisierung. Typisierte Konstanten sind in in in in der Verwendung sicherer als als als als defines, wie wir wir sie sie sie sie von c her kennen. kennen weil Typprüfung möglich ist. Eine so so definierte Konstante darf überall, wo wo ein Eine konstanter so so definierte Wert Konstante erwartet darf wird, überall, stehen. wo wo ein konstanter Wert erwartet wird, stehen. 19
Typisierte Konstanten #include #include <iostream> using using namespace std; std;./a.out size of vc: 5 const const int int N=5; N=5; char char vc[n]; vc[n]; int int main() main() { cout cout << << "size "size of of vc: vc: " << << (sizeof (sizeof vc) vc) << << endl; endl; return return 0; 0; } Das geht in in c nicht 20
Typisierte Konstanten Besondere Bedeutung hat const in in c++ in in Verbindungmit Pointern. Ein Pointer kann selbst eine Konstante sein, dann kann dieser Pointer nicht geändert werden, der Wert, auf den der Pointer zeigt, ist ist aber veränderbar. Ein Pointer kann auf eine Konstante zeigen. Dann kann der Pointer modifiziert werden, der Wert, auf den der Pointer zeigt, jedoch nicht. 21
Typisierte Konstanten Schließlisch kann es es auch einen konstanten Pointer geben, der auf eine Konstante zeigt. Regel: const vor vor dem Typ eines Pointers definiert einen Pointer auf auf einen Wert, der der nicht überschrieben werden darf. const nach dem Typ eines Pointers definiert einen Pointer, der der nicht modifiziert werden kann 22
Typisierte Konstanten Die erste Variante kennen wir schon von c: c: int int strcmp(const char char *s1, *s1, const char char *s2); Hier zeigen die Pointer auf werte, die von der Funktion nicht verändert werden (können/dürfen) Die zweite Variante sieht etwa so so aus: 23
Typisierte Konstanten #include #include <iostream> <iostream> #include #include <iomanip> <iomanip> #include #include <cstring> <cstring> using using namespace namespace std; std; // // Das Das Urteil Urteil des des Herrschers: Herrschers: char char vbuf[]="haengen, vbuf[]="haengen, nicht nicht laufen laufen lassen"; lassen"; char char * const const pbuf=vbuf; pbuf=vbuf; // // Initialisierung Initialisierung ist ist zwingend zwingend int int main() main() { pbuf++; pbuf++; cout cout << << "size "size of of vbuf: vbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof vbuf) vbuf) << << " Text: Text: " << << vbuf vbuf << << endl; endl; cout cout << << "size "size of of pbuf: pbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof pbuf) pbuf) << << " Text: Text: " << << pbuf pbuf << << endl; endl; return return 0; 0; } 24
Typisierte Konstanten Hier habe wir ihn, den konstanten Pointer Er Er muss initialisert werden. Der Versuch ihn zu zu incrementieren wird mit mit einem Compilerfehler geahndet: error: increment of of read only variable pbuf Das Ändern der Daten aud die der Pointer zeigt, ist ist kein Problem, wie nachfolgendes Beispiel zeigt, dieser Umstand rettet Leben!! 25
Typisierte Konstanten Hier habe wir ihn, den konstanten Pointer Er Er muss initialisert werden. Der Versuch ihn zu zu incrementieren wird mit mit einem Compilerfehler geahndet: error: increment of of read only variable pbuf Das Ändern der Daten, auf die der Pointer zeigt, ist ist kein Problem, wie nachfolgendes Beispiel zeigt, dieser Umstand rettet Leben!! 26
Typisierte Konstanten... // // Das Das Urteil Urteil des des Herrschers Herrschers char char vbuf[]="haengen, vbuf[]="haengen, nicht nicht laufen laufen lassen"; lassen"; char char * const const pbuf=vbuf; pbuf=vbuf; int int main() main() { pbuf[ pbuf[ 7]=' 7]=' '; '; Pbuf[14]=','; Pbuf[14]=','; cout cout << << Die Die Message Message des des Boten: Boten: << << endl; endl; cout cout << << "size "size of of vbuf: vbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof vbuf) vbuf) << << " Text: Text: " << << vbuf vbuf << << endl; endl; cout cout << << "size "size of of pbuf: pbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof pbuf) pbuf) << << " Text: Text: " << << pbuf pbuf << << endl; endl; return return 0; 0; } 27
Typisierte Konstanten Das Urteil der der Herrschers: haengen, nicht laufen lassen Glücklicherweise können wir wir über unseren konstanten Pointer die die Daten ändern in in die die message des Boten:./a.out./a.out Die Die message message des des Boten: Boten: size size of of vbuf: vbuf: 30 30 Text: Text: haengen haengen nicht, nicht, laufen laufen lassen lassen size size of of pbuf: pbuf: 8 Text: Text: haengen haengen nicht, nicht, laufen laufen lassen lassen 28
Typisierte Konstanten // // Das Das Utreil Utreil des des Herrschers: Herrschers: char char vbuf[]="haengen, vbuf[]="haengen, nicht nicht laufen laufen lassen"; lassen"; // // Konstanter Konstanter Pointer Pointer auf auf konstante konstante Daten Daten const const char char * const const pbuf=vbuf; pbuf=vbuf; int int main() main() { // // pbuf[ pbuf[ 7]=' 7]=' '; '; // // bringt bringt jetzt jetzt Compilerfehler Compilerfehler // // pbuf[14]=','; pbuf[14]=','; // // assignment assignment of of read only read only location location...... cout cout << << "Die "Die message message des des Boten:" Boten:" << << endl; endl; cout cout << << "size "size of of vbuf: vbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof vbuf) vbuf) << << " Text: Text: " << << vbuf vbuf << << endl; endl; cout cout << << "size "size of of pbuf: pbuf: " << << setw(4)<<(sizeof setw(4)<<(sizeof pbuf) pbuf) << << " Text: Text: " << << pbuf pbuf << << endl; endl; return return 0; 0; 29
Typisierte Konstanten In In diesem letzten Fall ist ist es es unmöglich, die die Daten zu zu maipulieren und auch der der Pointer kann nicht verändert werden. Besondere Bedeutung haben diese Konstrukte, wenn sie sie in in Parameterlisten von Funktionen auftauchen. Merke: const vor vor dem Typ: Die Daten sind konstant const nach dem Typ: Der Pointer ist ist konstant, Es Es ist ist eine Initialisierung des Pointers erforderlich 30
C-Code in Headerfiles Einbinden von C Code C Code in in Headerfiles müssen gesondert gekennzeichnet werden. extern "C" {... } Nur so so ist ist ein Linken der zugehörigen Objektdateien/Bibliotheken möglich 31
Der Scopeoperator :: Der scopeoperator beeinflusst die Regen der Gültigkeit von Bezeichnern. Angenommen es es gibt lokal und global jeweils einen Bezeichner mit dem selben Namen, z.b. amount. Verwendet man innerhalb der der Funktion (des Blocks), in in der der amount lokal definiert ist ist diesen Namen, so so ist ist natürlich dieser lokale Bezeichner gültig. Setzt man den Scopeoperator davor, so so werden die die normalen Gültigkeitsregeln aufgehoben und es es wird der der globale an an Stelle des lokalen Bezeichners gültig. 32 tig.
Der Scopeoperator :: #include #include <iostream> using using namespace std; std; int int amount=123; int int main() main() { int int amount=456; cout<< cout<< "extern "extern Amount: Amount: "<<::amount<<endl; cout<< cout<< "local "local Amount: Amount: "<< "<< amount<<endl; return return 0; 0; }./a.out./a.out extern extern Amount123 local local Amount456 33