Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++
Prev Contents / Index Next

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? For example:

int b[6] = {0};

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 expression above is a 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, in which 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[42]:

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 of 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 (which is typically the case for a well-designed class in C++), or even if everything’s public but there’s a constructor, things are different. In the examples above, 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);
}; 

You must indicate constructor calls. The best approach is the explicit one as follows:

Y y1[] = { 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
#include <iostream>
using namespace std;

class Z {
  int i, j;
public:
  Z(int ii, int jj);
  void print();
};

Z::Z(int ii, int jj) {
  i = ii;
  j = jj;
}

void Z::print() {
  cout << "i = " << i << ", j = " << j << endl;
}

int main() {
  Z zz[] = { Z(1,2), Z(3,4), Z(5,6), Z(7,8) };
  for(int i = 0; i < sizeof zz / sizeof *zz; i++)
    zz[i].print();
} ///:~

Notice that it looks like an explicit constructor is called for each object in the array.

Thinking in C++
Prev Contents / Index Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire