2009-05-29 17 views

Antwort

202

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. Bedenken Sie:

class A 
{ 
public: 
    A() { x = 0; } 
    A(int x_) { x = x_; } 
    int x; 
}; 

class B 
{ 
public: 
    B() 
    { 
     a.x = 3; 
    } 
private: 
    A a; 
}; 

In diesem Fall wird der Konstruktor für B wird der Standard-Konstruktor für A, rufen und dann initialisieren a.x bis 3. Ein besserer Weg für B wäre ‚s Konstruktor direkt anrufen A‘ s Konstruktor in die Initialisiererliste:

B() 
    : a(3) 
{ 
} 

nennen würde diese nur A ‚s A(int) Konstruktor und seine Standard-Konstruktor nicht. In diesem Beispiel ist der Unterschied vernachlässigbar, aber stellen Sie sich vor, dass der Standardkonstruktor von A mehr getan hat, wie z. B. das Reservieren von Speicher oder das Öffnen von Dateien. Du würdest das nicht unnötig machen wollen.

Wenn darüber hinaus eine Klasse keinen Standardkonstruktor haben, oder Sie haben eine const Membervariable, Sie müssen eine Initialisiererliste verwenden:

class A 
{ 
public: 
    A(int x_) { x = x_; } 
    int x; 
} 

class B 
{ 
public: 
    B() : a(3), y(2) // 'a' and 'y' MUST be initialized in an initializer list; 
    {     // it is an error not to do so 
    } 
private: 
    A a; 
    const int y; 
}; 
+0

Klassenmitglieder verwenden, die Klassen sind, können POD sein, auch –

+1

ein Muss ist auch wichtig bei einer Referenz – 4pie0

+1

Warum nicht „a (3);“ oder "a = A (3);" im Körper des Standardkonstruktors von B? – Sergey

34

Neben den Leistungs oben genannten Gründen, wenn Ihre Klasse speichert Verweise auf Objekte, die als Konstruktorparameter übergeben werden, oder Ihre Klasse hat const-Variablen, dann haben Sie keine andere Wahl als die Verwendung von Initialisierungslisten.

+6

Gleiches gilt für konstante Mitglieder, glaube ich. –

6

Neben den Leistungsproblemen gibt es noch eine weitere sehr wichtige, die ich Code-Wartbarkeit und Erweiterbarkeit nennen würde.

Wenn ein T ein POD ist und Sie eine Initialisierungsliste bevorzugen, dann müssen Sie nicht mehr um die Initialisierung herum Änderungen vornehmen, um unnötige Konstruktoraufrufe zu vermeiden, da diese bereits optimiert sind .

Wenn Typ T über einen Standardkonstruktor und einen oder mehrere benutzerdefinierte Konstruktoren verfügt und Sie einmal das Standard-Konstruktor entfernen oder ausblenden, müssen Sie bei Verwendung der Initialisierungsliste den Code für Ihren Benutzer nicht aktualisieren -definierte Konstruktoren, weil sie bereits korrekt implementiert sind.

Gleiche mit const Mitglieder oder Referenzelemente, sagen wir mal zunächst T wie folgt definiert ist:

struct T 
{ 
    T() { a = 5; } 
private: 
    int a; 
}; 

Als nächstes Sie sich entscheiden, eine als const zu qualifizieren, wenn Sie Initialisierungsliste von Anfang an verwenden würde, dann ist diese eine einzige Zeile ändern, aber wie oben die T definiert zu haben, es erfordert auch die Konstruktordefinition zu graben Zuordnung zu entfernen:

struct T 
{ 
    T() : a(5) {} // 2. that requires changes here too 
private: 
    const int a; // 1. one line change 
}; 

es ist kein Geheimnis, dass die Wartung, wenn Code wesentlich einfacher und weniger fehleranfällig ist war geschrieben nicht von einem "Code Monke" y "aber von einem Ingenieur, der Entscheidungen aufgrund einer tieferen Überlegung über das macht, was er tut.

+2

+1 für "code monkey" – Invictus

4

Bevor der Rumpf des Konstruktors ausgeführt wird, werden alle Konstruktoren für seine Elternklasse und dann für seine Felder aufgerufen. Standardmäßig werden die Konstruktoren ohne Argumente aufgerufen. Initialisierungslisten ermöglichen Ihnen auszuwählen, welcher Konstruktor aufgerufen wird und welche Argumente der Konstruktor erhält.

Wenn Sie ein Referenz- oder ein Const-Feld haben oder wenn eine der verwendeten Klassen keinen Standardkonstruktor hat, müssen Sie eine Initialisierungsliste verwenden.

2
// Without Initializer List 
class MyClass { 
    Type variable; 
public: 
    MyClass(Type a) { // Assume that Type is an already 
        // declared class and it has appropriate 
        // constructors and operators 
     variable = a; 
    } 
}; 

Hier Compiler folgt folgenden Schritte ein Objekt des Typs zu schaffen MyClass
1. des Typkonstruktor zuerst für „a“ bezeichnet wird.
2. Der Zuweisungsoperator von „Typ“ ist im Inneren des Körpers von MyClass() Konstruktor aufgerufen zuweisen

variable = a; 
  1. Und dann schließlich destructor von „Type“ genannt wird für "a", da es den Rahmen sprengt.

    Betrachten wir nun den gleichen Code mit MyClass() Konstruktor mit Initialisierungsliste

    // With Initializer List 
    class MyClass { 
    Type variable; 
    public: 
    MyClass(Type a):variable(a) { // Assume that Type is an already 
           // declared class and it has appropriate 
           // constructors and operators 
    } 
    }; 
    

    Mit der Initialisierungsliste, wie folgt durch Compiler gefolgt:

    1. Copy-Konstruktor von „Typ“ Klasse ist aufgerufen, um zu initialisieren: Variable (a). Die Argumente in der Initialisiererliste werden verwendet, um Konstrukt "Variable" direkt zu kopieren.
    2. Destruktor von "Typ" wird für "a" aufgerufen, da es den Gültigkeitsbereich verlässt.
+2

Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung aus dem Code wirklich, die Qualität Ihres Posts zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in der Zukunft beantworten, und diese Leute könnten die Gründe für Ihren Codevorschlag nicht kennen. Bitte versuchen Sie auch nicht, Ihren Code mit erklärenden Kommentaren zu füllen, dies reduziert die Lesbarkeit sowohl des Codes als auch der Erklärungen! http://meta.stackexchange.com/q/114762/308249 – davejal

+2

Bitte, schreiben Sie Ihr eigenes Verständnis oder teilen Sie einfach Link zur ursprünglichen Quelle (hier, geeksforgeeks.com), anstatt nur kopieren-einfügen. – yuvi

10
  1. Initialisierung der Basisklasse

Ein wichtiger Grund für die Verwendung Konstruktor Initialisiererliste, die hier nicht in den Antworten erwähnt wird, ist die Initialisierung der Basisklasse.

Gemäß der Reihenfolge der Konstruktion sollte die Basisklasse vor der untergeordneten Klasse erstellt werden. Ohne Konstruktorinitialisiererliste ist dies möglich, wenn Ihre Basisklasse über einen Standardkonstruktor verfügt, der unmittelbar vor dem Eingeben des Konstruktors der Kindklasse aufgerufen wird.

Wenn Ihre Basisklasse jedoch nur den Konstruktor parametrisiert hat, müssen Sie die Konstruktorinitialisiererliste verwenden, um sicherzustellen, dass Ihre Basisklasse vor der Kindklasse initialisiert wird.

  1. Initialisierung der Subobjekte, die nur parametrisierte Konstruktoren

Mit Konstruktor Initialisiererliste

  • Effizienz haben, können Sie Ihre Datenelemente initialisieren genauer Status, den Sie in Ihrem Code benötigen, anstatt sie zunächst auf ihren Standardzustand & zu initialisieren und dann ihren Status auf den zu ändern, den Sie benötigen dein Code.

    1. Initialisierung nicht-static const Datenelemente

    Wenn nicht-statische const Datenelemente in Ihrer Klasse Standardkonstruktoren haben & Sie nicht Konstruktor Initialisiererliste verwenden Sie Sie können sie nicht in den beabsichtigten Status initialisieren, da sie auf ihren Standardzustand initialisiert werden.

    1. Initialisierung von Daten Referenzelemente

    Angaben Referenz Mitglieder intialized werden müssen, wenn Compiler tritt Konstruktor als Referenz später initialisiert & nicht nur erklärt werden kann. Dies ist nur mit der Konstruktorinitialisierungsliste möglich.

  • 0

    Syntax:

    class Sample 
        { 
        public: 
         int Sam_x; 
         int Sam_y; 
    
        Sample(): Sam_x(1), Sam_y(2)  /* Classname: Initialization List */ 
        { 
          // Constructor body 
        } 
        }; 
    

    Need der Initialisierung Liste:

    class Sample 
    { 
        public: 
         int Sam_x; 
         int Sam_y; 
    
        Sample()  */* Object and variables are created - i.e.:declaration of variables */* 
        { // Constructor body starts 
    
         Sam_x = 1;  */* Defining a value to the variable */* 
         Sam_y = 2; 
    
        } // Constructor body ends 
        }; 
    

    in dem obigen Programm, wenn der Konstruktor der Klasse ausgeführt wird, Sam_x und Sam_y erstellt werden. Dann werden diese Elementdatenvariablen im Konstruktorhauptteil definiert.

    Use Cases:

    1. Konst und Referenzvariablen in der Klasse

    In C Variablen muss bei der Erstellung definiert werden. Auf die gleiche Weise müssen wir in C++ die Variablen Const und Reference während der Objekterstellung mit der Initialisierungsliste initialisieren. Wenn wir nach der Objekterstellung eine Initialisierung durchführen (Inside-Konstruktor), erhalten wir einen Kompilierzeitfehler.

    1. Mitglied Objekte Sample1 (Basis) Klasse, die nicht Standardkonstruktors

      class Sample1 
      { 
          int i; 
          public: 
          Sample1 (int temp) 
          { 
           i = temp; 
          } 
      }; 
      
          // Class Sample2 contains object of Sample1 
      class Sample2 
      { 
          Sample1 a; 
          public: 
          Sample2 (int x): a(x)  /* Initializer list must be used */ 
          { 
      
          } 
      }; 
      

    haben Während Objekt für abgeleitete Klasse erstellen, die intern wird Klasse abgeleitet Anrufe Konstruktor und ruft Basisklassenkonstruktor auf (Standard). Wenn die Basisklasse keinen Standardkonstruktor hat, erhält der Benutzer einen Kompilierzeitfehler.Um zu vermeiden, müssen wir entweder

    1. Default constructor of Sample1 class 
    2. Initialization list in Sample2 class which will call the parametric constructor of Sample1 class (as per above program) 
    
      haben
    1. Klasse Konstruktor des Parameternamen und Daten Mitglied einer Klasse sind die gleichen:

      class Sample3 { 
          int i;   /* Member variable name : i */ 
          public: 
          Sample3 (int i) /* Local variable name : i */ 
          { 
           i = i; 
           print(i); /* Local variable: Prints the correct value which we passed in constructor */ 
          } 
          int getI() const 
          { 
           print(i); /*global variable: Garbage value is assigned to i. the expected value should be which we passed in constructor*/ 
           return i; 
          } 
      }; 
      

    Wie wir alle wissen, lokale Variable hat höchste Priorität dann globale Variable, wenn beide Variablen denselben Namen haben. In diesem Fall berücksichtigt das Programm den "i" -Wert {sowohl die linke als auch die rechte Seitenvariable. h.: ​​i = i} als lokale Variable in Sample3() -Konstruktor und Klassenmitgliedsvariable (i) wurde überschrieben. Um zu vermeiden, müssen wir entweder

    1. Initialization list 
        2. this operator. 
    
    Verwandte Themen