6

In the c++ code that I am reading through there are some arrays initialised like

int *foo = new int[length];

and some like

int *foo = new int[length]();

My quick experimentation could not detect any difference between these two, yet they are used right next to one another.

Is there a difference, if so what?

Edit; since there is an assertion that the first one should give indeterminate output here is a test showing a suspicious number of 0s;

[s1208067@hobgoblin testCode]$ cat arrayTest.cc
//Test how array initilization works
#include <iostream>
using namespace std;
int main(){
int length = 30;
//Without parenthsis
int * bar = new int[length];
for(int i=0; i<length; i++) cout << bar[0] << " ";

cout << endl;
//With parenthsis 
int * foo = new int[length]();
for(int i=0; i<length; i++) cout << foo[0] << " ";


cout << endl;
return 0;
}
[s1208067@hobgoblin testCode]$ g++ arrayTest.cc
[s1208067@hobgoblin testCode]$ ./a.out
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
[s1208067@hobgoblin testCode]$ 

Edit 2; apparently this test was flawed, dont trust it - look at the answers for details

Jekowl
  • 317
  • 2
  • 12
  • No there is no difference, its just calling the constructor which takes no parameters –  Jul 02 '15 at 13:50
  • @nilo, `int` does not have a default constructor (for the edit, or any constructor), and there is a difference. – chris Jul 02 '15 at 13:50
  • `int` does have a default constructor - try `cout << int() << endl`. – celticminstrel Jul 02 '15 at 13:52
  • 1
    @celticminstrel, You're printing a value-initialized `int`. Same syntax doesn't imply that it has a default constructor. (For reference, *The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, whose value is that produced by value-initializing (8.5) an object of type T; no initialization is done for the void() case.*) – chris Jul 02 '15 at 13:52
  • 2
    See [Do the parentheses after the type name make a difference with new?](http://stackoverflow.com/questions/620137/do-the-parentheses-after-the-type-name-make-a-difference-with-new) This is case A, and is the same in C++11. – chris Jul 02 '15 at 13:54
  • 1
    I've never actually seen the parentheses with array initialization. – celticminstrel Jul 02 '15 at 13:57
  • @chris That is not exactly true. Bjarne himself said that "Built-in types are considered to have constructors (whatever words the standard use to describe their behavior).". They might not have a construct like a class as member function but I guess thats a flaw in the design of c++, making it confusing. Some say direct initialization some say constructor even though it might be incorrect. –  Jul 02 '15 at 14:01
  • 1
    @nilo, Yes, you could think of them being special non-class types with constructors. Unfortunately, it doesn't always work out so consistently. You would expect that `int x;` uses the "default constructor" and that `int x = int();` also uses the default constructor because that's what happens when you replace `int` with a suitable user-defined type. However, they actually do different things. I guess the better analogy would be the behaviour of `struct S {int x;};` when doing `S s;` and `S s = S();`, though it's about as intuitive as the plain `int` case. – chris Jul 02 '15 at 14:09
  • @chris I agree, I've done some research and I think that calling direct initialization for constructor (for built-in types) is wrong since it doesn't behave like class constructors which people associate the word constructor to. –  Jul 02 '15 at 14:22

2 Answers2

12

This line default-initializes length ints, which is to say you will get a bunch of ints with indeterminate value:

int *foo = new int[length];

This line value-initializes them instead, so you get all zeros:

int *foo = new int[length]();
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    In quick experimentation both seem to have only 0s in. Is this just chance for the first version? – Jekowl Jul 02 '15 at 13:52
  • 1
    @Jekowl, Yes, printing them in the first case is undefined behaviour. – chris Jul 02 '15 at 13:53
  • @Jekowl Are you in debug build? – Chris O Jul 02 '15 at 13:53
  • @Chris I'm compiling with $ g++ fileName.cc in the terminal (then running the resulting file) I don't think that's debug mode? – Jekowl Jul 02 '15 at 13:54
  • I get the same results with clang - in the first case, it outputs all 0's. – celticminstrel Jul 02 '15 at 13:55
  • 4
    Indeterminate doesn't mean "not 0". It means "can be anything." Having them happen to be 0 is acceptable, but not something you should rely on. – Barry Jul 02 '15 at 13:58
  • @Barry yes, but it would seem surprising to get 10 zeros in a row that way. Possible however, which is why I was asking. – Jekowl Jul 02 '15 at 14:00
  • 1
    Acceptable under the standard, certainly, but it looks like it's being zero-initialized both ways... – celticminstrel Jul 02 '15 at 14:01
  • This answer seems correct in all respects. The other one had the advantage of explaining the thing that looks surprising. – Jekowl Jul 02 '15 at 14:19
  • 1
    @Jekowl You accept the answer that you feel helped you the most - you don't have to explain it. I won't take it personally :) – Barry Jul 02 '15 at 15:03
7

Using the parentheses guarantees that all elements of the array are initialized to 0. I just tried using the following code:

#include <iostream>
using namespace std;

int main(int,char*[]){
    int* foo = new int[8];
    cout << foo << endl;
    for(int i = 0; i < 8; i++)
        foo[i] = i;
    delete[] foo;
    foo = new int[8];
    cout << foo << endl;
    for(int i = 0; i < 8; i++)
        cout << foo[i] << '\t';
    cout << endl;
    delete[] foo;
    foo = new int[8]();
    cout << foo << endl;
    for(int i = 0; i < 8; i++)
        cout << foo[i] << '\t';
    cout << endl;
    delete[] foo;
    return 0;
}

When I compile and run this, it looks like foo is allocated in the same memory location each time (though you probably can't rely on this). The full output of the above program for me is:

0x101300900
0x101300900
0   1   2   3   4   5   6   7   
0x101300900
0   0   0   0   0   0   0   0

So, you can see that the second allocation of foo doesn't touch the memory allocated, leaving it in the same state that it was left in from the first allocation.

celticminstrel
  • 1,637
  • 13
  • 21