CS 225 Class Example Notes


This is an annotated version of the example code that is located here, without the comments.
 

#include <stdio.h>
#include <stream.h>
#include <stdlib.h>
#include <string.h>

class foo {
    public:    // first we do all the public interface methods
Anything in the public section can be used by other parts of the program. These are put first to empasize that these are the only methods and data that can be used  by the outside world. There need to be methods declared here to access the private data if the outside program is going to read or write the private data.

If you want the outside program to have access, you need at least one method to read and one to write each private data element. Some private data is used by the class and is not available to  the user. Traditionally, these access routine are called set and get methods.

    // these are used to put values in the private data elements
    void set_a(int x) { a=x;}
This is a method used to set the value of on eof the private date elements. All the code for the method is included right here. This is called an inline method. See  the set_title() method below for an example of a method that is not inline. Usually, if the method is only a few lines long, it is best written inline. Longer functions should be written outside the class. This looks like an ordinary function definition because it is. C++ methods can do anything any other function can do. Note that the variable a is not defined in the method. The data elements of the class are available to the methods as if they were passed into the methods. They don't appear in the function header, but are visible in the function as if they were local variables. So in this case, setting a to x copies the value passed into the method into the private data element a.

    void set_b(int x) { b=x;}
    void set_title(char *);
This method only has the function header here. The function body is defined below, outside the class. But since the function header is located here inside the public section of the class, it is a public method of the class.

    // these are used to get the values from the private data elements
    int get_a() { return(a);}
    int get_b() { return(b);}
These are much like the set methods. Their purpose is to copy information from the private data elements to the outside world. Again, you don't have to declare the data elements, they are already there.

    // these are the constructors
    foo() { a = 0; b = 0; title = NULL;}   // default constructor
    foo(int x) { a=x; b = 0; title = NULL; }
    foo(int x,int y) { a= x; b = y; title=NULL;}
    // notice that one constructor can call another
    // and  they can call other methods
    foo(int x,int y,char * str) : a(x),b(y) { set_title(str);}
One of the goals of C++ is to prevent uninitialized variables. So every class has a constructor. A constructor is a special method that is called whenever a variable of the type of the class is created. A variable os the class type is called an instance of the class. There are several constructors here. C++ can allow several methods with the same name because the true name of a function is the combination of the return type,  the function name and the function parameters. SO in this case, foo() and foo(int x) are different because, while they have the same return type (nothing) and the same name (foo), they have different arguments. The combination of things that make up the true name is called the signature. It is verty common to have multiple constructors to allow the user to make use of various bits of information they might have. If they know nothing, the first constructor will initialize everything to 0. If they know the value of a, then the second one is better. The constructors are called in a way that looks like a function call. The constructor with no arguments is called the default constructor. It is used when you don't specify anything. It is also used when you create arrays. The examples in the main() function below show the different constructor usages.

    // these are defined outside the class declaration
    int bigger();
    void print_title();

    /* there is only one destructor. Note that the only
       way for title to be non-null is if we set the title
       so we should delete the space we created
    */
    ~foo() { cout << "the destructor is running" << endl;
             if(title != NULL) delete [] title;}
Many times a class allocates memory at runtime to make space for user input. It is important that this space get returned to the operating system when the class is finished with it to prevent memory from running out. C++ has no automatic memory cleanup (garbage collection) so you must do it. The simplest way is to put this job in the destructor. The destructor is another special method that is called when the instance of the class is no longer in use. If you create an instance of the class in a function, when the function exits the instance disapears and the destructor is called. In this class, since the constructors all set the string pointer element to NULL, we know that if the pointer is non-null, we should call delete to return the memory we got using new.

    private:  // these are private data, not visible outside the class
Any class elements, either data or functions that are placed inside the private section, cannot be used directly by the outside world.  So if we have an instance of this calss called X, we are not allowed to use the reference, X.a or to call X.set_length(), as these are private. Other class methods may call private methods or use private data.
    int a,b;
    char *title;
    // private methods can only be called by other class methods
    void set_length() { a = strlen(title);}
}; // class foo

The function bigger() is declared as a public method on the class foo. But the code for it is not within the class. To make sure that both the compiler and the user knows that this function is part of the class, we use the scope resolution operator to connect the function to the class. All external class methods start with a return type as usual and then have the name of the function with a prefix of class_name::. The class name in this case is foo and the :: is the scope resolution operator. Note that the data elements a and b are used as if they were already defined, just like the inline functions above.
int
foo::bigger()
{
   if(a < b) return(b);
   return(a);
} // bigger

void
foo::set_title(char * str)
{
   // note the use of cerr to print the error to stderr
   if( (title = new char[strlen(str)+1]) == NULL) {
      cerr << "no memory for to save title :" << str << ":" << endl;
      return;
   }
   (void) strcpy(title,str);
   set_length();
} // set_title

In foo::print_title, we check for NULL before printing. In general, it is wise to check if a pointer is null before using it.

void
foo::print_title()
{
   if(title != NULL)
      cout << title << endl;
   else
      cout << "NULL" << endl;
} // print_title

int
main(int argc, char **argv)
{
   foo X,Y(6),Z(7,8),W(1,2,"hello");

   printf("X.a = %d X.b = %d ",X.get_a(),X.get_b());
   X.print_title();
   printf("Y.a = %d Y.b = %d ",Y.get_a(),Y.get_b());
   Y.print_title();
   printf("Z.a = %d Z.b = %d ",Z.get_a(),Z.get_b());
   Z.print_title();
   printf("W.a = %d W.b = %d ",W.get_a(),W.get_b());
   W.print_title();
/*
   here is what I get when I run this with g++
 
X.a = 0. X.b = 0 NULL
Y.a = 6. Y.b = 0 NULL
Z.a = 7. Z.b = 8 NULL
W.a = 5. W.b = 2 hello
the destructor is running
the destructor is running
the destructor is running
the destructor is running

*/
 
   /* the following statements are illegal
      remove the comment markers and see what your compiler does
      with them
   */
//    X.a = 5;
//    cout << "a = " << X.a << endl;
//    X.set_length();

/* here are the errors that g++ gives me
class.cc: In function `int main(int, char **)':
class.cc:100: member `a' is a private member of class `foo'
class.cc:101: member `a' is a private member of class `foo'
class.cc:39: `void foo::set_length()' is private
class.cc:102: within this context

*/
} // main