The Craft Of Coding

CPP 7-Mar Encapsulation, Polymorphism and Inheritance

Posted by Pete Sun, 06 Mar 2016 05:28:00 GMT

To date we have only used arrays to contain groups of similar items. Arrays are however relatively limited because they are fixed size, so need extra work if you want to add more elements than were originally anticipated. The way around this limitation is to use the Standard Template Library containers (also known as the STL Containers). These STL containers encapsulate all the complexity of managing a dynamic collection and provide a simple set of methods to interact with the container.

STL Vector

The vector class is a template class, which means that when you declare the instance of the vector you decide what kinds of things can be put into the vector.

std::vector<int> intVector;
std::vector<string> strVector;
std::vector<QuizResult> qrVector;

These are type safe collections, in that you can only put ints into intVector, and strings into strVector. The most efficient way of adding elements to a Vector is to add them at the end with the push_back method.

 intVector.push_back(1);

To access the contents of a Vector you can use the standard array access syntax intVector[1], but a better way is to use iterators. An iterator is dereferenced using the normal mechanism that is used to dereference a pointer, as shown in the example below.

    vector<string> strings ;

    for (vector<string>::iterator it = strings.begin(); it != strings.end(); ++it) {
        cout << ' ' << *it << endl;
    }

Polymorphism and Inheritance

In programming, there are often cases where you have several similar kinds of things that you want to be able to treat similarly, but still have them have their own unique behavior. The basic types int,float and double are examples of this, all respond to + - / *, and for purposes of arithmetic you can treat them the same (but the exact answer you get to an operation depend on the types of the values that you use the operator on).

Standard Contrived Example

The classical example is an Animal class, that is subclassed into Cat and others. The Animal class is said to be a pure virtual class since there are no Animals in existence. There are things that are kinds of animals (e.g Cats), but no Animals. So we say that a Cat is A Kind Of Animal, and in C++ this is expressed as

// in Animal.h
//pure virtual hello so cannot create an animal
class Animal  
{
    public:
        Animal();
        virtual ~Animal();
        virtual string hello(string name) = 0;
};

//in Cat.h
class Cat : public Animal
{
    public:
        Cat();
        virtual ~Cat();
        virtual string hello(string name);
};

Note the line class Cat : public Animal which denotes that Cat is publicly inheriting the characteristics of Animal. Then in the Cat.cpp file, you just need to implement the Cat version of the hello method.

string Cat::hello(string name) {
    return "the cat says hi to " + name;
}

This then gives you the ability to ask any kind of Animal to run their version of the hello method. It is guaranteed to be type safe, since the compiler will complain if you try to assign anything that is not a kind of Animal to a variable that is intended to hold the subclasses of Animal

    for (vector<string>::iterator it = strings.begin(); it != strings.end(); ++it) {
        Animal* pAnimal = AnimalFactory::Factory(*it);
        if (pAnimal) {
            cout << ' ' << pAnimal->hello("You") << endl;
        }
        else {
            cout << "Factory does not know that animal: " << *it << endl;
        }
    }

To make the above work, just need to implement the AnimalFactory::Factory method to encapsulate (hide) the details of how to create the subclasses of Animal (and potentially even what subclasses of Animal exist).

In this example, the Factory method is static so it can be called on the class, you do not need to create an instance of the AnimalFactory object in order to use this method.

// AnimalFactory.h
class AnimalFactory
{
    public:
        static Animal* Factory(string animal);
};

Defining the static method is the same as normal, the only difference is that static methods do not have a this pointer, since they run within the context of the class, not an object.

//AnimalFactory.cpp
Animal* AnimalFactory::Factory(string animal){
    if (animal.compare("cat")==0) {
        return new Cat();
    }
    return NULL;
}

Tasks for the week

  1. In a new project, use a vector of strings to contain a series of strings entered via cin, then use an iterator to print each one of those strings (after you have finished the input of the strings).
  2. Use the above Animal and AnimalFactory code to explore the idea of inheritance and polymorphism. Use the Cat example to create other subclasses of Animal, and then extend the code in your new project to create the appropriate subclasses depending on the strings entered
  3. Document two or more alternate high level designs for the calculator, identifying the steps in the process and potential classes you might need.
  4. By end of day on 12th March, please email pete@thecraftofcoding.com your zipped up collect and report projects from the Jan 18th class (do NOT rewrite them to use the STL vector, just keep the current arrays).

© Pete McBreen 2016 … once you have a dream you’ve got somewhere closer to a reality. — Terry Pratchett (Raising Steam)