it-swarm.com.de

Warum sollte ich die Mitgliederinitialisierungsliste vorziehen?

Ich bin teilweise damit beschäftigt, Member-Initialisierungslisten mit meinen Konstruktoren zu verwenden ... aber ich habe die Gründe dafür längst vergessen ... 

Verwenden Sie in Ihren Konstruktoren Elementinitialisierungslisten? Wenn ja warum? Wenn nein, warum nicht? 

182
paxos1977

Für POD class-Mitglieder macht es keinen Unterschied, es ist nur eine Frage des Stils. Bei Klassenmitgliedern, die Klassen sind, wird ein unnötiger Aufruf eines Standardkonstruktors vermieden. Erwägen:

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 ruft der Konstruktor für B den Standardkonstruktor für A auf und initialisiert a.x auf 3. Eine bessere Methode wäre, wenn der Konstruktor von B direkt den Konstruktor von A in der Initialisierungsliste aufruft:

B()
  : a(3)
{
}

Dies würde nur den A(int)-Konstruktor von A und nicht seinen Standardkonstruktor aufrufen. In diesem Beispiel ist der Unterschied vernachlässigbar. Stellen Sie sich jedoch vor, dass der Standardkonstruktor von A mehr getan hat, z. B. Speicher zuweisen oder Dateien öffnen. Sie würden das nicht unnötig machen wollen.

Wenn eine Klasse keinen Standardkonstruktor hat oder Sie eine const-Membervariable haben, verwenden Sie must eine Initialisierungsliste:

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;
};
241
Adam Rosenfield

Abgesehen von den oben genannten Leistungsgründen haben Sie, wenn Ihre Klasse Verweise auf als Konstruktorparameter übergebene Objekte speichert oder Ihre Klasse über konstante Variablen verfügt, keine andere Wahl als die Verwendung von Initialisierungslisten.

40
Naveen
  1. Initialisierung der Basisklasse

Ein wichtiger Grund für die Verwendung der Konstruktor-Initialisierungsliste, die hier nicht erwähnt wird, ist die Initialisierung der Basisklasse.

Gemäß der Reihenfolge der Konstruktion sollte die Basisklasse vor der Kindklasse erstellt werden. Ohne die 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 nur den Konstruktor parametrisiert hat, müssen Sie die Konstruktor-Initialisierungsliste verwenden, um sicherzustellen, dass Ihre Basisklasse vor der untergeordneten Klasse initialisiert wird.

  1. Initialisierung von Teilobjekten, die nur über parametrisierte Konstruktoren verfügen

  2. Effizienz

Mit der Konstruktor-Initialisierungsliste initialisieren Sie Ihre Datenelemente so, dass sie den genauen Status bestimmen, den Sie in Ihrem Code benötigen, anstatt sie zunächst auf ihren Standardstatus zu initialisieren und dann ihren Status in den Status zu ändern, den Sie in Ihrem Code benötigen.

  1. Nicht statische const-Datenelemente initialisieren

Wenn nicht statische const-Datenelemente in Ihrer Klasse über Standardkonstruktoren verfügen und Sie keine Konstruktor-Initialisierungsliste verwenden, können Sie sie nicht in den beabsichtigten Status initialisieren, da sie auf ihren Standardstatus initialisiert werden.

  1. Initialisierung von Referenzdatenelementen

Referenzdatenelemente müssen initialisiert werden, wenn der Compiler in den Konstruktor eintritt, da Referenzen nicht einfach später deklariert und initialisiert werden können. Dies ist nur mit der Konstruktor-Initialisierungsliste möglich.

14
yuvi

Neben den Leistungsproblemen gibt es noch einen weiteren, den ich als Wartungs- und Erweiterbarkeit von Code bezeichnen würde.

Wenn ein T POD ist und Sie die Initialisierungsliste bevorzugen, müssen Sie, wenn T einmal in einen Nicht-POD-Typ geändert wird, nichts an der Initialisierung ändern, um unnötige Konstruktoraufrufe zu vermeiden, da dieses bereits optimiert ist.

Wenn der Typ T über einen Standardkonstruktor und einen oder mehrere benutzerdefinierte Konstruktoren verfügt und Sie sich einmal dafür entscheiden, den Standardkonstruktor zu entfernen oder auszublenden, müssen Sie bei der Verwendung der Initialisierungsliste keinen Code aktualisieren, wenn Ihre benutzerdefinierten Konstruktoren da sind Sie sind bereits korrekt implementiert.

Das Gleiche gilt für const-Member oder Referenz-Member. Angenommen, T ist wie folgt definiert:

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

Als Nächstes entscheiden Sie sich dafür, a als const zu qualifizieren. Wenn Sie die Initialisierungsliste von Anfang an verwenden würden, war dies eine Änderung in einer einzelnen Zeile. Da das T jedoch wie oben definiert ist, müssen Sie auch die Konstruktordefinition übergeben, um die Zuweisung 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 viel einfacher und weniger fehleranfällig ist, wenn Code nicht von einem "Code-Affen" geschrieben wurde, sondern von einem Ingenieur, der Entscheidungen aufgrund tieferer Überlegungen über das, was er tut, trifft.

8
mloskot

Bevor der Rumpf des Konstruktors ausgeführt wird, werden alle Konstruktoren für die übergeordnete Klasse und dann für die Felder aufgerufen. Standardmäßig werden die Konstruktoren ohne Argumente aufgerufen. In Initialisierungslisten können Sie auswählen, welcher Konstruktor aufgerufen wird und welche Argumente er vom Konstruktor erhält. 

Wenn Sie über ein Referenz- oder ein const-Feld verfügen oder eine der verwendeten Klassen über keinen Standardkonstruktor verfügt, müssen Sie eine Initialisierungsliste verwenden.

5
Jamal Zafar
// 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 folgt der Compiler den folgenden Schritten, um ein Objekt vom Typ MyClass zu erstellen 
1. Der Konstruktor von Type wird zuerst für "a" aufgerufen. 
2. Der Zuweisungsoperator von „Type“ wird im Rumpf des MyClass () - Konstruktors zum Zuweisen aufgerufen 

variable = a;
  1. Und schließlich wird der Destruktor von „Type“ als „a“ bezeichnet, da er außerhalb des Gültigkeitsbereichs liegt.

    Betrachten Sie nun denselben Code mit MyClass () - Konstruktor mit der Initializer-Liste

    // 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
    }
    };
    

    Bei der Initialisierungsliste folgen die folgenden Schritte vom Compiler:

    1. Der Copy-Konstruktor der Klasse "Type" wird aufgerufen, um die Variable (a) zu initialisieren. Die Argumente in der Initialisierungsliste werden verwendet, um das Konstrukt "Variable" direkt zu kopieren.
    2. Der Destruktor von „Type“ wird für „a“ aufgerufen, da er außerhalb des Gültigkeitsbereichs liegt.
2
Rahul Singh

Nur um zusätzliche Informationen hinzuzufügen, um zu zeigen, wie groß die Mitgliederinitialisierungsliste sein kann. Im Leetcode 303 Range Sum Query - Immutable https://leetcode.com/problems/range-sum-query-immutable/ müssen Sie einen Vektor mit einer bestimmten Größe konstruieren und initialisieren, um ihn auf Null zu setzen. Hier sind zwei verschiedene Implementierungen und Geschwindigkeitsvergleiche.

Ohne Member-Initialisierungsliste, kostete es mich für 212 ms, um AC zu erhalten.

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) {
    preSum = vector<int>(nums.size()+1, 0);
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};

Jetzt mit der Elementinitialisierungsliste, ist die Zeit, um AC zu erhalten, ungefähr 108 ms. Bei diesem einfachen Beispiel ist es offensichtlich, dass die Member-Initialisierungsliste wesentlich effizienter ist. Die gesamte Messung bezieht sich auf die Laufzeit von LC.

class NumArray {
public:
vector<int> preSum;
NumArray(vector<int> nums) : preSum(nums.size()+1, 0) { 
    int ps = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        ps += nums[i];
        preSum[i+1] = ps;
    }
}

int sumRange(int i, int j) {
    return preSum[j+1] - preSum[i];
}
};
0
Yi Wang

Syntax:

  class Sample
  {
     public:
         int Sam_x;
         int Sam_y;

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

Erforderliche Initialisierungsliste:

 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
  };

im obigen Programm werden beim Ausführen des Konstruktors der Klasse Sam_x und Sam_y erstellt. Anschließend werden im Konstruktorkörper diese Elementdatenvariablen definiert.

Anwendungsfälle:

  1. Const- und Referenzvariablen in einer Klasse

In C müssen die Variablen must während der Erstellung definiert werden. Auf dieselbe Weise in C++ müssen wir die Variablen Const und Reference während der Objekterstellung unter Verwendung der Initialisierungsliste initialisieren. Wenn wir nach der Objekterstellung eine Initialisierung durchführen (Inside-Konstruktorkörper), wird ein Fehler beim Kompilieren angezeigt.

  1. Elementobjekte der Klasse Sample1 (Basis), die keinen Standardkonstruktor haben

     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 */
      {
    
      }
     };
    

Beim Erstellen eines Objekts für eine abgeleitete Klasse, die intern den Konstruktor für abgeleitete Klassen und den Basisklassenkonstruktor (Standard) aufruft. Wenn die Basisklasse keinen Standardkonstruktor hat, wird dem Benutzer ein Fehler bei der Kompilierung angezeigt. Um zu vermeiden, müssen wir entweder haben 

 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)
  1. Der Parametername und der Member der Klasse eines Klassenkonstruktors sind identisch:

     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, hat die lokale Variable die höchste Priorität und dann die globale Variable, wenn beide Variablen denselben Namen haben. In diesem Fall berücksichtigt das Programm den Wert "i" {sowohl linke als auch rechte Variable. Das heißt: i = i} als lokale Variable in Sample3 () - Konstruktor und Klassenmitgliedsvariable (i) wurde überschrieben. Um dies zu vermeiden, müssen wir beide verwenden 

  1. Initialization list 
  2. this operator.
0
Eswaran Pandi