C++

What is C++ ?

C++ is a programming language closely related to C

It is reasonably compatible - you can call C libraries from C++ and C++ libraries from C (same ABI)

But it is not fully compatible:

Why not teach it?

C++ is much bigger and more complicated than C

Most advantages apply to multi-module programs, which are barely touched on in this unit

The main extra concepts and features are object oriented, but in an old, complicated and confusing style

It is better to study object oriented programming 'properly' with Java in the second teaching block, then C++ becomes easy

What advantages does it have?

If you program in C in a very carefully disciplined way, there is nothing you can't do

The main advantage of C++ is the availability of lots of good libraries, e.g. for cryptography, gaming and so on

Also, programming can be safer, more convenient, and better structured, especially for variable-size data

However, C++ is often badly misused, or programs are over-engineered, so it too has to be used in a very carefully disciplined way

Preview

The remaining slides aim to give you a glimpse of what C++ is like

You can think of C++ as C with extra features (though some minor details are different because the standards never quite agree)

Classes are structs

Probably, the most important extra concept is the class:

class point { int x, y; };

To begin with, think of class as just a synonym for struct, defining fields of objects

You get the type point for free, as if you had typed

typedef class point point;

Constructor

A class gives you a constructor for free:

point *p = new point();

This is a safer version of a C memory allocation:

point *p = malloc(sizeof(point));

In fact you can still write this malloc call in C++, except you have to add a cast

Private

Unlike structs, everything in a class is private by default

p->x = 3; // ERROR

You can use the public: label to make items public:

class point {
  public:
    int x, y;
};

Example

#include <stdio.h>

class point { public: int x, y; };

int main() {
    point *p;
    p = new point();
    p->x = 2;
    printf("%d\n", p->x);
}

A C++ programmer would not normally use stdio or printf because C++ has its own IO library

Compiling

Most C compilers are also C++ compilers, called with a different name to show that you want C++

So point.cpp would be compiled with one of:

g++ -std=c++11 -g -o point point.cpp
clang++ -std=c++11 -g -o point point.cpp

You may find you need to use -o point.exe with clang on Windows, as with C

Methods

class point {
    int x, y;
  public:
    point(int x0, int y0) { x = x0; y = y0; }
    int getX() { return x; }
    int getY() { return y; }
};

A method is a function 'attached' to an object

point *p = new point(2, 3);
printf("%d\n", p->getX());

Overriding

point(int x0, int y0) { x = x0; y = y0; }

This line overrides the default constructor

A constructor has the same name as the class, and has an implicit return type (point *)

Calling

printf("%d\n", p->getX());

In this line, p->getX() is a method call

In C, it would be getX(p), the object you are calling the method on is an implicit extra first argument

An advantage is that with p->getX() lots of classes can have different getX methods, i.e. a class provides a namespace

Accessing fields

int getX() { return x; }

In this line, x is accessed directly (methods in a class can access its private fields)

The equivalent in C would be:

int getX(point *this) { return this->x; }

The name this in C++ can be used to access the implicit extra argument, if you need to

C Modules

Here's the point type implemented in a modular way in C, using an opaque structure declaration in the header

struct point;
typedef struct point point;
point *newPoint(int x0, int y0);
int getX(point *p);
int getY(point *p);
#include "point.h"
point *newPoint(int x0, int y0) { ... }
int getX(point *p) { ... }
int getY(point *p) { ... }

C++ Modules

Here's the 'standard' approach in C++

class point {
    int x, y;
  public:
    point(int x0, int y0);
    int getX();
    int getY();
};
#include "point.hpp"
point::point(int x0, int y0) { ... }
int point::getX() { return x; }
int point::getY() { return y; }

Exposure

The class definition is in the header:

class point {
    int x, y;
    ...
};

The fields are private so that, even though they are exposed in the header, other modules can't access them

They have to be in the header, so that an implicit sizeof can be used in calls to new point(...) from other modules

Declarations

The methods are declared rather than being defined:

class point {
    ...
  public:
    point(int x0, int y0);
    int getX();
    int getY();
};

External methods

The methods are defined with a point:: prefix:

point::point(int x0, int y0) { ... }

The point:: prefix says that the method is being defined inside the point class, even though the definition is physically outside the class

A weakness

There is a weakness in the 'standard' approach:

#include <SDL2/SDL.h>
class display {
    SDL_Window *window;
    ...
};

Even though the fields are private, their types are not, so in this case an SDL header has to be included

And now the SDL API has leaked all over the program

Why it matters

Why does the leak of SDL matter?

#include <SDL2/SDL.h>
...

Because you are tempted to mention SDL in other modules, for example using SDL_Colour to pass colours around

And then your whole program is SDL-dependent

What you should do

SDL should be included like this:

#include <SDL2/SDL.h>
...

It should be included in display.cpp in order to implement the display module, not in display.hpp to make it visible everywhere

Then a change to a different graphics library affects only one module

Is there a fix?

The official fix uses the "PIMPL strategy", but that seems too complex because each object is represented by two objects (it is a good example of the way over-engineering creeps into C++ programs

Instead, there are some simpler ways to fix the problem

Fix 1: use disguised types

In the header file, declare type struct Window; and give the window field the type struct Window *

In the implementation file, use casts to convert between struct Window * and SDL_Window *

This has the advantage of sticking with the official C++ way of presenting classes

Fix 2: the header

Here's a second fix - change the header file to:

class point {
  public:
    int getX();
    int getY();
};
point *newPoint(int x0, int y0);

The point type is effectively an interface, with no data

Fix 2: the implementation

Here's the fixed cpp file:

#include "point.hpp"
class pointI: public point {
    int x, y;
  public:
    pointI(int x0, int y0) { x = x0; y = y0; }
    int getX() { return x; }
    int getY() { return y; }
};
point *newPoint(int x0, int y0) {
    return new pointI(x0, y0);
}
point::getX() { return ((pointI *) this)->getX(); }
point::getY() { return ((pointI *) this)->getY(); }

Child class

class pointI: public point {
    int x, y;
  public:
    pointI(int x0, int y0) { x = x0; y = y0; }
    int getX() { return x; }
    int getY() { return y; }
};

The first line makes pointI a child class of point

It is the "point implementation" class

No other module has direct access to it - the only access is via the point interface

The newPoint function

point *newPoint(int x0, int y0) {
    return new pointI(x0, y0);
}

Instead of a constructor, an ordinary C-like function is defined for building new point objects, so there is no longer any need for the fields to be exposed

The only subtlety is that the value returned has type pointI * whereas the function is declared to return point *

But that's OK because the child class relationship means that a pointI is a point

Factory functions

Functions like newPoint are called factory functions, and are potentially much more flexible than conventional constructors

In fact, according to some people (including me), constructors using the new keyword are a mistake in object oriented languages generally (C++, C#, Java, JavaScript, Python, ...) and should be eliminated

Stubs

point::getX() { return ((pointI *) this)->getX(); }
point::getY() { return ((pointI *) this)->getY(); }

These functions cast the implicit extra argument this from point * to pointI *, so as to replace the point versions of getX/getY with the pointI versions

The third fix will get rid of the need for these stubs

Fix 3: the header

Here's the fixed header file:

class point {
  public:
    virtual int getX() = 0;
    virtual int getY() = 0;
};
point *newPoint(int x0, int y0);

Fix 3: implementation

Here's the fixed cpp file:

#include "point.hpp"
class pointI: public point {
    int x, y;
  public:
    pointI(int x0, int y0) { x = x0; y = y0; }
    int getX() { return x; }
    int getY() { return y; }
};
point *newPoint(int x0, int y0) {
    return new pointI(x0, y0);
}

Virtual methods

virtual int getX() = 0;

The getX method is declared virtual

That means the class has a child class which overrides the getX method, and in a call p->getX(), the compiler does not know whether variable p holds an object of class point or its child class pointI, so it has to compile the call differently

Compiling virtual methods

virtual int getX() = 0;

How does a call p->getX() get compiled?

It can't be compiled as getX(p) because there are two versions of getX

Instead, each object has an implicit extra class field, and the call is implemented as p->class->getX(p) (which can be done in C using function pointers)

Abstract classes

virtual int getX() = 0;

The =0 at the end of the declaration of getX means that it is an abstract method

That means there isn't a version for the point class, only for child classes

And so the point class has no constructor and you can't make point objects, only objects of child classes

Abstract classes are sometimes called interfaces

Review

Only two features of C++, classes and modules, have been covered, though they are key features

The advantages are fairly minor, because almost everything in C++ boils down to some fairly simple variations on C

There are rather a lot of new details to understand and master

But mastery opens up a new world of libraries and design possibilities