NOTE: this website is out of date. This is the course web site from a past quarter. If you are a current student taking the course, you should visit the current class web site instead. If the current website is not yet visible by going to cs111.stanford.edu, it may be accessible by visiting this link until the new page is mounted at this address. Please be advised that courses' policies change with each new quarter and instructor, and any information on this out-of-date page may not apply to you.
Written by John Ousterhout and Nick Troccoli
Classes
We have used classes on the prior assignments - they are a way to define a new variable type. Check out our C/C++ guide for some resources on classes in C++.
Static Methods
Note that most of the methods of the Thread class
are declared as static; this means that
the methods are associated with the Thread class rather than a
specific Thread object. For a normal method, such as schedule,
you invoke it using an instance of the class, like this:
Thread *thread = new Thread(myFunc);
...
thread->schedule();
A static method is declared with the keyword static.
For example, here is the declaration of current in thread.hh:
static Thread *current();
You invoke it using the class, like this:
Thread *t = Thread::current();
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. A static method can't reference instance variables
of the "current object" because there is no "current object".
Static methods are useful in situations where there isn't an object 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; most of the static methods operate implicitly on the thread that is currently running.
Static Variables
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_;
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.
A static variable has only one instance, whose storage is allocated
outside any object. When a method refers to a
static variable, it will access the same variable regardless of the
current object. 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.
Extra: Initialization Lists
While not required on this assignment, you may sometimes see initialization lists in C++ used to initialize instance variables. 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, there are some benefits to using initialization lists - for instance, it turns out that in C++, if you initialize an instance variable in the constructor, that may actually be the second time C++ initializes that instance variable; as it may initialize it prior to an "empty" value, and then again when you set it in the constructor. However, if you use an initialization list, it avoids this double-initialization, which can improve efficiency. If you're interested in learning more about initialization list uses you can check out this guide here.