简体   繁体   中英

Why does an array of user defined type have to call the default constructor thereof?

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!

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

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;

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. (ie 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];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM