1114 Stimmen

Machen die Klammern nach dem Typnamen bei new einen Unterschied?

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

Test* test = new Test;

et

Test* test = new Test();

4voto

ThatsJustCheesy Punkte 1243

Die Regeln für new sind analog zu dem, was passiert, wenn Sie ein Objekt mit automatischer Speicherdauer initialisieren (obwohl die Syntax wegen der lästigen Parsen etwas anders sein kann).

Wenn ich sage:

int my_int; // default-initialize  indeterminate (non-class type)

Entonces my_int hat einen unbestimmten Wert, da es sich um eine Nicht-Klassenart handelt. Alternativ kann ich auch eine Wertinitialisierung durchführen my_int (die für Nicht-Klassentypen null-initialisiert) wie folgt:

int my_int{}; // value-initialize  zero-initialize (non-class type)

(Natürlich kann ich nicht mit () denn das wäre eine Funktionsdeklaration, aber int() funktioniert genauso wie int{} um ein Provisorium zu konstruieren).

Für Klassenarten hingegen:

Thing my_thing; // default-initialize  default ctor (class type)
Thing my_thing{}; // value-initialize  default-initialize  default ctor (class type)

Der Standardkonstruktor wird aufgerufen, um eine Thing keine Ausnahmen.

Die Regeln lauten also mehr oder weniger:

  • Ist es eine Klassenart?
    • YES : Der Standardkonstruktor wird aufgerufen, unabhängig davon, ob er wertinitialisiert ist (mit {} ) oder standard-initialisiert (ohne {} ). (Bei der Wertinitialisierung gibt es ein zusätzliches vorheriges Nullsetzungsverhalten, aber der Standardkonstruktor hat immer das letzte Wort).
    • NO : Wurde {} verwendet?
      • YES : Das Objekt ist wertinitialisiert, was bei Nicht-Klassentypen mehr oder weniger eine Null-Initialisierung bedeutet.
      • NO : Das Objekt ist standardmäßig initialisiert, was bei Nicht-Klassentypen dazu führt, dass es einen unbestimmten Wert hat (es ist effektiv nicht initialisiert).

Diese Regeln lauten genau new Syntax, mit der zusätzlichen Regel, dass () kann ersetzt werden durch {} denn new wird nie als Funktionsdeklaration geparst. Also:

int* my_new_int = new int; // default-initialize  indeterminate (non-class type)
Thing* my_new_thing = new Thing; // default-initialize  default ctor (class type)
int* my_new_zeroed_int = new int(); // value-initialize  zero-initialize (non-class type)
     my_new_zeroed_int = new int{}; // ditto
       my_new_thing = new Thing(); // value-initialize  default-initialize  default ctor (class type)

(Diese Antwort berücksichtigt konzeptionelle Änderungen in C++11, die in der oberen Antwort nicht enthalten sind; insbesondere wird eine neue Skalar- oder POD-Instanz, die mit einem unbestimmten Wert enden würde, nun technisch standardinitialisiert (was bei POD-Typen technisch einen trivialen Standardkonstruktor aufruft). Dies führt zwar nicht zu großen praktischen Änderungen im Verhalten, vereinfacht aber die Regeln etwas).

2voto

uqb Punkte 240

Ich habe unten einige Beispielcodes geschrieben, als Ergänzung zu der Antwort von Michael Burr:

#include <iostream>

struct A1 {
    int i;
    int j;
};

struct B {
    int k;
    B() : k(4) {}
    B(int k_) : k(k_) {}
};

struct A2 {
    int i;
    int j;
    B b;
};

struct A3 {
    int i;
    int j;
    B b;
    A3() : i(1), j(2), b(5) {}
    A3(int i_, int j_, B b_): i(i_), j(j_), b(b_) {}
};

int main() {
    {
        std::cout << "Case#1: POD without ()\n";
        A1 a1 = {1, 2};
        std::cout << a1.i << " " << a1.j << std::endl;
        A1* a = new (&a1) A1;
        std::cout << a->i << " " << a->j  << std::endl;
    }
    {
        std::cout << "Case#2: POD with ()\n";
        A1 a1 = {1, 2};
        std::cout << a1.i << " " << a1.j << std::endl;
        A1* a = new (&a1) A1();
        std::cout << a->i << " " << a->j  << std::endl;
    }
    {
        std::cout << "Case#3: non-POD without ()\n";
        A2 a1 = {1, 2, {3}};
        std::cout << a1.i << " " << a1.j << " " << a1.b.k << std::endl;
        A2* a = new (&a1) A2;
        std::cout << a->i << " " << a->j << " " << a->b.k << std::endl;
    }
    {
        std::cout << "Case#4: non-POD with ()\n";
        A2 a1 = {1, 2, {3}};
        std::cout << a1.i << " " << a1.j << " " << a1.b.k  << std::endl;
        A2* a = new (&a1) A2();
        std::cout << a->i << " " << a->j << " " << a1.b.k << std::endl;
    }
    {
        std::cout << "Case#5: user-defined-ctor class without ()\n";
        A3 a1 = {11, 22, {33}};
        std::cout << a1.i << " " << a1.j << " " << a1.b.k << std::endl;
        A3* a = new (&a1) A3;
        std::cout << a->i << " " << a->j << " " << a->b.k << std::endl;
    }
    {
        std::cout << "Case#6: user-defined-ctor class with ()\n";
        A3 a1 = {11, 22, {33}};
        std::cout << a1.i << " " << a1.j << " " << a1.b.k  << std::endl;
        A3* a = new (&a1) A3();
        std::cout << a->i << " " << a->j << " " << a1.b.k << std::endl;
    }
    return 0;
}

/*
output with GCC11.1(C++20)
Case#1: POD without ()
1 2
1 2
Case#2: POD with ()
1 2
0 0
Case#3: non-POD without ()
1 2 3
1 2 4
Case#4: non-POD with ()
1 2 3
0 0 4
Case#5: user-defined-ctor class without ()
11 22 33
1 2 5
Case#6: user-defined-ctor class with ()
11 22 33
1 2 5
*/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X