The Craft Of Coding

CPP 21-Mar OO Design

Posted by Pete Sat, 19 Mar 2016 03:12:00 GMT

OO Design

When implementing the calculator, need to identify the key concepts that we can encode as classes to implement the required functionality. Where possible we should use existing classes that are stable and known to work correctly. We should only define new classes when existing ones do not meet the needs of the application.

We also need to identify the parts of the problem that are well known with a readily available solution.

  1. Prompting for input cout from iostream
  2. Reading input cin also from iostream
  3. Splitting input string into characters or strings, done in CPP 6
  4. Saving strings into vector of strings in vector session
  5. Converting strings into useful objects See AnimalFactory
  6. Dealing with vector of useful objects — more thought and design needed, probably have to build some sort of tree
  7. Walking over tree to print out bracketed expression will use cout from iostream, walking tree requires more thought and design
  8. Walking tree to evaluate the overall expression — same walking mechanism as above, need more thought and design on the evaluation
  9. Printing result cout from iostream

So the complex parts are 6. 7 and 8, but these are heavily influenced by the objects we use to represent the expression in 5. So we need to spend time understanding how to model (and subsequently code) these objects such that we can implement the calculator. The things we have to deal with are doubles, ( and ), +, - , * and / .

Simplest way to attack this problem is to take your test cases/sample inputs and use those to validate any design ideas BEFORE you think about trying to code anything. And when you do start to code it, make the program deal with the simple cases first, adding complexity as you get the simple cases working. In this respect we are following the model we used when building the AnimalFactory to convert between strings and Animal subclasses.

Simplest case is the input of an empty string, or if that is not in your inputs, then the simplest case is probably just a number such as 1.

© 2016 Pete McBreen She was already learning that if you ignore the rules people will, half the time, quietly rewrite them so that they don’t apply to you. — Terry Pratchett (Equal Rites)

CPP 14 Mar Debugging

Posted by Pete Tue, 08 Mar 2016 04:00:00 GMT

Debugging

In order to do a good job in debugging, your first need some good test cases and the expected outputs. Make sure that your sample TestScores.txt are set up so the expected numbers in the QuizReport.txt are easy to confirm.

  • When using debugging outputs, make sure that you do not produce so much output that you cannot see what is happening.
  • Put in ONE debugging output to confirm or reject a SINGLE prediction about the code, then run the program to find out if your prediction is correct. This is sometimes known as Scientific Debugging
  • After your prediction has been confirmed/rejected, remove the debugging statements that supported that test - you know the answer to your prediction, so that code is no longer needed.
  • Keep a WRITTEN list of your predictions and what you now know about the program (later on you MIGHT be able to keep it all in your head, but that is unlikely when you have complex programs)
  • Make a SINGLE, testable change to the program in response to what you learn from your prediction.
  • Remember to make and WRITE down a prediction before running the program.
  • The key goal when running the program like this is to be surprised - you want to learn something that does not match your prediction.
  • Start your debugging process at the start of the program sequence - it is pointless to check the final outputs if you are not sure that the inputs are being read correctly.
  • Initially simplify the program inputs as much as possible, so ONE Quiz Result initially, make sure that case works, then add more as you validate that it seems to work for the previous inputs.

© 2016 Pete McBreen Seeing, contrary to popular wisdom, isn’t believing. It’s where belief stops, because it isn’t needed anymore. — Terry Pratchett (Pyramids)

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)