Reputation: 2180
Please consider the following example
struct Foo
{
int bar;
Foo(int i):bar(i){cout << "real ctor\n";}
Foo(){cout << "default ctor\n";}
};
int main()
{
Foo fooArr[3];//default ctor called 3 times
for(int i=0;i!=3;++i)cout << fooArr[i].bar << endl;//bare memory junk
cout << endl;
vector<Foo> fooVec;
for(int i=0;i!=3;++i){
fooVec.push_back(Foo(i)); //only real ctor called
cout << fooVec[i].bar << endl;//real thing
}
cout << endl;
int iArr[3];
for(int i=0;i!=3;++i)cout << iArr[i] << endl;//bare memory junk
}
I don't want any user of Foo
to call its default constructor, because it's not in my design. But I'd like my users to be able to use an array of Foo
, to support that, I was forced to provide a pointless and confusing Foo::Foo(). I just don't understand why does the C++ standard force programmers to do such a thing. What is the rationale behind it? Why the inconsistency? Could any of you smart guys who get this explain it to me, please? Thanks in advance!
Upvotes: 2
Views: 4451
Reputation: 227468
The rationale is that the array is full of default constructed elements, so the type of the elements must be default constructible. If you initialized the array with some values, the default construction wouldn't be required:
Foo fooArr1[3]; // full of default constructed Foos
Foo fooArr[3] = {1,2,3}; // default constructor not required. Foo(int) called.
Note that the second line in the code example uses the implicit conversion from int
to Foo
provided by the implicit Foo(int)
converting constructor.
The reason you have to provide your own default constructor is that you have declared one constructor, which disables the automatic generation of the default constructor. The rationale behind this is that if you need to provide some constructor, it is likely that you also want to do something special in the default constructor.
If you really are worried about user provided constructors, then you can make your class a real aggregate and use aggregate initialization:
struct Foo
{
// no user declared constructors
int foo;
};
int main()
{
Foo fooArr1[3]; // OK
Foo fooArr[3] = { {1}, {2}, {3} }; // aggregate construction
}
In C++11 you can enable the compiler generated default constructor using default
:
Foo()=default;
Upvotes: 3
Reputation: 7858
You can make arrays of Foo
even if it doesn't have a default constructor. It's just that the elements have to be constructed when you declare the array. So you can do this:
Foo fooArr[] = { Foo( 1 ), Foo( 2 ), Foo( 3 ) };
The alternative is to use a a dynamic array (your vector<Foo>
example, which is probably best) or an array of pointers to Foo
(like shared_ptr<Foo> arrFoo[3]
)
shared_ptr<Foo> arrFoo[3];
arrFoo[2].reset( new Foo(3) );
A final note about vector<Foo>
: since the size of the array is known in advance, you can improve performance by reserving enough space in the vector for all future Foo
s:
vector<Foo> arrFoo;
arrFoo.reserve( 3 );
for( int i = 0; i<3; ++i )
arrFoo.push_back( Foo( i ) );
EDIT: Your question was why do you have to have a default constructor to make a static array of the type. I thought the answer was clear but I'll try to explain it.
Foo bar1; Foo bar2;
creates two objects using the default constructor, since no arguments were provided.
Foo bar[2];
is essentially the same thing. It declares two objects that need to be constructed. There is no way to declare an object without constructing it - that's the very point of declaring it in the first place.
A static array in C++ is just a bunch of objects placed contiguously memory. It's not a separate object.
Hope that makes sense.
Upvotes: 4
Reputation:
You have to choose: either not defining a default constructor, and therefore, you can't declare an array of Foo
. Or declaring a default constructor (empty even) and can declare an array of Foo
.
If you have dealt before with OOP languages such as C# or Java, and you have a class Foo
and Foo[] arr
, then you don't have to declare default constructor, because the array in these languages carries only references (addresses) to objects. The array itself is an object, so arr
when created will == null
. When using arr = new Foo[3];
then we make a new object of array that contains 3 references: arr == { null, null, null }
. Then you assign an object to each reference: for (int i = 0; i < 3; ++i) arr[i] = new Foo(i);
.
However, C++ is different because the arrays carry the object itself rather than a reference to it. So, when carrying the object itself, it must have no-parameter constructor to be called with each object. (i.e. in C++: Foo arr[3];
then arr = { objectOfFoo, objectOfFoo, objectOfFoo }
A solution to your problem may found by decalring an array of pointers:
Foo * arr[10] = { 0 }; // arr = { NULL, NULL, NULL, ... , NULL }
for (int i = 0; i < 10; ++i) arr[i] = new Foo(3); // you don't have to declare default constructor
// some using of array
// C++ doesn't have a garbage collector
for (int i = 0; i < 10; ++i) delete arr[i];
Upvotes: 1