CSC 076

Programming in C++

Summary of 2/12 Lecture

 

Vectors

This is essentially a data type which behaves like a one-dimensional array, except that it allows range checking and it is possible to assign the values of one vector to another in an assignment statement.  Let's look at an example.  Recall the example from last week:

Example 1.  Write a program which creates an array called 'list', initializes it, and outputs it.

//File:    array.cpp
#include <iostream.h>
int main()
{
    int    list[10]={10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
            k;

    for (k=0; k<10; k++)
        cout << list[k] << endl;

    return 0;
}

Example 1A.  Write a program that creates vector called 'list', initializes it, and outputs it.

//File:    vector.cpp
//The following header files conform to the new standard C++.  They drop the .h extension used in C programs.
#include
<iostream>
#include <iomanip>
#include <vector>    //necessary to define vector
using namespace std;    //necessary, but we'll talk about this later
int main()
{
    vector<int> list(10);     //defines a list of 10 int's
   
    //Since vectors don't allow the convenient {} initializing, the following loop
   //is required.
   for (int k=0; k<10; k++)
    {
        list[k] = 10*(k+1);
    }
   
   for (k=0; k<10; k++)
    {
        cout << list[k] << endl;
    }
   
   return 0;
}

At first glance, it looks as if vectors are somewhat more limiting than the built in C++ arrays.  However, a closer look reveals some "nice" features.  For example, attempting to access array elements outside the range 0..9 can be flagged.  Also, assignments like

otherlist    =    list;

are perfectly legal for vectors.  There are other useful features of vectors which I hope you will explore.

Example 2.  Write a program, using vectors, with functions that read a list of numbers, average a list of numbers, and write a list of numbers respectively.

//File:    average2.cpp
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

//Since vectors are not passed by reference, the & is necessary (& v)
void read (istream & in, vector<int> & v, int & n)
{
    int number;
    n=0;
    while (in>>number)
    {
        v.resize(++n);    //Vectors allow dynamic sizing of list!
        v[n-1]=number;
    }
}

int average (vector <int> v, int n)
{
    int sum=0;
    for (int k=0; k<n; k++)
    {
        sum+=v[k];
    }
    return sum/n;
}

void write (ostream & out, vector<int> v, int n)
{
    for (int k=0; k<n; k++)
    {
        out << v[k] << endl;
    }
}

int main()
{
    vector<int> v;
    ifstream fin ("list.txt");
    ofstream fout ("list_out.txt");
    int n;
   
    read (fin, v, n);
    write (fout, v, n);
   
    fout << "The average of the " << n << " items in the list is: "
        << average (v, n) << endl;
       
    return 0;
}

The following solution illustrates the advantage of combining const and &.  This allows the best of both worlds; i.e., 'v' and 'n' are reference parameters and so making copy of actual parameters is unnecessary.  In addition, 'v' and 'n' are protected from change by the use of const.

//File:    average3.cpp
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

//Since vectors are NOT automatically passed by reference
//the & is necessary (& v)
void read (istream & in, vector<int> & v, int & n)
{
    n=0;
    int number;
    while (in>>number)
    {
        v.resize(++n);    //Vectors allow dynamic sizing of the list!
        v[n-1]=number;
    }
}

//The advantages of pass by reference,
//namely, the economic use of memory,
//together with the safety of pass by value
//can be achieved by the joint use of const and &
int average (const vector<int> & v, const int & n)
{
    int sum=0;
    for (int k=0; k<n; k++)
    {
        sum+=v[k];
    }
    return sum/n;
}

void write (ostream & out, const vector<int> & v, const int & n)
{
    for (int k=0; k<n; k++)
    {
        out << v[k] << endl;
    }
}

int main()
{
    vector<int> v;
    ifstream fin ("list.txt");
    ofstream fout ("list_out.txt");
   
    int n;
    read (fin, v, n);
    write (fout, v, n);
   
    fout << "The average of the " << n << " items in the list is: "
        << average (v, n) << endl;
       
    return 0;
}

You should look closely at the above solution to make sure you understand the use of value and reference parameters.  Also, don't miss the comment in the average function which emphasizes the merits of employing reference parameters in conjunction with const.

The above solution is certainly adequate to the task, but it doesn't take full advantage of the vector class.  The resize method is just one of several incorporated into the vector class.  In particular, the actual size of the vector can be accessed through the size() method.  The above solution can therefore be modified as follows:

Solution 2.  Uses the built-in size() method of the vector class to access the size attribute of v.

//File:    average4.cpp
//This version of the vector average problem exploits the ability of
//vectors to monitor their own size through the size() method.
//This means that we can drop the third parameter, n, in each of
//the following functions

#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

//Since vectors are NOT automatically passed by reference
//the & is necessary (& v)
void read (istream & in, vector<int> & v)  //don't need the third parameter, n
{
    int number;
    int n=0;    //LOCAL VARIABLE NOW
    while (in>>number)
    {
        v.resize(++n);    //Vectors allow dynamic sizing of the list!
        v[n-1]=number;
    }
}

//The advantages of pass by reference,
//namely, the economic use of memory,
//together with the safety of pass by value
//can be achieved by the joint use of const and &
int average (const vector<int> & v)      //don't need the third parameter, n
{
    int sum=0;
    for (int k=0; k<v.size(); k++)
    {
        sum+=v[k];
    }
    return sum/v.size();
}

void write (ostream & out, const vector<int> & v)
{
    for (int k=0; k<v.size(); k++)
    {
        out << v[k] << endl;
    }
}

int main()
{
    vector<int> v;
    ifstream fin ("list.txt");
    ofstream fout ("list_out.txt");
    //int n        NOT NECESSARY ANYMORE
   
    read (fin, v);
    write (fout, v);
   
    fout << "The average of the " << v.size() << " items in the list is: "
        << average (v) << endl;
       
    return 0;
}


Lab Exercise.  Write a program that sorts an array.  Use three functions; read(), sort(), and write().  If you get discouraged, feel free to check my solution (sort.cpp, using arrays, and sortVector.cpp, using vectors).


We revisited the vector<> type for further clarification.

//File:  vectors.cpp
#include
<iostream>
#include <vector>
using namespace std;
int main()
{
        int      list1 [5] = {0,1,2,3,4},
                 list2 [5];

        vector<int> v1(5),     //v(5) equivalent to list[5]
                           v2(5);

        for (int k=0; k <5; k++)     //vector<int> v1(list1, list1 + 5);     is EQUIVALENT.
        {                                         //It initializes v1 to share the values of list1;       
                    v1 [k] = k;               //i.e, from the value pointed to by list1
}                                                 //up to and including the value pointed to by list1 + 4.

        v2 = v1; // ==> LEGAL
        // list2 = list1; // ==> ILLEGAL!

        // Prints out the values stored in the lists
        for (int j=0; j < 5; j++)
        {
                cout << "list1 [" << j << "] = "
                       << list1[j] << endl;
                cout << "v1 [" << j << "] = "
                       << v1 [j] << endl;
                cout << "v2 [" << j << "] = "
                       << v2 [j] << endl;
                cout << endl;
        }

        return 0;
}

The output is as follows:

list1 [0] = 0
v1 [0] = 0
v2 [0] = 0

list1 [1] = 1
v1 [1] = 1
v2 [1] = 1

list1 [2] = 2
v1 [2] = 2
v2 [2] = 2

list1 [3] = 3
v1 [3] = 3
v2 [3] = 3

list1 [4] = 4
v1 [4] = 4
v2 [4] = 4

NOTE that the capacity of the vector can be initialized in a fashion similar to the array; i.e.,

list1 [5] is equivalent to v (5)

In each case, contiguous memory locations are set aside for 5 integers, indexed from 0 to 4.  On the other hand, there is no corresponding way to initialize the values of vectors.  The following would be illegal:

vector<int>    v(5) = {0, 1, 2, 3, 4}     //ILLEGAL

However, it is possible to initialize the vector v to share the values of the predefined array list1 as follows:

       int list1 [5] = {0,1,2,3,4}
        vector<int> v1(list1, list1+5);

The parameters list1 and list1+5 are pointers to the corresponding entries in the array list1.

Two-Dimensional Arrays

Two-dimensional arrays can be initialized in much the same way as one-dimensional arrays.  Suppose we want to create a two-dimensional array with 3 rows and 2 columns as follows:

1 2
3 4
5 6

We have

//File: array2.cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    int list [3][2] =     {
                {1, 2},
                {3, 4},
                {5, 6}
                };
               
    //int list [3][2]={1,2,3,4,5,6};    EQUIVALENT
    //Again, list2 = list1 ILLEGAL
   
    //Outputs [0,0] [0,1] [1,0][1,1] [2,0] [2,1]
    for (int j=0; j<3; j++)
    {
        for (int k=0; k<2; k++)
        {
            cout << setw(5) << list [j][k];
        }
        cout << endl;
    }

    return 0;
}

1 2
3 4
5 6

Since computers typically store multidimensional arrays in contiguous memory locations, in row major order, an equivalent initialization statement would be:

int list [3][2] = {1, 2, 3, 4, 5, 6};

As with one-dimensional arrays, assignment of one array to another is forbidden; i.e.,

list2 = list1;        ==>     ILLEGAL

Back to CSC 076 Home Page