Frage

Ich bin teilweise Memberinitialisierungsliste Listen mit meiner Konstrukteurs mit ... aber ich habe lange die Gründe für diesen seit vergessen ...

Sie Memberinitialisierungsliste Listen in Ihren Konstrukteure verwenden Sie? Wenn ja warum? Wenn nicht, warum nicht?

War es hilfreich?

Lösung

Für POD Klassenmitglieder, macht es keinen Unterschied, es ist nur eine Frage des Stils. Für die Teilnehmer, die Klassen sind, dann vermeidet es einen unnötigen Anruf an einen Default-Konstruktor. 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 rufen Sie den Standard-Konstruktor für A und dann a.x bis 3. initialisieren Ein besserer Weg für B Konstruktor würde direkt A Konstruktor in der Initialisierungsliste aufrufen:

B()
  : a(3)
{
}

Dies würde nur A des A(int) Konstruktor aufrufen und seine Standard-Konstruktor nicht. In diesem Beispiel ist der Unterschied zu vernachlässigen, aber vorstellen, wenn Sie Standardwert, A Konstruktor werden mehr getan haben, wie das Zuweisen von Speicher oder das Öffnen von Dateien. Sie würden nicht, dass unnötig machen wollen.

Wenn darüber hinaus eine Klasse keinen Standardkonstruktor haben, oder Sie haben eine const Membervariable, Sie muss 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;
};

Andere Tipps

oben Neben den Performance-Gründen erwähnt, wenn Ihre Klasse speichert Verweise auf Objekte als Konstruktor Parameter übergeben oder Ihre Klasse hat konstante Variablen dann haben Sie keine andere Wahl, als initializer Listen verwendet wird.

  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, Basisklasse sollte vor dem Kind Klasse gebaut werden. Ohne Konstruktor Initialisiererliste ist dies möglich, wenn Ihre Basisklasse Standardkonstruktor hat, die gerade genannt wird, bevor der Konstruktor der Kinderklasse eingeben.

Aber, wenn Ihre Basisklasse nur parametrisierte Konstruktor hat, dann müssen Sie Konstruktor Initialisiererliste verwenden, um sicherzustellen, dass Ihre Basisklasse vor Kind Klasse initialisiert wird.

  1. Initialisierung von Subobjekte, die nur parametrisierte Konstruktoren

  2. Effizienz

Mit Konstruktor Initialisiererliste initialisieren Sie Ihre Datenelemente zu dem genauen Zustand, die Sie in Ihrem Code benötigen, anstatt sie zuerst auf den Standardzustand zu initialisieren und dann ihren Zustand auf die einem Wechsel Sie in Ihrem Code benötigen.

  1. Initialisierung nicht-static const Datenelemente

Wenn nicht-statische const Datenelemente in Ihrer Klasse Standardkonstruktoren haben und Sie nicht Konstruktor Initialisiererliste verwenden, werden Sie nicht in der Lage sein, sie zu beabsichtigten Zustand zu initialisieren, wie sie auf ihren Standardzustand initialisiert werden.

  1. Initialisierung von Referenzdatenelementen

Referenzdatenelemente müssen initialisiert werden, wenn Compiler Konstruktor tritt als Referenz kann später nicht mehr nur deklariert und initialisiert werden. Dies ist nur möglich mit Konstruktor Initialisiererliste.

Neben den Performance-Probleme, es ist ein anderer sehr wichtig, die ich Code Wartbarkeit und Erweiterbarkeit nennen würde.

Wenn ein T POD und starten Sie Initialisierungsliste bevorzugen, dann, wenn eine Zeit T auf einen Nicht-POD-Typ ändern, werden Sie nichts um Initialisierung ändern müssen, um unnötige Konstruktor zu vermeiden, fordert, da es bereits optimiert ist.

Wenn Typ T hat Standardkonstruktors hat und eine oder mehr benutzerdefinierten Konstrukteure und eine Zeit, die Sie entscheiden, den Standard ein entfernen oder zu verbergen, dann, wenn Initialisierungsliste verwendet wurde, brauchen Sie nicht den Code zu aktualisieren, wenn Ihre benutzer- definiert Konstrukteure, weil sie bereits korrekt umgesetzt werden.

Same 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ächst Sie entscheiden, wie const a zu qualifizieren, wenn Sie Initialisierungsliste von Anfang an verwenden würden, dann war dies eine einzige Zeile zu ä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 wesentlich einfacher ist und weniger fehleranfällig, wenn Code geschrieben wurde, nicht von einem „Code Affen“, sondern von einem Ingenieure, die Entscheidungen auf der Grundlage vertiefter Betrachtung macht, was er tut.

Vor dem Körper des Konstruktor ausgeführt wird, Klasse alle der Konstrukteure für seine Eltern und dann für seine Felder aufgerufen werden. Standardmäßig werden die sind kein Argument Konstrukteure aufgerufen. Initialisierung Listen können Sie wählen, welche Konstruktor aufgerufen wird und welche Argumente, dass Konstruktor erhält.

Wenn Sie eine Referenz oder ein const Feld, oder wenn eine der Klassen verwendet wird, nicht über einen Standardkonstruktor verfügen, müssen Sie eine Initialisierungsliste verwenden.

// 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 Compiler wie folgt ein Objekt vom Typ MyClass
erstellen      1. Die Art Konstruktor wird zuerst genannt für „a“.
    2. Der Zuweisungsoperator von „Typ“ ist im Inneren des Körpers von MyClass () Konstruktor aufgerufen zuweisen

variable = a;
  1. Und dann destructor schließlich von „Typ“ für „a“ bezeichnet wird, da sie den Gültigkeitsbereich verlässt.

    Jetzt den gleichen Code mit MyClass () Konstruktor mit Initialisierungsliste betrachten

    // 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 werden folgende Schritte durch Compiler gefolgt:

    1. Kopieren Konstruktor „Typ“ Klasse aufgerufen wird zu initialisieren: Variable (a). Die Argumente in Initialisiererliste werden verwendet, um zu kopieren zu konstruieren „Variable“ direkt an.
    2. Destructor von „Type“ für „a“ bezeichnet, da sie den Gültigkeitsbereich verlässt.

Nur einige zusätzliche Informationen hinzuzufügen, wie viel Unterschied Liste der Memberinitialisierungsliste zu demonstrieren mak kann . Im leetcode 303 Bereich Sum Abfrage - Unveränderliche, https://leetcode.com/problems / Entfernungs-Summe-Abfrage-unveränderliche / , wo Sie zu bauen und initialisieren benötigen einen Vektor mit bestimmten Größe auf Null. Hier zwei unterschiedliche Implementierung und Geschwindigkeitsvergleich.

Ohne Memberinitialisierungsliste Liste zu bekommen AC es kostete mich über 212 ms .

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 Initialisierungsliste , die Zeit AC zu erhalten, ist über 108 ms . Mit diesem einfachen Beispiel ist es ziemlich offensichtlich, dass, Initialisierungsliste ist viel effizienter . All die Messung der 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];
}
};

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. Dann in Konstruktorrumpf, wird dieses Mitglied Datenvariablen definiert.

Use Cases:

  1. Konst und Referenzvariablen in der Klasse

In C Variablen muss bei der Erstellung definiert werden. die gleiche Art und Weise in C ++, müssen wir die Konst und Bezugsgröße bei der Objekterstellung durch Verwendung Initialisierung Liste initialisieren. wenn wir Initialisierung nach Objekterstellung (Innen Konstruktorrumpf) tun, werden wir Zeitfehler erhalten kompilieren.

  1. Die Mitgliedobjekte Sample1 (Basis) Klasse, die nicht Standard-Konstruktor 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 */
      {
    
      }
     };
    

Während Objekt für abgeleitete Klasse zu schaffen, die intern werden Anrufe Klassenkonstruktors abgeleitet und ruft Basisklassenkonstruktor (Standard). wenn Basisklasse nicht Standardkonstruktor hat, wird der Benutzer erhält Zeitfehler kompilieren. 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)
  1. Klasse Konstruktor des Parameternamen und Daten Mitglied einer Klasse sind gleich:

     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 mit der höchsten Priorität dann globale Variable, wenn beide Variablen denselben Namen haben. In diesem Fall sollten Sie das Programm „i“ Wert {beide linken und rechten Seite variabel. das heißt: i = i} als lokaler Variable in Sample3 () Konstruktor und Klasse Membervariable (i) bekamen außer Kraft setzen. Um zu vermeiden, müssen wir entweder verwenden

  1. Initialization list 
  2. this operator.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top