Video: C++ Tutorial for Beginners - Full Course 2024
Von Stephen R. Davis
C ++ ist keine einfache Programmiersprache. Nur durch Erfahrung werden Ihnen die unzähligen Kombinationen von Symbolen natürlich erscheinen. Dieser Cheat Sheet gibt Ihnen jedoch einige solide Tipps, wie Sie den Übergang vom C ++ - Anfänger zum C ++ - Guru erleichtern können: Lernen Sie, komplexe C ++ - Ausdrücke zu lesen. lernen, Zeigerprobleme zu vermeiden; und herausfinden, wie und wann man tiefe Kopien anfertigt.
Wie man einen komplexen C ++ Ausdruck liest
C ++ ist voll von kleinen Symbolen, von denen jedes zur Bedeutung von Ausdrücken beiträgt. Die Regeln der C ++ - Grammatik sind so flexibel, dass diese Symbole in nahezu undurchdringlichen komplexen Kombinationen kombiniert werden können. Ausdrücke in der einfacheren C-Sprache können so unklar sein, dass es früher einen jährlichen Wettbewerb gab, wer das am wenigsten bekannte Programm schreiben konnte und wer es verstehen konnte.
Es ist nie eine gute Idee zu versuchen, komplexen Code zu schreiben, aber Sie werden manchmal über Ausdrücke in C ++ laufen, die auf den ersten Blick etwas verwirrend sind. Verwenden Sie einfach die folgenden Schritte, um sie herauszufinden:
-
Beginnen Sie mit den meisten eingebetteten Klammern.
Beginnen Sie mit der Suche nach den äußersten Klammern. Suchen Sie in diesen nach eingebetteten Klammern. Wiederholen Sie den Vorgang, bis Sie sich bis zum tiefsten Paar von Klammern gearbeitet haben. Beginnen Sie zuerst mit der Auswertung dieses Unterausdrucks, indem Sie die folgenden Regeln verwenden. Sobald Sie diesen Ausdruck verstanden haben, wechseln Sie zur nächsten Ebene zurück und wiederholen Sie den Vorgang.
-
Bewerten Sie die einzelnen Operationen in der Reihenfolge der Rangfolge.
Die Reihenfolge, in der die Operatoren ausgewertet werden, wird durch die Vorrangigkeit des Operators in der Tabelle bestimmt. Die Indirektion kommt vor der Multiplikation, die vor der Addition liegt, also addiert das Folgende 1 plus 2 mal den Wert, auf den * ptr zeigt.
int i = 1 + 2 * * ptr;
Präzedenz | Operator | Bedeutung |
---|---|---|
1 | () (unär.) | Aufruf einer Funktion |
2 | * und -> (unär) | Dereferenz ein Zeiger |
2 | - (unär) | Gibt das Negativ seines Arguments zurück |
3 | ++ (unär.) | Inkrement |
3 > - (unär.) | Dekrement | 4 |
* (binär) | Multiplikation | 4 |
/ (binär) | Division | 4 |
% (binär) | Modulo | 5 |
+ (binär) | Addition | 5 |
- (binär) | Subtraktion | 6 |
&& (binär) | Logisches UND | 6 |
! ! | Logisches ODER | 7 |
=, * =,% =, + =, - = (special) | Zuweisungstypen | Bewerten Sie Operationen gleicher Priorität von links nach rechts (außer Zuweisung, das geht den anderen Weg). |
-
Die meisten Operatoren mit derselben Priorität werden von links nach rechts ausgewertet. Das Folgende fügt also 1 zu 2 hinzu und fügt das Ergebnis zu 3 hinzu:
int i = 1 + 2 + 3;
Die Reihenfolge der Bewertung einiger Operatoren spielt keine Rolle. Zum Beispiel funktioniert die Addition von links nach rechts genauso wie von rechts nach links. Die Reihenfolge der Auswertung macht für einige Operationen wie Division einen großen Unterschied. Das Folgende teilt 8 durch 4 und dividiert das Ergebnis durch 2:
int i = 8/4/2;
Die Hauptausnahme dieser Regel ist die Zuweisung, die von rechts nach links ausgewertet wird:
a = b = c;
Dies ordnet c zu b und das Ergebnis zu a zu.
Werten Sie Unterausdrücke in keiner bestimmten Reihenfolge aus.
-
Betrachten Sie den folgenden Ausdruck:
int i = f () + g () * h ();
Die Multiplikation hat eine höhere Priorität, daher können Sie davon ausgehen, dass die Funktionen g () und h () vor f () aufgerufen werden, dies ist jedoch nicht der Fall. Der Funktionsaufruf hat die höchste Priorität von allen, daher werden alle drei Funktionen aufgerufen, bevor entweder die Multiplikation oder die Addition ausgeführt wird. (Die von g () und h () zurückgegebenen Ergebnisse werden multipliziert und dann zu den von f () zurückgegebenen Ergebnissen addiert.)
Die einzige Zeit, in der die Reihenfolge der Funktionen aufgerufen wird, ist, wenn die Funktion Nebenwirkungen hat. B. eine Datei öffnen oder den Wert einer globalen Variablen ändern. Sie sollten Ihre Programme definitiv nicht schreiben, damit sie von diesen Art der Nebenwirkungen abhängen.
Führen Sie Konvertierungen nur dann durch, wenn dies erforderlich ist.
-
Sie sollten nicht mehr Typumwandlungen vornehmen als absolut notwendig. Der folgende Ausdruck hat zum Beispiel mindestens drei und möglicherweise vier Typkonvertierungen:
float f = 'a' + 1;
Das Zeichen 'a' muss zu einem Int befördert werden, um die Addition auszuführen. Das int wird dann in ein Double konvertiert und dann in ein einfaches Fließkomma umgewandelt. Denken Sie daran, dass alle Arithmetik entweder in int oder double ausgeführt wird. Generell sollte vermieden werden, Arithmetik bei Zeichentypen durchzuführen und insgesamt einen float mit einfacher Genauigkeit zu vermeiden.
5 Wege, Zeigerprobleme in C ++ zu vermeiden
In C ++ ist ein
Zeiger eine Variable, die die Adresse eines Objekts im internen Speicher des Computers enthält. Verwenden Sie diese Schritte, um Probleme mit Zeigern in C ++ zu vermeiden: Initialisieren Sie Zeiger, wenn sie deklariert sind.
-
Lassen Sie Zeigervariablen nie uninitialisiert - Dinge wären nicht so schlimm, wenn nicht initialisierte Zeiger immer zufällige Werte enthielten - die große Mehrheit der zufälligen Werte sind ungültige Zeigerwerte und bewirken, dass das Programm abstürzt, sobald sie verwendet werden. Das Problem ist, dass nicht initialisierte Variablen dazu neigen, den Wert von anderen, zuvor verwendeten Zeigervariablen zu übernehmen. Diese Probleme sind sehr schwierig zu debuggen.
Wenn Sie nicht wissen, woran Sie sonst noch einen Zeiger initialisieren sollen, initialisieren Sie ihn auf nullptr. Nullptr ist garantiert eine illegale Adresse.
Nullen Sie Zeiger ab, nachdem Sie sie verwendet haben.
-
Gleichermaßen immer eine Zeigervariable auf Null setzen, sobald der Zeiger nicht mehr gültig ist, indem ihm der Wert nullptr zugewiesen wird. Dies ist besonders dann der Fall, wenn Sie einen Speicherblock mit delete an den Heap zurückgeben. immer den Zeiger auf null setzen, nachdem der Heapspeicher zurückgegeben wurde.
Weisen Sie Speicher aus dem Heap zu und geben Sie ihn auf derselben "Ebene" an den Heap zurück, um Speicherlecks zu vermeiden.
-
Versuchen Sie immer, einen Speicherblock auf der gleichen Abstraktionsebene, wie Sie ihn zugewiesen haben, in den Heap zurückzuladen. Dies bedeutet im Allgemeinen, dass versucht wird, den Speicher auf derselben Ebene von Funktionsaufrufen zu löschen.
Fange eine Ausnahme, um den Speicher bei Bedarf zu löschen.
-
Vergessen Sie nicht, dass eine Ausnahme fast jederzeit auftreten kann. Wenn Sie beabsichtigen, die Ausnahme abzufangen und weiter zu arbeiten (anstatt das Programm abstürzen zu lassen), vergewissern Sie sich, dass Sie die Ausnahme abfangen und alle Speicherblöcke an den Heapspeicher zurückgeben, bevor die Zeiger, die auf sie zeigen, den Speicherbereich verlassen. hat verloren.
Stellen Sie sicher, dass die Typen genau übereinstimmen.
-
Stellen Sie immer sicher, dass die Zeigertypen mit dem erforderlichen Typ übereinstimmen. Neudefinieren Sie keinen Zeiger ohne einen bestimmten Grund. Betrachten Sie Folgendes:
void fn (int * p); void myFunc () {char c = 'a'; char * pC = & c; fn ((int *) pC);}
Die obige Funktion wird ohne Beanstandung kompiliert, da der Zeichenzeiger pC in ein int * umgeformt wurde, um der Deklaration von fn (int *) zu entsprechen; Dieses Programm wird jedoch sicherlich nicht funktionieren. Die Funktion fn () erwartet einen Zeiger auf eine volle 32-Bit-Ganzzahl und nicht auf ein Rinky-Dink 8-Bit-Zeichen. Diese Art von Problemen ist sehr schwer zu klären.
Wie und wann tiefe Kopien in C ++ erstellt werden
Klassen, die Ressourcen in ihrem Konstruktor zuweisen, sollten normalerweise einen Kopierkonstruktor enthalten, um Kopien dieser Ressourcen zu erstellen. Das Zuweisen eines neuen Speicherblocks und das Kopieren des Inhalts des Originals in diesen neuen Block wird als Erstellen einer
Tiefkopie (im Gegensatz zur standardmäßigen flachen Kopie) bezeichnet. Verwenden Sie die folgenden Schritte, um zu bestimmen, wie und wann tiefe Kopien in C ++ erstellt werden: Erstellen Sie immer eine tiefe Kopie, wenn der Konstruktor Ressourcen zuweist.
-
Standardmäßig erstellt C ++ so genannte "flache" Member-by-Member-Kopien von Objekten, wenn diese an Funktionen oder als Ergebnis einer Zuweisung übergeben werden. Sie müssen die Standardoperatoren für flache Kopien durch ihre Entsprechungen für tiefe Kopien für alle Klassen ersetzen, die Ressourcen im Konstruktor zuweisen. Die am häufigsten verwendete Ressource, die zugewiesen wird, ist Heap-Speicher, der vom neuen Operator zurückgegeben wird.
Fügen Sie immer einen Destruktor für eine Klasse ein, die Ressourcen zuweist.
-
Wenn Sie einen Konstruktor erstellen, der Ressourcen zuweist, müssen Sie einen Destruktor erstellen, der sie wiederherstellt. Keine Ausnahmen.
Deklariert den Destruktor immer als virtuell.
-
Ein häufiger Anfängerfehler besteht darin, zu vergessen, den Destruktor als virtuell zu deklarieren. Das Programm läuft gut, bis ein ahnungsloser Programmierer kommt und von Ihrer Klasse erbt. Das Programm scheint weiterhin zu funktionieren, aber da der Destruktor in der Basisklasse möglicherweise nicht korrekt aufgerufen wird, verliert der Speicher wie ein Sieb aus Ihrem Programm, bis es schließlich abstürzt. Dieses Problem ist schwer zu finden.
Fügen Sie immer einen Kopierkonstruktor für eine Klasse hinzu, die Ressourcen zuweist.
-
Der Kopierkonstruktor erstellt eine ordnungsgemäße Kopie des aktuellen Objekts, indem er Speicher des Heapspeichers reserviert und den Inhalt des Quellobjekts kopiert.
Überschreiben Sie immer den Zuweisungsoperator für eine Klasse, die Ressourcen zuweist.
-
Programmierer sollten von übergeordneten Operatoren abgehalten werden, aber der Zuweisungsoperator ist eine Ausnahme. Sie sollten den Zuweisungsoperator für alle Klassen überschreiben, die Ressourcen im Konstruktor zuweisen.
Der Zuweisungsoperator sollte drei Dinge tun:
Stellen Sie sicher, dass das linke und das rechte Objekt nicht dasselbe Objekt sind. Mit anderen Worten, stellen Sie sicher, dass der Anwendungsprogrammierer nicht etwas wie (a = a) geschrieben hat. Wenn sie es sind, tue nichts.
-
Rufen Sie den gleichen Code wie der Destruktor auf dem linken Objekt auf, um seine Ressourcen zurückzugeben.
-
Rufen Sie den gleichen Code wie ein Kopierkonstruktor auf, um eine tiefe Kopie des Objekts der rechten Hand in das Objekt der linken Hand zu erstellen.
-
Wenn dies nicht möglich ist, löschen Sie den Kopierkonstruktor und den Zuweisungsoperator, damit das Programm keine Kopien Ihres Objekts erstellen kann.
-
-
Wenn Sie dies nicht einmal tun können, weil Ihr Compiler die C ++ 2011 Delete-Konstruktor-Funktion nicht unterstützt, erstellen Sie einen leeren Kopierkonstruktor und einen Zuweisungsoperator und deklarieren Sie diese, um andere Klassen davon abzuhalten, sie zu verwenden.