Does a type require a default constructor in order to declare an array of it?

痞子三分冷 提交于 2019-12-10 18:29:38

问题


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:


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.




回答2:


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).




回答3:


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.




回答4:


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.




回答5:


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




回答6:


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



回答7:


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).




回答8:


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();



回答9:


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[].



来源:https://stackoverflow.com/questions/2231414/does-a-type-require-a-default-constructor-in-order-to-declare-an-array-of-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!