ChatGPT解决这个技术问题 Extra ChatGPT

Do the parentheses after the type name make a difference with new?

If 'Test' is an ordinary class, is there any difference between:

Test* test = new Test;

and

Test* test = new Test();
This is related to (but not identical to) stackoverflow.com/questions/1613341/…
Just use new Test() to make sure it is zero-initialized

C
Callum Watkins

Let's get pedantic, because there are differences that can actually affect your code's behavior. Much of the following is taken from comments made to an "Old New Thing" article.

Sometimes the memory returned by the new operator will be initialized, and sometimes it won't depending on whether the type you're newing up is a POD (plain old data), or if it's a class that contains POD members and is using a compiler-generated default constructor.

In C++1998 there are 2 types of initialization: zero and default

In C++2003 a 3rd type of initialization, value initialization was added.

Assume:

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 a C++98 compiler, the following should occur:

new A - indeterminate value

new A() - zero-initialize

new B - default construct (B::m is uninitialized)

new B() - default construct (B::m is uninitialized)

new C - default construct (C::m is zero-initialized)

new C() - default construct (C::m is zero-initialized)

In a C++03 conformant compiler, things should work like so:

new A - indeterminate value

new A() - value-initialize A, which is zero-initialization since it's a POD.

new B - default-initializes (leaves B::m uninitialized)

new B() - value-initializes B which zero-initializes all fields since its default ctor is compiler generated as opposed to user-defined.

new C - default-initializes C, which calls the default ctor.

new C() - value-initializes C, which calls the default ctor.

So in all versions of C++ there's a difference between new A and new A() because A is a POD.

And there's a difference in behavior between C++98 and C++03 for the case new B().

This is one of the dusty corners of C++ that can drive you crazy. When constructing an object, sometimes you want/need the parens, sometimes you absolutely cannot have them, and sometimes it doesn't matter.


@j_random_hacker, new A() will default-initialize the object in C++98, like it does with new B(), new B, new C() and new C, but not with new A. That is, default initialization is always done in C++98 when either: 1) The class is a non-POD and the initializer is missing, or 2) The initializer is (). default-initialization zero-initializes the object if it's a POD, but calls the default constructor for non-PODs.
Can someone add what is the case in C++11 now?
@Jon: With C++11 you can do this in stack too; B obj{}; will make the object value-initialized (to 0s) as opposed to B obj; which will be default-initialized (garbage).
You say that "sometimes you absolutely cannot have them [parentheses]". What situation is it where you can't add them?
So the tl;dr is that new A gives members an indeterminate value and new A() initializes member values to 0... unless A has a destructor defined, in which case both expressions give members indeterminate values... unless A also has a constructor defined, in which case both expressions zero-initialize members... unless it's a C++03 compiler in which case new A() will "value initialize" the members instead, which is different somehow(?). So simple.
S
SebastianWilke

new Thing(); is explicit that you want a constructor called whereas new Thing; is taken to imply you don't mind if the constructor isn't called.

If used on a struct/class with a user-defined constructor, there is no difference. If called on a trivial struct/class (e.g. struct Thing { int i; };) then new Thing; is like malloc(sizeof(Thing)); whereas new Thing(); is like calloc(sizeof(Thing)); - it gets zero initialized.

The gotcha lies in-between:

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

The behavior of new Thingy; vs new Thingy(); in this case changed between C++98 and C++2003. See Michael Burr's explanation for how and why.


h
hivert

In general we have default-initialization in first case and value-initialization in second case.

For example: in case with int (POD type):

int* test = new int - we have any initialization and value of *test can be any.

int* test = new int() - *test will have 0 value.

next behaviour depended from your type Test. We have defferent cases: Test have defult constructor, Test have generated default constructor, Test contain POD member, non POD member...


佚名

No, they are the same. But there is a difference between:

Test t;      // create a Test called t

and

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

This is because of the basic C++ (and C) rule: If something can possibly be a declaration, then it is a declaration.

Edit: Re the initialisation issues regarding POD and non-POD data, while I agree with everything that has been said, I would just like to point out that these issues only apply if the thing being new'd or otherwise constructed does not have a user-defined constructor. If there is such a constructor it will be used. For 99.99% of sensibly designed classes there will be such a constructor, and so the issues can be ignored.


Note that this is a particularly important point because the line "Test t(5);" is equivalent to "Test t = Test(5);" -- but "Test t();" is very different from "Test t = Test();". +1
-1, I disagree with your statement that the issues can be ignored. You don't have to know the rules precisely, but you should be aware of them in case you have to new a class without a user-defined default constructor (you should then either write the constructor or look up the rules).
-1 for a known incorrect answer. Your Edit ignores the presence of code written by former C programmers who didn't understand/use constructors.
What about classes like struct point { float v[3]; };? For things like that, a constructor would be a bad idea, as it would prevent all the nice properties that come with being POD and aggregate. So "the issues can be ignored" is just wrong, imo.
But they are not the same. This answer is plain wrong. It should be fixed or removed, because it seems to have caused some confusion, judging by the high number of up-votes.
E
Evan Shaw

Assuming that Test is a class with a defined constructor, there's no difference. The latter form makes it a little clearer that Test's constructor is running, but that's about it.


T
ThatsJustCheesy

The rules for new are analogous to what happens when you initialize an object with automatic storage duration (although, because of vexing parse, the syntax can be slightly different).

If I say:

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

Then my_int has an indeterminate value, since it is a non-class type. Alternatively, I can value-initialize my_int (which, for non-class types, zero-initializes) like this:

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

(Of course, I can't use () because that would be a function declaration, but int() works the same as int{} to construct a temporary.)

Whereas, for class types:

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

The default constructor is called to create a Thing, no exceptions.

So, the rules are more or less:

Is it a class type? YES: The default constructor is called, regardless of whether it is value-initialized (with {}) or default-initialized (without {}). (There is some additional prior zeroing behavior with value-initialization, but the default constructor is always given the final say.) NO: Were {} used? YES: The object is value-initialized, which, for non-class types, more or less just zero-initializes. NO: The object is default-initialized, which, for non-class types, leaves it with an indeterminate value (it effectively isn't initialized).

YES: The default constructor is called, regardless of whether it is value-initialized (with {}) or default-initialized (without {}). (There is some additional prior zeroing behavior with value-initialization, but the default constructor is always given the final say.)

NO: Were {} used? YES: The object is value-initialized, which, for non-class types, more or less just zero-initializes. NO: The object is default-initialized, which, for non-class types, leaves it with an indeterminate value (it effectively isn't initialized).

YES: The object is value-initialized, which, for non-class types, more or less just zero-initializes.

NO: The object is default-initialized, which, for non-class types, leaves it with an indeterminate value (it effectively isn't initialized).

These rules translate precisely to new syntax, with the added rule that () can be substituted for {} because new is never parsed as a function declaration. So:

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)

(This answer incorporates conceptual changes in C++11 that the top answer currently does not; notably, a new scalar or POD instance that would end up an with indeterminate value is now technically now default-initialized (which, for POD types, technically calls a trivial default constructor). While this does not cause much practical change in behavior, it does simplify the rules somewhat.)


u
uqb

I wrote some sample codes below, as a supplement to the answer of 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
*/