c++ initialize - Machen die Klammern hinter dem Typnamen einen Unterschied zu neuen?





static member (6)


Nein, sie sind gleich. Aber es gibt einen Unterschied zwischen:

Test t;      // create a Test called t

und

Test t();   // declare a function called t which returns a Test

Dies liegt an der grundlegenden Regel von C ++ (und C): Wenn etwas möglicherweise eine Deklaration sein kann, dann ist es eine Deklaration.

Edit: Zu den Initialisierungsproblemen bezüglich POD und Nicht-POD Daten, während ich mit allem, was gesagt wurde, einverstanden bin, möchte ich nur darauf hinweisen, dass diese Probleme nur dann gelten, wenn das Ding, das neu oder anders gebaut wird, kein a benutzerdefinierter Konstruktor Wenn es einen solchen Konstruktor gibt, wird es verwendet. Für 99,99% der vernünftig entworfenen Klassen wird es einen solchen Konstruktor geben, und so können die Probleme ignoriert werden.

Wenn "Test" eine gewöhnliche Klasse ist, gibt es einen Unterschied zwischen:

Test* test = new Test;

und

Test* test = new Test();



Im Allgemeinen haben wir Default-Initialisierung im ersten Fall und Wert-Initialisierung im zweiten Fall.

Zum Beispiel: im Fall mit int (POD-Typ):

  • int* test = new int - wir haben irgendeine Initialisierung und der Wert von * test kann beliebig sein.

  • int* test = new int() - * test hat den Wert 0.

Das nächste Verhalten hängt von Ihrem Typ Test ab. Wir haben unterschiedliche Fälle: Test hat default Konstruktor, Test hat Standardkonstruktor generiert, Test enthält POD Mitglied, nicht POD Mitglied ...




Lassen Sie uns pedantisch werden, denn es gibt Unterschiede, die das Verhalten Ihres Codes beeinflussen können. Viele der folgenden Aussagen stammen aus Kommentaren zu einem "Old New Thing" -Artikel .

Manchmal wird der vom neuen Operator zurückgegebene Speicher initialisiert, und manchmal hängt es nicht davon ab, ob der Typ, den Sie gerade neu erstellen, ein POD (einfache alte Daten) ist oder ob es eine Klasse ist, die POD-Mitglieder enthält Vom Compiler generierter Standardkonstruktor.

  • In C ++ 1998 gibt es 2 Arten der Initialisierung: Null und Standard
  • In C ++ 2003 wurde eine 3. Art der Initialisierung, Wert Initialisierung hinzugefügt.

Annehmen:

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

In einem C ++ 98-Compiler sollte das folgende auftreten:

  • new A - unbestimmter Wert
  • new A() - Nullinitialisierung

  • new B - Standardkonstrukt (B :: m ist nicht initialisiert)

  • new B() - Standardkonstrukt (B :: m ist nicht initialisiert)

  • new C - Standardkonstrukt (C :: m ist Null-initialisiert)

  • new C() - Standardkonstrukt (C :: m ist Null-initialisiert)

In einem C ++ 03-konformen Compiler sollten die Dinge so funktionieren:

  • new A - unbestimmter Wert
  • new A() - value-initialize A, das ist Null-Initialisierung, da es ein POD ist.

  • new B - default-initialisiert (lässt B :: m nicht initialisiert)

  • new B() - value-initialisiert B, das alle Felder null initialisiert, da sein Standard-ctor Compiler generiert und nicht benutzerdefiniert ist.

  • new C - default-initialisiert C, das den Standard-ctor aufruft.

  • new C() - value-initialisiert C, das den Standard-ctor aufruft.

In allen Versionen von C ++ gibt es also einen Unterschied zwischen dem new A und dem new A() weil A ein POD ist.

Und es gibt einen Unterschied im Verhalten zwischen C ++ 98 und C ++ 03 für den Fall new B() .

Dies ist eine der staubigen Ecken von C ++, die dich verrückt machen kann. Wenn Sie ein Objekt konstruieren, wollen / brauchen Sie manchmal die Parens, manchmal können Sie sie absolut nicht haben, und manchmal ist es egal.




new Thing(); ist explizit, dass Sie einen Konstruktor namens new Thing; Man nimmt an, dass es Ihnen nichts ausmacht, wenn der Konstruktor nicht aufgerufen wird.

Wenn es in einer Struktur / Klasse mit einem benutzerdefinierten Konstruktor verwendet wird, gibt es keinen Unterschied. Wenn eine triviale Struktur / Klasse aufgerufen wird (zB struct Thing { int i; }; ), dann new Thing; ist wie malloc(sizeof(Thing)); während new Thing(); ist wie calloc(sizeof(Thing)); - Es wird Null initialisiert.

Die Gotcha liegt dazwischen:

struct Thingy {
  ~Thingy(); // No-longer a trivial class
  virtual WaxOn();
  int i;
};

Das Verhalten des new Thingy; gegen new Thingy(); In diesem Fall wurde zwischen C ++ 98 und C ++ 2003 gewechselt. Siehe Michael Burrs Erklärung für wie und warum.




Wenn Test eine Klasse mit einem definierten Konstruktor ist, gibt es keinen Unterschied. Die letztere Form macht es ein wenig klarer, dass Testers Konstruktor läuft, aber das ist schon alles.




Eigentlich ist eine Referenz nicht wirklich ein Zeiger.

Ein Compiler behält "Verweise" auf Variablen bei und ordnet einen Namen einer Speicheradresse zu. Das ist seine Aufgabe, einen beliebigen Variablennamen beim Kompilieren in eine Speicheradresse zu übersetzen.

Wenn Sie eine Referenz erstellen, teilen Sie dem Compiler nur mit, dass Sie der Zeigervariable einen anderen Namen zuweisen. Deshalb können Referenzen nicht "auf Null zeigen", da eine Variable nicht sein kann und auch nicht.

Zeiger sind Variablen; Sie enthalten die Adresse einer anderen Variablen oder können Null sein. Wichtig ist, dass ein Zeiger einen Wert hat, während eine Referenz nur eine Variable hat, auf die sie verweist.

Nun einige Erklärung für echten Code:

int a = 0;
int& b = a;

Hier erstellen Sie keine weitere Variable, die auf a ; Sie fügen einfach einen anderen Namen zum Speicherinhalt hinzu, der den Wert von a . Dieser Speicher hat jetzt zwei Namen, a und b , und kann mit beiden Namen angesprochen werden.

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

Beim Aufruf einer Funktion generiert der Compiler normalerweise Speicherplätze für die Argumente, in die kopiert werden soll. Die Funktionssignatur definiert die zu erstellenden Leerzeichen und gibt den Namen an, der für diese Leerzeichen verwendet werden soll. Durch das Deklarieren eines Parameters als Referenz wird der Compiler lediglich angewiesen, den Speicherbereich der Eingabevariablen zu verwenden, anstatt während des Methodenaufrufs einen neuen Speicherplatz zuzuweisen. Es mag seltsam erscheinen, wenn Sie sagen, dass Ihre Funktion eine im aufrufenden Bereich deklarierte Variable direkt bearbeitet. Beachten Sie jedoch, dass beim Ausführen von kompiliertem Code kein Bereich mehr vorhanden ist. Es gibt nur einfachen Speicher, und Ihr Funktionscode könnte alle Variablen beeinflussen.

Nun kann es Fälle geben, in denen Ihr Compiler die Referenz beim Kompilieren möglicherweise nicht kennt, z. B. wenn Sie eine externe Variable verwenden. Eine Referenz kann also als Zeiger im zugrunde liegenden Code implementiert sein oder nicht. In den Beispielen, die ich Ihnen gegeben habe, wird es höchstwahrscheinlich nicht mit einem Zeiger implementiert.





c++ constructor initialization new-operator c++-faq