vector<int> Inheritance Notes

inheritance is a way of creating a new class from an existing class

for example, consider the standard vector<int>

it already exists, and we can use it to do things like this:

vector<int> v;
v.push_back(4);
v.push_back(5);
v.push_back(1);
for(int i = 0; i < v.size; i++) {
    cout << v[i] << "\n";
}

now suppose we want a new class that is just like vector<int>, but the vector has a name, e.g. we want to be able to do things like this:

int_vec v("Table 2.1");
v.push_back(4);
v.push_back(5);
v.push_back(1);
v.sort();

cout << v.get_name() << "\n";
for(int i = 0; i < v.size; i++) {
    cout << v[i] << "\n";
}
cout << "sum = " << v.sum() << "\n";

this int_vec does everything a vector<int> does, except it also has

  • a constructor and getter for its name
  • methods sort and sum (that do what they say)

to create int_vec, we will use inheritance

this means we will inherit all the functionality of vector<int> into int_vec, and then add extra member variables and methods

we can start like this:

#include <iostream>
#include <vector>

using namespace std;

class int_vec : public vector<int> {
    // nothing yet!
}; // class int_vec

int main() {
    int_vec v;
    v.push_back(4);
    v.push_back(5);
    v.push_back(1);
    for(int n : v) cout << n << "\n";
}

the line class int_vec : public vector<int> uses inheritance to define a new class named int_vec that has all the functionality of vector<int>

this by itself is no great thing: practically int_vec is just another name for vector<int>

A Note on public Inheritance

note that we write : public vector<int>

  • the public qualifier here means that any public member variables or methods in vector<int> remain public, and similarly private member variables remain private
  • C++ also allows : protected vector<int>`, which inherits all member variables or methods in vector<int> so that they are protected in int_vec; while we’re not using protected much, if at all, in this course, a protected member is essentially one that is accessible in a class, or in any class that inherits from it
  • C++ also allows : private vector<int>` inheritance, which inherits all member variables or methods in vector<int> so that they are private in int_vec

in this course, inheritance will always mean public-style inheritance, e.g. : public vector<int>

public-style inheritance means that the access level of the member variables and methods in the class being inherited from don’t change

Adding Some Helper Functions

something that is easy to do is to add helper functions, e.g.:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

class int_vec : public vector<int> {
public:
    int sum() const {
        return accumulate(begin(), end(), 0);
    }
}; // class int_vec


int main() {
    int_vec v;
    v.push_back(4);
    v.push_back(5);
    v.push_back(1);
    for(int n : v) cout << n << "\n";
    cout << "sum = " << v.sum() << "\n";
}

sum returns the sum of all the elements in the int_vec

begin() and end() are methods defined in vector<int> that return iterators

  • iterators are a generalization of pointers

  • begin() returns an iterator to the first element of the int_vec

  • end() returns an iterator to one past the last element of the int_vec

  • accumulate(begin() end(), 0) is thus the sum of all the elements in the int_vec

  • you could also use a loop instead:

    int sum() const {
        int result = 0
        for(int n : *this) result += n;
        return result;
    }
    

here’s another example where we add a helper method for sorting:

// ...
#include <algorithm>

using namespace std;

class int_vec : public vector<int> {
public:

    // ...

    void sort() {
        std::sort(begin(), end());
    }
}; // class int_vec

int main() {
    int_vec v;
    v.push_back(4);
    v.push_back(5);
    v.push_back(1);
    v.sort();
    for(int n : v) cout << n << "\n";
    cout << "sum = " << v.sum() << "\n";
}

the sort() method is not const because it may actually modify the underlying numbers

  • also note that we must write std::sort in the body to avoid recursively calling the sort() method

Adding a Name

now lets add a string for the name of the int_vec:

// ...

#include <string>

// ...

class int_vec : public vector<int> {
    string name;
public:
    int_vec(const string& s)
    : vector<int>(),  // call default constructor of vector<int>
      name(s)
    { }

    string get_name() const {
        return name;
    }

    // ...

}; // class int_vec

first, notice how the constructor calls the vector<int>() default constructor

this is important!: we must call a vector<int> to ensure that it is initialized properly

after the vector<int> constructor finishes, we then construct the things specific to int_vec, i.e. we initialize name

second, we add a getter called get_name that returns the name of the int_vec

  • we might also add a set_name(string) method to set the name, but for this class we’ve decided that you can’t change a name once you’ve set it

An int_vec is a vector<int>

an important idea here is that int_vec extends the functionality of vector<int> by adding new methods and member variables

an int_vec can do everything a vector<int> does, and more

for example, suppose we write a function called summarize:

// ...

void summarize(const vector<int>& v) {
    for(int i = 0; i < v.size(); i++) {
        cout << "v[" << i << "] = " << v[i] << "\n";
    }
    cout << "size : " << v.size() << "\n";
}

int main() {
    int_vec v("Table 1");
    v.push_back(4);
    v.push_back(5);
    v.push_back(1);
    v.sort();

    summarize(v);
}

summarize takes a vector<int> as input, but we pass it an int_vec in the code above

that’s okay, because an int_vec can do everything a vector<int> does (and more)

but now suppose we add the function fancy_summarize:

void summarize(const vector<int>& v) {
    for(int i = 0; i < v.size(); i++) {
        cout << "v[" << i << "] = " << v[i] << "\n";
    }
    cout << "size: " << v.size() << "\n";
}

void fancy_summarize(const int_vec& v) {
    cout << v.get_name() << ":\n";
    for(int n : v) cout << "   " << n << "\n";
    cout << "sum = " << v.sum() << "\n";
}

int main() {
    int_vec v("Table 1");
    v.push_back(4);
    v.push_back(5);
    v.push_back(1);
    v.sort();

    summarize(v);
    cout << "\n";
    fancy_summarize(v);
}

fancy_summarize takes an int_vec as input, and so we can pass v to it

however, this code does not work with fancy_summarize:

int main() {
    vector<int> w;

    fancy_summarize(w);  // error: doesn't compile
}

this causes a compiler error because you cannot pass a vector<int> to a function that needs an int__vec

it is clear why this can’t work: inside of fancy_summarize, v.get_name() and v.sum() are called, but a vector<int> does not have a get_name or sum method

Constructor: Reading Numbers from a File

most of our constructors have been very simple — often empty!

so here is an int_vec constructor that opens a file of numbers and reads them in:

// ...

#include <fstream>

using namespace std;

class int_vec : public vector<int> {
   // ...
public:
    int_vec(const string& s)
    : vector<int>(),  // call default constructor of vector<int>
      name(s)
    { }

    int_vec(const string& s, const string& fname)
    : vector<int>(), name(s)
    {
        ifstream infile(fname);
        if (infile.fail()) {
            cmpt::error("int_vec: cannot open file");
        }
        int num = 0;
        while (infile >> num) {
            push_back(num);
        }
    }

    // ...
};

int main() {
    int_vec v("Table 2", "nums.txt");
    fancy_summarize(v);
}

note that if fname cannot be read, then infile.fail() will be true, and so we throw an error

this is important: this particular constructor does not let us create an int_vec if fname cannot be properly read

however, if fname is the name of a file that contains some non-integer values, then the constructor will usually stop reading the file as soon as it encounters the first non-integer

  • this might not be the best behavior
  • it might be better to throw an exception if a non-integer is encountered

one other annoyance here is that this constructor requires two strings, and its up to the programmer to remember the name of the vector comes before the name of the file