.. _lecture8: C++ Objects and Classes ####################### .. include:: /header.inc .. vim:filetype=rst spell: At last we get to the point where we can introduce the modern way to program. By now, you have enough experience with the basics of creating programs to be able to see what is going on and appreciate why this new style of programming was developed in the first place! A History Lesson **************** Way back in the dark ages of programming (let's see, that was when I started to program!), data was the most important concept in programming. Defining a bunch of variables that held the information you needed to manipulate was about the first thing programmers did. After all of the data was in place, the programmers started working out sequences of statements that manipulated that data and moved it around. When computers were big and expensive, not much data could actually be processed, so the programmers found this to be a reasonable way to manage their programs. Then came complexity! As computers grew in size, and programs grew as well, the complexity of the programs started to become a problem. Keeping track of what parts of the program manipulated what data became a real difficult thing to track. Programming language designers started thinking about better ways to organize the data, and we found ourselves working with things like records (or structures as `C` and `C++` programmers like to call them). This new way to organize data helped somewhat, but, still, it was difficult to track where data went, and what parts of the program modified things. Objects! ******** When the programmers studied the issue of how data got manipulated, it dawned on someone that we were actually bringing all the pieces of information about some real-world object together in one container. What we needed to do was to find a way to package not just the data but the operations on that data as well. And `Object Orientation` came into the world! Well, actually, it was not quite this way. But the new way of programming seeks to keep our focus on real-world concepts. People interact with other people, people interact with other things - like cars, computers, doors, banks, etc, etc. If we start classifying these things - call them objects - we can define a set of data items that characterize the objects, and define a set of functions that manipulate those data items. Here is the big leap in logic: Instead of letting other program components manipulate the data about an object, let's restrict access to that data and only allow the object, itself, to manipulate its own data. Phew, that is a big change! Now, how are we going to get our programs to work? We need a new way of building our programs. Classes ******* First off, we need a way to manufacture these objects and get their data components set up. We need a standard pattern for each kind of object we need to create in our programs. In keeping with how humans characterize similar objects, we might as well use the term `Class` to classify things! .. code-block:: c class class-name { // member variables // member methods }; The class-name is chosen to describe the objects we are defining in this declaration. We are actually is setting up a new data type in our program, here, and we can create real objects of this type - as we shall see. Member Variables ================ The data that characterize an object live in every object we create with this `class`. Each data item is a member of the `class`. We declare these data items the same way we have always declared them, except now, we are just describing the data items, not actually creating them. A typical example of such a description might look like this: .. code-block:: c class Rectangle // member variables int width; int height; int startx; int starty; }; We also call these variables ``attributes``. Either term means the same thing: a variable that will store a unique value for each real "object" we create using the class. When we get around to creating actual objects using this definition, each such object will have its own copy of these member variables. That makes sense, since every rectangle object may have it's own size and position! Here is a sample declaration that creates such a rectangle: .. code-block:: c Rectangle mainWindow; This looks very much like other declarations we have set up, except now the type name is our class name. .. note:: Convention says we always name classes with a capital letter. Member Methods ============== We call functions that manipulate data in each object `methods`. This terminology dates back to the early days of object-oriented thinking. But they are still functions! .. code-block:: text class Rectangle // member variables int width; int height; int startx; int starty; // member functions void setWidth(int width); void setHeight(int height); void set_x_pos(int xpos); void set_y_pos(int ypos); }; These additions again describe the functions - er - `methods` we want to use to manipulate the data items in the objects. They sure look like prototypes, don't they? That is exactly what they are - and they provide enough information for us to use them - remember the contract concept we discussed when we were going over prototypes earlier? But, we have a problem with this definition! Access Specifiers ================= By default all the member variables are only visible to member methods defined as part of the class. And member methods are only visible to other member methods inside the class as well. That does not seem very useful! We need to control who can see components of the class. .. code-block:: c class Rectangle // member variables private: int width; int height; int startx; int starty; // member functions public: void setWidth(int width); void setHeight(int height); void set_x_pos(int xpos); void set_y_pos(int ypos); }; The keywords ``private`` and ``public`` control who has access to the names (of variables or functions - er - methods) in the class. With the definition here, no outside user of this class can directly access the member variables. But outside users can call the methods! We have constructed a control barrier around our data. Using this scheme, we have full control over the code that manipulates the data associated with each object. No outside code can accidentally mess with the object data. Neat, but how do we work with the data? Defining Member Functions ========================= If you look at the above examples, we have not defined the actual methods, just the prototypes for those methods. We need to build the actual methods. .. code-block:: c void Rectangle::setWidth(int w) { width = w; } void Rectangle::setHeight(int h) { height = h; } Notice the use of the class name before the name of the method. The method body can access the member variables by simply using the name of the variable. Now, we are making some progress! Now that we know how to define the class, let's build a few objects and manipulate them! .. code-block:: c Rectangle rect1, rect2; rect1.setWidth(10); rect2.setWidth(40); rect1.setHeight(10); rect2.setHeight(20); Here we define two distinct objects. Each one has it's own private internal variables. Setting them is done through the methods defined for the class. Notice how we activate a method. We pick an object name (for instance rect1) and use a dot then the method name followed by the arguments for the method. Does this look familiar? The method code defined in the class definition is called up and provided with information that helps it locate a particular object. That particular object's member variables are accessed by the code. If we make the same call again with a different object name, the member variables used the second time will be those for the second object! Organizing Programs with Classes ******************************** It is common to create a new class in a file all by itself. It is also common to create a header file for that class that will be included in any file that wants to manipulate objects created from the class definition. Here is how we should build our code: .. code-block:: c #ifndef RECTANGLE_H #define RECTANGLE_H // Class Rectangle Declaration class Rectangle { // member variables private: int width; int height; int startx; int starty; // member functions public: void setWidth(int width); void setHeight(int height); void set_x_pos(int xpos); void set_y_pos(int ypos); }; #endif This should look familiar again! And the actual Class Implementation File look like this: .. code-block:: c #include "Rectangle.h" // Implementation of the Rectangle Class // setWidth - set the width of a Rectangle object void Rectangle::setWidth(int w) { width = w; } // setHeight - set the height of a Rectangle object void Rectangle::setHeight(int h) { height = h; } ... Now, your main program can simply include the class header file, and set off building objects as it wishes! Thinking about Classes ********************** When you decide to create a class, you usually (not always) have come to a point where you think you will need several objects that are similar in nature, but differ in details. The Rectangle is an example. If we were designing a graphics program that displayed several rectangles on the screen, each would have it's own particular data values, but the code to manipulate that data would be the same for all rectangles. That is how our class definition works. Classes serve as templates for one or more real objects in your program. There is no rule that says you must have more than one object in any given class. One is enough. So, we see that the Class Definition is just a blueprint for a real object. Objects act just like other variables - they are just new containers with data inside. The fact that we have included a set of functions inside the definition is the only new idea. Why was that done? To Control how the data in our objects is accessed. You need to turn your thinking around a bit when you use object oriented programming. Objects interact by sending Messages ************************************ We no longer think about calling a function to manipulate data scattered around our programs. Instead, we think about our program as a number of objects sending messages to each other! A message is just a method call. The calling part of the program wants the object to do something, but really does not care how it is done! So, we send a message to the object and let it figure out how to respond! Programming becomes a process of designing intelligent classes and them teaching objects created with the class interesting tricks. For instance, we might like our Rectangles to be able to be seen on a screen, or disappear. We would implement this new behaviour by creating new methods to make this happen: .. code-block:: c // Member functions void showWindow(); void hideRectangle(); Now, we would need to write the actual code to display a single rectangle, using the member variables to control where the rectangle appears on the screen. We would add code to the part of the program that is actually controlling the screen to set up our rectangles, then cause them to be displayed by sending the appropriate message to one or all of the objects. Guess what, this is exactly how Windoze works behind the screen! Here is a problem for you to think about For your next lab (next week), we will build a program that simulates pool balls rolling around on a pool table (not the full game, just the moving part). We need to figure out what objects we have to work with first. Any ideas pop into your mind? Let's consider the ball itself: What variables would it take to track where a pool ball is at any moment, and where it might be headed? These will become the instance variables for the class we will design to model each pool ball. Next, we need to figure out what behavior we want out pool balls to have. When we run this game, we will have a clock ticking away (just a simple loop). Every time the clock ticks, we will need to calculate where each ball is based on where it was at the last tick, and what direction it was moving in. (If you remember your high-school physics, this is not that hard to figure out!) There are a few wrinkles in this game. If a ball runs into the edge of the pool table, it will bounce - right? So, we need some way for another object to tell the ball that it has hit something, and let the ball take care of the bounce. Another wrinkle is that one ball can run into another ball. How are we going to figure that out? If the balls can only respond to commands to move (at each clock tick) or bounce (if someone tells them they have hit something) then who is going to figure out if the balls have run into something? Hint - it is that other object mentioned above. This discussion is not complete enough to work out in detail, but it is enough to get you thinking about how you might create this game using objects and messages. We will continue the analysis next time