assign5 C++ Guide

Written by Nick Troccoli, based on a guide by John Ousterhout.

Classes

We have used classes on the prior assignments - they are a way to define a new variable type. This CS106B page gives a great refresher on classes and object-oriented programming.

Static Methods

For a normal instance method, such as schedule, you invoke it using an instance of the class, like this:

Thread thread(myFunc);
...
thread.schedule();

A static method is one that isn't called a particular instance of the class. It's associated with the Thread class rather than a specific Thread object. You invoke it using the class, like this:

Thread::exit();

When a normal method is executing, a pointer to the current object is available as the variable this, and you can access instance variables in that object directly. When a static method executes, there is no current object and no this variable available to the code of the method.

Static methods are useful in situations where there isn't an object of the type available to use for invoking the method, or where the functionality provided by the method doesn't relate to a specific instance of the class. The static methods in the Thread class are declared as static because they can be invoked in places where there is no Thread object available.

Static Variables

Normal variables defined in classes (called instance variables or member variables) are part of objects: there is a separate instance of the variable in each instance of the object. When a method refers to an instance variable, it accesses the variable associated with the current object.

Classes can also contain static variables. A static variable is one that is declared with the keyword static. For example, in the Thread class we have defined the ready queue as a static variable with this declaration:

static std::queue<Thread*> ready_;

A static variable has only one instance, whose storage is allocated outside any object. In other words, a static class variable means there is one copy of it across all instances of that type, as opposed to instance variables, where there is one copy of it for each instance of that type. For example, declaring static int x as a private variable inside some class MyClass means that all variables of type MyClass share the same x. Declaring it just as int x means that each variable of type MyClass has its own separate x that is independent from others.

Static variables are useful for holding information that is shared across all of the objects of the class; for example, the ready queue is shared by all threads. Static methods often access static variables (for example, the redispatch method will need to access ready_.)

A quirk of static variables is that they must be defined as well as declared. The code above declares the variable ready_ but does not actually define it (i.e. allocate storage for it). The definition of ready_ is in the file thread.cc:

std::queue<Thread*> Thread::ready_;

Notice that the variable is named with the prefix Thread::, similar to a static method.

new and delete

In C++, the new operator allocates memory on the heap, and delete frees that memory on the heap. So if we want to make a pointer to a heap-allocated int, we could do:

int *ptr = new int;
...
delete ptr;

delete deletes the memory that the pointer points to, so the pointer can be used to point to something else.

Initialization Lists

Most of the time, we can initialize instance variables in the constructor or the header file. However, sometimes we must initialize them via an initialization list; this is a list of initializations that happen when a new object is constructed but before the constructor body is called. It looks like this:

class MyClass {
...
private:
    int x;
}

...
// constructor
MyClass(int num) : x(num) {
    ...
} 

Notice the colon after the constructor, followed by a list of one or more initializations. We can initialize instance variables here - for example, x is initialized to have the value of num. While in this case we also could have initialized x in the constructor body by doing x = num, for some types (e.g. std::function) on this assignment the implementation will only work if you initialize it via the initialization list. While the only occurrence of initialization lists on this assignment is to initialize an std::function instance variable in Thread, if you're interested in learning more about their uses you can check out this guide here.