Ich bin teilweise daran interessiert, Mitgliederinitialisierungslisten mit meinen Konstruktoren zu verwenden ... aber ich habe die Gründe dafür längst vergessen ...
Verwenden Sie Mitgliedsinitialisierungslisten in Ihren Konstruktoren? Wenn ja warum? Wenn nicht, warum nicht?
c++
oop
object-construction
paxos1977
quelle
quelle
Antworten:
Für POD- Klassenmitglieder macht es keinen Unterschied, es ist nur eine Frage des Stils. Bei Klassenmitgliedern, bei denen es sich um Klassen handelt, wird ein unnötiger Aufruf eines Standardkonstruktors vermieden. Erwägen:
In diesem Fall
B
ruft der Konstruktor für den Standardkonstruktor für aufA
und initialisiert ihn danna.x
auf 3. Eine bessere Möglichkeit wäre, wennB
der Konstruktor den Konstruktor direktA
in der Initialisierungsliste aufruft:Dies würde nur
A
denA(int)
Konstruktor und nicht den Standardkonstruktor aufrufen . In diesem Beispiel ist der Unterschied vernachlässigbar, aber stellen Sie sich vor,A
der Standardkonstruktor hat mehr getan, z. B. Speicher zuweisen oder Dateien öffnen. Sie würden das nicht unnötig tun wollen.Wenn eine Klasse keinen Standardkonstruktor oder eine
const
Mitgliedsvariable hat, müssen Sie außerdem eine Initialisierungsliste verwenden:quelle
Abgesehen von den oben genannten Leistungsgründen haben Sie keine andere Wahl, als Initialisierungslisten zu verwenden, wenn Ihre Klasse Verweise auf Objekte speichert, die als Konstruktorparameter übergeben wurden, oder wenn Ihre Klasse const-Variablen enthält.
quelle
Ein wichtiger Grund für die Verwendung der Konstruktorinitialisiererliste, die hier in den Antworten nicht erwähnt wird, ist die Initialisierung der Basisklasse.
Gemäß der Reihenfolge der Konstruktion sollte die Basisklasse vor der untergeordneten Klasse erstellt werden. Ohne Liste der Konstruktorinitialisierer ist dies möglich, wenn Ihre Basisklasse über einen Standardkonstruktor verfügt, der unmittelbar vor der Eingabe des Konstruktors der untergeordneten Klasse aufgerufen wird.
Wenn Ihre Basisklasse jedoch nur einen parametrisierten Konstruktor hat, müssen Sie die Konstruktorinitialisierungsliste verwenden, um sicherzustellen, dass Ihre Basisklasse vor der untergeordneten Klasse initialisiert wird.
Initialisierung von Unterobjekten, die nur parametrisierte Konstruktoren haben
Effizienz
Mithilfe der Konstruktorinitialisiererliste initialisieren Sie Ihre Datenelemente auf den genauen Status, den Sie in Ihrem Code benötigen, anstatt sie zuerst auf ihren Standardstatus zu initialisieren und dann ihren Status auf den Status zu ändern, den Sie in Ihrem Code benötigen.
Wenn nicht statische const-Datenelemente in Ihrer Klasse Standardkonstruktoren haben und Sie keine Konstruktorinitialisierungsliste verwenden, können Sie sie nicht in den beabsichtigten Zustand initialisieren, da sie in ihren Standardzustand initialisiert werden.
Referenzdatenelemente müssen initialisiert werden, wenn der Compiler den Konstruktor betritt, da Referenzen nicht einfach deklariert und später initialisiert werden können. Dies ist nur mit der Konstruktorinitialisiererliste möglich.
quelle
Neben den Leistungsproblemen gibt es noch einen sehr wichtigen, den ich als Wartbarkeit und Erweiterbarkeit des Codes bezeichnen würde.
Wenn ein T POD ist und Sie anfangen, die Initialisierungsliste zu bevorzugen, müssen Sie, wenn T einmal in einen Nicht-POD-Typ wechselt, nichts an der Initialisierung ändern, um unnötige Konstruktoraufrufe zu vermeiden, da diese bereits optimiert ist.
Wenn Typ T über einen Standardkonstruktor und einen oder mehrere benutzerdefinierte Konstruktoren verfügt und Sie sich einmal dazu entschließen, den Standardkonstruktor zu entfernen oder auszublenden, müssen Sie bei Verwendung der Initialisierungsliste den Code nicht aktualisieren, wenn Ihre benutzerdefinierten Konstruktoren vorhanden sind Sie sind bereits korrekt implementiert.
Nehmen wir an, T ist zunächst wie folgt definiert:
Als nächstes entscheiden Sie sich, eine als const zu qualifizieren. Wenn Sie die Initialisierungsliste von Anfang an verwenden würden, wäre dies eine Änderung in einer einzelnen Zeile. Wenn Sie jedoch das T wie oben definiert haben, müssen Sie auch die Konstruktordefinition ausgraben, um die Zuordnung zu entfernen:
Es ist kein Geheimnis, dass die Wartung viel einfacher und weniger fehleranfällig ist, wenn Code nicht von einem "Code-Affen" geschrieben wurde, sondern von einem Ingenieur, der Entscheidungen trifft, die auf tieferen Überlegungen darüber beruhen, was er tut.
quelle
Bevor der Hauptteil des Konstruktors ausgeführt wird, werden alle Konstruktoren für seine übergeordnete Klasse und dann für seine Felder aufgerufen. Standardmäßig werden die Konstruktoren ohne Argumente aufgerufen. In Initialisierungslisten können Sie auswählen, welcher Konstruktor aufgerufen wird und welche Argumente der Konstruktor erhält.
Wenn Sie eine Referenz oder ein const-Feld haben oder wenn eine der verwendeten Klassen keinen Standardkonstruktor hat, müssen Sie eine Initialisierungsliste verwenden.
quelle
Hier führt der Compiler die folgenden Schritte aus, um ein Objekt vom Typ MyClass
1 zu erstellen. Der Konstruktor des Typs wird zuerst für "a" aufgerufen.
2. Der Zuweisungsoperator von "Type" wird innerhalb des zuweisenden MyClass () -Konstruktors aufgerufen
Und schließlich wird der Destruktor von "Type" für "a" aufgerufen, da er außerhalb des Gültigkeitsbereichs liegt.
Betrachten Sie nun denselben Code mit dem MyClass () -Konstruktor mit der Initializer-Liste
Bei der Initialisierungsliste werden vom Compiler die folgenden Schritte ausgeführt:
quelle
Nur um einige zusätzliche Informationen hinzuzufügen, um zu demonstrieren, wie viel Unterschied die Mitgliederinitialisierungsliste machen kann . In der Leetcode 303 Range Sum Query - Unveränderlich, https://leetcode.com/problems/range-sum-query-immutable/ , wo Sie einen Vektor mit einer bestimmten Größe erstellen und auf Null initialisieren müssen. Hier sind zwei verschiedene Implementierungs- und Geschwindigkeitsvergleiche.
Ohne Mitgliederinitialisierungsliste kostete es mich ungefähr 212 ms , um AC zu bekommen .
Bei Verwendung der Mitgliederinitialisierungsliste beträgt die Zeit zum Abrufen des Wechselstroms ungefähr 108 ms . Mit diesem einfachen Beispiel ist es ziemlich offensichtlich, dass die Mitgliederinitialisierungsliste viel effizienter ist . Die gesamte Messung erfolgt aus der Laufzeit von LC.
quelle
Syntax:
Liste der erforderlichen Initialisierungen:
Wenn im obigen Programm der Konstruktor der Klasse ausgeführt wird, werden Sam_x und Sam_y erstellt. Dann werden im Konstruktorkörper diese Elementdatenvariablen definiert.
Anwendungsfälle:
In C, Variablen müssen bei der Erstellung definiert werden. Auf die gleiche Weise müssen wir in C ++ die Variablen Const und Reference während der Objekterstellung mithilfe der Initialisierungsliste initialisieren. Wenn wir nach der Objekterstellung eine Initialisierung durchführen (innerhalb des Konstruktorkörpers), wird ein Fehler bei der Kompilierung angezeigt.
Mitgliedsobjekte der Klasse Sample1 (Basis), die keinen Standardkonstruktor haben
Beim Erstellen eines Objekts für eine abgeleitete Klasse, das intern den Konstruktor für abgeleitete Klassen und den Konstruktor für Basisklassen aufruft (Standard). Wenn die Basisklasse keinen Standardkonstruktor hat, wird dem Benutzer ein Fehler bei der Kompilierung angezeigt. Um dies zu vermeiden, müssen wir beides haben
Der Parametername des Klassenkonstruktors und das Datenelement einer Klasse sind identisch:
Wie wir alle wissen, hat die lokale Variable die höchste Priorität als die globale Variable, wenn beide Variablen denselben Namen haben. In diesem Fall berücksichtigt das Programm den Wert "i" {sowohl die linke als auch die rechte Variable. dh: i = i} als lokale Variable im Sample3 () -Konstruktor und Klassenklassenvariable (i) wurde überschrieben. Um dies zu vermeiden, müssen wir entweder verwenden
quelle
Wie in den C ++ - Kernrichtlinien erläutert. C.49: Initialisierung der Zuweisung in Konstruktoren vorziehen , verhindert unnötige Aufrufe von Standardkonstruktoren.
quelle