views:

167

answers:

9

I noticed that when you declare an array, the default constructor must be needed. Is that right? Is there any exception?

For example,

struct Foo{
Foo(int i  ) {}
};

int main () {
    Foo f[5];
    return 0;
}

The code above does not compile.

+1  A: 

There is no exception. In what could be seen as exception, there is a compiler declared default constructor.

AProgrammer
+1  A: 

Yes, you need the default constructor here because Foo f[5]; actually creates 5 Foos. You can work around this by making it a Foo* f[5] and then creating the 5 Foos with new.

For example:

Foo* f[5];
for(int i = 0; i < 5; ++i) {
    f[i] = new Foo(i);
}

// later on...
f[0]->whatever();
Evan Teran
+2  A: 

That code doesn't compile because the compiler doesn't know what you want to pass to the constructor of each element, of course. There are basically two ways to go about it:

  1. Make the array a vector, and pass it the desired size plus a single element -- this gives each element the same argument.

  2. Make the array an array of pointers, and construct each element using a for loop and the new operator. The drawback of course, is that you have to free each element later as well.

Frederik Slijkerman
+2  A: 

See the C++ FAQ Lite, section 10.5

When you create an array, the default constructor gets called for each element in the array.

"If your class doesn't have a default constructor, you'll get a compile-time error when you attempt to create an array "

Prefer to use std::vector instead of the built-in arrays, though.

Bill
+5  A: 

Other answers are all right but, for completeness: You could also use the array initialization syntax:

Foo f[5] = {1,2,3,4,5};

This works if Foo's ctor is not explicit. If it was, you'd have to be.... explicit:

Foo f[5] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)};

Note1: There is a difference between the two cases that may not be obvious and is thus worth noting: In the first, the array elements are directly constructed from the ints in the initialization list, by invoking the Foo(int) ctor. In the second, the initialization list is made of Foos constructed with the explicit Foo(int) ctor, and the array elements are copy constructed from the elements in the initialization list. A copy ctor for Foo is thus required in the latter case.

[1] Thanks to MSalters for the comment.

Éric Malenfant
The second example requires an available copy ctor, in particular one that can copy temporaries. You'd still have problems creating an array of `auto_ptr<T>` s.
MSalters
@MSalters: Thanks for this great comment. I added a note to the answer.
Éric Malenfant
+1  A: 

Note that a default constructor is not required if you use std::vector instead of an array - you can specify the constructor to be used:

std::vector <Foo> f;                // OK
std::vector <Foo> f( 5, Foo(0) );   // also OK
anon
A: 

Warning: slightly off-topic.

If you have a class without default constructor, you absolutely need to have an array, and you don't want to incur into the overhead of dynamic memory allocation, you can use an array of boost::optionals:

boost::optional<Foo> foos[4];  // Stack storage allocated but no objects 
                               // constructed (roughly equivalent to what 
                               // you get with vector<T>::reserve)

if(foos[2]) // Check if the third element has been constructed
{
     foos[2]->bar(); // Access members of Foo with arrow    
}

foos[1] = Foo(1, "a"); // Constructs the second element

foos[1].reset(); // Destroy second element (storage remains there though)

Unfortunately, you won't be able to pass this to a function expecting a true Foo[].

Manuel
+5  A: 

The issue has nothing to do with arrays at all.

When you default-initialize an object of class type, the default constructor is required. If your class has no default constructor, then you have no other choice but to supply an explicit initializer when creating objects of that class. That's all.

By declaring a non-default constructor in your class Foo, you disabled the implicit generation of the default one, so now you'll have to supply an initializer every time you create a Foo object, regardless of whether it is an array element or not.

This declaration

Foo f; // ERROR

is not an array, but it will not compile for the very same reason. In order for it to compile you'll have to supply an explicit initializer

Foo f(3); // OK

The same thing happens with array, except that in that case you have to supply an initializer for each element using the aggregate initializer syntax

Foo f[5] = { 1, 2, 3, 4, 5 };

Of course, if you end up in a context where aggregate initializer syntax is not allowed (in the current version of C++ standard), like constructor initializer list or new-expression, then you are indeed screwed. In such contexts the only way out is to provide the default constructor in array element type (as long as you stick with built-in arrays).

AndreyT
+1  A: 

No it doesn't.

An array, in C/C++ is a block of memory. Creating an array is reserving that block. Creating the objects is "1. Allocating the space 2. Running the constructors on each block" So, if you have an object without a constructor, you can still make an array of it (since that object has a size and since memory understands "size").

In short, it doesn't make any difference whatsoever. You will be running the constructors as you create the objects to fill the array with, or as said previously, when you assign it to something (which in turn, allocates space, runs the constructor).

wan
So everyone else answering here is wrong?
anon
Besides, how can you have an "object without a constructor"? That just doesn't make sense - _classes_ have constructors, and _all classes_ have at least one constructor. If you don't declare one, the compiler will.
MSalters