C++ - Funktionen und mehr Kerstin Gößner und Ralf Wondratschek Übersicht Deklaration, Definition und Initialisierung Variablen- und Konstantendeklaration Funktionsaufrufe und –rückgabewerte Technische Grundlage eines Funktionsaufrufs Headerdateien Gültigkeitsbereiche (Scopes) Namensbereiche Using-Deklaration Main-Funktion C++ - Funktionen und mehr 2 Deklaration, Definition und Initialisierung C++ - Funktionen und mehr 3 Funktion Unter einer Funktion versteht man ein Unterprogramm, das eine bestimmte Aufgabe erfüllt. Eine Funktion ist eine Ansammlung von Anweisungen, der ein Name gegeben wird und die jederzeit im Programm über diesen Namen aufgerufen und ausgeführt werden kann. Eine Funktion ist praktisch ein Unterprogramm, das Daten verarbeitet und einen Wert zurückgibt. C++ - Funktionen und mehr 4 Prozedur Was ist mit Prozeduren? Sind vorhanden, jedoch keine strenge Unterscheidung zu einer Funktion. Es gibt keine explizite Trennung. C++ - Funktionen und mehr 5 Problem?! void a() { //... b(); //... } void b() { //... c(); //... } void c() { //... a(); //... } void b() { //... c(); //... } void c() { //... a(); //... } void a() { //... b(); //... } void c() { //... a(); //... } void a() { //... b(); //... } void b() { //... c(); //... } C++ - Funktionen und mehr 6 Deklaration Eine Deklaration ist eine Anweisung, die einem Namen einen Gültigkeitsbereich einführt und einen Typ für das benannte Element und optional einen Initialisierer angibt. Variablen: <extern> [Typ][Name]<Initialisierer> ; Funktionen: [Rückgabetyp][Name] ( <Liste formaler Argumente>); C++ - Funktionen und mehr 7 Deklaration - Beispiele Variablen: int i; static int j = 0; extern double k; Funktionen: double foo(); double foo(double a, double b); double foo(const int& a); C++ - Funktionen und mehr 8 Deklaration - Fortsetzung Leicht erklärt: Eine Deklaration gibt dem Compiler an, dass es etwas gibt! In Headerdateien zu finden Schnittstellen, die an anderer Stelle definiert sind Fehlerhaftes Beispiel: int main() { cout << fib(number) << endl; } C++ - Funktionen und mehr 9 Definition Eine Definition ist eine Deklaration, die das Element vollständig spezifiziert. Sie gibt an, worauf sich ein Name konkret bezieht. Für Definitionen wird Speicher reserviert. Keine doppelten Definitionen, mehrfache konsistente Deklarationen sind dagegen möglich. Variablen: [Typ][Name]<Initialisierer> ; Funktionen: [Rückgabetyp][Name] ( <Liste formaler Argumente>) { <Anweisungen>} C++ - Funktionen und mehr 10 Initialisierung Eine Initialisierung ist implizit eine Definition. Durch eine Initialisierung wird eine Variable mit einem bestimmten Wert vorbelegt. Beispiel: int i = 5; Vorsicht: extern int i = 5; Dabei handelt es sich um eine Initialisierung, dennoch muss i an anderer Stelle definiert werden, da hier i keinen Speicher zugewiesen bekommt. C++ - Funktionen und mehr 11 Gegenüberstellung - Beispiele Deklaration: Definition: Initialisierung: extern int i; int i; int i = 5; void foo(); void foo(){ } C++ - Funktionen und mehr 12 Variablen- und Konstantendeklaration C++ - Funktionen und mehr 13 Konstantendeklaration Erweitert normale Variablendeklaration mit dem Schlüsselwort „const“. const [Typ][Name]<Initialisierer> ; Beispiel: const int i = 5; C++ - Funktionen und mehr 14 Spätes Initialisieren Möglichst vermeiden, doch manchmal unumgänglich. Warnung vor Spaghetti-Code! bool isRunning = true; //... int value; if (isRunning) { value = 9; } else { value = 7; } C++ - Funktionen und mehr 15 Standardinitialisierung Standardkonstruktor bei String, Vector Beispiel: string s = ""; == string s; Vorsicht bei eingebauten Typen, z.B. int und double: Global automatisch mit 0 initialisiert Lokal und als Klassenmember jedoch nicht C++ - Funktionen und mehr 16 Funktionsaufrufe und -rückgabewerte C++ - Funktionen und mehr 17 Funktionsaufrufe und -rückgabewerte Deklaration: [Rückgabetyp][Name] ( <Liste formaler Argumente> ); Definition: [Rückgabetyp][Name] ( <Liste formaler Argumente> ) { <Anweisungen> } C++ - Funktionen und mehr 18 Rückgabewerte Wenn eine Funktion mit einem Rückgabewert deklariert wurde, braucht der Funktionrumpf stets eine return Anweisung. Die Rückgabe muss dem Rückgabetyp entsprechen. Vorsicht bei Verschachtelungen, Rückgabe nicht vergessen! Kein Rückgabetyp mit Schlüsselwort void. C++ - Funktionen und mehr 19 Rückgabewerte - Beispiele void checkRunning(bool isRunning){ if (isRunning) { return; } else { std::cout << "is not running" << std::endl; } } C++ - Funktionen und mehr int getRandomNumber() { return 4; } 20 Ausdrucksstarke Namen „selbstkommentierend“ Konventionen einhalten A foo(double a) { //... return k; } Circle getCircle (double radius) { //... return circle; } C++ - Funktionen und mehr 21 C++ - Funktionen und mehr 22 Liste formaler Argumente auch als Parameter bezeichnet Unterschiede bei Deklaration möglich: int fibonacci(int number); int fibonacci(int); Für Compiler kein Unterschied C++ - Funktionen und mehr 23 Parameter weglassen? int find1 (string text, int value, int hint) { int position = 0; //... return position; } int find2 (string text, int value, int) { int position = 0; //... return position; } find1("15845985", 9, 5); // Aufruf korrekt find2("15845985", 9, 5); // Aufruf nicht korrekt C++ - Funktionen und mehr 24 Überladen von Funktionen int find (string text, int value) { int position = 0; //... return position; } int find (string text, int value, int hint) { int position = 0; //... return position; } find("15845985", 9); // Aufruf obere Funktion find("15845985", 9, 5); // Aufruf untere Funktion Zuordnung der Funktion anhand deren Signatur. Signatur besteht aus dem Namen und den Parametern, jedoch nicht dem Rückgabetyp! C++ - Funktionen und mehr 25 C++ - Funktionen und mehr 26 C++ - Funktionen und mehr 27 Pass-by-value Kopie der Variable wird übergeben Parameter entsprechen lokalen Variablen Einfach und effizient für kleine Werte int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 28 Pass-by-reference Übergibt Referenz statt Kopie des Objekts Wird signalisiert durch kaufmännisches Und (&) Ändern von Objekten erlaubt Sinnvoll zum Beispiel bei Initialisierungsmethoden, zum Sortieren etc. Aliase möglich int i = 5; int& k = i; i++; k++; std::cout << i << " == " << k << std::endl; //7 == 7 C++ - Funktionen und mehr 29 Pass-by-const-reference Übergibt Referenz des Objektes, jedoch wird das Objekt nie verändert Sinnvoll zum Beispiel zum Ausgeben eines Vektors void makeBinary(const int value, string& result) { if (value == 1) { result.insert(0, "1"); } else { if (value % 2 == 1) { result.insert(0, "1"); } else { result.insert(0, "0"); } makeBinary(value / 2, result); } } C++ - Funktionen und mehr 30 Was verwenden? 1.) Pass-by-value bei nur sehr kleinen Objekten verwenden. 2.) Stets pass-by-const-reference für große Objekte, die nicht verändert werden müssen, verwenden 3.) Lieber Ergebnisse zurückgeben statt Referenzobjekte zu verändern. 4.) Pass-by-reference nur im Notfall verwenden. C++ - Funktionen und mehr 31 Argumentüberprüfung und -umwandlung Übergeben eines Arguments bedeutet implizit Initialisieren der Parameter mit den übergebenen Argumenten. Typumwandlungen sind möglich, aber mit Vorsicht zu genießen! bool isPositive (const int value) { return value >= 0; } int main() { cout << isPositive(-0.9) << endl; //1 } C++ - Funktionen und mehr 32 Technische Grundlage eines Funktionsaufrufs C++ - Funktionen und mehr 33 Implementierung von Funktionsaufrufen Bei einem Funktionsaufruf wird eine Datenstruktur eingerichtet. Function activation record beinhaltet: Parameter / lokale Variablen Implementierungsdetails - Kosten für Aktivierung nicht von Größe abhängig. - Datensätze voneinander unabhängig (sonst Probleme bei Rekursion) - Stack wächst und schrumpft nach dem LIFO Prinzip Beispiel Auruf von: fib(3); C++ - Funktionen und mehr 34 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 35 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 2 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 36 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 2 ______________________ Implementierungsdetails value := 1 ______________________ int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } Implementierungsdetails C++ - Funktionen und mehr 37 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 2 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 38 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 2 ______________________ Implementierungsdetails value := 0 ______________________ int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } Implementierungsdetails C++ - Funktionen und mehr 39 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 2 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 40 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 41 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails value := 1 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 42 Implementierung von Funktionsaufrufen value := 3 ______________________ Implementierungsdetails int fib(int value) { if (value == 0) { return 0; } else if (value == 1) { return 1; } else { return fib(value - 1) + fib(value - 2); } } C++ - Funktionen und mehr 43 Headerdateien C++ - Funktionen und mehr 44 Headerdateien Wichtiges Mittel zur Verwaltung von Deklarationen und Definitionen Benutzung von „fremden“ Implementierungen wie sqrt() oder cout möglich Arbeitsersparnis Übernehmen von bereits implementierten Elementen C++ - Funktionen und mehr 45 Was genau ist ein Header? Ein Header ist eine Datei mit einer Zusammenfassung vieler Deklarationen. Endung: [fileName].h Zugehörige Definitionen befinden sich in anderen Dateien mit .cpp Endung. Einfügen von Headerdateien in den Quelltext mit Hilfe von #include Beispiel: #include <stdlib.h> C++ - Funktionen und mehr 46 #include Eine #include [fileName.h] Anweisung entspricht dem Kopieren von Textzeilen aus der Datei fileName.h. Bearbeitung von #include Zeilen vor allen anderen Schritten ist Zwang sog. Präprozessor-Direktive Einfügen von #include in Dateien mit Definitionen und Dateien mit Verwendung der Deklarationen. C++ - Funktionen und mehr 47 #include - Beispiel std_lib_facilities.h enthält Deklarationen von Elementen der Standardbibliothek (cout, vector, sqrt(), …) #include "std_lib_facilities.h"; using namespace std; int main() { double a = sqrt(4); cout << a; } //Ausabe: 2 using namespace std; int main() { double a = sqrt(4); cout << a; } //error C3861: "sqrt": Bezeichner wurde nicht gefunden //error C2065: 'cout': nichtdeklarierter Bezeichner C++ - Funktionen und mehr 48 Gültigkeitsbereiche (Scopes) C++ - Funktionen und mehr 49 Gültigkeitsbereiche (scopes) Scope = bestimmter Abschnitt in Programmcode Wurden Namen im Scope deklariert, sind diese bis Ende des Scopes gültig („in scope“). Namen aus einem Gültigkeitsbereich sind auch in seinen eingeschlossenen Scopes sichtbar. Globaler Gültigkeitsbereich steht über allen anderen C++ - Funktionen und mehr 50 Gültigkeitsbereiche - Beispiel #include <iostream> using namespace std; void g() { int x = 4; int y = x * x; cout << y << " "; } void h() { int y = 0; cout << y << " "; } //g(): y ist nur in g() gültig (genauso für h()) int main() { g(); h(); } //Ausgabe: 16 0 C++ - Funktionen und mehr 51 Arten von Scopes 1. Globaler Gültigkeitsbereich liegt außerhalb jedes anderen Scopes 2. Namensbereich 3. Klassenbereich 4. Lokaler Gültigkeitsbereich 5. Anweisungsbereich C++ - Funktionen und mehr 52 Namensbereich explizit benannter Scope liegt innerhalb des globalen Scopes oder anderem Namensbereich namespace Namespace1 { namespace Namespace2 { void f() { cout << "Hallo!"; } } } void main() { f(); } // error C3861: "f": Bezeichner wurde nicht gefunden. C++ - Funktionen und mehr 53 Namensbereich Richtiger Aufruf eines im Namensbereich definierten Elementes namespace Namespace1 { namespace Namespace2 { void f() { cout << "Hallo!"; } } } void main() { Namespace1::Namespace2::f(); } //Ausgabe: Hallo! C++ - Funktionen und mehr 54 Klassenbereich Gültigkeit nur innerhalb einer Klasse class MyClass1 { public: void f() { cout << "Hallo!"; } }; class MyClass2 { public: void f() { cout << "Hallo!"; } }; //beide Methosen können den gleichen Namen haben, da //sie in unterschiedlichen Klassen definiert sind C++ - Funktionen und mehr 55 Lokaler Gültigkeitsbereich Gültig in Bereich zwischen {}-Klammern eines Blocks oder einer Funktion void main() { bool a = true; if (a) { int x = 4; cout << x; } else { int x = 5; cout << x; } x = x + 1; cout << x; } // error C2065: 'x': nichtdeklarierter Bezeichner C++ - Funktionen und mehr 56 Lokaler Gültigkeitsbereich Beheben des Problems void main() { int x; bool a = true; if (a) { x = 4; cout << x << '\t‘; } else { x = 5; cout << x << '\t‘; } x = x+1; cout << x << '\t‘; } //Ausgabe: 4 5 C++ - Funktionen und mehr 57 Anweisungsbereich Deklaration im Kopf der for-Schleife Gültig nur innerhalb der for-Schleife for (int i = 1; i < 10; i++) { cout << "i: " << i << " \t"; } i = 4; // error C2065: 'i': nichtdeklarierter Bezeichner C++ - Funktionen und mehr 58 Aufgabe von Scopes Namen lokal halten, damit Kollisionen vermieden werden. void f() { int x = 4; cout << x << "\t"; } void g() { int x = 4; ++x; cout << x << "\t"; f(); } //lokale Deklaration //Ausgabe: 4 5 4 int x = 4; void f() { cout << x << "\t"; } void g() { ++x; cout << x << "\t"; f(); } //globale Deklaration //Ausgabe: 4 5 5 Problem bei globaler Deklaration: x wird verändert schwierig zu warten schnell unvorhergesehene Ergebnisse C++ - Funktionen und mehr 59 Regeln Namen so lokal wie möglich halten! Keine Kollision mit gleichnamigen Elementen anderer Programmierer Je länger der Gültigkeitsbereich, desto aussagekräftiger der Name! Beispiel: i nicht als globalen Namen verwenden (Bedeutung unklar), allerdings als Zählvariable für Schleifen ist i sinnvoll. So wenig globale Variablen wie möglich benutzen Programme werden deutlich übersichtlicher Achtung! Namen von Blöcken / Anweisungen können Gültigkeit verlieren („go out of scope“) C++ - Funktionen und mehr 60 Verschachtelung C++ -Konstrukte, die Scopes definieren, sind selbst in Scopes eingeschlossen 1. Funktionen innerhalb von Klassen (Memberfunktionen) 2. Klassen in Klassen (Memberklassen, „eingebettete Klassen“) 3. Klassen innerhalb von Funktionen (lokale Klasse) 4. Funktionen innerhalb von Funktionen (lokale Funktionen, „eingebettete Funktionen“) 5. Blöcke innerhalb von Funktionen oder anderen Blöcken (verschachtelte Blöcke) nicht zu vermeiden C++ - Funktionen und mehr 61 Memberfunktionen häufigster Fall class myClass{ public: void f() { //do something } void g() { //do something else } }; //beide Funktionen in Klasse //definiert class myClass{ public: void f(); void g() { //do something else } }; void myClass::f() { //do something } //eine Funktion in Klasse //definiert, eine in Klasse //deklariert und außerhalb //definiert C++ - Funktionen und mehr 62 Memberklassen nur für Implementierung komplizierter Klassen hilfreich class MyClass { class MyMemberClass { class AnotherMemberClass { //... } //... } //... } C++ - Funktionen und mehr 63 Lokale Klasse Vermeiden! Unübersichtlich, Funktionen werden zu lang void f() { //... class MyClass { //... }; //... class MyClass2 { //... }; //... } C++ - Funktionen und mehr 64 Eingebettete Funktionen nicht erlaubt in C++, Zurückweisung durch Compiler void f() { //... void g() { //... } //... } // error C2601: 'g': Lokale Funktionsdefinitionen sind // unzulässig C++ - Funktionen und mehr 65 Namensbereiche C++ - Funktionen und mehr 66 Namensbereiche C++ bietet ein Sprachelement zur Definition von Scopes an: namespace Eine namespace ist eine Gruppierung von Deklarationen. Zusammenfassen von Klassen, Funktionen, Daten und Typen zu einem benannten Teil eines Programms C++ - Funktionen und mehr 67 Namensbereiche - Beispiel namespace Grapics { struct Color {/*...*/}; struct Shape {/*..*/}; struct Line:Shape {/*...*/}; struct Funtion:Shape {/*...*/}; struct Text:Shape {/*...*/}; //... int gui_mai() {/*...*/}; } //die Elemente sind alle im Namensbereich Graphics definiert C++ - Funktionen und mehr 68 Namensbereiche Problem: Verwendung von globalen Namensbereich Kollision bei Gleichbenennung zweier Klassen Verwendung von namespace (Bibliothek) keine Kollision bei gleichen Namen in unterschiedlichen Bibliotheken Vollqualifizierter Name: [name_of_namespace]::[name_of_member] C++ - Funktionen und mehr 69 Beispiel von Seite 54//55 namespace Namespace1 { namespace Namespace2 { void f() { cout << "Hallo!"; } } } namespace Namespace1 { namespace Namespace2 { void f() { cout << "Hallo!"; } } } void main() { f(); } // error C3861: "f": // Bezeichner wurde nicht // gefunden. void main() { Namespace1::Namespace2::f(); } //Ausgabe: Hallo! C++ - Funktionen und mehr 70 Using-Deklaration C++ - Funktionen und mehr 71 Using-Deklaration Schreiben von vollqualifizierten Namen dauert bei häufiger Verwendung lange. Festlegung: using [name_of_namespace]::[name_of_member] bedeutet immer name_of_member Beispiel: std::string name; using std::string; //string bedeutet nun immer std::string string name2; C++ - Funktionen und mehr 72 Using-Direktive using-Deklaration nicht ausreichend bei vielen Elementen im namespace Verwendung von using-Direktive Deklaration aus namespace wird immer benutzt, wenn keine Deklaration im aktuellen Scope existiert Befehl: using namespace [name_of_namespace]; Beispiel: using namespace std; C++ - Funktionen und mehr 73 Using-Direktive Verwendung möglichst nur bei Namensbereichen wie std (weitverbreitet) sonst Gefahr von Unübersichtlichkeit Using-Deklaration übersichtlicher, da expliziter Schlechte Angewohnheit: using-Direktive in Headerdatei (kein Umgehen möglich) Empfehlung: using namespace std; in std_lib_facilities platzieren (Schreibarbeit) C++ - Funktionen und mehr 74 Main-Funktion C++ - Funktionen und mehr 75 Main-Funktion Main = normale Funktion mit dem Privileg, dass sie als Erstes ausgeführt wird Parameterüber- und Rückgabe möglich Aufruf von main ist gleichzusetzen mit Start von Programm Funktionsparameter müssen von außen übergeben werden Programmstart in Shell: hinter dem Programmnamen angegebene Parameter werden als Funktionsparameter übernommen Programmstart von grafischer Oberfläche: mit der Wahl von verschiedenen Einstellungen können Funktionsparameter erzeugt werden C++ - Funktionen und mehr 76 Parameter der Main-Funktion Unterschiedliche Anzahl von zu übergebenden Parametern an die Main-Funktion ist möglich Funktionskopf: void main(int argc, char **argv) argc – Integer, Anzahl der übergebenen Parameter 1. Parameter ist Programmname selbst, auch wenn sonst keine weiteren Parameter existieren argc >= 1 argv – Zeiger auf einen char-Zeiger char **argv gleichbedeutend mit char *argv[] (Zeiger auf ein Feld von charZeigern ist gleichbedeutend einem Zeiger auf ein Element) C++ - Funktionen und mehr 77 Rückgabewerte der Main-Funktion Main ist auch in der Lage, Parameter zurück zu geben int main(int argc, char **argv) Normalerweise ist Rückgabewert unwichtig, Ausnahme: Benutzen einer Batch-Datei Batch-Datei = Textdatei, in der hintereinander alle Befehle stehen, die abgearbeitet werden sollen In Batch-Datei kann auf Rückgabe des Programms reagiert werden Rückgabe von 0: erfolgreiches Beenden Rückgabe von Wert != 0: Fehler, Abbruch der Batch-Datei in den meisten Fällen Mit return muss der Wert zurückgegeben werden C++ - Funktionen und mehr 78 Quellen Einführung in die Programmierung mit C++ - Bjarne Stroustrup C++ Programmierung – André Willms Internetquellen (Zuletzt geprüft am 03.05.2011 10 Uhr): http://de.wikibooks.org/wiki/C%2B%2BProgrammierung/_Weitere_Grundelemente/_Prozeduren_und_Funktionen http://de.wikibooks.org/wiki/C++-Programmierung:_Variablen_und_Konstanten http://www.c-plusplus.de/forum/61231 http://www.highscore.de/cpp/einfuehrung/funktionen.html http://www.informit.de/books/c++21/data/kap05.htm http://www.imb-jena.de/~gmueller/kurse/c_c++/c7up.html C++ - Funktionen und mehr 79