C++ Tutorial
Erste Schritte mit C ++
Suche…
Bemerkungen
Das 'Hello World'-Programm ist ein allgemeines Beispiel, das einfach zur Überprüfung der Anwesenheit von Compiler und Bibliothek verwendet werden kann. Es verwendet die C ++ - Standardbibliothek mit std::cout
von <iostream>
und hat nur eine zu kompilierende Datei, wodurch die Möglichkeit möglicher Benutzerfehler während der Kompilierung minimiert wird.
Das Kompilieren eines C ++ - Programms unterscheidet sich grundsätzlich zwischen Compilern und Betriebssystemen. Das Thema Kompilieren und Erstellen enthält Details zum Kompilieren von C ++ - Code auf verschiedenen Plattformen für verschiedene Compiler.
Versionen
Ausführung | Standard | Veröffentlichungsdatum |
---|---|---|
C ++ 98 | ISO / IEC 14882: 1998 | 1998-09-01 |
C ++ 03 | ISO / IEC 14882: 2003 | 2003-10-16 |
C ++ 11 | ISO / IEC 14882: 2011 | 2011-09-01 |
C ++ 14 | ISO / IEC 14882: 2014 | 2014-12-15 |
C ++ 17 | TBD | 2017-01-01 |
C ++ 20 | TBD | 2020-01-01 |
Hallo Welt
Dieses Programm druckt Hello World!
zum Standardausgabestrom:
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
Sehen Sie live auf Coliru .
Analyse
Lassen Sie uns jeden Teil dieses Codes detailliert untersuchen:
#include <iostream>
ist eine Präprozessoranweisung , die den Inhalt der Standard-C ++ - Headerdateiiostream
.iostream
ist eine Standard-Bibliothekskopfdatei , die Definitionen der Standard-Eingabe- und Ausgabeströme enthält. Diese Definitionen sind imstd
Namespace enthalten, der unten erläutert wird.Die Standard-E / A-Ströme bieten Programmen die Möglichkeit, Eingaben von einem externen System zu erhalten und an dieses auszugeben - normalerweise das Terminal.
int main() { ... }
definiert eine neue Funktion namensmain
. Konventionell wird diemain
bei Ausführung des Programms aufgerufen. In einem C ++ - Programm darf nur einemain
vorhanden sein, und es muss immer eine Zahl vom Typint
werden.Das
int
ist der Rückgabetyp der Funktion. Der von dermain
zurückgegebene Wert ist ein Beendigungscode.Konventionell wird ein Programm-Exit-Code von
0
oderEXIT_SUCCESS
von einem System, das das Programm ausführt, als Erfolg interpretiert. Jeder andere Rückkehrcode ist mit einem Fehler verbunden.Wenn keine
return
- Anweisung vorhanden ist, diemain
(und damit das Programm selbst) liefert0
standardmäßig. In diesem Beispiel müssen wirreturn 0;
nicht explizit schreibenreturn 0;
.Alle anderen Funktionen, mit Ausnahme derjenigen, die den
void
Typ zurückgeben, müssen explizit einen Wert entsprechend ihrem Rückgabetyp zurückgeben, sonst dürfen sie überhaupt nicht zurückgeben.
std::cout << "Hello World!" << std::endl;
druckt "Hallo Welt!" zum Standardausgabestrom:std
ist ein Namespace , und::
ist der Operator für die Bereichsauflösung , der Lookups für Objekte nach Namen in einem Namespace ermöglicht.Es gibt viele Namespaces. Hier verwenden wir
::
zu zeigen, dass wircout
aus demstd
Namespace verwenden möchten. Weitere Informationen finden Sie unter Scope Resolution Operator - Microsoft-Dokumentation .std::cout
ist die Standardausgabe Objekt, in definierteniostream
, und druckt es auf die Standardausgabe (stdout
).<<
ist in diesem Zusammenhang der Stream-Einfügungsoperator , der so genannt wird, weil er ein Objekt in das Stream- Objekt einfügt .Die Standardbibliothek definiert den Operator
<<
, um Daten für bestimmte Datentypen in Ausgabeströme einzufügen.stream << content
fügtcontent
in den Stream ein und gibt denselben, aber aktualisierten Stream zurück. Dadurch können Stream-Einfügungen verkettet werden:std::cout << "Foo" << " Bar";
druckt "FooBar" auf die Konsole."Hello World!"
ist ein Zeichenkettenliteral oder ein "Textliteral". Der Stream-Einfügeoperator für Zeichenkettenliterale wird in der Dateiiostream
definiert.std::endl
ist ein spezielles E / A-Stream-Manipulatorobjekt , das auch in der Dateiiostream
definiert ist. Durch Einfügen eines Manipulators in einen Stream wird der Status des Streams geändert.Der Stream-Manipulator
std::endl
führt zwei Dinge aus: Zuerst fügt er das Zeilenende-Zeichen ein und leert dann den Stream-Puffer, damit der Text auf der Konsole angezeigt wird. Dadurch wird sichergestellt, dass die in den Stream eingefügten Daten tatsächlich auf Ihrer Konsole angezeigt werden. (Stream-Daten werden normalerweise in einem Puffer gespeichert und dann in Batches "geleert", sofern Sie nicht sofort eine Spülung erzwingen.)Eine alternative Methode, die den Flush vermeidet, ist:
std::cout << "Hello World!\n";
Dabei ist
\n
die Escape-Zeichenfolge für das Zeilenvorschubzeichen.Das Semikolon (
;
) benachrichtigt den Compiler darüber, dass eine Anweisung beendet wurde. Alle C ++ - Anweisungen und Klassendefinitionen erfordern ein abschließendes Semikolon.
Bemerkungen
Ein Kommentar ist eine Möglichkeit, beliebigen Text in den Quellcode einzufügen, ohne dass der C ++ - Compiler ihn mit funktionaler Bedeutung interpretiert. Kommentare werden verwendet, um Einblick in das Design oder die Methode eines Programms zu geben.
Es gibt zwei Arten von Kommentaren in C ++:
Einzeilige Kommentare
Die doppelte Schrägstrichfolge //
markiert den gesamten Text bis zu einer neuen Zeile als Kommentar:
int main()
{
// This is a single-line comment.
int a; // this also is a single-line comment
int i; // this is another single-line comment
}
C-Style / Block-Kommentare
Mit der Sequenz /*
wird der Beginn des Kommentarblocks und mit der Sequenz */
das Ende des Kommentars angegeben. Der gesamte Text zwischen den Start- und Endsequenzen wird als Kommentar interpretiert, auch wenn der Text ansonsten eine gültige C ++ - Syntax ist. Diese werden manchmal als "C-style" -Kommentare bezeichnet, da diese Kommentarsyntax von der C ++ - Vorgängersprache C übernommen wird:
int main()
{
/*
* This is a block comment.
*/
int a;
}
In jedem Blockkommentar können Sie alles schreiben, was Sie möchten. Wenn der Compiler auf das Symbol */
stößt, wird der Blockkommentar abgebrochen:
int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}
Das obige Beispiel ist gültiger C ++ - Code (und C-Code). Wenn Sie jedoch /*
in einem Blockkommentar enthalten, kann dies bei einigen Compilern zu einer Warnung führen.
Block-Kommentare können auch innerhalb einer einzelnen Zeile beginnen und enden. Zum Beispiel:
void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
Bedeutung der Kommentare
Wie bei allen Programmiersprachen bieten Kommentare mehrere Vorteile:
- Explizite Dokumentation des Codes, um das Lesen / Verwalten zu erleichtern
- Erläuterung des Zweckes und der Funktionalität des Codes
- Details zum Verlauf oder zur Begründung des Codes
- Platzierung von Copyright / Lizenzen, Projektnotizen, besonderen Dank, Mitwirkenden, etc. direkt im Quellcode.
Kommentare haben jedoch auch ihre Nachteile:
- Sie müssen beibehalten werden, um Änderungen im Code widerzuspiegeln
- Exzessive Kommentare machen den Code weniger lesbar
Der Bedarf an Kommentaren kann reduziert werden, indem klarer, selbstdokumentierender Code geschrieben wird. Ein einfaches Beispiel ist die Verwendung von erklärenden Namen für Variablen, Funktionen und Typen. Das Ausrechnen von logisch zusammenhängenden Aufgaben in diskrete Funktionen geht damit Hand in Hand.
Kommentarmarken zum Deaktivieren von Code
Während der Entwicklung können Kommentare auch dazu verwendet werden, Teile des Codes schnell zu deaktivieren, ohne ihn zu löschen. Dies ist häufig für Test- oder Debugging-Zwecke hilfreich, eignet sich jedoch nur für temporäre Bearbeitungen. Dies wird oft als "Auskommentieren" bezeichnet.
In ähnlicher Weise ist es verpönt, alte Versionen eines Codes in einem Kommentar zu Referenzzwecken zu behalten, da dies Dateien stört und dabei wenig Wert bietet, verglichen mit der Erforschung der Geschichte des Codes über ein Versionssystem.
Funktion
Eine Funktion ist eine Codeeinheit, die eine Folge von Anweisungen darstellt.
Funktionen können Argumente oder Werte annehmen und einen einzelnen Wert zurückgeben (oder nicht). Um eine Funktion zu verwenden, wird ein Funktionsaufruf für Argumentwerte verwendet und die Verwendung des Funktionsaufrufs selbst wird durch seinen Rückgabewert ersetzt.
Jede Funktion verfügt über eine Typensignatur - die Typen ihrer Argumente und den Typ ihres Rückgabetyps.
Funktionen werden von den Konzepten der Prozedur und der mathematischen Funktion inspiriert.
- Hinweis: C ++ - Funktionen sind im Wesentlichen Prozeduren und folgen nicht der genauen Definition oder den Regeln mathematischer Funktionen.
Funktionen dienen häufig dazu, eine bestimmte Aufgabe auszuführen. und kann von anderen Teilen eines Programms aufgerufen werden. Eine Funktion muss deklariert und definiert werden, bevor sie an anderer Stelle in einem Programm aufgerufen wird.
- Hinweis: Beliebte Funktionsdefinitionen können in anderen enthaltenen Dateien versteckt sein (häufig zur Vereinfachung und Wiederverwendung in vielen Dateien). Dies ist eine übliche Verwendung von Header-Dateien.
Funktionserklärung
Eine Funktionsdeklaration gibt dem Compiler die Existenz einer Funktion mit ihrem Namen und ihrer Typensignatur an. Die Syntax lautet wie folgt:
int add2(int i); // The function is of the type (int) -> (int)
Im obigen Beispiel erklärt die Funktion int add2(int i)
dem Compiler Folgendes:
- Der Rückgabetyp ist
int
. - Der Name der Funktion lautet
add2
. - Die Anzahl der Argumente für die Funktion ist 1:
- Das erste Argument ist vom Typ
int
. - Das erste Argument wird im Inhalt der Funktion mit dem Namen
i
.
- Das erste Argument ist vom Typ
Der Argumentname ist optional. Die Deklaration für die Funktion könnte auch folgende sein:
int add2(int); // Omitting the function arguments' name is also permitted.
Gemäß der Eindefinitionsregel kann eine Funktion mit einer bestimmten Typensignatur nur einmal in einer gesamten C ++ - Codebasis deklariert oder definiert werden, die für den C ++ - Compiler sichtbar ist. Das heißt, Funktionen mit einer bestimmten Typensignatur können nicht neu definiert werden - sie müssen nur einmal definiert werden. Folgendes ist daher kein gültiges C ++:
int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.
Wenn eine Funktion nichts zurückgibt, wird ihr Rückgabetyp als void
. Wenn keine Parameter erforderlich sind, sollte die Parameterliste leer sein.
void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Funktionsaufruf
Eine Funktion kann aufgerufen werden, nachdem sie deklariert wurde. Das folgende Programm ruft beispielsweise add2
mit dem Wert 2
innerhalb der Funktion von main
:
#include <iostream>
int add2(int i); // Declaration of add2
// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.
int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}
Hier ist add2(2)
die Syntax für einen Funktionsaufruf.
Funktionsdefinition
Eine Funktionsdefinition * ähnelt einer Deklaration, enthält jedoch auch den Code, der ausgeführt wird, wenn die Funktion innerhalb ihres Rumpfes aufgerufen wird.
Ein Beispiel für eine Funktionsdefinition für add2
könnte sein:
int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."
int j = i + 2; // Definition of a variable j as the value of i+2.
return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}
Funktionsüberladung
Sie können mehrere Funktionen mit demselben Namen, aber unterschiedlichen Parametern erstellen.
int add2(int i) // Code contained in this definition will be evaluated
{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}
int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}
Beide Funktionen werden mit dem gleichen Namen add2
. Die eigentliche Funktion, die aufgerufen wird, hängt jedoch direkt von der Menge und dem Typ der Parameter im Aufruf ab. In den meisten Fällen kann der C ++ - Compiler berechnen, welche Funktion aufgerufen werden soll. In einigen Fällen muss der Typ explizit angegeben werden.
Standardparameter
Standardwerte für Funktionsparameter können nur in Funktionsdeklarationen angegeben werden.
int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.
In diesem Beispiel kann multiply()
mit einem oder zwei Parametern aufgerufen werden. Wenn nur ein Parameter angegeben wird, hat b
den Standardwert 7. Standardargumente müssen in den letzten Argumenten der Funktion angegeben werden. Zum Beispiel:
int multiply(int a = 10, int b = 20); // This is legal
int multiply(int a = 10, int b); // This is illegal since int a is in the former
Spezielle Funktionsaufrufe - Operatoren
Es gibt spezielle Funktionsaufrufe in C ++, die eine andere Syntax als name_of_function(value1, value2, value3)
. Das häufigste Beispiel sind Operatoren.
Bestimmte spezielle Zeichenfolgen, die vom Compiler auf Funktionsaufrufe reduziert werden, z. B. !
, +
, -
, *
, %
und <<
und viele mehr. Diese Sonderzeichen stehen normalerweise im Zusammenhang mit der nicht-programmierenden Verwendung oder werden für Ästhetik verwendet (z. B. wird das Zeichen +
sowohl in der C ++ - Programmierung als auch in der Mathematik als Zusatzsymbol erkannt).
C ++ behandelt diese Zeichenfolgen mit einer speziellen Syntax. Im Wesentlichen wird jedoch jedes Auftreten eines Operators auf einen Funktionsaufruf reduziert. Beispielsweise der folgende C ++ - Ausdruck:
3+3
entspricht dem folgenden Funktionsaufruf:
operator+(3, 3)
Alle Namen der operator
beginnen mit dem operator
.
Während in C ++ unmittelbarem Vorgänger C die Namen der Operatorfunktionen nicht durch Angabe zusätzlicher Definitionen mit unterschiedlichen Typensignaturen unterschiedlichen Bedeutungen zugewiesen werden können, ist dies in C ++ gültig. Das "Ausblenden" zusätzlicher Funktionsdefinitionen unter einem eindeutigen Funktionsnamen wird in C ++ als Operatorüberladung bezeichnet und ist in C ++ eine relativ verbreitete, aber keine universelle Konvention.
Sichtbarkeit von Funktionsprototypen und Deklarationen
In C ++ muss Code vor der Verwendung deklariert oder definiert werden. Das folgende Beispiel erzeugt beispielsweise einen Fehler bei der Kompilierung:
int main()
{
foo(2); // error: foo is called, but has not yet been declared
}
void foo(int x) // this later definition is not known in main
{
}
Es gibt zwei Möglichkeiten, dies zu beheben: Setzen Sie entweder die Definition oder Deklaration von foo()
vor seiner Verwendung in main()
. Hier ist ein Beispiel:
void foo(int x) {} //Declare the foo function and body first
int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
Es ist jedoch auch möglich, die Funktion "vorwärts zu deklarieren", indem nur eine "Prototyp" -Deklaration vor ihrer Verwendung gesetzt wird und der Funktionskörper später definiert wird:
void foo(int); // Prototype declaration of foo, seen by main
// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}
void foo(int x) //Must match the prototype
{
// Define body of foo here
}
Der Prototyp muss den Rückgabetyp ( void
), den Namen der Funktion ( foo
) und die foo
der Argumentliste ( int
) angeben. Die Namen der Argumente sind jedoch NICHT erforderlich .
Eine gängige Möglichkeit, dies in die Organisation von Quelldateien zu integrieren, besteht darin, eine Header-Datei mit allen Prototypdeklarationen zu erstellen:
// foo.h
void foo(int); // prototype declaration
und dann die vollständige Definition an anderer Stelle angeben:
// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
und verknüpfen foo.o
nach dem Kompilieren die entsprechende Objektdatei foo.o
mit der kompilierten Objektdatei, in der sie in der Verknüpfungsphase verwendet wird, main.o
:
// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
Ein „nicht aufgelöstes externes Symbol“ -Fehler auftritt , wenn der Funktionsprototyp und Anruf existieren, aber der Funktionskörper ist nicht definiert. Das Auflösen kann schwieriger sein, da der Compiler den Fehler erst im letzten Verknüpfungsschritt meldet und nicht weiß, in welche Zeile im Code gesprungen werden soll, um den Fehler anzuzeigen.
Der Standard-C ++ - Kompilierungsprozess
Ausführbarer C ++ - Programmcode wird normalerweise von einem Compiler erzeugt.
Ein Compiler ist ein Programm, das Code aus einer Programmiersprache in eine andere Form übersetzt, die für einen Computer (mehr) direkt ausführbar ist. Die Verwendung eines Compilers zum Übersetzen von Code wird als Kompilierung bezeichnet.
C ++ erbt die Form des Kompilierungsprozesses von seiner "übergeordneten" Sprache C. Nachfolgend finden Sie eine Liste mit den vier Hauptschritten der Kompilierung in C ++:
- Der C ++ - Präprozessor kopiert den Inhalt aller enthaltenen Header-Dateien in die Quellcodedatei, generiert Makrocode und ersetzt symbolische Konstanten, die mit #define definiert sind, durch ihre Werte.
- Die vom C ++ - Präprozessor erzeugte erweiterte Quellcodedatei wird in eine für die Plattform geeignete Assemblersprache kompiliert.
- Der vom Compiler generierte Assembler-Code wird zu einem geeigneten Objektcode für die Plattform zusammengestellt.
- Die vom Assembler generierte Objektcodedatei wird mit den Objektcodedateien für alle Bibliotheksfunktionen verknüpft, die zum Erstellen einer ausführbaren Datei verwendet werden.
- Hinweis: Ein Teil des kompilierten Codes ist miteinander verknüpft, jedoch nicht zum Erstellen eines endgültigen Programms. Normalerweise kann dieser "verknüpfte" Code auch in ein Format gepackt werden, das von anderen Programmen verwendet werden kann. Dieses "Bündel von verpacktem, verwendbarem Code" bezeichnen C ++ - Programmierer als Bibliothek.
Viele C ++ - Compiler können bestimmte Teile des Kompilierungsprozesses zur Vereinfachung oder zur zusätzlichen Analyse zusammenführen oder deren Zusammenführung aufheben. Viele C ++ - Programmierer verwenden verschiedene Werkzeuge, aber alle Werkzeuge folgen im Allgemeinen diesem allgemeinen Prozess, wenn sie an der Erstellung eines Programms beteiligt sind.
Der Link unten erweitert diese Diskussion und bietet eine schöne Grafik, um zu helfen. [1]: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
Präprozessor
Der Präprozessor ist ein wichtiger Teil des Compilers.
Es bearbeitet den Quellcode, schneidet einige Bits heraus, ändert andere und fügt andere Dinge hinzu.
In Quelldateien können Präprozessoranweisungen eingefügt werden. Diese Anweisungen weisen den Präprozessor an, bestimmte Aktionen auszuführen. Eine Direktive beginnt mit einem # in einer neuen Zeile. Beispiel:
#define ZERO 0
Die erste Präprozessoranweisung, die Sie treffen werden, ist wahrscheinlich die
#include <something>
Richtlinie. Was sie tut , ist nimmt alle something
und fügt es in der Datei , wenn die Richtlinie war. Das Hallo Weltprogramm beginnt mit der Zeile
#include <iostream>
In dieser Zeile werden die Funktionen und Objekte hinzugefügt, mit denen Sie die Standardeingabe und -ausgabe verwenden können.
Die C-Sprache, die auch den Präprozessor verwendet, hat nicht so viele Header-Dateien wie die C ++ - Sprache, aber in C ++ können Sie alle C-Header-Dateien verwenden.
Die nächste wichtige Richtlinie ist wahrscheinlich die
#define something something_else
Richtlinie. Dies teilt dem Präprozessor mit, dass er beim Durchlaufen der Datei jedes Vorkommen von something
mit something_else
ersetzen sollte. Es kann auch Dinge ähnlich wie Funktionen machen, aber das zählt wahrscheinlich als fortgeschrittenes C ++.
Die something_else
wird nicht benötigt, aber wenn Sie something
als nichts definieren, dann verschwinden außerhalb der Präprozessoranweisungen alle Vorkommen von something
.
Dies ist aufgrund der Direktiven #if
, #else
und #ifdef
tatsächlich hilfreich. Das Format für diese wäre folgendes:
#if something==true
//code
#else
//more code
#endif
#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
Diese Anweisungen fügen den Code ein, der sich im True-Bit befindet, und löschen die False-Bits. Dies kann verwendet werden, um Codebits zu haben, die nur in bestimmten Betriebssystemen enthalten sind, ohne dass der gesamte Code neu geschrieben werden muss.