Aggregate
initialization
An
aggregate
is just what it sounds like: a bunch of things clumped together. This
definition includes aggregates of mixed types, like
structs
and
classes.
An array is an aggregate of a single type.
Initializing
aggregates can be error-prone and tedious. C++
aggregate
initialization
makes it much safer. When you create an object that’s an aggregate, all
you must do is make an assignment, and the initialization will be taken care of
by the compiler. This assignment comes
in several flavors, depending on the type of aggregate you’re dealing
with, but in all cases the elements in the assignment must be surrounded by
curly braces. For an array of built-in types this is quite simple:
int a[5] = { 1, 2, 3, 4, 5 };
If
you try to give more initializers than
there are array elements, the compiler gives an error message. But what happens
if you give
fewer
initializers, such as
Here,
the compiler will use the first initializer for the first array element, and
then use zero for all the elements without initializers. Notice this
initialization behavior doesn’t occur if you define an array without a
list of initializers. So the above expression is a very succinct way to
initialize an
array to zero,
without using a
for
loop, and without any possibility of an off-by-one error (Depending
on the compiler, it may also be more efficient than the
for
loop.)
A
second shorthand for arrays is
automatic
counting,
where you let the compiler determine the size of the array based on the number
of initializers:
int
c[] = { 1, 2, 3, 4 };
Now
if you decide to add another element to the array, you simply add another
initializer. If you can set your code up so it needs to be changed in only one
spot, you reduce the chance of errors during modification. But how do you
determine the size of the array? The expression
sizeof
c / sizeof *c
(size of the entire array divided by the size of the first element) does the
trick in a way that doesn’t need to be changed if the array size changes:
for(int i = 0; i < sizeof c / sizeof *c; i++)
c[i]++;
Because
structures are also aggregates, they can be initialized in a similar fashion.
Because a C-style
struct
has all its members
public,
they can be assigned directly:
struct X {
int i;
float f;
char c;
};
X x1 = { 1, 2.2, 'c' };
If
you have an array of such objects, you can initialize them by using a nested
set of curly braces for each object:
X
x2[3] = { {1, 1.1, 'a'}, {2, 2.2, 'b'} };
Here,
the third object is initialized to zero.
If
any of the data members are
private,
or even if everything’s public but there’s a constructor, things
are different. In the above examples, the initializers are assigned directly to
the elements of the aggregate, but constructors are a way of forcing
initialization to occur through a formal interface. Here, the constructors must
be called to perform the initialization. So if you have a
struct
that looks like this,
struct Y {
float f;
int i;
Y(int a); // Presumably assigned to i
};
You
must indicate constructor calls. The best approach is the explicit one as
follows:
Y
y2[] = { Y(1), Y(2), Y(3) };
You
get three objects and three constructor calls. Any
time
you have a constructor, whether it’s a
struct
with all members
public
or a
class
with
private
data members, all the initialization must go through the constructor, even if
you’re using aggregate initialization.
Here’s
a second example showing multiple constructor arguments:
//: C06:Multiarg.cpp
// Multiple constructor arguments
// with aggregate initialization
class X {
int i, j;
public:
X(int ii, int jj) {
i = ii;
j = jj;
}
};
int main() {
X xx[] = { X(1,2), X(3,4), X(5,6), X(7,8) };
} ///:~
Notice
that it looks like an explicit but unnamed constructor is called for each
object in the array.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru