überladen - standardkonstruktor c++




Gibt es in C++ einen impliziten Standardkonstruktor? (8)

  • Generiert der Compiler automatisch einen Standardkonstruktor?
  • Führt der implizit generierte Standardkonstruktor eine Nullinitialisierung durch?

Wenn du die Sprache des Standards von 2003 legal parst, dann sind die Antworten ja und nein . Dies ist jedoch nicht die ganze Geschichte, da im Gegensatz zu einem benutzerdefinierten Standardkonstruktor ein implizit definierter Standardkonstruktor nicht immer beim Erstellen eines Objekts von Grund auf verwendet wird - es gibt zwei andere Szenarien: keine Konstruktion und teilweisen Werteinitialisierung .

Der "no construction" -Fall ist eigentlich nur eine technische Eigenschaft, da er funktional nicht anders ist als der triviale Standardkonstruktor. Der andere Fall ist interessanter: member-wise value-initialization wird mit "()" aufgerufen [als würde man explizit einen Konstruktor aufrufen, der keine Argumente hat] und umgeht das, was man technisch als Standardkonstruktor bezeichnet . Stattdessen führt er rekursiv eine Wert-Initialisierung für jedes Datenelement durch, und für primitive Datentypen wird schließlich eine Null-Initialisierung durchgeführt .

Der Compiler stellt also zwei verschiedene implizit definierte Standardkonstruktoren zur Verfügung . Eine davon führt eine Null-Initialisierung der Grundelementdaten durch und die andere nicht. Hier sind einige Beispiele, wie Sie jeden Typ von Konstruktor aufrufen können:

    MyClass a; // default-construction or no construction
    MyClass b = MyClass(); // member-wise value-initialization

und

    new MyClass; // default-construction or no construction
    new MyClass(); // member-wise value-initialization

Hinweis: Wenn ein benutzerdefinierter Standardkonstruktor vorhanden ist, ruft die teilweisen Wertinitialisierung diesen einfach auf und stoppt.

Hier ist eine etwas detaillierte Aufschlüsselung dessen, was der Standard dazu sagt ...

  • Wenn Sie keinen Konstruktor deklarieren, erstellt der Compiler implizit einen Standardkonstruktor [12.1-5].

  • Der Standardkonstruktor initialisiert primitive Typen nicht [12.1-7]

    MyClass() {} // implicitly defined constructor
    
  • Wenn Sie ein Objekt mit "()" initialisieren, ruft dies nicht direkt den Standardkonstruktor auf. Stattdessen initiiert es eine lange Sequenz von Regeln namens Wert-Initialisierung [8.5-7]

  • Der Nettoeffekt der Wertinitialisierung ist, dass der implizit deklarierte Standardkonstruktor nie aufgerufen wird . Stattdessen wird eine rekursive elementweise Wertinitialisierung aufgerufen, die schließlich alle primitiven Elemente initialisiert und den Standardkonstruktor für alle Elemente aufruft, die über einen vom Benutzer deklarierten Konstruktor [8.5-5] verfügen.

  • Wert-Initialisierung gilt auch für primitive Typen - sie werden Null-initialisiert. [8.5-5]

    a = int(); // equivalent to int a=0;
    

All dies ist für die meisten Zwecke wirklich strittig. Der Schreiber einer Klasse kann im Allgemeinen nicht davon ausgehen, dass Datenelemente während einer impliziten Initialisierungssequenz auf Null gesetzt werden. Daher sollte jede selbstverwaltende Klasse ihren eigenen Konstruktor definieren, wenn sie über primitive Datenelemente verfügt, die initialisiert werden müssen.

Also, wann ist das wichtig?

  • Unter Umständen kann generischer Code die Initialisierung unbekannter Typen erzwingen. Value-Initialisierung bietet eine Möglichkeit, dies zu tun. Denken Sie daran, dass die implizite Zero-Initialisierung nicht auftritt, wenn der Benutzer einen Konstruktor bereitgestellt hat.

  • Standardmäßig sind die Daten von std :: vector value-initialisiert. Dies kann verhindern, dass Speicherdebugger logische Fehler identifizieren, die ansonsten nicht initialisierten Speicherpuffern zugeordnet sind.

    vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
    
  • Ganze Arrays von Primitivtypen oder Strukturen vom Typ "plain-alt-data" (POD) können mit Hilfe der Wertinitialisierungssyntax auf Null initialisiert werden.

    new int[100]();
    

Dieser Beitrag enthält mehr Details zu Variationen zwischen den Versionen des Standards und enthält auch einen Fall, in dem der Standard in wichtigen Compilern unterschiedlich angewendet wird.

In dem Buch, das ich gerade lese ( C ++ Without Fear ) heißt es, dass der Compiler, wenn Sie keinen Standardkonstruktor für eine Klasse deklarieren, einen für Sie bereitstellt, der "jedes Datenelement" ausfüllt. Ich habe damit experimentiert und sehe kein Nullsetzungsverhalten. Ich kann auch nichts finden, was dies bei Google erwähnt. Ist das nur ein Fehler oder eine Eigenart eines bestimmten Compilers?


C ++ garantiert nicht das Löschen von Speicher. Java und C # machen (sozusagen).

Einige Compiler könnten das, sind aber nicht davon abhängig.


C ++ generiert einen Standardkonstruktor. Wenn nötig (bestimmt zur Kompilierzeit glaube ich), wird es auch einen Standardkopiekonstruktor und einen Standardzuweisungskonstruktor erzeugen. Ich habe jedoch nichts über Garantien für das Löschen von Speicher gehört.


Der Compiler generiert Standardkonstruktoren und -destruktoren, wenn keine von Benutzern erstellten vorhanden sind. Diese werden den Status von Datenmembern nicht ändern.

In C ++ (und C) ist der Inhalt von zugewiesenen Daten nicht garantiert. In Debug-Konfigurationen setzen einige Plattformen dies auf einen bekannten Wert (zB 0xFEFEFEFE), um Bugs zu identifizieren, aber das sollte nicht verlässlich sein.


Der Standardkonstruktor, der für eine Klasse erstellt wird, initialisiert nicht integrierte Typen, sondern ruft den Standardkonstruktor für alle benutzerdefinierten Member auf:

class Foo
{
public:
     int x;
     Foo() : x(1) {}
};

class Bar
{
public:
     int y;
     Foo f;
     Foo *fp;
};

int main()
{

    Bar b1; 
    ASSERT(b1.f.x == 1); 
    // We know nothing about what b1.y is set to, or what b1.fp is set to.

    // The class members' initialization parallels normal stack initialization.
    int y;  
    Foo f; 
    Foo *fp; 
    ASSERT(f.x == 1);
    // We know nothing about what y is set to, or what fp is set to.

}

Ich denke, es ist erwähnenswert, dass der Standardkonstruktor nur vom Compiler erstellt wird, wenn Sie überhaupt keinen Konstruktor bereitstellen. Das heißt, wenn Sie nur einen Konstruktor angeben, der ein Argument akzeptiert, erstellt der Compiler nicht den Standard-Konstruktor no-arg für Sie.

Das Nullsetzungsverhalten, von dem Ihr Buch spricht, ist wahrscheinlich spezifisch für einen bestimmten Compiler. Ich habe immer angenommen, dass es variieren kann und dass Sie alle Datenmitglieder explizit initialisieren sollten.


Nullen ist nur bei Globalen möglich. Wenn also Ihr Objekt im globalen Gültigkeitsbereich deklariert ist, werden seine Mitglieder auf Null gesetzt:

class Blah
{
public:
    int x;
    int y;
};

Blah global;

int main(int argc, char **argv) {
    Blah local;
    cout<<global.x<<endl;  // will be 0
    cout<<local.x<<endl;   // will be random
}

Wenn Sie keinen Konstruktor definieren, definiert der Compiler einen Standardkonstruktor für Sie.

Die Umsetzung dieses

Standardkonstruktor ist:

  • Standardkonstrukt die Basisklasse (wenn die Basisklasse keinen Standardkonstruktor hat, ist dies ein Kompilierungsfehler)
  • Standardkonstrukt jede Mitgliedsvariable in der Reihenfolge der Deklaration. (Wenn ein Member keinen Standardkonstruktor hat, ist dies ein Kompilierungsfehler).

Hinweis:
Die POD-Daten (int, float, pointer, etc.) haben keinen expliziten Konstruktor, aber die Standardaktion ist, nichts zu tun (in der C ++ - Philosophie; wir wollen nicht für etwas bezahlen, es sei denn wir fragen explizit danach) .

Wenn kein Destruktor / Copy-Konstruktor / Zuweisungsoperator definiert ist, erstellt der Compiler einen dieser Konstrukte (also hat eine Klasse immer einen Destruktor / Konstruktor / Zuweisungsoperator kopieren (es sei denn, Sie betrügen und deklarieren einen explizit, aber definieren ihn nicht)).
Die Standardimplementierung ist:

Destruktor:

  • Wenn ein benutzerdefinierter Destruktor definiert ist, führen Sie den bereitgestellten Code aus.
  • Rufen Sie den Destruktor jedes Mitglieds in umgekehrter Reihenfolge der Deklaration auf
  • Rufen Sie den Destruktor der Basisklasse auf.

Konstruktor kopieren:

  • Rufen Sie die Basisklasse Copy Constructor auf.
  • Rufen Sie den Kopierkonstruktor für jede Elementvariable in der Reihenfolge der Deklaration auf.

Aufgabenverwalter:

  • Rufen Sie den Basisklassenzuweisungsoperator auf
  • Rufen Sie den Zuweisungsoperator jeder Mitgliedsvariablen in der Reihenfolge der Deklaration auf.
  • Gib eine Referenz zurück.

Hinweis Kopieren Sie den Konstruktions- / Zuweisungsoperator von POD-Daten kopiert nur die Daten (daher das Problem der flachen Kopie in Verbindung mit RAW-Zeigern).





constructor