# Programming: Principles and Practice Using C++: Vectors and Arrays

• Print
This chapter is from the book

## 18.2 Initialization

Consider our vector as it was at the end of Chapter 17:

```class vector {
int sz;                  // the size
double* elem;            // a pointer to the elements
public:
vector(int s)                                                       // constructor
:sz{s}, elem{new double[s]} { /* . . . */ }                   // allocates memory
~vector()                                                        // destructor
{ delete[] elem; }                                         // deallocates memory
// . . .
};```

That’s fine, but what if we want to initialize a vector to a set of values that are not defaults? For example:

`vector v1 = {1.2, 7.89, 12.34 };`

We can do that, and it is much better than initializing to default values and then assigning the values we really want:

```vector v2(2);           // tedious and error-prone
v2[0] = 1.2;
v2[1] = 7.89;
v2[2] = 12.34;```

Compared to v1, the “initialization” of v2 is tedious and error-prone (we deliberately got the number of elements wrong in that code fragment). Using push_back() can save us from mentioning the size:

```vector v3;                            // tedious and repetitive
v2.push_back(1.2);
v2.push_back(7.89);
v2.push_back(12.34);```

But this is still repetitive, so how do we write a constructor that accepts an initializer list as its argument? A { }-delimited list of elements of type T is presented to the programmer as an object of the standard library type initializer_list<T>, a list of Ts, so we can write

```class vector {
int sz;                // the size
double* elem;          // a pointer to the elements
public:
vector(int s)          // constructor (s is the element count)
:sz{s}, elem{new double[sz]}     // uninitialized memory for elements
{
for (int i = 0; i<sz; ++i) elem[i] = 0.0;  // initialize
}
vector(initializer_list<double> lst)            // initializer-list constructor
:sz{lst.size()}, elem{new double[sz]}     // uninitialized memory
// for elements
{
copy( lst.begin(),lst.end(),elem);    // initialize (using std::copy(); §B.5.2)
}
// . . .
};```

We used the standard library copy algorithm (§B.5.2). It copies a sequence of elements specified by its first two arguments (here, the beginning and the end of the initializer_list) to a sequence of elements starting with its third argument (here, the vector’s elements starting at elem).

Now we can write

```vector v1 = {1,2,3};       // three elements 1.0, 2.0, 3.0
vector v2(3);            // three elements each with the (default) value 0.0```

Note how we use ( ) for an element count and { } for element lists. We need a notation to distinguish them. For example:

```vector v1 {3};         // one element with the value 3.0
vector v2(3);         // three elements each with the (default) value 0.0```

This is not very elegant, but it is effective. If there is a choice, the compiler will interpret a value in a { } list as an element value and pass it to the initializer-list constructor as an element of an initializer_list.

In most cases — including all cases we will encounter in this book — the = before an { } initializer list is optional, so we can write

```vector v11 = {1,2,3};      // three elements 1.0, 2.0, 3.0
vector v12 {1,2,3};        // three elements 1.0, 2.0, 3.0```

The difference is purely one of style.

Note that we pass initializer_list<double> by value. That was deliberate and required by the language rules: an initializer_list is simply a handle to elements allocated “elsewhere” (see §B.6.4).