Archive for the ‘C++’ Category

New software

Wednesday, December 5th, 2007

I added a new page for my new program, screenshoot.exe. Read about it here.

Virtual Interfaces in C++

Sunday, November 18th, 2007

I read about the following way of implementing virtual interfaces in Exceptional C++ Style, but I only really realised the need for it this evening.

Suppose I have a class like this:

class Object
{
  public:
    virtual void translate (Vector d) = 0;
};

Other classes will inherit from Object, and they will override translate() as necesssary.

class Ball : public Object
{
  private:
    Vector m_position;
  public:
    virtual void translate (Vector d)
    {
        m_position += d;
    }
};

Now, suppose later on I decide that I want to keep track of the last time an Object was moved. I might add a variable to store the time of the last movement. I can then set this variable to be the current time in Object::translate().

class Object
{
  private:
    float m_timeOfLastMove;
  public:
    virtual void translate (Vector d)
    {
        m_timeOfLastMove = getCurrentTimeFromSomewhere();
    }
};

Now, because translate() was abstract, every other class that I have derived from Object has implemented its own custom implementation of translate(). I now need to go through all my code, and add a call to Object::translate() in each implementation of translate().

class Ball : public Object
{
  private:
    Vector m_position;
  public:
    virtual void translate (Vector d)
    {
        Object::translate(d);
        m_position += d;
    }
};

This involes a lot of copying and pasting of code, and I end up with calls to Object::translate(d) all over my code. If I forget to add one of these, the compiler will not complain. If I derive a new class from Object later on, and forget to add this call to the base implementation, I have created a new bug also. The bug will likely not lead to a crash immediately, as I am not doing anything particularily dangerous by not calling the base implementation of translate() in this case. If the bug does not lead to a crash, I might not know that the bug exists for some time.

If I had seperated the implementation of translate() from the public interface right from the start, the code would have been more flexible.

class Object
{
  private:
    virtual void translateImplementation (Vector d) = 0;

  public:
    void translate (Vector d)
    {
        translateImplemenentation(d);
    }
};

The Object class now has two contracts: Any Object can be translated by calling Object::translate(), and any class that derives from Object must implement translateImplementation(). I am free to change one of these without affecting the other. For example, I could rename the translate() function to move(), and I would not have to change any class the derives from Object.

Suppose I wanted to allow for rotation in the movement of Objects. I could change translateImplementation(Vector d) to transformImplementation(Matrix m).

class Object
{
  private:
    virtual void transformImplementation (Matrix m) = 0;

  public:
    void translate (Vector d)
    {
        Matrix m;
        m.translation = d;
        transformImplementation(m);
    }

    void transform (Matrix m)
    {
        transformImplementation(m);
    }

};

All classes that derive from Object would have to change their implementation of translateImplementation(), but all code that calls Object::translate() would not have to be to changed. I can keep the existing translate() function, but also add a transform() function.

The fact that I have made translateImplementation() private is very important. If any other code wants to translate an instance of Object, it must call Object::translate(). It is illegal for any code to call translateImplementation() directly.

Now if I want to go back and modify translate(), it is much easier for me. I may want to add some profiling or logging to all calls to translate(), or I might want to impose some pre-conditions or post-conditions. I can be confident that any additions to the translate() function will not be bypassed by some other class which derived from Object.