Learn from a particle physicist

Sep 12, 2016 learning C++ phys-492/592

int var = 3; // declare an integer with an initial value of 3

is easy to understand. int declares the type of the variable var to be integer. Following this concept, we can declare the type of another variable to be Human:

Human mike;

Where mike is the name of the variable, Human is the type of the variable. This type is much more complicated than int, but nevertheless, it is still a type.

It is easy to initialize an integer. All it takes is just a number. It takes much more to create a virtual human being in your code. For example, you may need to specify mike’s height and weight. One = sign is not enough. So we use the following syntax in C++ to specify more than one properties of a complex type:

Human mike(float height=1.75*meter, float weight=135*pounds);

Following the same idea, we can have

Fish salmon;

and much more. In C++, the complex type has a name called “class”. Human and Fish are hence classes. mike and salmon are called the object of class Human and Fish, respectively. They are variables of complex types.

Now, our mike is hungry and he wants to eat fish. To describe this in our code, we can have

Human mike(float height=1.75*meter, float weight=135*pounds);
Fish salmon;
mike.Eat(salmon);

Eat is a function that a Human has. The dot in between mike and Eat is the way in C++ to call a function related to a complex type, which is called a member function of the class Human. Having member functions is the biggest difference between a class (a complex type) and a basic type like int. Basic types cannot DO things. Classes can.

Class Human and Fish is not defined in the standard C++ library. We will have to define them by ourselves before using them. This is how we do it in C++:

class Food // Fish is a type of Food, we define Food before Fish
{
   public:
      float fWeight; // class Food's member variable
      // Constructor is a special member function.
      // It has the same name as the class, returns nothing,
      // and can be used to initialize member variables.
      Food(float weight): fWeight(weight) {};
}

class Fish: public Food // class Fish inherits from class Food
{
   public:
      // Food's constructor is called in Fish's constructor
      Fish(float weight=0): Food(weight) {};
      // member function
      void Swim(); // just a declaration, to be defined elsewhere
}

class Human: public Food // a human can be another human's food :)
{
   public:
      float fHeight; // class Human's member variable
      // class Human's constructor member function
      Human(float height=0, float weight=0): Food(weight), fHeight(height) {};
      // member function
      void Eat(Food food) { fWeight=fWeight+food.fWeight; }
}

Since a class is a complex type, we need probably more than one variables to save some settings of it, they are called member variables. Traditionally, member variables start with an f to distinguish them from normal variables. Unless they are claimed to be public, they cannot be used outside of the class. Member variables can be initialized through a special member function called constructor, which has the same name as the class and returns nothing. As its name indicates, a constructor is used to construct (initialize is probably a better word) the class. When you declare a new object of a class, the constructor is called to initialize the object. There can be multiple constructors in a class, each of them gives a different initialization method.

Another interesting thing that a class can do but a basic type cannot is that a class can inherits from another class. As illustrated in our example, Fish inherits fWeight from Food. By inheritance we avoid duplicating some variables or functions in multiple classes. For example, we can have class Human and Bird inheriting fWeight from Food as well. Food is called the parent (or mother) class, Human, Fish and Bird are children (or daughters) of Food. Children classes all have the same member variables and functions inherited from their parent. In addition, they can have their unique features. For example, Fish can Swim(), Bird can Fly().

Since a class is a complex type, the OS has to allocate a block of memory to store it. There are two ways to do it, the fast but short-lived way and the slow but long-lived way. The memory claimed in the former way is called the stack. The memory claimed in the latter way is called the heap. A professional description of the stack and the heap is available in stackoverflow. As an end user, you just need to remember the followings:

  1. It is faster to declare a piece of memory in the stack than in the heap.
  2. An object stored in the stack can only live till the end of its scope. An object stored in the heap can live till the end of the program or till it is explicitly deleted in some other place in the program.

In the examples above, all the objects are declared in the stack. The way to declare an object in the heap is the following:

Human *jack = new Human(float height=1.90*meter, float weight=158*pounds);

We say that jack is a pointer, pointing to an Human object allocated in the heap using the new operator. This piece of memory can be manually deleted this way:

delete jack;

Keep forgetting deleting memory blocks allocated in the heap in a long-running program can cause a big problem called memory leak, as the program claims more and more memory as time goes by. This is not as serious in a program that only runs for a very short period and does not claim memory blocks constantly in the heap, because the occasionally leaked memory will be freed anyhow when the program ends.

The way to call a member function using a pointer is the following:

jack->Eat(mike); // use -> instead of . to call member function