7.11. Introduction to C++ Standard Library Class Template vector

We now introduce C++ Standard Library class template vector, which represents a more robust type of array featuring many additional capabilities. As you'll see in later chapters, C-style pointer-based arrays (i.e., the type of arrays presented thus far) have great potential for errors. For example, as mentioned earlier, a program can easily "walk off" either end of an array, because C++ does not check whether subscripts fall outside the range of an array. Two arrays cannot be meaningfully compared with equality operators or relational operators. As you'll see in Chapter 8, pointer variables (known more commonly as pointers) contain memory addresses as their values. Array names are simply pointers to where the arrays begin in memory, and, of course, two arrays will always be at different memory locations. When an array is passed to a general-purpose function designed to handle arrays of any size, the size of the array must be passed as an additional argument. Furthermore, one array cannot be assigned to another with the assignment operator(s)—array names are const pointers, and, as you'll see in Chapter 8, a constant pointer cannot be used on the left side of an assignment operator. These and other capabilities certainly seem like "naturals" for dealing with arrays, but C++ does not provide such capabilities. However, the C++ Standard Library provides class template vector to allow programmers to create a more powerful and less error-prone alternative to arrays. In Chapter 11, Operator Overloading; String and Array Objects, we present the means to implement such array capabilities as those provided by vector. You'll see how to customize operators for use with your own classes (a technique known as operator overloading).

The vector class template is available to anyone building applications with C++. The notations that the vector example uses might be unfamiliar to you, because vectors use template notation. Recall that Section 6.18 discussed function templates. In Chapter 14, we discuss class templates. For now, you should feel comfortable using class template vector by mimicking the syntax in the example we show in this section. You'll deepen your understanding as we study class templates in Chapter 14. Chapter 20 presents class template vector (and several other standard C++ container classes) in detail.

The program of Fig. 7.26 demonstrates capabilities provided by C++ Standard Library class template vector that are not available for C-style pointer-based arrays. Standard class template vector provides many of the same features as the Array class that we construct in Chapter 11, Operator Overloading; String and Array Objects. Standard class template vector is defined in header <vector> (line 11) and belongs to namespace std (line 12). Chapter 20 discusses the full functionality of standard class template vector.

Fig. 7.26. C++ Standard Library class template vector.

 

 1  // Fig. 7.26: fig07_26.cpp
 2   // Demonstrating C++ Standard Library class template vector.
 3   #include <iostream>
 4   using std::cout;
 5   using std::cin;
 6   using std::endl;
 7
 8   #include <iomanip>
 9   using std::setw;
10
11   #include <vector> 
12   using std::vector;
13
14   void outputVector( const vector< int > & ); // display the vector
15   void inputVector( vector< int > & ); // input values into the vector
16
17   int main()
18   {
19     vector< int > integers1( 7 ); // 7-element vector< int >  
20     vector< int > integers2( 10 ); // 10-element vector< int >
21
22      // print integers1 size and contents
23      cout << "Size of vector integers1 is " << integers1.size()
24         << "\nvector after initialization:" << endl;
25      outputVector( integers1 );
26
27      // print integers2 size and contents
28      cout << "\nSize of vector integers2 is " << integers2.size()
29         << "\nvector after initialization:" << endl;
30      outputVector( integers2 );
31
32      // input and print integers1 and integers2
33      cout << "\nEnter 17 integers:" << endl;
34      inputVector( integers1 );
35      inputVector( integers2 );
36
37      cout << "\nAfter input, the vectors contain:\n"
38         << "integers1:" << endl;
39      outputVector( integers1 );
40      cout << "integers2:" << endl;
41      outputVector( integers2 );
42
43      // use inequality (!=) operator with vector objects
44      cout << "\nEvaluating: integers1 != integers2" << endl;
45
46      if ( integers1 != integers2 )
47         cout << "integers1 and integers2 are not equal" << endl;
48
49      // create vector integers3 using integers1 as an         
50      // initializer; print size and contents                  
51      vector< int > integers3( integers1 ); // copy constructor
52
53      cout << "\nSize of vector integers3 is " << integers3.size()
54         << "\nvector after initialization:"   << endl;
55      outputVector( integers3 );
56
57      // use overloaded assignment (=) operator              
58      cout << "\nAssigning integers2 to integers1:" << endl; 
59      integers1 = integers2; // assign integers2 to integers1
60
61      cout << "integers1:" << endl;
62      outputVector( integers1 );
63      cout << "integers2:" << endl;
64      outputVector( integers2 );
65
66      // use equality (==) operator with vector objects
67      cout << "\nEvaluating: integers1 == integers2" << endl;
68
69      if ( integers1 == integers2 )
70         cout << "integers1 and integers2 are equal" << endl;
71
72      // use square brackets to create rvalue
73      cout << "\nintegers1[5] is " << integers1[ 5 ];
74
75      // use square brackets to create lvalue
76      cout << "\n\nAssigning 1000 to integers1[5]" << endl;
77      integers1[ 5 ] = 1000;
78      cout << "integers1:" << endl;
79      outputVector( integers1 );
80
81      // attempt to use out-of-range subscript
82      cout << "\nAttempt to assign 1000 to integers1.at( 15 )" << endl;
83      integers1.at( 15 ) = 1000; // ERROR: out of range
84      return 0;
85   } // end main
86
87   // output vector contents
88   void outputVector( const vector< int > &array )
89   {
90      size_t i; // declare control variable
91
92      for ( i = 0; i < array.size(); i++ )
93      {
94         cout << setw( 12 ) << array[ i ];
95
96         if ( ( i + 1 ) % 4 == 0 ) // 4 numbers per row of output
97            cout << endl;
98      } // end for
99
100      if ( i % 4 != 0 )
101         cout << endl;
102   } // end function outputVector
103
104   // input vector contents
105   void inputVector( vector< int > &array )
106   {
107      for ( size_t i = 0; i < array.size(); i++ )
108         cin >> array[ i ];
109   } // end function inputVector

					  

 

Size of vector integers1 is 7
vector after initialization:
           0           0           0           0
           0           0           0
Size of vector integers2 is 10
vector after initialization:
           0           0           0           0
           0           0           0           0
           0           0

Enter 17 integers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

After input, the vectors contain:
integers1:
           1           2           3           4
           5           6           7
integers2:
           8           9          10          11
          12          13          14          15
          16          17

Evaluating: integers1 != integers2
integers1 and integers2 are not equal

Size of vector integers3 is 7
vector after initialization:
           1           2           3           4
           5           6           7

Assigning integers2 to integers1:
integers1:
           8           9          10           11
          12          13          14           15
          16          17
integers2:
           8           9          10           11
          12          13          14           15
          16          17

Evaluating: integers1 == integers2
integers1 and integers2 are equal

integers1[5] is 13

Assigning 1000 to integers1[5]
integers1:
           8           9          10           11
          12        1000          14           15
          16          17

Attempt to assign 1000 to integers1.at( 15 )

abnormal program termination

					  


Lines 19–20 create two vector objects that store values of type intintegers1 contains seven elements, and integers2 contains 10 elements. By default, all the elements of each vector object are set to 0. Note that vectors can be defined to store any data type, by replacing int in vector< int > with the appropriate data type. This notation, which specifies the type stored in the vector, is similar to the template notation that Section 6.18 introduced with function templates. Again, Chapter 14 discusses this syntax in detail.

Line 23 uses vector member function size to obtain the size (i.e., the number of elements) of integers1. Line 25 passes integers1 to function outputVector (lines 88–102), which uses square brackets, [] (line 94), to obtain the value in each element of the vector for output. Note the resemblance of this notation to that used to access the value of an array element. Lines 28 and 30 perform the same tasks for integers2.

Member function size of class template vector returns the number of elements in a vector as a value of type size_t (which represents the type unsigned int on many systems). As a result, line 90 declares the control variable i to be of type size_t, too. On some compilers, declaring i as an int causes the compiler to issue a warning message, since the loop-continuation condition (line 92) would compare a signed value (i.e., int i) and an unsigned value (i.e., a value of type size_t returned by function size).

Lines 34–35 pass integers1 and integers2 to function inputVector (lines 105–109) to read values for each vector's elements from the user. The function uses square brackets ([]) to form lvalues that are used to store the input values in each vector element.

Line 46 demonstrates that vector objects can be compared with one another using the != operator. If the contents of two vectors are not equal, the operator returns true; otherwise, it returns false.

The C++ Standard Library class template vector allows you to create a new vector object that is initialized with the contents of an existing vector. Line 51 creates a vector object integers3 and initializes it with a copy of integers1. This invokes vector's so-called copy constructor to perform the copy operation. You'll learn about copy constructors in detail in Chapter 11. Lines 53–55 output the size and contents of integers3 to demonstrate that it was initialized correctly.

Line 59 assigns integers2 to integers1, demonstrating that the assignment (=) operator can be used with vector objects. Lines 61–64 output the contents of both objects to show that they now contain identical values. Line 69 then compares integers1 to integers2 with the equality (==) operator to determine whether the contents of the two objects are equal after the assignment in line 59 (which they are).

Lines 73 and 77 demonstrate that a program can use square brackets ([]) to obtain a vector element as an rvalue and as an lvalue, respectively. Recall from Section 5.9 that an rvalue cannot be modified, but an lvalue can. As is the case with C-style pointer-based arrays, C++ does not perform any bounds checking when vector elements are accessed with square brackets. Therefore, you must ensure that operations using [] do not accidentally attempt to manipulate elements outside the bounds of the vector. Standard class template vector does, however, provide bounds checking in its member function at, which "throws an exception" (see Chapter 16, Exception Handling) if its argument is an invalid subscript. By default, this causes a C++ program to terminate. If the subscript is valid, function at returns the element at the specified location as a modifiable lvalue or an unmodifiable lvalue, depending on the context (non-const or const) in which the call appears. Line 83 demonstrates a call to function at with an invalid subscript. The resulting output varies by compiler.

In this section, we demonstrated the C++ Standard Library class template vector, a robust, reusable class that can replace C-style pointer-based arrays. In Chapter 11, you'll see that vector achieves many of its capabilities by "overloading" C++'s built-in operators, and you'll see how to customize operators for use with your own classes in similar ways. For example, we create an Array class that, like class template vector, improves upon basic array capabilities. Our Array class also provides additional features, such as the ability to input and output entire arrays with operators >> and <<, respectively.