foundations of object oriented programming using c++evan.weaver/oopfoundations.pdf ·...

158
Foundations of Object Oriented Programming Using C++ by Evan Weaver School of Computer Studies Seneca College of Applied Arts and T echnology November 2003 ©1996-2003 by Evan Weaver and Seneca College. Effective 2014, this work is made available under the Creative Commons Attribution 4.0 International License. Visit http://creativecommons.org/licenses/by/4.0/ for details on how you may use it.

Upload: others

Post on 21-Jun-2020

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of Object Oriented Programming

Using C++

by Evan Weaver

School of Computer Studies Seneca College of Applied Arts and Technology

November 2003

©1996-2003 by Evan Weaver and Seneca College. Effective 2014, this work is made available under the Creative Commons Attribution 4.0 International License. Visit http://creativecommons.org/licenses/by/4.0/ for details on how you may use it.

Page 2: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of Object Oriented Programming Using C++ by Evan Weaver School of Computer Studies Seneca College of Applied Arts and Technology November 2003 Edition Table of Contents PREFACE.................................................1 Chapter 1. INTRODUCTION.................................3 The Hoopla About OOP............................3 A Bit of History - Structured Programming.......4 The OOP Revolution..............................4 Those Buzzwords Again...........................5 Objects Really Are Like Objects.................6 Chapter 2. STRUCTURES IN C AND C++......................9 Structures in C.................................9 Structures in C++..............................14 Prototypes Required in C++.....................15 Struct Keyword Not Repeated in C++.............16 New Comment Style in C++.......................17 Chapter 3. BASICS OF C++ CLASSES.......................21 Private Parts..................................22 Overloading Functions..........................24 Friends........................................26 Chapter 4. MORE ABOUT C++ CLASSES......................29 Constructors and Destructors...................29 Casting About..................................29 Casts can look like functions in C++...........30 Destructors....................................31 C++ Allows Variables To Be Defined Anywhere....33 Overloading Operators..........................33 Operators Can Be Members.......................36 The "this" Keyword.............................37 Some Exceptional Operators.....................38 Chapter 5. POINTERS, REFERENCES AND ARRAYS.............41 Review of Pointers.............................41 Review of Arrays...............................42 New and Delete.................................43 Dealing with Insufficient Memory...............45 References.....................................46 Pointers and References to Constant Data.......48 Copy Constructors and = Operators..............50 Example: A String Class........................51 Chapter 6. INTRODUCTION TO C++ STREAMS.................59 Streams........................................59 Controlling Ostream Output.....................62 Controlling Istream Input......................63 Writing Custom Iostream Operators..............65 Chapter 7. INHERITANCE.................................69 Deriving One Class From Another................69 Constructing and Destroying Derived Objects....72 Virtual Functions..............................72 Virtual Destructors............................74 Protected Members..............................75 Chapter 8. INTRODUCTION TO OO ANALYSIS AND DESIGN......79 Appendix A. SAMPLE PROBLEMS............................83 Appendix B. OPERATOR PRECEDENCE.......................153 Appendix C. RESERVED WORDS............................155

Page 3: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Preface Almost every C++ textbook published makes an attempt to cover the C++ language in a fairly complete manner. At Seneca we have found that the entire language cannot be adequately covered in one semester. Yet it doesn't seem practical to teach just a few chapters from such a book, since there would be major topic areas, which we consider important, that would have to be skipped. It is also awkward to cover in class exactly what we want to cover, expecting the students to wade through more complex (and complete) chapters of a book, trying to find the relatively simple topics taught in class. These notes are an attempt to cover the major object oriented features of C++ (specifically encapsulation, polymorphism and inheritance), while trying to give the student a useful set of programming skills. The sacrifice made herein is completeness - no topic is covered completely, and many topics (such as virtual base classes and templates) are not touched at all. The expectation is that later subjects in the curriculum will "fill in the blanks". Furthermore, these notes are intended for use by those who already have a basic, but not necessarily thorough, understanding of programming in the C language. Specifically, the reader is assumed to be familiar with the following C concepts: - data types (char, int, etc.), variables and constants - logic control structures (if, if/else, switch, while, do/while, for) - basic operators (assignment, arithmetic, relational, logical) - functions, parameter passing and return values - passing the address of a variable to a function in order to have the function change that variable - one-dimensional arrays - character strings (char arrays with a terminating null byte) - library functions to: do basic terminal input/output (printf, scanf, etc.) access sequential files (fopen, fprintf, etc.) manipulate character strings (strcpy, strcmp, etc.) These items will not be reviewed in these notes, so it is recommended that the reader have some fairly complete C reference available. Most people would also find it useful to have at least one other C++ reference on hand. Preface Page 1

Page 4: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Preface Page 2

Page 5: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 1. Introduction The Hoopla About OOP Object oriented programming (OOP) is one of the most frequently used buzzwords when people talk about modern programming. Yet many in the computer industry will admit to having only a vague idea of what OOP is, with no real understanding of it. This is because (1) the benefits being claimed for OOP (reduced development time, re-usability of previously written code, etc.) are exactly the same benefits that were claimed for the last major programming paradigm, structured programming, which became popular in the 1970s. (C is an example of a structured programming language). As a result, many people think that the "same old thing" is simply being repackaged and sold back to them, at a higher cost. (2) the actual differences between traditional structured programming concepts and OOP concepts are quite minor, or, rather, quite subtle. With most things in life, it takes a fair amount of experience to appreciate subtlety, and programming is no exception. (3) new terms, such as encapsulation, polymorphism and inheritance, are being used to highlight the differences in the OOP approach. Unfortunately, most people find these terms intimidating. (4) for simple programming applications, the traditional structured approaches are more than adequate. Most people learn new programming languages through simple examples. Consequently, OOP languages often seem needlessly complex, since the problems being approached can be more easily solved the old way. Most of the advantages of OOP become obvious only when the scale of a program grows large enough that well-structured (but not OO) code becomes hard to maintain. The whole reason for having programming methodologies (such as structured programming or OOP) is to simplify the programming process. The universe around us is, on one hand, a very complicated place. On the other hand, most things that we have to deal with can be viewed fairly simply, if we look at them the right way. A programming methodology can help to give us a way to view something, so that the task of simulating it on a computer (which is basically what programming is) is possible, at the very least. Chapter 1 - Introduction Page 3

Page 6: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition A Bit of History - Structured Programming At its simplest, the "structured programming" view is that a computer program is a description of the logic to be followed by the computer. Since this description may be very long and complicated, it would be easier to create in small pieces. Most structured programming methodologies are centered around trying to divide the program to be written into smaller sub-programs, each of which is small enough to be written fairly easily. Structured languages support the division of a program's logic into small pieces that are reasonably independent from each other. For example, C, a structured language, allows you to write a program as a collection of functions, where the data shared between the functions can be clearly identified as the list of parameters passed between the functions. Structured programming techniques were quite effective for a long time. But as computers become used to control more and more things, our desires for what we want the computer to do only increase. Computer programs have always been a simulation of some aspect of our world. As time goes on, we expect these simulations to be closer to reality. Since reality gets more complex the closer you look at it, this means that we expect programs today to be more complex than the programs of twenty years ago. The problem with the structured approach is that as logic complexity increases, so does the complexity of the data being managed by the logic. The more you try to control with the computer, the more pieces of data the program will need to keep track of. In a language like C, you either end up passing zillions of parameters between functions, or you start to violate structured programming rules by using global variables to hold shared data (which reduces the independence of the functions). Both of these approaches start to become unwieldy after a while. In short, the structured approach allows us to sub-divide the logic of a program into smaller pieces of logic, but doesn't address the issue of sub-dividing the data into smaller groups of related data. The OOP Revolution The revolutionary, yet very subtle, concept in OOP is that a program is not just logic, but really is a combination of logic and the data on which that logic operates. To subdivide a program adequately into smaller programs, both logic and data must be subdivided at the same time. Now, this may sound like the obvious thing to do, but for years it wasn't! Part of the problem is that the structured languages were designed for dividing up the logic, but not the data, and things can get Chapter 1 - Introduction Page 4

Page 7: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition messy very quickly if you attempt to divide both simultaneously. OOP languages, like C++, directly support this simultaneous subdivision. (In much the same way, when structured programming first came into vogue, it was messy to use a language that didn't support subdivision of the logic). The term object simply refers to a collection of related data and the logic that is appropriate for manipulating that data. The goal of OOP is to subdivide a problem into a set of objects, each of which is small enough to write fairly easily. In C++, an object is implemented by creating a "class", which is a set of data variables and a set of functions that operate on those variables. Even the most primitive computer languages have objects. For example, in C, an int is an object. An int can hold a whole number (the data), and you may add, subtract, multiply, divide, compare and assign that number (the logic). Another example of an object in C is an array of ints. Such an array is a collection of ints (the data), each of which individually has the properties of an int, where you specify by position which of the ints you want to work with at any point in time (the logic). In non-OOP languages, we tend to call these objects the "data types". Many people learning OOP for the first time like to mentally substitute the term "data type" every time they see the term "object". The difference between an OOP language and one that is not is whether or not the programmer, who uses the language, can create new objects. With C, you are stuck with the objects that are built into the language. In C++, the programmer can create new objects at any time. Those Buzzwords Again Whenever OOP is mentioned, the terms encapsulation, polymorphism and inheritance are never far behind. Encapsulation refers to placing all the appropriate logic together with all the relevant data in the design of an object. Encapsulation is the basic essential concept in OOP. Also included in the concept of encapsulation is the ability to hide, from other parts of the program, details of implementation that don't affect how the object is used. As a simplistic example, you need not know how an int is stored to be able to use ints effectively in a C program. You don't really need to know if it is stored in binary or decimal, or if it is being held electronically or biologically or mechanically. All you need to know is how to use an int. If an object has been encapsulated properly, all the logic necessary to manage the object will be in one place (within the Chapter 1 - Introduction Page 5

Page 8: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition object), and the object will be reasonably easy to use (because nasty complexities have been hidden inside the object). Polymorphism is the ability to make the same name perform more or less the same operation, regardless of the object being used. Even C makes use of polymorphism with its built-in objects. For example, + means more or less the same thing whether the objects being added are ints or doubles, even though ints and doubles are quite different things internally. In C++, you can extend + to have meaning for any objects you create, if it seems appropriate to do so. (Polymorphism applies to any logic, whether it is an operator, such as +, or a function with a name). Included in the concept of polymorphism is the ability for the language translator (the compiler in the case of C++) to determine the appropriate version of a piece of logic from the context, without the programmer having to be explicit. For example, in the expression x + y, the programmer doesn't worry about whether an int addition or a double addition (or some other addition) is being done, because the compiler will automatically select the most appropriate kind of addition to do, based on the data types of x and y. Inheritance is the ability to create new objects that are similar to existing objects, by specifying only the parts of the code that are new or different. The new object is said to be "based on" or "derived from" the existing object, and automatically inherits all the data fields and pieces of logic from the existing object, unless otherwise stated. Objects Really Are Like Objects Normally, when we encounter an object in the real world, we tend to characterize it by the things we can see, hear, touch, smell, taste and do with (or to) the object. The emphasis in our perception is how we interface with the object. The internal workings of an object are of no concern to us if they don't affect how we deal with the object. The objects we observe are almost always comprised of other objects. We see a car, and we think of how it looks, how well it handles, how comfortable it is. This same car has brakes, and engine, wheels, windows, and so on. Each of these objects has its own characteristics that we consider important. If we change the brakes on a car, it is crucial that the new brakes fit the car and work the same way. But it is entirely possible that we might be able to find new brakes that work much better than the old ones. In this case, we are able to improve the performance of the car by changing the old brakes for new, improved ones. This improvement can be made without changing any other part of the car. Chapter 1 - Introduction Page 6

Page 9: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition OOP lets us view programming in almost the same way as this way we naturally view objects in our world. If our program is comprised of objects that are categorized by how they interact with other programs (not by how they are implemented), then if we find, or write, a new object that functions the same way as one of the program's objects, but is faster, or more reliable, or better in some other way, then we can improve the program simply by replacing the old object with the new one. Well, enough of this theoretical discussion. Frankly, most people will find the preceding discussion to be relatively meaningless at this stage. After you have had some actual experience with OOP, you may want to come back to review this chapter, and you may find that you actually understand it! Just keep in mind that the small examples with which we must necessarily deal at this introductory level do not fully exploit the advantages of OOP. Until you are familiar enough with the syntax to begin developing somewhat complex applications, you may not appreciate all the new syntax you are about to learn. Remember that the benefits are real and will eventually become apparent to you. And also remember, many years from now, that no one ever said OOP is the ultimate programming paradigm. Just as structured programming was good enough for many years worth of applications, OOP will have its time in vogue. But as computer applications become more and more complex, even OOP will be unable to handle it, and something better still will come along. In the meantime, let's study C++. Chapter 1 - Introduction Page 7

Page 10: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 1 - Introduction Page 8

Page 11: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 2. Structures in C and C++ Structures in C Before we get into the creation of C++ objects using classes, let us examine C language structures. A structure in C is a way to create an aggregate data type, a data type comprised of different pieces. An array is an example of an aggregate data type with which you are already familiar. In an array, all the elements are of the same data type, and are identified by their positions within the array. In a structure, on the other hand, the elements may be of different types, and are identified by unique names. The elements of a structure are sometimes called fields of the structure, or members of the structure. We will use the term members. There are two steps in using a structure. The first is to lay out what the various members of the structure will be (in other words, to declare the structure), and the second is to define and use variables of the new data type that you have created by declaring the structure. [Experienced C programmers will know that it is possible to declare a structure at the same time as you create variables of that type. For stylistic reasons, however, we will always separate the two activities]. You declare a structure using the keyword struct, and the following model: struct tag { type1 member1; type2 member2; ... }; where tag is the name you wish to give to the structure, type1 is the data type of the first member, member1 is the name you wish to use for the first member of the structure, and so on. Note that the member declarations look just like normal variable declarations, and that the members need not be of simple data types, but may be pointers, arrays or structures themselves. The rules for picking structure tags and member names are exactly the same as the rules for picking other identifier names such as variable names or function names. To define a variable that stores a structure, you use the struct keyword in a slightly different way: Chapter 2 - Structures in C and C++ Page 9

Page 12: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition struct tag variable; where tag is the name for a structure you have already declared and variable is the name you wish to use for the variable. In essence, once you declare a structure tag, then the construct struct tag becomes a new data type. In fact, using this just like a built-in data type, you may define variables that are pointers to structs or even arrays of structs. [Note that in C++, the rules are a little different: the keyword struct may be omitted except when originally declaring the structure.] To actually use a structure variable, you may: (1) use the individual members of the structure or (2) pass the structure between functions. To use a member of a structure, you must identify both the name of the structure variable and the name of the individual member (because there may be many variables of the same structure type, having the same member names). You do this by giving the name of the structure variable, followed by a period, followed by the member name (e.g. variable.member1). You may use this rather long name in any situation that is appropriate for a variable of the same type as the member. Let's start to develop an example. Suppose that we want to create a structure to hold a date, with three members: the day number (an int), the month name (a string), and the year number (also an int). struct date { int day; char month[10]; int year; }; is fine to create a new data type, struct date. Now, to create a variable of this type, say to store the date on which an employee started working for a company: struct date start_date; creates a variable named start_date, which will store one of these dates. We could now do things like: start_date.day = 1; strcpy(start_date.month, "January"); start_date.year = 1993; if we wanted to "hard-code" some values for the date, or Chapter 2 - Structures in C and C++ Page 10

Page 13: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition printf("Enter month: "); gets(start_date.month); printf("Enter day: "); scanf("%d", &start_date.day); printf("Enter year: "); scanf("%d", &start_date.year); if we wanted to have the user enter the date interactively. Note that the order in which the members appear in the structure has no bearing on the order in which you may access them. Also, keep in mind that the structure tag itself (in this case, "date") is only used when creating a variable (in this case, "start_date") and not when accessing a variable's members. What is the main advantage to being able to group a bunch of related fields into a structure? Simplicity, particularly involving the interaction between functions: it is easier to pass one parameter (e.g. a struct date) rather than three (e.g. a month, a day and a year). The advantage becomes more obvious with structures that have many more members than just three. One problem with passing a structure between functions is that, since most structures tend to be large, it can be inefficient to pass an entire structure. (Remember that when you pass something to a function, a copy of what you are passing is made). Consequently, most programmers, in the interest of efficient use of resources, will pass the address of a structure, rather than passing the structure itself. (Note that the only way to pass an array - another potentially large and inefficient piece of data - is by passing the address of the start of the array, so that this is not an issue with arrays). It is therefore common to see parameters declared something like this: struct date *pdate (i.e. the local variable "pdate" points to a "struct date" coming from the calling function). As with any other piece of data passed into a pointer, if we want to use or modify the originating data, we use *pdate Unfortunately, the address resolution operator, "*", has a lower priority than the member specifier, ".", in an expression like *pdate.day /* NOTE: THIS IS WRONG!!! */ and so parentheses are needed to access members of a structure Chapter 2 - Structures in C and C++ Page 11

Page 14: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition passed this way, such as: (*pdate).day There is a slightly more convenient notation for accessing member of a structure, given a pointer to the structure: pdate->day The simplest way to remember this is: if you have a structure, use "." to identify a member, but if you only have a pointer to a structure, use "->" instead of "." thereby avoiding the necessity of parentheses. One last important stylistic point is that normally the structure is declared globally (i.e. outside any function), while definitions of structure variables are usually local to the various situations in which the variables are required. The structure declaration must be global because the data type it creates is used when prototyping functions which receive or return that kind of data. We now have enough syntax rules to develop a simple complete example using structures. The following C program has the user enter five dates, and a description for each date, and then display a table showing the dates and descriptions. For simplicity, no data validation is done. | /* Example Program prog2a by Evan Weaver, 16-May-1995 | This shows typical use of structures in C by having the | user enter several dates along with descriptions, and then | displays a summary of the data entered. | */ | #include <stdio.h> | | #define COUNT 5 /* number of times to repeat */ | #define SLENGTH 61 /* size for char strings */ | struct date { | int day; /* day of the month (1-31) */ | char month[10]; /* month of the year (e.g. "January") */ | int year; /* year (e.g. 1995) */ | }; | void clearinput(void); | void enter(struct date *pdate, char desc[]); | void report(struct date dts[], char descs[][SLENGTH], int count); | | /* In the following program, two contrasting styles | ** have been intentionally mixed, to provide a wider | ** variety of syntax examples. Normally, either both | ** the data entry loop and the reporting loop would be | ** in main(), or both would be delegated to functions | */ Chapter 2 - Structures in C and C++ Page 12

Page 15: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | void main(void) | { | struct date dates[COUNT]; /* important dates */ | char descriptions[COUNT][SLENGTH]; /* descriptions for dates */ | int i; | | for (i = 0; i < COUNT; i++) | enter(&dates[i], descriptions[i]); | | report(dates, descriptions, COUNT); | } | | /* Clear out any remaining characters | ** in the input buffer. | */ | void clearinput(void) { | while (getchar() != '\n') | /* intentionally empty loop! */ ; | } | | /* Make user enter a date and its | ** description, at the terminal. | */ | void enter(struct date *pdate, char desc[]) | { | printf("Enter the month: (e.g. January ) "); | gets(pdate->month); | printf("Enter the day: (between 1 and 31) "); | scanf("%d", &pdate->day); | printf("Enter the year: (e.g. 1995) "); | scanf("%d", &pdate->year); | clearinput(); | printf("Enter a description for this date: "); | gets(desc); | } | | /* Displays a report of important dates. "dts" is an | ** array of dates, "descs" is a corresponding array of | ** descriptions for those dates, and "count" is the number | ** of dates in the arrays. | */ | void report(struct date dts[], char descs[][SLENGTH], int count) | { | int i; | | printf("\n\nImportant Dates:\n\n"); | for (i = 0; i < count; i++) | printf("%s %d, %d is %s\n", dts[i].month, dts[i].day, | dts[i].year, descs[i]); | } Chapter 2 - Structures in C and C++ Page 13

Page 16: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Exercise 2.1: Improve the data entry of dates in prog2a, so that the user is asked to re-enter any date data that is invalid. Exercise 2.2: Write a program that asks the user to enter a date, and then searches through a text file, called dates.txt, for all lines that begin with that date, displaying any lines that match. You may assume that lines will be no longer than 80 characters and will look something like the following: February 2, 1996: Ground-hog Day August 11, 1995: Last day of summer classes Exercise 2.3: There are some standard C library functions that are used to manipulate date/time combinations (including getting the current date and time). Figure out what each of the members of the structure tagged "tm" are. (Hint: start by looking up what the library functions time, localtime, mktime, and asctime do). Structures in C++ As we have already mentioned, an object is a combination of logic and the data managed by that logic. In C++, the structure is the basis for objects. The members of a structure are not limited to being data, but may be functions as well. For example, let us extend the date structure from the last section to include two functions: one to set the date, and the other to display the date. struct date { int day; char month[10]; int year; void set(int d, char m[], int y) { day = d; strcpy(month, m); year = y; } void print() { printf("%s %d, %d", month, day, year); } }; The function to set the date, which we have called set, has no return value (void), but is supposed to be passed an int representing the day (d), a string containing the month (m) and an int holding the year (y). All that the function does is set the various data members of the structure. Similarly, the function to display the date, which we have called print, has no return value and is passed no parameters. Chapter 2 - Structures in C and C++ Page 14

Page 17: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Prototypes Required in C++ C++ is an extension of the C language that allows for OOP. It was developed by Bjarne Stroustrup of Bell Labs. For the most part, C++ is a superset of C. However, Mr. Stroustrup took advantage of the opportunity to "fix" a few minor things. Whenever we encounter one of these changes, the explanation will be indented like this, indicating that we are discussing a minor rule change from C rather than an important OO topic. In C, if a function prototype has no parameters listed (i.e. nothing is between the the parentheses), that simply means that you do not want to specify what the parameters are and the compiler will not necessarily reject subsequent calls to the function that have parameters. The correct way, in C, to specify that a function has no parameters is to use the word "void" as the parameter list when prototyping the function, and when writing the header line of the function. In C++, the decision was made to force the programmer to either fully prototype or actually define each function before it is used. This prevents silly program errors that occur because a function prototype or a header file was forgotten. (It also unfortunately breaks some sloppy but not incorrect programs). Consequently, it is impossible "not to specify what the parameters are" in C++. Another decision was made then to allow empty parentheses, in C++, to mean the same thing as having "void" as the parameter list - an indication that the function has no parameters. One of the most important things to note about the member functions of a structure is that they may reference the data members of the same structure variable (and in fact the other function members as well) without the need to qualify which structure variable the members came from. For example, in the "set" member function, the line day = d; sets the variable "day", which is one of the other members of the same structure. There is no need to specify which structure variable "day" came from: it will be assumed that "day" comes from the same variable whose "set" function is being called. A program that wanted to define and set a date could do something like: date start_date; start_date.set(1, "January", 1995); Chapter 2 - Structures in C and C++ Page 15

Page 18: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Struct Keyword Not Repeated in C++ Since structures (along with classes) form the basis for creating new data types in C++, it was deemed cumbersome to have to repeat the struct keyword every time you create variable of that type. In C++, you only need to use struct when you are first declaring a structure tag. From that point on, it suffices to use the tag itself as the data type for any variables you create. When we define a variable of a new type that we have created, we are creating one instance of that type. One program typically uses many instances of each type it creates. Instantiation refers to the concept of creating an instance of an object (a structure or class). Later we will see how to make things happen automatically each time an object is instantiated. If we want to display the "start_date" variable, we can simply call its "print" function: start_date.print(); Note how the structure variable's name must be specified when calling a structure's member functions. The structure variable (or instance) given is sometimes called an implicit parameter to the function. (The function needs to know which instance to work with, yet the instance is not one of the things in the explicit parameter list for the function.) Just as a C compiler only needs to know the prototype for a function in order to compile code that calls the function, a C++ compiler only needs to know the data members and the prototypes for the member functions of a structure in order to compile code that uses that structure. The actual logic for the structure's member functions can appear outside the structure declaration, as long as each function thus defined has the structure tag, followed by two colons, immediately preceding the function name. For example, the basic declaration of a structure might appear in a header file. Let us call the following file "date.h": | // Example Header file date.h by E. Weaver, 23-May-1995 | | struct date { | int day; // day of the month | char month[10]; // month name, e.g. "January" | int year; // year, e.g. 1995 | void set(int d, char m[], int y); // Set date to given values | void print(); // Display date on standard output | void input(); // Accept date from standard input | }; Chapter 2 - Structures in C and C++ Page 16

Page 19: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition New Comment Style in C++ While C++ allows for regular C-style comments, which begin with "/*" and end with "*/", it also allows for a new style of comment which begins with "//" and ends with the end of the line. While this kind of comment cannot be embedded in the middle of a line, it has the major advantage that you will never forget to end it. (A common, and very frustrating, source of C bugs is forgetting to close comments, thereby accidentally commenting out large portions of code). The file, date.h, would then be included (using #include) in any source file that wanted to use the "date" type. Another file, which we will call "date.cpp" (or "date.cxx" or "date.C" depending on your C++ compiler), would contain the actual code for the member functions: | // Example Source File date by E. Weaver, 23-May-1995 | // This contains the code for the function members of a date. | | #include <stdio.h> | #include <string.h> | #include "date.h" // struct layout for date | | // set date to values given (day, month, year) | void date::set(int d, char m[], int y) | { | day = d; | strcpy(month, m); | year = y; | } | | // display date on standard output device | void date::print() | { | printf("%s %d, %d", month, day, year); | } | | // get date from standard input (very basic!) | void date::input() | { | scanf("%s%d,%d", month, &day, &year); | } This file would be compiled separately and linked in with any program that wants to use dates. For example, we could modify our previous program: Chapter 2 - Structures in C and C++ Page 17

Page 20: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | /* Example Program prog2b by Evan Weaver, 23-May-1995 | This is an adaptation of prog2a, using a C++ struct | to store a date | */ | #include <stdio.h> | #include "date.h" | | #define COUNT 5 /* number of times to repeat */ | #define SLENGTH 61 /* size for char strings */ | void clearinput(void); | void enter(date *pdate, char desc[]); | void report(date dts[], char descs[][SLENGTH], int count); | | /* In the following program, two contrasting styles | ** have been intentionally mixed, to provide a wider | ** variety of syntax examples. Normally, either both | ** the data entry loop and the reporting loop would be | ** in main(), or both would be delegated to functions | */ | int main() | { | date dates[COUNT]; /* important dates */ | char descriptions[COUNT][SLENGTH]; /* descriptions for dates */ | int i; | | for (i = 0; i < COUNT; i++) | enter(&dates[i], descriptions[i]); | | report(dates, descriptions, COUNT); | return 0; | } | | /* Clear out any remaining characters | ** in the input buffer. | */ | void clearinput(void) { | while (getchar() != '\n') | /* intentionally empty loop! */ ; | } | | /* Make user enter a date and its | ** description, at the terminal. | */ | void enter(date *pdate, char desc[]) | { | printf("Enter a date: (e.g. January 1, 1995) "); | pdate->input(); | clearinput(); | printf("Enter a description for this date: "); | gets(desc); | } | Chapter 2 - Structures in C and C++ Page 18

Page 21: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | /* Displays a report of important dates. "dts" is an | ** array of dates, "descs" is a corresponding array of | ** descriptions for those dates, and "count" is the number | ** of dates in the arrays. | */ | void report(date dts[], char descs[][SLENGTH], int count) | { | int i; | | printf("\n\nImportant Dates:\n\n"); | for (i = 0; i < count; i++) { | dts[i].print(); | printf(" is %s\n", descs[i]); | } | } There are a couple of interesting points to make about this new version of the program. The first is that everything dealing with dates has been simplified. Each operation with a date is now a single statement, rather than three statements. The second point is that the program doesn't make use of the "set" function for a date. In general, when we design an object, we attempt to identify and implement the kinds of operations that are likely to be done to the object. For objects that will hold some sort of data, like a date, three operations that easily come to mind are: (1) setting the object to some value, (2) inputting a value for the object and (3) outputting the value stored in the object. In our example, all three of these things are easy to implement, and so we did, even though the specific application we developed didn't require (1). It is highly likely, however, that almost any other program we might write using dates would need (1), so we threw it in. This is not to say that it is good to develop reams of code that will never actually be used. Rather, the point to be made is that it is good to develop objects in as general a framework as possible, to maximize the possibility that the object can be used, hopefully without modification, in as many situations as possible. Techie types will want to note that there is a slight difference between coding the member functions inside the structure declaration and coding them separately. In fact, coding functions inside the structure declaration makes them "inline" functions, if possible, which means that no actual function call will really be made, but rather the code for the function will be inserted in each place the function is called (similar to a #define macro in C). Extensive use of inline functions will make the executable program code a little bit faster and a little bit bigger, which is good and bad at the same time. For most programming, the differences are so slight as to be not worth much worry, so we will generally code our functions separately Chapter 2 - Structures in C and C++ Page 19

Page 22: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition from the structure declarations, in order to make the structure declarations as simple and clear as possible. If you want to know more about the subtleties involved with inline functions, see a more complete C++ reference. Exercise 2.4: Add some error checking to the "input" routine for a date. Make the user re-enter the date (after displaying a message like "bad date! re-enter:") if the month name isn't valid, if the day is not appropriate for the month (such as February 31, 1992), if the year is less than 0 or more than 10000, or if something else, such as no comma or non-numeric data in the day or year spots, is wrong. Exercise 2.5: Modify the "set" function for a date to do some error checking. If any of the parameters are invalid, set the month name to "Invalid", and set the day and year to 0. Also write a test program to try out your modification. Chapter 2 Summary In C, you can make a new data type called a structure, where one variable of this type has many different data members (also called elements or fields) of possibly different data types. You use the keyword, struct, to create a structure. A structure tag identifies the new data type, and is used to define variables of the new type. Each member of a struct has a unique name. A member of a struct is identified using both the struct variable and the member name. A period (.) is used to separate the variable and the member name, although an arrow (->) is used to separate them if a pointer is used instead of a struct variable. The source code for one program may be split over several source files, with related definitions in each source file. Declarations of the things defined in a source file are placed in a header file, to be included (using #include) by any source file that wants to access those things. In C++, a structure may also have function members, which are referenced the same way that the data members are, using a period (.) or an arrow (->). When coding a member function, the other members of the same structure may be used without specifying which structure variable they are from, in which case the current structure is assumed. When coding a member function outside the structure declaration, the structure tag, followed by two colons (::), must precede the function name in the function's header line. Chapter 2 - Structures in C and C++ Page 20

Page 23: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 3. Basics of C++ Classes Before we start into C++ classes, let's improve the "date" structure with which we have been working. One "bad" implementation point in the date was the decision to store the month as a character string. This was done (1) because the user typically likes to enter and see months by name rather than, say, by month number, and (2) we wanted to see an example of a structure with different sorts of fields. If the month had been stored as a number, all three fields would have been ints, and we wouldn't have had a chance to see how an array member is handled. The problem with having a character string for the month is that if we want to do any sort of calculation with dates, such as finding the number of days between two dates, or determining whether one date is chronologically before another, having the month as a number would be much easier. We can easily adjust the date structure to allow for this: struct date { int day; // day of the month int month; // month number (1-12) int year; // year, e.g. 1995 void set(int d, char m[], int y); // Set date to given values void print(); // Display date on standard output void input(); // Accept date from standard input }; To compensate for this change, we will need to change the logic for the set, print and input functions. Since all these functions will now require us to switch between month names and numbers, let us first write a function to get a month name, given its number: void getmonth(int monthno, char monthname[]) { char mnames[13][10] = { "Unknown", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; strcpy(monthname, mnames[(monthno >= 1 && monthno <= 12) ? monthno : 0]); } Notice how this function sets the month name to "Unknown" if the month number is not between 1 and 12. This is an example of conservative programming, where a function does not necessarily assume that its parameters contain valid data. Now date's functions will be easy to code. For example, Chapter 3 - Basics of C++ Classes Page 21

Page 24: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition // set date to values given (day, month, year) void date::set(int d, char m[], int y) { int i; char monthname[10]; day = d; month = 0; // month is unknown if it doesn't get set below for (i = 1; i <= 12; i++) { getmonth(i, monthname); if (strcmp(m, monthname) == 0) month = i; } year = y; } (Recall that strcmp returns a zero if the two strings are the same). The other two functions can be just as simply modified. What we have done is changed the implementation of how the date is stored, without affecting the things we can do with the date. We have made it easier to add calculations to the date (which we plan to do soon!). We have made the input and output routines for the date more complex, but this will be more than made up for once we start to do calculations with dates. What we have really done is to store the date in the most convenient manner for the computer, and to put the work of translating that data to a format humans prefer into the input and output functions, where such a burden is most appropriate. The program we wrote at the end of chapter two is able to use this new version of a date without modification. Only the date header file and the date source file need to be changed. The reason this works is that our program only used the date's functions, input and print, but never specifically used the month field itself. Private Parts But what about other programs that use a date? If there are any programs using a date that do use the month field directly, they will have to be changed. It would be nice if we could change an implementation detail of an object, such as we have just done, without having to rewrite the programs that are already using that object. Similarly, suppose we improve the set and input functions so that they will perform some sort of validation. Then a program would not be able to set a date to an incorrect value (thereby making one less place we have to go looking for bugs when trying to fix a program), unless, of course, that program directly manipulates some of the data members. It would be advantageous Chapter 3 - Basics of C++ Classes Page 22

Page 25: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition if we were able to prevent programs from directly accessing some of the members of a structure. Well, there is a way to "hide" members of a structure, so that they can only be accessed by other members of the same structure. If the keyword private: appears before a member declaration in a structure, then all members declared thereafter will be inaccessible, except to other members of the structure. Similarly, the keyword public: makes the members declared after it available to any function that uses the structure. Note that each "public:" or "private:" encountered overrides any previous one. It might make sense for us to change date yet again: struct date { private: int day; // day of the month int month; // month number (1-12) int year; // year, e.g. 1995 public: void set(int d, char m[], int y); // Set date to given values void print(); // Display date on standard output void input(); // Accept date from standard input }; This way, programs can set a date, input one interactively, and print one out. But what if you want a program to do something else with a date? The whole point is that if you want to do something with a date, then this new functionality should be added to the date itself. By making certain members private you: (1) ensure that those members are only accessed through member functions, which allows you the opportunity to validate that the members are used properly. This is particularly important for data members. (2) can hide details of how the object is implemented, so that you can improve the performance of the object in the future without affecting its use. (3) ensure that new functionality is added to the object itself, where it is easy to find, rather than having little pieces of new functionality buried in hundreds of different programs all over the place. Chapter 3 - Basics of C++ Classes Page 23

Page 26: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition One problem with programmers, however, is that they are inherently lazy. They are, after all, still human. Since the members of a structure are public by default, most people wouldn't bother explicitly hiding members that should be private. And making structure members private by default would break all the existing C code that uses structures. So C++ creates a new aggregate data type, called a class, that is exactly like a structure except that the members are private, unless otherwise declared. Everything else about a class is the same as a structure. Simply use the keyword "class" in place of the keyword "struct". The class is the preferred basis for an object in C++, since in a class you must make a decision about what should be private and what should be public. From this point on, we will use and refer to classes, rather than structures, even though everything we will say applies equally well to structures. Most programmers continue to use struct when they are building aggregate data types where every member is data and is public. In other words, when a C-style struct is all that is needed, a struct is commonly used. But as soon as any member functions are required, it is common practice to use a class instead. The preferred look to our date becomes: class date { int day; // day of the month int month; // month number (1-12) int year; // year, e.g. 1995 public: void set(int d, char m[], int y); // Set date to given values void print(); // Display date on standard output void input(); // Accept date from standard input }; Overloading Functions C++ allows you to give multiple definitions for a function. The compiler will automatically select the most appropriate version to use each time you call the function, by looking at the parameter list to the function. This is called overloading a function, and is one way in which C++ supports the concept of polymorphism. For example, suppose that in our date, we want to continue to be able to set a date by giving it a day number, a month name and a year number, but we would also like to be able to set a date by giving it a day number, a month number, and a year. The most appropriate name for such a function might be set, even though we already have a function named set. Our date could become Chapter 3 - Basics of C++ Classes Page 24

Page 27: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition class date { int day; // day of the month int month; // month number (1-12) int year; // year, e.g. 1995 public: void set(int d, char m[], int y); // Set date to ... void set(int d, int m, int y); // ... given values void print(); // Display date on standard output void input(); // Accept date from standard input }; where we have added, into the date source file, void date::set(int d, int m, int y) { day = d; month = m; year = y; } If a program were using a date called, say, x, then both x.set(25, "February", 1967); and x.set(15, 3, 1994); would work as expected. To decide which function to actually call, a C++ compiler looks not just at the function name, but at the function's signature, which is the function name, the types of parameters and their order. To overload a function, the different versions of a function must have different parameter lists. It is not sufficient just to have different return types, nor is it suitable to have the same types of parameters in the same order but with different local names. If you call a function that has been overloaded, the arguments you supply need not match one of the available signatures exactly. The compiler will make its standard attempts to convert the arguments to types that will match one of the function versions, and will select the "best" fit. (For example, if you pass an int, and no version of the function expects to receive an int, but one version does expect to receive a double, then that version will be used, and the int being passed will be converted to a double). Most of the fairly complete C++ reference books explain the exact set of conversions that the compiler will attempt, if you are interested in the details. Finally, note that any function may be overloaded, not just member functions of a class. Chapter 3 - Basics of C++ Classes Page 25

Page 28: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Friends There are times when you would like to write a function that can have access to the private members of an object, yet is not itself a member of the object. The reason for wanting to do this is syntactical. For example, if we wanted to create a member function of a date that can determine the maximum of two dates, we could write a member function date maximum(date another); Then, if we wanted to compute the maximum of two dates, say x and y, and store the result in a third date, say z, we would have to do something like z = x.maximum(y); which is not very intuitive looking, to say the least. It would be much nicer to be able to say something like z = maximum(x, y); In the latter, maximum is clearly not a member, yet computing the maximum of two dates certainly involves access to the members day, month and year, which are private. C++ allows us to specify that a function, which is not a member function, may access an object's private members. Such a function is called a friend. A friend function is identified by including the function's prototype within the class declaration, preceded by the keyword "friend". The function may then be written normally, and any accesses of the private members will not be flagged as errors by the compiler. In case of our date class, its declaration would become: class date { int day; // day of the month int month; // month number (1-12) int year; // year, e.g. 1995 public: void set(int d, char m[], int y); // Set date to given values void print(); // Display date on standard output void input(); // Accept date from standard input friend date maximum(date a, date b); // Find latest date }; and the code for maximum might be: Chapter 3 - Basics of C++ Classes Page 26

Page 29: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition date maximum(date a, date b) { if (a.year > b.year || (a.year == b.year && (a.month > b.month || (a.month == b.month && a.day > b.day)))) return a; else return b; } Note that even though a friend function is not a member, it must be prototyped inside the class declaration. [For those that are interested, note also that another class may be declared as a friend to a class. In this case, the line friend class classname; should appear in the class declaration, where classname is the name of the class whose member functions may access the private members of the class. The friend class must itself be declared at some point outside (and after) the declaration of the class which is granting friendship. Think twice, however, before making one class a friend to another: since friends are a way to bypass the normal privacy controls, they can make debugging much more difficult when you find that the private parts of one class are being trashed by another class' member functions.] Exercise 3.1: Throughout this chapter, we have added several bits and pieces to the date class. Put all of these together, and write a test program to try everything out. You should end up with three files: (1) a header file for a date, which has the class declaration and the function prototype for the getmonth function, (2) the source code for a date, which has the code for all the member functions (including both versions of set), the code for getmonth and the code for maximum, and (3) a main program that tests all aspects of the date class, including the maximum function. Exercise 3.2: Add a new version of the set function to the date class. This new version should have no parameters and should return nothing. It should set the date to be the current system date. Hint: use the time function to get the current date and time, and then use the localtime function to convert this to a form from which you can easily extract the necessary data. Both functions are declared in the <time.h> system header file. Chapter 3 - Basics of C++ Classes Page 27

Page 30: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 3 Summary The phrases "public:" and "private:" are used to control whether (public) or not (private) certain members of a structure are accessible from functions that are not members of the structure. A class is the same as a struct, with one difference: the members of a struct are public unless otherwise specified, whereas the members of a class are private unless otherwise specified. If a structure is going to have function members, it is preferable to make a class rather than a struct. The same function name may be used with different parameter lists (or signatures) to define the "same" function several ways, in which case the compiler will automatically select which version of the function to use, based on the match between the arguments given in the function call and the parameter lists in the available versions of the function. This is called overloading a function. Both member functions and globally defined functions may be overloaded. A function that is not a member of a class may be identified, within the class declaration, as a friend to the class, in which case the function may access the private members of the class. Chapter 3 - Basics of C++ Classes Page 28

Page 31: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 4. More About C++ Classes Constructors and Destructors An important element of the control of objects is the ability to specify certain actions that must occur when an object is instantiated (for example, when you create a variable instance of a class) and other actions that must occur when an object is destroyed (such as when the variable goes out of scope). A constructor is a member function that is automatically called when instantiation of the class occurs. It is like a normal function except that (1) it has the same name as the class itself, and (2) it has no return value. Typically, constructors are used to initialize the data fields of an object, although they are not restricted to this kind of activity. Constructors may have parameters, in which case arguments must be supplied at the time of instantiation. For example, inside our date class we could create a constructor (in the public section of the class declaration) date(int d, char m[], int y); and code it as follows: date::date(int d, char m[], int y) { set(d, m, y); } A program using a date could then declare a variable, named, say, x, as follows: date x(1, "January", 1980); If you do not want to specify any arguments at all at the time of instantiation, then you must either have no constructors or have a constructor that requires no parameters. A constructor with no parameters is called the default constructor. Constructors, like normal functions, may be overloaded, so you may supply as many versions of the constructor as you find appropriate, as long as the signatures are different. Note that a constructor has no return value, partly because you never actually call it. It is automatically called, and its purpose is to manipulate the other members of the object, not to return a value. Casting About A constructor also implements functionality a lot like the cast Chapter 4 - More About C++ Classes Page 29

Page 32: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition operation in C. In C, if you enclose a data type in parentheses, this converts the expression immediately to the right into the specified data type. More correctly, it creates a temporary expression whose value is the value of the expression (to the right) converted to the data type. For example, in the statement x = n / m; suppose that x is a double, while n and m are integers. Since both operands of the division are integers, an integer division will be done, even though the final destination is a double. x = n / (double)m; fixes this problem by converting m (the expression to the right of the double cast) to a double, thereby causing a double division to be performed. Note that m itself isn't harmed; a temporary double, containing a converted copy of the value in m, is used in the calculation. Similarly, if we had a date variable named, say, x, we could do something like: x = date(15, "March", 1991); which effectively creates a temporary date value, passes the three parameters to its constructor, and finally assigns that date to x. Another example is x = maximum(x, date(30, "April", 1995)); which calls the maximum() function (which, you will recall, we wrote to expect two dates as arguments) to set x to be the later of itself and April 30, 1995. Seeing this, you might wonder why we bothered writing the set() functions. In fact, had we known about constructors at the time, we probably would have written constructors rather than the set() functions. Casts can look like functions in C++ Because constructors can function in C++ much like the cast operation, C++ allows casts to the built-in data types to look like functions as well. Thus the statement x = n / (double)m; may, in C++, also be written as x = n / double(m); This way the same syntax can be used for the built-in Chapter 4 - More About C++ Classes Page 30

Page 33: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition types and your own types (through the constructors you write for your own types). The only problem comes up when trying to cast addresses to addresses of other types. If, say, ptri is a pointer to an int, and ptrc is a pointer to a char, one could be cast to the other in C as: ptrc = (char *)prti; The C++ "function-style" version of this would be ptrc = (char *)(ptri); where, unfortunately, there is no way to do without the parentheses holding the char and the * together. Destructors A destructor is similar to a constructor except that (1) its name is a tilde (~) followed by the name of the class, (2) it is automatically called when an instance of an object is destroyed (such as when a variable goes out of scope), rather than when created, and (3) since it is not possible to pass values to a destructor, there is only one version allowed: the destructor with an empty parameter list. The purpose of a destructor is to clean up after the object is gone (or rather as it is being removed since the destructor is called just before the space allocated for instance of the object is released). In most situations, some sort of cleanup is unnecessary; the memory that was automatically allocated for the instance of the object is also automatically freed. But if the object specifically ties up additional resources while it is active, the destructor is a simple way to automate the freeing up of these resources. As an example of the usefulness of constructors and destructors, let us create another object, a simple character-by-character input file. As you should already know, to process a file, we must first open it, then we may access it, and finally we must close it. By creating a class to access a file, we can open the file automatically when the file variable is declared, and we can have the file automatically closed when we are done with the variable. First let's see the header file for a simple input file class, which we will call "infile", that simply allows us to read one character at a time: Chapter 4 - More About C++ Classes Page 31

Page 34: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | // Example Header File infile.h by E. Weaver, 06-Jun-1995 | #include <stdio.h> | | class infile { | FILE *fp; | | public: | infile(char filename[]); // open file | ~infile(); // close file | int readchar(); // read next char or EOF | }; | And here is the source code for the infile class: | // Example Source File infile by E. Weaver, 06-Jun-1995 | #include "infile.h" | | // Constructor: requires name of file to be read | // Note that fopen returns NULL (i.e. 0) if the | // file cannot be read | infile::infile(char filename[]) | { | fp = fopen(filename, "r"); | } | | // Destructor: close the file if it was opened | infile::~infile() | { | if (fp) | fclose(fp); | } | | // reads next character from the file, or EOF | int infile::readchar() | { | if (fp) | return fgetc(fp); | else | return EOF; | } The following program is an example of using the infile object to count how many times the letter A appears in a file named data.txt. In the program, note how the need to explicitly open and close the file has disappeared. The file will be opened when the variable f is defined, and it will be closed when f disappears, which, in this case, is when the main() function terminates. Chapter 4 - More About C++ Classes Page 32

Page 35: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | // Example Source File prog4a by E. Weaver, 06-Jun-1995 | #include "infile.h" | | int main() | { | infile f("data.txt"); | int count = 0, c; | | while (EOF != (c = f.readchar())) | if (c == 'a' || c == 'A') | count++; | printf("data.txt has %d occurrences of the letter A\n", | count); | | return 0; | } C++ Allows Variables To Be Defined Anywhere In C, local variables must be defined at the beginning of a block of code (i.e. code enclosed in braces), and the variable lasts until the end of the block of code. In C++, because of constructors, defining a variable may cause some code to be executed. You are allowed to declare variables anywhere you want in C++, so that you have some control over exactly when the constructor is run. If you do declare a variable part-way through a code block, it is then active until the end of the code block. The part of the program during which a variable is visible is called its scope. Current ergonomic design principles dictate that it is best to put controls near the things being controlled. Thus, in automobiles, we usually find sun-roof controls near the sun-roof and door controls near (or on) the doors. Similarly, since C++ allows you to define variables wherever you like, most programmers will wait to define a variable until it is needed, rather than defining them all at the top of the function as required in C. It is common then to see loops like: for (int i = 0; i < 10; i++) { ..... } where i is defined and initialized in the first part of the for statement. Overloading Operators The built-in operators in C++ can be overloaded in much the same way as functions. In fact, an operator is coded exactly like a function with a special name: the keyword operator followed by Chapter 4 - More About C++ Classes Page 33

Page 36: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition the operator itself. To overload the operator +, for example, write a function named "operator+". Normally, an operator has either one operand (a unary operator, such as the logical not operator !) or two operands (a binary operator. such as the addition operator +). When you are overloading an operator, you specify either one parameter, indicating a unary operator, or two parameters, indicating a binary operator. For example, it might make sense to define the + symbol to allow us to add an integer to a date, giving a new date. A program with a date variable, say, x, could then advance x forward by a week with the statement like: x = x + 7; or could find the date 90 days before x by using the expression x + -90 in a calculation. Before implementing this operator, let us write a function to determine the number of days in a given month. Note that we need to know the year in which the month occurs, in case of February in a leap year. int monthdays(int mth, int yr) { int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // look up days in month, adding 1 for Feb in a leap year return days[mth] + (mth==2 && (yr%400==0 || yr%4==0 && yr%100) ? 1 : 0); } Now we can add an integer to a date, by successively going forward (if positive) or backward (if negative) in time until we reach the destination date. First, we should add the line: friend date operator+(date d, int n); to the public section of our date, since we will probably need access to the private members in order to do such a calculation. Then we can write the new version of + as follows: Chapter 4 - More About C++ Classes Page 34

Page 37: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition date operator+(date d, int n) { int leftinmonth; if (n > 0) // advance in time while (n > 0) { // chip away until we get there // How many days are left in this month? leftinmonth = monthdays(d.month, d.year) - d.day; if (leftinmonth >= n) { // more days than we need d.day += n; n = 0; } else { // not enough, so go to start of next month d.day = 1; if (d.month < 12) d.month++; else { d.month = 1; d.year++; } n -= leftinmonth + 1; } } else { // go back in time n = -n; // get number of days (absolute value) while (n > 0) { // chip away until we get there // Is destination date in the current month? if (d.day > n) { // yes d.day -= n; n = 0; } else { // no, so go to end of previous month n -= d.day; if (d.month > 1) d.month--; else { d.month = 12; d.year--; } d.day = monthdays(d.month, d.year); } } } return d; } For symmetry, we would probably also want to define an operator to add the other way around: Chapter 4 - More About C++ Classes Page 35

Page 38: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition date operator+(int n, date d) { return d + n; } As well, it would be nice to be able to subtract an integer from a date: date operator-(date d, int n) { return d + (-n); } Note that these last two operators do not need to be declared as friends to the date, since they do not directly access the date's private members. Operators Can Be Members An overloaded operator may be a member rather than a friend under certain conditions. A binary operator may be declared as a member, if you want the operator's left argument to be the object of which it is a member. In this case, you code the operator with one parameter, representing the right argument. The left argument is assumed to be the object itself. Similarly, a unary operator may be declared as a member if you code the operator as having no parameters; the object itself will be treated as the right (and only) argument for the operator. The above + operator, which adds a date and an integer, could have been prototyped within the date declaration as: date operator+(int n); This way, if there were a date variable named, say, x, the expression x + 365 would call x's member operator +, passing it 365 as a parameter. Note how this is different from calling a member function (which would require a period), and looks identical, in terms of use, to the way it looks if the operator is a friend with two parameters rather than a member with one parameter. To code this member version of the operator, the header line would change to date date::operator+(int n) Chapter 4 - More About C++ Classes Page 36

Page 39: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition and, to avoid having to change the rest of the code, we could make a local copy of the current object right at the beginning of the operator's definition: date d(day, month, year); or, using something new we will explain shortly, date d; d = *this; (Note that if we modified the code to use the members of the object directly, rather than making a copy of it, then any changes to the day, month and year fields would cause a change to the object itself. While this is not desirable in this example, it would be useful if we were overloading an assignment operator). It is a very subtle point to decide whether an operator that needs private access should be written as a friend or as a member, since either will work well in most situations. The substantive difference between the two is that if the operator is a member, the left argument must be an instance of the class for which the operator is a member, whereas if the operator is a friend, the left argument merely needs to be "cast-able" to the required class type. (In the case of unary operators, substitute "argument" for "left argument" in the previous sentence). A general rule of thumb is to declare operators as non-members unless the operator is designed to change the left argument (or argument, for unary operators) itself, in which case the operator would be better as a member. Using this rule of thumb, the friend version of the + operator would be slightly preferable to the member version. Incidentally, you should be aware that just because you choose to code an operator as a non-member, that doesn't necessarily mean that the operator will have to be a friend. If a non-member operator can be coded using only public features of the class, then there is no reason to make it a friend. But if there isn't enough public functionality to implement the operator, it is cleaner simply to make the operator a friend, rather than making a public member function, which does the work that the operator needs to accomplish, whose only purpose is to be called by the operator. The "this" Keyword There are times, such as the situation above, where you are writing a member function and need to refer to the object itself (even though you don't need to refer to the object in order to access its members). The C++ keyword this is defined to be a Chapter 4 - More About C++ Classes Page 37

Page 40: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition pointer to the current object. Thus, if you want to return the current object, you can use * (address resolution) to do something like: return *this; Similarly, you can pass the object itself (using "*this") or the object's address (using "this") to another function. Some Exceptional Operators Some operators have extra rules regarding how they can be overloaded. For example, when you overload the assignment operator (=), it must be coded as a member. Also, recall that the ++ and -- operators each have two unary uses. For example, if ++ is placed before its argument (prefix), then the variable is incremented before its value is returned, while if ++ is placed after its argument (postfix), the variable is also incremented but its value before incrementing is returned. When you overload these operators, you will probably want to make similar distinctions. If you overload operator++ or operator-- as a unary operator, you will be defining the "prefix" uses of these operators. In order to define the postfix uses you need to add an extra, dummy parameter of type int. This parameter is not to be used - it is just provided as a way to distinguish the coding of pre- and postfix versions of the operators. For example, to code both pre- and postfix ++ as member operators for our date class, we would have to code both date date::operator++() { return *this = *this + 1; } and date date::operator++(int) { date rc = *this; *this = *this + 1; return rc; } (after prototyping them inside the class declaration, of course). Note how in the postfix version, we haven't even named the int parameter, since we aren't going to look at it - its presence is just what the compiler is using to signal that this is the "other" version of ++. If we were to code postfix ++ or -- as a non-member operator, then it would have two parameters, where the second parameter is the "dummy" int. Chapter 4 - More About C++ Classes Page 38

Page 41: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Exercise 4.1: In this chapter, we have added a constructor, two member operators and three non-member operators to the date class. Actually put these pieces in the right places, and write a test program to make sure it all works as it should. While you are at it, add two more constructors (one that has no parameters and sets the date to January 1, 1970, and another that has three int parameters holding the day, month and year numbers, respectively) and four member operators: date operator--(); date operator--(int); date operator+=(int n); date operator-=(int n); which do the obvious things. (Note that since these operators will change the object itself, they have been specified as members.) Exercise 4.2: Determine the output of the following program: | #include <stdio.h> | #include <string.h> | | class x { | char title[21]; | public: | x() { | strcpy(title, "no name"); | printf("%s created\n", title); | } | x(char s[]){ | strcpy(title, s); | printf("%s created\n", title); | } | ~x() { printf("%s destroyed\n", title); } | void foo() { | x a("baby"); | printf("Inside foo of %s\n", title); | } | }; | int main() | { | x y; | printf("In main\n"); | x z("Zed not zee!"); | printf("Still in main\n"); | z.foo(); | y.foo(); | printf("Leaving main\n"); | return 0; | } Chapter 4 - More About C++ Classes Page 39

Page 42: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 4 Summary Special code, called a constructor, that will be automatically executed whenever an instance of a class is created, may be written. A constructor has the same name as the class tag, and looks like a function, except that it has no return value. Like a function, the constructor may be overloaded. The constructor with an empty parameter list is called, except when values are given at the time a class instance is defined, in which case the normal function overloading rules determine which version of the constructor is used. A variable may be declared anywhere in a program. Among other things, this gives you control over exactly when the constructor will be executed. A built-in data type may be used like a function to convert data of a different built-in type to a temporary value of the specified type. This is called a cast. If there is a constructor with parameters, the class tag may be used as a cast, to convert data (matching the parameter list) to a temporary instance of the class. Similar to a constructor, a destructor may be written. The destructor is called automatically when the instance of an object disappears. The destructor has a name composed of a tilde (~) followed by the class tag. The destructor looks like a function with an empty parameter list, except that it has no return value. The built-in operators can be overloaded to work with classes. To overload an operator, write a function named operator@, where @ is replaced with the operator in question, that has between zero and two parameters. A unary operator should have zero (if it is a member of a class) or one (if it is global) parameter(s). A binary operator should have one (if it is a member of a class) or two (if it is global) parameter(s). The keyword "this", used in the code for a member function, constructor or destructor, is a pointer to the current instance of the class. The = operator must always be coded as a member, and postfix ++ and -- are coded by using an extra dummy int parameter to distinguish them from the prefix versions of the same operators. Chapter 4 - More About C++ Classes Page 40

Page 43: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 5. Pointers, References and Arrays Review of Pointers As you will recall, in C all parameters are passed by value. In order to have a function change a variable, it is necessary for the calling function to pass the address of the variable in question (using &, which means "address of"), and for the function itself to resolve the corresponding pointer parameter (using *, which means "data at") in order to change the variable. Also, you will recall that to declare a pointer, which will store an address, you use *, as in: int *p; where the pointer p will be used to store the address of an int. Literally, this kind of declaration means "the data at p is an int". Always be sure that a pointer has been set, so that it actually points somewhere, before you attempt to use it. It is a common "beginner's" programming error to declare a pointer (as above) and then update the memory location it points to, such as *p = 1; In this case, the program will try to place the value, 1, somewhere, but since p itself hasn't been initialized, we don't know exactly where! (In the situation where a pointer is declared as a parameter, it will automatically be initialized to contain the address being passed, so this is not a concern). Pointers are commonly used in parameter passing, but they do have other uses. Most notable is the concept of dynamic memory allocation. In dynamic memory allocation, the program requests memory as it is running, rather than specifying memory requirements at compile time. For example, a program that doesn't use dynamic memory allocation might declare an array, which is necessarily of some fixed size, rather than being the exact size needed in the current run of the program. A similar program that uses dynamic memory allocation would declare a pointer, instead of an array, and ask for the exact amount of memory required when it is running and knows exactly how big an array is needed. The program would then be given (typically, by the operating system) the address in memory where this block of memory (if available) resides, which is then stored in the pointer. The pointer can then be used as if it were an array, the difference being that its size was determined at run-time, and presumably might be different from one run of the program to another. C programmers who have used the library functions malloc (to request a block of memory) and free (to give the memory back when done) will already be familiar with this concept. In C++, Chapter 5 - Pointers, References and Arrays Page 41

Page 44: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition dynamic memory allocation is built into the language with the keywords new and delete. Before looking at these, however, let us review how C++ handles arrays. Review of Arrays A one-dimensional array is declared by placing a number (which must be a constant) in square brackets after the variable name. For example, double x[100]; declares an array of 100 doubles, named x. To use one of these doubles, the array name is used, followed by square brackets containing a number between 0 and one less than the array's size. For example, x[45] = 3.14; sets the 46th double in x to the value 3.14. Similarly, int i = 0; while (i < 100) { printf("%10.2lf ", x[i++]); if (i % 5 == 0) printf("\n"); } displays all the elements of x, five per line. You may recall that an array is passed simply by using its name. The function receiving the array declares the corresponding parameter as if it were an array, although the number in between the square brackets is omitted. (Actually, you may place a size in the square brackets, but the compiler will ignore it). You may also recall that if a function is passed an array, and the function changes elements of that array, then the originating array is also changed. This is because an array name, without the square brackets, is really just an alternate notation for the address of the first element of the array. In our array of doubles, for example, the name: x is the same as computing the address of the array's start: &x[0] In fact, then, when you pass an array, you are really just passing the address of the array, even though you haven't used & to calculate an address. Chapter 5 - Pointers, References and Arrays Page 42

Page 45: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Similarly, the parameter declaration double a[] is really equivalent to double *a This might lead you to suspect that a[i] is the same as *(a + i) and you would be right. The square brackets are really an alternate notation for referring to the data at a given memory location. The arithmetic calculation a + i which adds a pointer and an integer, really means "i" memory locations after "a", where the size of each location depends on the data type to which a points. (On a machine where doubles are 8 bytes big, then, "a+i" would be i*8 bytes after where a points). Note that, unlike arrays, where the variable name merely refers to the memory location of the array, variable names of structures and classes do represent the entire object, and if you want an address, you must still explicitly calculate it using the & operator. New and Delete In C++, the keyword new is an operator that generates a run-time request for a block of memory. After "new", you give a data type (which may be a class) and possibly a set of square brackets containing a number (which may be a calculation). "New" requests the amount of space for an item of that type (or the specified number of items, if the square brackets are used), and then returns the address of the newly allocated memory. It is important that you store this address in an appropriate pointer, or else the block of memory will become lost, and therefore useless. If you requested space for several items, you may use the pointer as if it were an array (since you now know that an array's name is just a pointer anyway). Chapter 5 - Pointers, References and Arrays Page 43

Page 46: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition For example, using our date class, let us first make sure there is a constructor with no parameters (which was part of exercise 4.1), by placing date(); in the public section of the date declaration, and by coding date::date() { set(1, "January", 1970); } This will allow us to instantiate dates without having to specify data for a constructor. date *p; declares p to be a pointer to a date. Note that since no date instance is being created, no constructor is called. The statement p = new date; will allocate space for a date, and call the date constructor (which will initialize the date to January 1, 1970). From this point on, the program may use this date by using *p (literally, the data at p) as it would any other date variable. To dynamically allocate an array of dates, suppose an integer variable named "n" contains the number of elements we would like to have in the array. Then date *array; array = new date[n]; allocates space for "n" dates, and then calls the default constructor for each one. (Note that if your class has any constructors at all, it must have a default constructor in order to be able to create arrays this way). The variable "array" may now be treated as an array of dates, with "n" elements. When memory is allocated with "new", you must remember to give the memory back when you are finished with it, using the keyword delete. After "delete", you either give the address (that "new" had originally given to you), or, if you had used square brackets to allocate space for several items, square brackets (with nothing in between them) followed by the address. The Chapter 5 - Pointers, References and Arrays Page 44

Page 47: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition square brackets tell the compiler that the address is the start of an array, rather than a single item. In the first case above, we would have to remember to code delete p; somewhere in the program, and in the second case: delete [] array; (If you forget the square brackets in the case of an array, then only the first element of the array, i.e. the data at the address given to delete, will be deallocated). Note that delete will call an object's destructor, if there is one. In the case of deletion of an array, each element's destructor will be called (in the reverse order that the constructors were called when new was used). Note also that delete doesn't change the contents of the pointer variable you pass to it - this pointer still contains the address of the block of memory which the program no longer owns. If we intend to reuse this pointer after calling delete, it is essential that we re-initialize it to point somewhere useful before using it again. Dealing with Insufficient Memory In these days of large virtual memory systems, dynamic memory allocation failure is rare. Usually, the system gets extremely slow, due to excessive thrashing, long before the theoretical limit is reached. (With virtual memory, disk space is used to extend available memory, and thrashing is when the system spends more time swapping data back and forth between memory and disk than it does doing useful work). Nonetheless, it is still possible to make a request with new that the system is unable to fulfill. Unfortunately, in the ANSI C++ standard approved in 1998, a major change was made in how new reacts when there is insufficient memory to fulfill the request. Since the change is fairly drastic, in that it breaks old C++ code, many compiler developers have been reluctant to fully implement the new standard. Prior to the 1998 standard, new would simply return the pointer value 0 when it failed, and this was easy to check in a program. Under the new standard, a failure will terminate the program with what is called an exception. Exceptions are another relatively new feature in C++, and are beyond the scope of what we are studying here, but suffice it to say that an exception is Chapter 5 - Pointers, References and Arrays Page 45

Page 48: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition something that, under normal circumstances, will abruptly terminate the program with a message related to the cause of the exception. Exceptions can be trapped, using a syntax called try/catch, which is also beyond our current scope. However we will show you just enough to trap the memory allocation failure, and set the pointer to zero in that case. Suppose that we have a variable, n, that contains the size of an int array we'd like to allocate. Then #include <new> ... assume that some code has set n to be the desired size int *array; try { array = new int[n]; } catch (std::bad_alloc) { array = 0; } Note that this uses a standard C++ header file <new>, which does not have a name ending with ".h". If you allocate arrays following this style, your logic will then be able simply to check the pointer to see if it is 0 or not. By the way, many of the standard header files contain a #define that defines the name NULL to stand for the address 0, so you may often see NULL (which is an address value of 0) used, rather than 0 (which is an int value which is automatically cast to an address in a context like this). If, on the other hand, you are using a compiler that doesn't implement this aspect of the ANSI standard, then array = new int[n]; will accomplish the same thing, and #include <new> will not be necessary. References In addition to variables, pointers and arrays, C++ has a fundamental storage type called a reference. A reference is a variable that, like a pointer, refers to some other data in the program. Yet, like a regular variable, the name of a reference refers to the data itself. A reference is declared the same way that a normal variable is declared, except that an ampersand (&) precedes the variable name. Thus, int &ri Chapter 5 - Pointers, References and Arrays Page 46

Page 49: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition declares ri to be a reference to an int, and date &rd declares rd to be a reference to one of our dates. (Note that this syntax is meaningless, in that there is no sensible literal translation of the symbols such as there is when declaring a pointer). As with pointers, defining a reference to an instance of a class does not instantiate an object, so no constructor will be called. Like pointers, references must be set to refer to existing objects before they are used. However, the only way to set a reference to refer to another object is by initializing the reference. Recall that a variable is initialized either through the mechanism of parameter passing (for parameters and return values) or else by specifying a value at the time the variable is defined (for variables that are not parameters). Unlike pointers, no programmer effort is required to calculate or resolve references. For example, if there were a variable date x; then date &rd = x; would define and initialize rd so that it refers to x, and rd.set(15, "July", 1989); would actually update the variable x. You can think of a reference as an alias for another variable. Normally, references are only used in parameter passing, to achieve the capabilities of parameter passing using pointers, without the associated syntactical headaches. As an example, consider the following typical use of pointers to change a parameter: // set the data at "pn" to be no bigger than "limit" void limit(int *pn, int limit) { if (*pn > limit) *pn = limit; } Such a function might be called to limit an int variable, say "num", to be less than or equal to a specified value, say 1000: Chapter 5 - Pointers, References and Arrays Page 47

Page 50: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition limit(&num, 1000); Although with experience you eventually remember where the asterisks and ampersands go, most people find that it takes a while to get this kind of parameter passing straight. With references, the situation is much simpler: // set the variable "n" to be no bigger than "limit" void limit(int &n, int limit) { if (n > limit) n = limit; } and to call the function to achieve the same result as before: limit(num, 1000); It is clear that references are much easier to use than pointers, if what you are trying to do is pass a parameter so that it can be changed. All you need to do is put an ampersand between the data type and the parameter name, and the compiler will then ensure that changes to the parameter are "passed back" to the corresponding argument variable. Technically, what really happens when you specify a parameter as a reference is that (1) the compiler will compute and pass the address of the corresponding argument when the function is called and (2) the address sent to the function will be resolved every time the parameter is used. In other words, reference parameters do exactly the same thing as pointer parameters, except that the compiler will do most of the work instead of the programmer. If you are wondering why C doesn't have references, the answer is because C does not force functions to be prototyped before use: the compiler MUST know the header line of a function with a reference parameter before it can compile a statement that calls it, in order to know that it has to compute the address of the argument matching the reference parameter. Later, we will see examples of functions where the return value is actually a reference to a variable. In such cases it is important that you ensure that the object to which you are returning a reference will still exist after the function has returned. (Among other things, this means that you should not return references to local variables). Pointers and References to Constant Data Often, passing by address or by reference is done, rather than the normal passing by value, purely for efficiency reasons. Making a copy of a large object takes time and memory, whereas passing the same object by address or by reference simply Chapter 5 - Pointers, References and Arrays Page 48

Page 51: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition involves copying an address (which typically is only 2 or 4 bytes big). In these cases, there is no intention of changing the variable even though there is a danger that the receiving function might actually change it. There is a keyword, const, which can be used to tell the compiler that a variable, once initialized, cannot be changed. The word const is simply placed at the beginning of the variable's definition. For example, const double pi = 3.141592654; defines a "variable" (although it is not truly a variable since it cannot be changed), named pi that is of type "double" and contains the value 3.141592654. Note that this provides an alternative to the practice of using #define to give a symbolic name to a constant value, and, in C++ at least, is actually preferred. (In C, const variables are not as efficient as #define names, so C programmers typically prefer to use #define. In C++, the efficiency issue with const variables has been addressed). The const keyword can be used to declare pointer (hence also array) and reference parameters, where the use of a pointer or reference is for efficiency reasons and not because we want to change the variable. The compiler will then not allow the data, referred to by the pointer or reference, to be changed. Thus we can have the efficiency of passing a pointer or a reference without the danger of (presumably unintentionally) modifying the original variable. When you do have a constant object (such as when passing a const reference to a class instance), you are obviously restricted to doing things that do not change the object. Unfortunately, the compiler assumes, for safety reasons, that any member function will change the object, unless otherwise specified. The way you tell the compiler that a member function does not change the object itself is to put the keyword const at the end of the function header line, both when defining the function and when simply prototyping it. While the uses of const may seem confusing at first glance, two simple guidelines help you decide when to use it: - whenever you pass a pointer, array or reference but do not want to change the originating data, declare the parameter as const by preceding the parameter's declaration with the word const. - whenever you write a member function that does not modify the object itself, declare the member function as const by placing the word const at the end of the function Chapter 5 - Pointers, References and Arrays Page 49

Page 52: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition prototype or header. For syntax examples, wait for the nstring class that is coming up soon. Copy Constructors and = Operators Normally, when an object is initialized (using = at the time of the definition of the variable), set using the assignment operator (=), or set through parameter passing, the data is simply copied from one object instance to the other, byte by byte, and no constructor is called. (Add the line "x w = z;" to exercise 4.2 to prove that when an object is initialized to be a copy of another instance of the same class, none of the coded constructors are called). There are times that this kind of copying, called member-wise copying, is not acceptable. For example, if an object dynamically allocates memory to itself when it is constructed, and frees up that memory when it disappears, problems would occur. Specifically, when the copy is made, the pointer (pointing to the dynamically allocated memory) would be copied exactly to the second object, but no copy would be made of the dynamically allocated memory itself (since that is not one of the members being copied). Both the original and the copy would point to the same dynamically allocated block of memory. When the copy of the object disappears, its destructor would then delete the memory pointed to by the pointer, even though the original object is still around and still needs that memory. The solution to such problems is to create a special constructor, called a copy constructor, which is a constructor that is passed a reference, or a const reference, to an object of the same class type. (With our date class, member-wise copying is fine and we don't need a copy constructor, but if we wanted one, we would prototype it, in the public section of the date, as: date(date &x); where the parameter, x, is a reference to a date. Here we have not used a const reference to a date, since we had not taken care, when writing the date, to identify which member functions do not change the date). If you have defined a copy constructor, it will automatically be called when initializing a variable and when a copy is made through parameter passing, and a member-wise copy will not be done. In these cases, a reference to the originating object (of which a copy is being made) is supplied automatically as the parameter to the constructor. For example, if a copy constructor had been coded for our date (not that we require one - Chapter 5 - Pointers, References and Arrays Page 50

Page 53: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition memberwise copying of our date is just fine), then it would be used in all three of the following situations: date x(y); // initializing using copy constructor syntax date z = y; // initializing using = syntax foo(y); // initializing through parameter passing where y is a date instance that already exists and foo is a function with the header line: void foo(date x) Note that if the assignment (=) operator is used after the variable has already been defined (i.e. to set an existing variable rather than to initialize a new variable), a member-wise copy will still be done and the copy constructor will not be called. You may, however, separately define an operator= member function, that takes a reference to another object of the same type as a parameter, to control this situation if a member-wise copy is not suitable. (Again using our date class as a syntax example, we would declare the following in the public section of the date: date operator=(date &x); Note, though, in some cases we might prefer to return a reference, rather than a copy as we have here, to avoid unnecessarily calling the copy constructor just for the return value). Typically, if you need to define a copy constructor, you will probably also need an operator=, and vice versa. The copy constructor gives an object, that does something like dynamically allocate memory, the opportunity to do special things, such as automatically allocate new memory for the copy, every time a copy is made of it. Similarly, the assignment operator, used to assign instances of the object, gives the opportunity to do similar but slightly different things, such as copy the contents of dynamically allocated memory. The following example will help to clarify both dynamic memory allocation and copy constructors. Example: A String Class One of the drawbacks of C, relative to many other languages, is that there is no built-in character string data type. There are standard library functions for handling null-terminated strings, using character arrays to store the strings, but the language Chapter 5 - Pointers, References and Arrays Page 51

Page 54: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition itself doesn't handle strings (except that the use of double quotes to enclose a string of characters will create a constant character array with a null character at the end). For instance, you cannot copy strings with the normal assignment operator, nor can you compare strings with the normal comparison operators. We now know enough C++ to be able to create a class to contain a variable length character string. We will create a class, named "nstring" (for new string) to accomplish this. At the time an nstring is instantiated, we want to be able to specify the maximum number of characters that can be stored in the nstring. We also want to arrange things so that no operation on the nstring will allow it to be overfilled (unlike the standard C library strings, which can easily be overfilled, causing the program to crash). To accomplish this, our nstring will make use of dynamic memory allocation, which in turn will require the use of a copy constructor. Since an nstring may be relatively large, we will pass references to nstrings, rather than passing copies of nstrings, whenever practical. We will also make it relatively easy to transfer data between an nstring and a standard C library string. The following declaration shows the aspects of the nstring we will implement. Note how we use a standard C library string to store the contents of the nstring. Our class is really just a "protective wrapper" to improve on something that already basically works. When looking at this code, look carefully at how const is used to identify the situations where references and pointers are being used for reasons other than to change the data, and to tell the compiler which member functions do not change the object itself. | class nstring { | char *data; // contents, stored as a regular C string | int len; // actual length of nstring | int max; // maximum length of nstring | void init(int size, const char *s); // common code shared by | // all the constructors | public: | nstring(); // Plain constructor makes an 80-byte nstring | nstring(int maxlen); // construct nstring of given capacity | nstring(const char *s); // construct nstring given a string | nstring(const char *s, int maxlen); // as above with capacity | nstring(const nstring &s); // copy constructor | nstring(const nstring &s, int maxlen);// as above with capacity | ~nstring(); // destructor | int length() const; // like strlen() for the nstring | void print() const; // displays nstring on standard output | nstring& operator+=(const nstring &s); // append another | nstring& operator=(const nstring &s); // copy another | nstring& operator=(const char *s); // copy C string Chapter 5 - Pointers, References and Arrays Page 52

Page 55: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | // Copy an nstring to a C-style string (looks like strcpy): | friend char *strcpy(char *s1, const nstring &s2); | }; | | // These non-members are also part of the nstring "package": | // Paste two nstrings together: | nstring operator+(const nstring &s1, const nstring &s2); | // Copy a C-style string to an nstring (look like strcpy): | nstring &strcpy(nstring &s1, const char *s2); In an nstring, we will store the string data itself, the length of the data currently in the nstring (which will be much quicker to access than it is to get the length of a C library string using the strlen function) and the maximum capacity of the string. Note that we also have a private function. We want to provide several different constructors that share the same fundamental logic to dynamically allocate space to the nstring. We will put this logic into a private function so that the various constructors can call it, but no outside function can. The actual code for the nstring class will require three standard header files: | #include <stdio.h> // only needed for the print() function | #include <string.h> // basic C library string routines | #include <new> // to handle allocation failure First, let us code the basics of initializing an nstring. We need to allocate enough memory to hold the data for the largest allowable string (plus an extra byte for the null-byte terminator), and initialize the data fields. | void nstring::init(int size, const char *s) | { | | len = max = 0; // in case we... | data = NULL; // ... can't make an nstring | | if (size > 0) { | try { | data = new char[size + 1]; | } | catch (std::bad_alloc) {} // note: data is already NULL | if (data) { | max = size; | *this = s; // note: this calls the overloaded = | // below to copy in a C-style string | } | } | } Chapter 5 - Pointers, References and Arrays Page 53

Page 56: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition The various constructors are now easy to write: | // Plain constructor: empty with 80-byte capacity. | nstring::nstring() | { | init(80, ""); | } | | // Constructor: empty with given capacity. | nstring::nstring(int maxlen) | { | init(maxlen, ""); | } | | // Constructor: same size and data as the given string. | // Note that this also provides a cast (which may be called | // automatically) from a C string to an nstring. | nstring::nstring(const char *s) | { | init(strlen(s), s); | } | | // Constructor: same data as the given string, but with | // capacity that may be different than the size of the string. | nstring::nstring(const char *s, int maxlen) | { | init(maxlen, s); | } | | // The copy constructor. Note that this will automatically be | // used whenever an nstring is passed as a normal parameter, | // but will NOT be called when one nstring is assigned to | // using =. (We do, however, define = separately.) | nstring::nstring(const nstring &s) | { | init(s.max, s.data); | } | | // Constructor: same data as the given nstring, but with | // possibly different capacity. Note that the source | // nstring is specified as a reference parameter to avoid | // making a temporary copy of it. Note that if s was | // never properly set, we use an empty string. | nstring::nstring(const nstring &s, int maxlen) | { | init(maxlen, s.data ? s.data : ""); | } Since the constructors allocate memory to the nstring, that memory must be returned at some point. The easiest way is to make the destructor return the memory, so that it will happen Chapter 5 - Pointers, References and Arrays Page 54

Page 57: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition automatically: | // Destructor: only free memory if any was allocated. | nstring::~nstring() | { | if (data) | delete [] data; | } The remaining member functions and operators are fairly straight-forward. | // Return length of the data currently stored. | int nstring::length() const | { | return len; | } | | // Display the nstring on standard output. Note | // that printf won't work right if data is NULL. | void nstring::print() const | { | if (data) | printf("%s", data); | } | | // Append the given nstring to the end of the nstring, | // stopping early, if necessary, to avoid overfilling | // the nstring. | nstring& nstring::operator+=(const nstring &s) | { | if (data) { // don't even try if data is NULL | for (int i = 0; len < max && i < s.len; i++, len++) | data[len] = s.data[i]; | data[len] = '\0'; | } | return *this; | } | | // Copy the contents of the given C-style string into | // the nstring, stopping early, if necessary, to avoid | // overfilling the nstring. | nstring& nstring::operator=(const char *s) | { | if (data) { // don't even try if data is NULL | for (len = 0; len < max && s[len]; len++) | data[len] = s[len]; | data[len] = '\0'; | } | | return *this; | } Chapter 5 - Pointers, References and Arrays Page 55

Page 58: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition | // Copy the contents of the given nstring into the | // nstring, stopping early, if necessary, to avoid | // overfilling. | nstring& nstring::operator=(const nstring &s) | { | *this = s.data ? s.data : ""; // calls overloaded = | return *this; | } Similarly, the non-member operators and functions are pretty simple. | // Creates a string which is the concatenation of | // the two specified nstrings. Note that the specified | // nstrings are passed as reference parameters to | // avoid making unnecessary copies of them. | nstring operator+(const nstring &s1, const nstring &s2) | { | nstring s3(s1, s1.length() + s2.length()); | | s3 += s2; // calls overloaded += | | return s3; | } | | // Like the standard strcpy function, except the source string | // is an nstring. Note that it calls the C-library strcpy. | char *strcpy(char *s1, const nstring &s2) | { | return strcpy(s1, s2.data ? s2.data : ""); | } | | // Like the standard strcpy function, except the | // destination is an nstring. Note that returning | // a reference to the destination nstring is | // similar to the way the C library's strcpy | // returns a pointer to the destination string. | nstring &strcpy(nstring &s1, const char *s2) | { | return s1 = s2; // calls overloaded = | } Exercise 5.1: Our example of an nstring does not define the comparison operators such as ==, >, and so on. Add those operators to the nstring class. (Remember that they should return true or false values). Note that if you do not make these operators members, then you may freely use a C-style string on either side of the comparison, since the compiler will automatically determine a need to cast the string to an nstring (using the appropriate nstring constructor). Be warned that this will not work if you have C-style strings on both sides of the comparison: in this case the compiler already knows what to do Chapter 5 - Pointers, References and Arrays Page 56

Page 59: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition (it will compare the addresses in memory of the two strings) and will not call your version of the operator. (You could explicitly cast one of the strings to an nstring, however, to force it to work). Exercise 5.2: Our nstring shows how library functions such as strcpy can be extended to include new types that we create. Extend a few other string functions (e.g. strncpy, strcat, gets) so that they will work with nstrings as well as C-style strings. Always make sure that an nstring can not accidentally be overfilled. Chapter 5 - Pointers, References and Arrays Page 57

Page 60: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 5 Summary Passing addresses into pointers is used to exchange data between functions in such a way that the called function can change the calling function's variables. Passing addresses into pointers is also the mechanism by which arrays are passed. In fact, array notation is just an alternative to pointer notation, provided in order to make array manipulation more convenient and readable. The keywords new and delete, which request and return (respectively) additional memory, give another use for pointers: to hold the address of the newly allocated memory while the program is using it. C++ adds a new variable type, the reference, which, when used in parameter passing, has the functionality of passing an address into a pointer with the syntactical simplicity of passing normal (by value) parameters. The keyword const can be used to say that data pointed to by a pointer, or referred to by a reference, cannot be changed. The keyword const can also be used to say that a member function does not modify the object of which it is a member. Unless a copy constructor is provided, any initialization of an object, either in the definition of an instance of the object or through parameter passing using normal (by value) parameters, will be a member-wise copy. Unless an assignment operator is provided that assigns one instance of an object to another, any assignment using = (other than initializations mentioned above) will be a member-wise copy. Chapter 5 - Pointers, References and Arrays Page 58

Page 61: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 6. Introduction to C++ Streams Streams At first glance, the following items seem to be quite different from one another: (1) a terminal (2) a file (3) a character string Yet, as a C programmer you may already be familiar with how similar they actually are. For example, you may write to a terminal using the printf function, and read from it using scanf. Almost identical functions are provided for reading to and from files (fprintf and fscanf), and to and from strings (sprintf, and sscanf). The reason that access to these things is so similar is that all three can be viewed quite simply, as a stream of bytes, either coming into the program or leaving it. The library functions' similarity reflects this commonality. One of the flaws of printf, scanf and their relatives is the fact that the format specifications (the layout string containing things like %s and %d) must match the other parameters that are passed. Since the format string may be a variable (and hence not known at compile time) the compiler has no way of being able to verify that it is correct. Furthermore, at run-time, the mechanism of parameter passing does not allow the type and number of parameters to be confirmed. The net result is that if the format specifications do not match the other parameters, the program will not work correctly, even though no errors will be flagged. Another flaw of the printf/scanf family is that it is not clear, without detailed investigation, that other functions (such as gets, getchar, puts and putchar) are very closely related, sharing common buffers and such. In C++, we already know that we can define the same name (or operator) to work in different ways with different types of data, and have the compiler automatically select the appropriate version. We can also group related functionality together in one place (creating an object). To take advantage of these features, new, object-oriented routines have been written to replace printf, scanf and the like. These objects are called streams, and can be incorporated in your program by including the appropriate header files. We will show basic use of the streams to do input from and output to the terminal, which uses <iostream>, although file access Chapter 6 - Introduction to C++ Streams Page 59

Page 62: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition (<fstream>) and string access (<sstream>) are similar. Note how, as with the #include file <new>, the standard C++ header files do not have names ending with ".h". This is another big change that came with the 1998 ANSI standard. Previous versions of these library objects were known as <iostream.h>, <fstream.h> and <strstream.h>. Most compilers come with both the old, ".h" versions of the library objects (for compatibility with older code), as well as the newer, ANSI compliant versions. Although all C library routines are available from a C++ program, it is not a good idea to mix <stdio.h> and <iostream> terminal input/output in the same program, since the two different approaches do not necessarily share common buffer areas, and do not synchronize with one another. (It is acceptable, however, to mix <stdio.h> file access with <iostream> terminal I/O, or <fstream> file access with <stdio.h> terminal I/O, and so on, as long as the same physical device isn't being accessed at the same time with two differing approaches). The <iostream> header file declares two important global variables: (1) std::cout, an instance of the class, std::ostream, which handles output to the terminal, and (2) std::cin, an instance of the class, std::istream, which handles input from the terminal. Data can be sent to std::cout to make it appear, nicely formatted, on the screen. Similarly data can be retrieved from std::cin, which will cause the program to wait for input from the user. What are all these "std::"s preceding the names? The 1998 ANSI standard also introduced the concept of a namespace, which is a way to group global names, so that you can have two global things of the same name as long as they are in different namespaces. To use something from a namespace, you precede the name of the thing you want with the namespace it is in, with :: separating the two, much the same way that you specify the class a particular member function definition belongs to. Creating your own namespaces is another one of those things that falls outside the bounds of what we are looking at here, but to use the ANSI library you must be able to use the things in a predefined namespace. Most of the global names that are very commonly used have been put in a namespace called "std". To avoid having to type in std:: all the time, you can issue the directive: using namespace std; after which, whenever the compiler sees a name it doesn't recognize, it will look for it in the namespace std without our having prefaced the name with "std::". From now on, we will Chapter 6 - Introduction to C++ Streams Page 60

Page 63: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition always put this in right after #include <iostream>. In all further discussions, we will also omit the std:: from the things in <iostream> that are in the namespace std. The <iostream> header file also redefines the seldom used bit-shifting operators, so that << (which normally shifts bits to the left) is used to send data to an ostream, when its left argument is an ostream, and >> (which normally shifts bits to the right) is used to retrieve data from an istream, when its left argument is an istream. (Note that >> and << still perform bit-shifting operations when the left argument is of a built-in data type, rather than an ostream or istream). For example, cout << "Hello, there!"; causes Hello, there! to appear on the terminal, while cout << 22 + 3; causes 25 to appear on the terminal. Similarly, if we have an integer variable named n, cin >> n; would let the user enter an integer, which will be placed in n. If we have a character array named s, cin >> s; would take a character string from the terminal and place it in s as a null-terminated string. (Like the %s specification for scanf, cin input to a string uses whitespace as a separator between input strings). The bit-shifting operators were selected for this duty for four reasons: (1) They are not used very often, so that giving them new meanings will not cause undue confusion. (In general, it is a very bad idea to give completely new meanings to existing operators). Chapter 6 - Introduction to C++ Streams Page 61

Page 64: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition (2) They have visual appeal, in that a statement like cout << 4; sort of looks like 4 is being sent to cout and cin >> x; kind of looks like something is coming from cin into x. (3) They have fairly low priority (less than arithmetic operations) so that lack of parentheses in statements like cout << 23 * 4 - 1; which outputs 91, will not cause problems. (4) They group left-to-right, so that things can be arranged as in the following: cout << "Hello there " << 2 + 6 << '!'; which works out to ((cout << "Hello there ") << (2+6)) << '!'; and causes the following to be output: Hello there 8! Note that to accomplish this, each version of << simply returns a reference to its left argument. In other words, each << (and each >>) returns the same stream that was used as its left argument. Controlling Ostream Output There are various public member functions of an ostream to control how data will be output. A full explanation can be found in many of the complete treatments of the C++ language. As an alternative, we will show only the most commonly required aspects. One aspect of output control is to specify the width of a field (using spaces to pad the field to the specified size, if necessary). The width member function allows you to give the size of the next field to be output. For example, cout.width(10); cout << n; outputs the variable, n, using a field width of 10 characters. Note that the width setting is automatically cancelled once a piece of data is output; you must issue calls to cout.width each time you want a field output with a certain width other than the default (which is normally just wide enough to display the data itself). If you specify a field width and want the data to be left justified, rather than the default of right justified, issue the statement: cout.setf(ios::left); Chapter 6 - Introduction to C++ Streams Page 62

Page 65: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition This setting, unlike the width setting, lasts until it is cancelled with: cout.unsetf(ios::left); You may also want to control the number of decimal places for the output of floats and doubles. If so, you must first: cout.setf(ios::fixed); to tell cout to use a fixed number of decimal places for floating point numbers. (The default is to show only the first 6 significant digits of a floating point number, regardless of where the decimal place would be). Then, if you want something other than 6 decimal places, for example 2 decimal places: cout.precision(2); Both of these settings last until they are cancelled (cout.unsetf(ios::fixed) to cancel the first, cout.precision(6) to cancel the second). Controlling Istream Input For "quick and dirty" input, cin is unbeatable for convenience. If you want to trap user errors, or do special processing, it can be less convenient (although admittedly more robust) than scanf. The value returned by cin.fail() will be true if the last input operation from cin failed for some reason, such as invalid data. This gives you the opportunity to validate data. Another member function, cin.eof(), returns true if the error was because end-of-file was encountered. End-of-file would be encountered if the user presses the EOF key (Control-D in a UNIX system, Control-Z in a DOS system), or if input was coming through command-line input redirection, and the input file just ran out of data. It is a good idea, when validating data, to make sure that you don't create infinite loops if end-of-file is encountered. Before proceeding further, cin, which is in a failure state, must be reset (unless, as is the case if we encounter end-of-file, we want to leave it in a failure state), with a call to cin.clear(); Like scanf, if cin encounters invalid data, that data gets left in the input stream, and must be extracted before you attempt to Chapter 6 - Introduction to C++ Streams Page 63

Page 66: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition re-input the data. Fortunately, there is an easy way to toss out the rest of the line in cin's input buffer: cin.ignore(2000, '\n'); will ignore up to 2000 characters, stopping when it reaches 2000 or processes a newline character, whichever comes first. Putting these all together, the following function is an example of validating input from the user. Its purpose is to have the user enter a valid double. If an entry error is made, the user is forced to re-enter the data. Once a valid double is entered, it is returned to the calling program. (Note that this function returns garbage if end-of-file is encountered). double getdouble() { double x; int ok; do { ok = 1; // assume the input is OK until proven wrong cin >> x; if (cin.fail() && !cin.eof()) { ok = 0; // well, it wasn't OK! cout << "Invalid number!...Please try again: "; cin.clear(); // reset the state of cin cin.ignore(2000, '\n'); // empty cin's buffer } } while (!ok); return x; } Simple character string input can easily be made to not overfill a character string by using the width member function, similar to that of cout. For example, char s[31]; cin.width(31); cin >> s; will accept no more than 30 characters into s. If more than 30 non-whitespace characters are entered, 30 are placed into s and the remainder are left in the input buffer for future processing. A null byte, to terminate the string, is placed after the data fed into s. Note that the width setting for cin, like that for cout, lasts only for the entry of one field. Another idiosyncrasy of cin is that all character input, including input of single characters, normally uses whitespace as a separator to be skipped. If you want to input character data without skipping whitespace, a good way to do it is to use Chapter 6 - Introduction to C++ Streams Page 64

Page 67: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition the get member function instead of the >> operator. This function has different versions. The two most useful versions are: char c; cin.get(c); which takes the next character out of the input stream and places it in the variable c, and char s[81]; cin.get(s, 81); which takes up to 80 characters, or until a newline is encountered, whichever comes first, from the input stream and places them in the array s, followed by a null-byte terminator. If a newline is encountered, it gets left on the input stream, so you will probably want to call the ignore member function described above (to throw away everything up to and including the newline character), whether or not too much data was entered. Writing Custom Iostream Operators A nice feature of cin and cout is that it is a simple matter to extend the >> and << operators to be able to handle your own classes. All you need to do is define an operator that takes a reference to a stream as a left parameter and one of your objects (or a reference to one) as a right parameter, returning a reference to the same stream that was passed. For example, with our date class, we could include a line friend ostream &operator<<(ostream &os, date &d); in the class declaration, and then code it as: ostream &operator<<(ostream &os, date &d) { char mon[10]; getmonth(d.month, mon); os << mon << ' ' << d.day << ", " << d.year; return os; } We can now do things like: cout << "So, your birthday is " << bday << '\n'; (where bday is an instance of the class date). Similarly, to be able to input to a date, we could add the line: Chapter 6 - Introduction to C++ Streams Page 65

Page 68: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition friend istream &operator>>(istream &is, date &d); to the date class declaration, and code it as: istream &operator>>(istream &is, date &d) { char mth[10], comma; int dy, yr; is.width(10); is >> mth >> dy >> comma >> yr; d.set(dy, mth, yr); return is; } (Note that this simple input routine does not do any validation). Exercise 6.1: Add validation to the date input (using >>) shown above, so that it forces the user to enter the date over and over again until a valid date is entered. Exercise 6.2: Write a cin-style input operator (>>) and a cout-style output operator (<<) for the nstring class presented in the previous chapter. Chapter 6 - Introduction to C++ Streams Page 66

Page 69: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 6 Summary The header file, <iostream>, declares some standard library classes to perform standard input and output with capabilities similar to many of the C library functions in the <stdio.h> header file. All the things declared in <iostream> are in namespaces (i.e. named groups), with most of them being in the namespace std. Adding the directive "using namespace std;" makes it unnecessary to preface most of the <iostream> names with "std::". An ostream is an object that can receive output from the program, where among other things, the << operator is used to "send" data to the ostream. The global variable cout is an instance of an ostream that represents the standard output device for the program. An istream is an object that can send input to the program, where the >> operator is used to "receive" data from the istream into a variable. The global variable cin is an instance of an istream that represents the standard input device for the program. It is common practice, when creating a class, to provide a version of the << operator to send an instance of the class to an ostream, and a version of the >> operator to receive an instance of the class from an istream. Chapter 6 - Introduction to C++ Streams Page 67

Page 70: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 6 - Introduction to C++ Streams Page 68

Page 71: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 7. Inheritance Deriving One Class From Another It is quite common to write a program that is very much like a previously written program, yet has some significant differences. With traditional languages, you typically duplicate a previously written program and then edit the copy, making the changes desired. When one program gets "cloned" like this, over and over again, a maintenance headache ensues. For example, if a bug is discovered in the original logic, you must remember to go fix the same bug in all of the copies. Similarly, if some fundamental, yet small, change of approach is desired in all of the similar programs, you must make the same change over and over again. The more times you make the same sort of change, the more likely you are to get a bit lazy and not test each version with the same thoroughness you did the first time. What you can easily end up with is a set of similar programs, some of which work correctly and some of which don't. Modular programming can go a long way toward alleviating this problem. You can modularize your code into functions, for example, some of which are general and can be used for all the similar programs, and some of which are specific to each individual program. The general functions can be placed in one source file that gets linked with all the different programs, so that the bulk of the work only gets coded once. The negative side to this approach is that it is not always clear from the start in what way future programs will be similar and in what ways they will differ. When reality doesn't quite fulfill your original plan (which is what always seems to happen), you either end up going back to slightly rework all of the previous similar programs (so that you can keep only one version of the shared logic) or, to save time, you just start duplicating parts of the general logic and modifying the different versions (getting back to the original situation). Object oriented languages have a mechanism, called inheritance, to help deal with this ongoing problem. A new object, called a derived class in C++, can be specified as a variation on an existing object, called a base class. Only the parts of the derived class that are different from the base class need to be coded when writing the derived class. Since you are not tinkering with the base class at all, instances of it will not be affected by the new code. And if the need to change the base class does arise, the changes are automatically "pushed through" to all of the derived classes simply by recompiling, presuming that the derived classes have not already reworked the elements being changed. Chapter 7 - Inheritance Page 69

Page 72: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition The basic format for creating a derived class is: class derived-class-name: public base-class-name { .... }; where only new or different members are specified inside the braces. The word "public" before the base class implies that the public features of the base class will also be public in the derived class. If the word "public" is omitted (or equivalently, if the word "private" is used instead), then the public members of the base class will be private to the derived class. There are occasional situations where you may want all the base class features to be hidden like this. For example, if you want to create a similar object with a completely different programming interface from that of the base class, you would probably want the base class to be private. In most cases, however, the base class should be public. Note that the private members of the base class are hidden from the derived class, as they are from any other function or class in your program. Just because you are deriving a new object from an existing one doesn't mean that you now have a license to bypass the protection that may have been built into the existing object. Note also that, when giving a new definition in a derived class for a base class member function, you may want to call the base class' version of the function. You cannot simply call the function, because that will execute the derived class version of the function! You can, however, precede the function name with the base class name and two colons (::) to explicitly state that you want the base class version of the function when you call it. As an example, let us suppose we want to create a class, called datetime, which is a date, but also holds the time of day. class datetime: public date { int hour; int minute; int second; public: datetime(); void set(int y, char mon[], int d, int h, int m, int s); void print(); }; where we have a constructor that initializes the new fields: Chapter 7 - Inheritance Page 70

Page 73: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition datetime::datetime() { hour = minute = second = 0; } and a new version of set, that lets us specify year, month name, day, hour, minute and second: void datetime::set(int y, char mon[], int d, int h, int m, int s) { date::set(d, mon, y); hour = h; minute = m; second = s; } and a new version of print, that shows the time as well as the date: void datetime::print() { printf("%02d:%02d:%02d on ", hour, minute, second); date::print(); } Note how the new versions of both set and print call the respective members of the base class to do some of the work. Be aware that it is not necessary to do this; it was merely convenient in this case to do so. If you have created a member in a derived class that has the same name as a member of the base class, as we have done with both set and print, above, then you must precede any use of the base class member with the base class name and double colon. At first, it may not seem necessary to code date::set(d, mon, y); as we have in the definition of datetime's set. After all, the date class' set has a signature that is different from datetime's set, so that set(d, mon, y); should be unambiguous. Nonetheless, it was deemed too confusing to allow this. The general rule that was adopted is: once a name has been defined in a derived class, it hides all uses of that name in the base class, unless you explicitly ask for a base class version of that name. Chapter 7 - Inheritance Page 71

Page 74: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Constructing and Destroying Derived Objects If the base class has a constructor, it is called before the derived class' constructor whenever the derived class is instantiated. Similarly, if there is a base class destructor, it is called AFTER the derived class' destructor (if there is one) when that instance disappears. Since constructors may require parameters, there is a special syntax for sending data to a base class constructor from the definition of a derived class constructor. For example, if we wanted a datetime constructor with six parameters in the same style as set function we wrote, we could include a suitable constructor prototype in the datetime class declaration: datetime(int y, char mon[], int d, int h, int m, int s); and code it as: datetime::datetime(int y, char mon[], int d, int h, int m, int s): date(d, mon, y) { hour = h; minute = m; second = s; } Here, some of the parameters given to the datetime constructor are passed on to the date constructor, which will be called before proceeding with the datetime constructor. Note that in addition to passing some parameters on to the base class constructor, you may pass constants or calculations that use some of the parameters. Note also that if you do not specify how to call the base class constructor, then either there must be a default constructor (i.e. one that takes no parameters) in the base class, in which case it will be used to construct the base class portion of the derived object, or there must be no base class constructor at all. Virtual Functions Consider the following function: void born(char name[], date &d, char town[]) { printf("Name:%s Born:", name); d.print(); printf(" Place:%s\n", town); } Chapter 7 - Inheritance Page 72

Page 75: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition If we have a variable that had been declared as date birthday(10, "January", 1968); then is is clear that born("Joe Blow", birthday, "Toronto"); would output something like: Name:Joe Blow Born:January 10, 1968 Place:Toronto If, instead, we declared datetime birthday(1968, "January", 10, 9, 57, 16); then born("Joe Blow", birthday, "Toronto, Ontario"); would also work (because, after all, a datetime is a date) and produce the same output. But there are situations where we would really prefer to see the output look like this: Name:Joe Blow Born:09:57:16 on January 10, 1968 Place:Toronto since the argument, birthday, is really a datetime with an improved version of the print function. The problem is that the born function is passed a reference to a date, locally named d. When the compiler encounters d.print(); it will call the print function for the date class, regardless of the fact that d may actually refer to an instance of an object that is based on a date, but is more than a date. It is possible, however, to specify, when coding a base class, that a particular function is intended to be redefined in derived classes, and that whenever the function is called, the most derived version available for that object instance should be used, regardless of the context of the function call. Such functions are called virtual functions, and you specify a member function as such by preceding the prototype for the function, in the base class declaration, with the word "virtual". In our date class, for example, we would change the line: void print(); to Chapter 7 - Inheritance Page 73

Page 76: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition virtual void print(); Then the born function would produce the output that includes time, when it is passed a datetime instance. (It will still output just a date, when it is passed an instance of a date that is not a datetime). When you declare a function as virtual, the compiler inserts a little bit of logic every time the function is called, checking to see which version of the function it should actually execute. It is important to realize that this check is done at run time, not compile time, since at compile time it may not be possible to determine the actual type of object being used. This is sometimes called late binding (the function call is bound to the object later [at run time] rather than sooner [at compile time]). Many people consider virtual functions to be the true embodiment of the concept of polymorphism. (We have been more generous, also including in polymorphism the simpler concept of function overloading). Note that if you declare a base class function as virtual but do not actually bother to redefine it in a derived class, then you will still run the base class version (the only one that exists) even when you you call it from a derived class instance. Note also that even though a function may be declared as virtual, you can explicitly run the base class version, by preceding the function name with the base class name and two colons(::). For example, in the born function above, the statement d.date::print(); would run the date version of print, even if d is a datetime and print is virtual. How do you decide whether to make a base class function virtual or not? In most situations, if you intend to redefine a function for derived classes, you should make it virtual. The extra overhead involved in calling a virtual function is very slight, and is not worth worrying about. The instances where you wouldn't want a virtual function are rare and need to be carefully thought out. Virtual Destructors In almost all cases where you plan to derive from a base class which has a destructor, that destructor should be declared to be virtual. For example, suppose that our date class required a destructor (even though we know in reality it doesn't need one). We could easily have a situation such as Chapter 7 - Inheritance Page 74

Page 77: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition date *p; if (choice == 2) // suppose 2 means we want a datetime p = new datetime(); else p = new date(); where eventually we will need delete p; If the destructor is not virtual, only the base (date) portion will be destroyed in the case where choice was 2, even though a datetime was what was really allocated. Often this may not cause a problem, since it might be that the only fancy things going on that cause the need for the destructor are in the base class, and the derived class might have nothing further than needs to be undone. But it is also possible that the base class might not need a destructor all, yet the derived class adds some feature such as dynamic memory allocation that does need undoing. Consequently, not only is it a good idea to make the base class destructor virtual, but it is even more foresighted to include a virtual destructor, even if it has an empty body, in every base class from which you expect many classes to be derived. Protected Members We have already seen that private members are private even to derived classes. It is occasionally useful, however, to have members which are private to everything except derived classes. This is possible in C++ by using the keyword protected: This is used exactly like "private:" and "public:" in a class declaration, and identify the following members as being private to everything except the class itself and derived classes. If a derived class is based on a public base class, then all protected members of the base class are also considered protected members of the derived class. (If the base class is private, then all the base class' protected and public members are private to the derived class). For example, our nstring class from Chapter 5 is quite simple and could form the basis for many derived classes. It might be desirable, however, to make the three member variables protected (rather than private, as we had coded it), to make it easier to write derived classes that manipulate the contents of the string. By making members protected, you are trading some level of Chapter 7 - Inheritance Page 75

Page 78: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition protection at the base class level for the ability to put some of the complexity (along with responsibility) at the derived class level, where it actually might be more appropriate. Nonetheless, in most cases, protected members should probably be avoided, and used only as a kludge (a quick fix to a less than ideal design). Exercise 7.1: As suggested above, make the nstring class' member variables protected, rather than private. Also, make the public member functions and operators (but not the constructors nor the destructor) virtual. Derive two classes from the nstring: a ustring and an lstring. A ustring is an uppercase string, and all letters stored in it are stored in uppercase. Similarly, an lstring is a lowercase string, and all letters stored in it are stored in lowercase. (If you are careful, you can save some work by deriving an lstring from a ustring). Exercise 7.2: Derive another class from the nstring, called oneblank, which works like an nstring but never has more than one consecutive blank. Every time data is placed into a oneblank, extraneous blanks are removed. Chapter 7 - Inheritance Page 76

Page 79: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 7 Summary A new (derived) class can be based on the publicly available features of an existing class. The derived class only needs to have new or different members coded for it; it automatically inherits any members that are not mentioned. An instance of a derived class may be used in any situation where an instance of the base class would work. When an instance of a derived class is created, first the base class portion of it is created, then the derived part. Unless another base class constructor is explicitly mentioned when coding the derived class constructor(s), the default base class constructor will be called when creating the base class part. When an instance of a derived class is destroyed, the derived part disappears first, then the base class part. A base class member function may be declared as virtual, in which case whenever the function is called, the most derived version available for the instance (which may be an instance of a derived class) will be called, regardless of the context in which the function is being called. In general, if you expect derived classes to provide different versions of a function, you should declare the function as virtual. Base class destructors should be declared to be virtual, since it is common to have base class pointers which point to derived objects, and deleting those objects is necessarily a base-class-context operation. Members of a class may be declared as protected, rather than public or private, in which case they are available for use by members (and friends) of the class and members (and friends) of a derived class, but not by other functions. The difference between protected and private is that private members may not be used by members of a derived class. Chapter 7 - Inheritance Page 77

Page 80: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 7 - Inheritance Page 78

Page 81: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Chapter 8. Introduction to Object Oriented Analysis and Design By now, we have covered the main OO concepts of encapsulation, inheritance and polymorphism in a fair bit of technical detail, using the C++ language. The concepts we have learned are much the same in any other OO language, although other terminology may be used. For example, in Smalltalk, the original OO language, the term method is the name used for what C++ calls a member function, and a message is simply a call of a member function. So, in C++ where we would call an object's member function, in Smalltalk we would send a message to an object, which would cause one of its methods to be executed. Other common OO terms for what C++ programmers call member functions are operations or behaviours. Similarly, some terms from other OO contexts for what is called member data in C++ are properties or attributes. Thankfully, the value of the OO approach does not depend on the precise terminology you use, but on the concepts underlying the terminology. A major part of programming is the process of abstraction, of identifying the aspects of something that are essential to a given situation. In programming, we create an abstract image of the situation that we are trying to simulate with a computer program before we start to code the solution. This is traditionally called "analysis and design", because the situation is first examined in detail (analysis) and then the major decisions of approach are made (design). OO languages, compared with more traditional structured languages, make abstraction easier, by bringing the computer implementation of an abstract concept closer to the way we naturally develop abstract concepts. When we look at the world around us, we tend to observe things by noticing the properties they exhibit. We easily ignore details that we consider unimportant, even though they may be necessary, to avoid cluttering our minds with needless detail. In C++, a type of thing becomes a class, a thing is the instance of a class, the important features become the public members and the unimportant but necessary details become the private members. With traditional programming languages, a further conversion is necessary, to take our natural abstract concepts and restructure them to fit the computer language's capabilities. Perhaps some examples of typical "things" that can easily become program objects would be helpful at this point. Until now, our examples have been "programmer's" objects, which help a programmer write typical programs, but don't really help speed the process of creating a program to solve a problem in the "real" world. Some typical objects are: - a savings account at a bank - a customer of a business Chapter 8 - Introduction to OO Analysis and Design Page 79

Page 82: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition - an inventory item - a chart of accounts - a student at a school - a subject taught at a school - a recipe - a phone book listing With each of these objects, a moment's thought would probably cause a long list of features to come to mind. Furthermore, ideas for other possible objects spring up, either things related to these objects or things which represent parts of these objects. The hardest part is probably not deciding what objects you need, but rather deciding what objects you can really do without! A design approach can help with this. The traditional analysis and design approach is an operation that tends to proceed in discrete steps. First the problem is analyzed, then a framework for a solution is designed, then the code to implement the design is developed, and finally the code is tested. The OO approach to program development is much more of an iterative process. Primary objects are identified when investigating the problem. The major parts of these objects can actually be coded, in a rough manner, as the investigation proceeds. Major tasks that need to be performed can be coded, also in an unrefined manner, to "trial-fit" the objects to see if the analysis is sound. If necessary, the objects can be modified, or even scrapped, before the design proceeds too far. Once the basic design is right, the objects, and the program(s) that use them, can easily be refined either to improve or complete the content of the solution, or to improve the look-and-feel of the program. There are some techniques to help with the initial design. An example is the use of CRC cards, first proposed by Timothy Budd. CRC stands for Class, Responsibilities and Collaborators. Each class is represented by an index card. On the front of the card is written the class name (Class), its public features (Responsibilities), and other classes that it uses, either as members, friends or as a base. On the back of the card, implementation notes (such as what the private members might be) can be made, as ideas occur. The idea of using index cards makes it easy for a group of people to sit around and evaluate the design, by passing around the cards, pulling out related cards and looking at them together, jotting down notes on the back, and making revisions, and perhaps even play-acting by having each person be a "virtual" implementation of several of the classes. After such a session, it is relatively straightforward for a programmer to sit down and code the objects, one for each card. Chapter 8 - Introduction to OO Analysis and Design Page 80

Page 83: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Some guidelines for object design are: - when first considering an object, concentrate on determining the public features (the ones you'll need to use) first, rather than the private features (the others you'll need in order to provide the public features). The implementation should be driven by the requirements, not the other way around. - as much as possible, make it so that the public functions can not "mess up" the object when a program passes invalid data to them. - be careful whenever you are tempted to use a friend, or to make members protected rather than private. There are good reasons for doing either of these, but there are not many! In particular, do not do either simply to avoid providing proper public functions to give you the access you need. - avoid using global variables, and don't make variables members when they can be localized to member functions. Both of these are just ways to avoid proper, explicit parameter passing. - don't be afraid to revise an object as programming progresses. OO design is an iterative process, and some things you just won't notice or think through properly until you've done some coding. Once an object is part of a finished program, however, you shouldn't change how its public features work (other than to add new features). You don't want to have to rewrite the program that is already finished. In the case where you have an object in use, yet you want to change how it works, derive a new object and play with that. - develop your own set of I/O objects (for interacting with the user and with files) and other general purpose objects (such as a date!) that you use for all your programs. This keeps you from re-inventing the same thing each time, and gives your code a consistent look, which will make it easier to maintain. Note that these sorts of objects usually have very little to do with any one specific application. Good, experienced OO programmers have a "toolkit" with which they can quickly build a functional application. - DO use the same name (or operator) for the same sorts of things for different objects. Conversely, do NOT use the same name for things that are significantly different, or your code will be needlessly confusing. - before coding any function or operator, write a few Chapter 8 - Introduction to OO Analysis and Design Page 81

Page 84: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition comment lines describing what the function does, what its parameters are and what its return value is. If it is hard to explain, it might not be such a good function to write! - when trying to decide whether a new class (let us call it A) should be derived from an existing class (let us call this B) or should simply have a member whose type is B, ask out loud the following questions: "Is an A a B?" and "Does an A have a B?". If the first answer is yes, then derive A from B. If the second answer is yes, then A should have an instance of B as a member. It can be surprising how often these simple questions, voiced out loud, can make the decision obvious. More technically minded people may ask themselves (quietly), "can I use an A in any situation where I can use a B?", and if this answer is yes then A should be derived from B. Finally, remember that programming is a difficult process. Tools such as object oriented languages can make the task more manageable, but they do not make it easy. After all, if it were easy to do, someone would write a program to do it. Chapter 8 - Introduction to OO Analysis and Design Page 82

Page 85: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Appendix A. Sample Problems This appendix contains various sample problems, most of which have appeared on tests over the years. There are two styles of problem here: walkthrough exercises, where you are given a C++ program and must determine its exact output, and word problems, where you are given a description of a problem and must write some C++ code that solves it. 1. Walkthrough. #include <stdio.h> class q1 { int id; public: q1() { id = 1; printf("mkdef: %d\n", id); } q1(int start) { id = start + 1; printf("mknew: %d\n", id); } ~q1() { printf("rm: %d\n", id); } int something(int n) { printf("La-de-da:%d\n", id); return n*(id + 3); } }; void foo(void) { q1 one(1); one.something(4); } int main() { q1 a; printf("%d\n", a.something(3)); foo(); q1 b(6); printf("Bye bye..."); return 0; } Appendix A - Sample Problems Page 83

Page 86: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 2. Develop and code a class, named employee, as described below. When an employee is created, an employee name of up to 30 characters (stored as a null-terminated string) must be supplied. An annual salary may also be supplied. If no salary is supplied, a salary of $20,000.00 is to be used. An employee has the following (publicly accessible) members: int raise(double increase) which increases the employee's annual salary by the amount passed as a parameter. This function normally returns a "true" value. However, if the new salary would be less than zero (because a large negative "increase" was specified), the salary is instead set to $0.00 and a "false" value is returned. void job() which displays the employee's name and job title. The job title is determined by salary: Worker for salaries of $30,000 or less, Clerk for salaries above $30,000 up to $45,000, and Executive for salaries over $45,000. void cheque() which displays the employee's name, job title, and net pay for a two-week period (1/26th of the annual salary, less 40% for fringe benefits and tax withholding). Appendix A - Sample Problems Page 84

Page 87: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 3. Walkthrough. #include <stdio.h> #include <string.h> class something { char str[31]; int n; public: something(); something(char s[]); ~something(); int dedah(char s[]); int dedah(int num); }; int main() { something la, lala("dedah"); // concentrate! la.dedah(lala.dedah("ladedah")); something looloo(""); looloo.dedah("so long"); looloo.dedah(5); return 0; } something::something() { strcpy(str, "nothing"); n = -6; } something::something(char s[]) { strcpy(str, s); if (str[0] == '\0') n = 0; else n = 2; } something::~something() { printf("Good Bye from %s %d\n", str, n); } int something::dedah(char s[]) { printf("was <%s>, <%d>\n", str, n); strcpy(str, s); n++; return n + 2; } int something::dedah(int num) { printf("was <%s>, <%d>\n", str, n); n = n + num; return n + 1; } Appendix A - Sample Problems Page 85

Page 88: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 4. Develop and code a class, named Bottle, with the following publicly accessible characteristics: - when an instance of a Bottle is created, a double (indicating the capacity of the Bottle) and a character string (indicating the unit of capacity, max. 30 characters) must be supplied. A new Bottle starts out empty. - there is a member function double fill(double) which is passed an amount to be poured into the Bottle. This is added to the amount already in the Bottle, unless the total would exceed the capacity of the Bottle, in which case only enough to fill the Bottle is added. The amount left over, if any, is returned. - there is a member function double fullness() which returns, as a percent, how full the Bottle is. - there is a member function void display() which displays the current state of the Bottle, in the following format: This 5.00 Litre bottle has 4.50 Litres in it. (where this particular Bottle has a capacity of 5.0, a unit of "Litre" and has had 4.5 poured into it). For example, the following program: int main() { Bottle x(3.5, "Gallon"); double spill = 0; while (spill == 0) { spill = x.fill(0.75); x.display(); } printf("Oops! We spilled %.2lf!\n", spill); printf("Bottle is %.2lf%% full.\n", x.fullness()); return 0; } would display: This 3.50 Gallon bottle has 0.75 Gallons in it. This 3.50 Gallon bottle has 1.50 Gallons in it. This 3.50 Gallon bottle has 2.25 Gallons in it. This 3.50 Gallon bottle has 3.00 Gallons in it. This 3.50 Gallon bottle has 3.50 Gallons in it. Oops! We spilled 0.25! Bottle is 100.00% full. Appendix A - Sample Problems Page 86

Page 89: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 5. Walkthrough. #include <stdio.h> #include <string.h> class abc { char def[21]; public: abc() { strcpy(def, "Help"); } abc(char s[]) { strcpy(def, s); } abc(char c) { def[0] = c; def[1] = '\0'; } void operator=(abc); // redefines = between abc's!!! friend abc operator+(abc, abc); void out() { printf("%s\n", def); } }; void abc::operator=(abc x) { int i, n = strlen(x.def); for (i = 0; i < n; i++) def[i] = x.def[n - (i + 1)]; def[n] = '\0'; } abc operator+(abc a, abc b) { int i = 0; abc c = a; while (c.def[i] != '\0') i++; for (int j = 0; b.def[j] != '\0'; j++) { c.def[i] = b.def[j]; i++; } c.def[i] = '\0'; return c; } int main() { abc x, y("!e"), z('M'); x = x + z; x.out(); y = y + x; y.out(); return 0; } Appendix A - Sample Problems Page 87

Page 90: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 6. Design and code a class named "animal", which stores critical information about an animal. An animal has the following publicly accessible members: void name(char s[]) - sets the animal's name to the string passed in s (e.g. "dog"). void name() - displays the animal's name on the standard output device. void noise(char s[]) - sets the noise the animal makes to the string passed in s (e.g. "woof"). void noise() - displays the animal's noise on the standard output device. When instantiated, an animal should be initialized to "human" (for the name) and "ouch" (for the noise), unless a name and a noise (in that order) are supplied as parameters, in which case the supplied name and noise are used. Also, write a function void OldMac(animal) which displays a version of the song "Old MacDonald's Farm" on the standard output device. For example the code: animal x("duck", "quack"); OldMac(x); would output: Old MacDonald had a farm, EIEIO And on that farm he had a duck, EIEIO With a quack, quack here And a quack, quack there Here a quack, there a quack Everywhere a quack, quack Old MacDonald had a farm, EIEIO Of course, everywhere that "duck" appears should be replaced with the animal's name, and everywhere that "quack" appears should be replaced with the animal's noise. You will receive a paltry bonus if your function displays the correct article (either a or an) depending on whether the name or noise begins with a consonant or a vowel. Appendix A - Sample Problems Page 88

Page 91: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 7. Walkthrough. #include <stdio.h> class crypt { char stuff[30]; int num; public: crypt(char s[]); void out(); friend crypt operator&&(crypt, crypt); }; crypt::crypt(char s[]) { num = 0; while (s[num] != '\0') num++; for (int i = 0; i < num; i++) stuff[i] = s[num - (i + 1)]; } void crypt::out() { int i = 0, j = 1; while (i < num) { printf("%c", stuff[i]); if (stuff[i + 1] == 'X') { i = i + 2; j = 1; } else { j++; i = i + j; } } printf("\n"); } crypt operator&&(crypt a, crypt b) { crypt c = a; c.stuff[c.num]='X'; c.num++; for (int i = 0; i < b.num; i++) c.stuff[c.num + i] = b.stuff[i]; c.num = c.num + b.num; return c; } int main() { crypt x("o*ygs+"), y("+h$d#o"); crypt z = x && y; z.out(); return 0; } Appendix A - Sample Problems Page 89

Page 92: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 8. Write the C++ code to implement a class called a fillup, which represents filling a car up with fuel. Background: Whenever putting fuel into the car, a car owner may record the odometer reading (measure of how many kilometres the car has travelled since new) and the number of litres of fuel placed in the tank. As long as the car owner always fills the tank all the way up, fuel consumption can be calculated by dividing the number of litres of fuel by the difference between the current odometer reading and the previous fill-up's odometer reading. This figure is multiplied by 100 to get the "litres of fuel per 100km". A lower L/100km figure means better fuel economy. A fillup has the following publicly accessible member functions: void previous(long k) - sets the previous odometer reading to the value passed in "k". If "k" is negative, zero is used instead, since odometers do not show negative values. If "k" is greater than 999999, then 999999 is used instead, since odometers only store 6 digits. void current(long k) - sets the current odometer reading to the value passed in "k". If "k" is negative, zero is used instead, since odometers do not show negative values. If "k" is greater than 999999, then 999999 is used instead, since odometers only store 6 digits. long km() - returns the number of kilometres travelled since the last fill-up. This is the difference between the current odometer reading and the previous one. If the current one is less than the previous one, this functions compensates for the "wrap-around" at the 1000000 kilometre mark. (E.g. if the previous is 999980 and the current is 120, then km() returns 140). void l(double litres) - sets the number of litres of fuel put into the car to the value passed in "litres". If "litres" is negative, 0 is used instead, since you can only put fuel into the car, not take it out. double l() - returns the number of litres of fuel put in. double lp100km() - returns the number of litres of fuel used per 100km travelled. When instantiated, a fillup may be supplied two longs (previous and current odometer readings) and a double (the number of litres of fuel put into the car), subject to the Appendix A - Sample Problems Page 90

Page 93: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition validations mentioned in the functions above. If no data is supplied, then all these values are assumed to be zero. Also, two fillups may be compared using the > sign. The result (an int, by the way, using 0 for false and anything other than 0 for true) will be true when the fuel economy of the left side is better than the fuel economy of the right side. (Remember that a lower L/100km figure means better fuel economy). For example, the following program displays 15.0 is the best int main() { fillup x(3000, 3200, 30), y; // x consumes 15 L/100km y.previous(999950); y.current(50); y.l(20); // y consumes 20 L/100km printf("%.1lf is the best\n", x > y ? x.lp100km() : y.lp100km()); return 0; } Appendix A - Sample Problems Page 91

Page 94: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 9. Walkthrough. #include <stdio.h> #include <string.h> class foo { char str[30]; int last; public: foo() { last = 0; } foo(char s[]) { last = strlen(s) - 1; *this = s; // uses operator= shown below } foo(char s[], int end) { last = end; *this = s; } void operator=(char s[]) { for (int i = last; i >= 0; i--) str[last - i] = s[i]; show(); } void show() { for (int i = 0; i <= last; i++) printf("%c ", str[i]); printf("\n"); } friend foo operator+(foo a, foo b); }; foo operator+(foo a, foo b) { foo c; int i; for (i = 0; i <= a.last; i++) c.str[i] = a.str[i]; for (i = 0; i <= b.last; i++) c.str[a.last + i + 1] = b.str[i]; c.last = a.last + b.last + 1; c.show(); return c; } int main() { foo alpha("alpha", 2), beta("ets"); foo gamma; gamma = alpha + beta; alpha = "derisive comments don't help"; gamma = gamma + alpha; return 0; } Appendix A - Sample Problems Page 92

Page 95: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 10. Write a class, named property, which is used by the Megacity to store the property tax information for a house. When instantiated, a property must be supplied an address (char string no longer than 60 characters), and may be supplied an assessed value (double) and a mill rate (double). (Either both numeric values will be supplied after the address, or no numeric values will be supplied). If no numeric values are supplied, then an assessed value of $100,000.00 and a mill rate of 1.0 are to be used. If, on the other hand, values are supplied but they are less than zero, then 0.0 is to be used in place of the offending value(s). There are member functions: int mill(double) - if the double value passed is greater than or equal to zero, then the mill rate for the property is reset to be that value, and a true value is returned. Otherwise, nothing is done other than returning a false value. double mill() - returns the current mill rate for the property. int assessed(double) - if the double value passed is greater than or equal to zero, then the assessed value for the property is reset to be that value, and a true value is returned. Otherwise, nothing is done other than returning a false value. double assessed() - returns the current assessed value of the property. void address(char s[]) - fills the array s with the address of the property. double taxes() - returns the annual taxes for the property, which is the result of multiplying the assessed value by the mill rate, and then dividing that by 100. As well, a double may be added to (using +), or subtracted from (using -), a property, which results in a property identical to the supplied property, except that its assessed value is the sum (using +) or difference (using -) of the assessed value (of the supplied property) and the supplied double. However, if this calculation would result in a negative assessed value, then the resulting property will have an assessed value of 0.0. For example, Appendix A - Sample Problems Page 93

Page 96: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition property x("1 Main St."), y("2 Main St.", 130000.00, 1.1); x.mill(1.24); printf("%.2lf %.2lf\n", x.taxes(), y.taxes()); x = x + 10000.00; char addr[61]; x.address(addr); printf("Tax for %s is now %.2lf\n", addr, x.taxes()); would display: 1240.00 1430.00 Tax for 1 Main St. is now 1364.00 Appendix A - Sample Problems Page 94

Page 97: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 11. Walkthrough. #include <stdio.h> #include <string.h> class skipper { char s[41]; int sk; int ln; void set(char str[], int n) { strcpy(s, str); ln = strlen(s); sk = n; } public: skipper(char str[]) { set(str, 2); } skipper(char str[], int n) { set(str, n); } void show() { for (int i = 0; i < ln; i += sk) printf("%c", s[i]); printf("\n"); } void operator+=(skipper x) { int i; for (i = 0; i < x.ln; i++) s[i + ln] = x.s[i]; ln += x.ln; if (sk < x.sk) sk = x.sk; } }; int main() { skipper one("ASCYEB"); one.show(); skipper two("OI F21 DPB4 A5", 3); two.show(); printf("ahem..."); two += one; two.show(); one += skipper("CLOWBY", 5); printf("is like "); one.show(); one += "UVLA"; printf("I mean "); one.show(); return 0; } Appendix A - Sample Problems Page 95

Page 98: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 12. Code a class called tvshow, which stores information about a TV show. When instantiated, two character strings (title of the show and name of the network on which the show appears) and an int (the Kneelson rating of the show) may be supplied. If the title is longer than 50 characters, only the first 50 characters are stored, and if the network name is longer than 20 characters only the first 20 characters are stored. Alternatively, a tvshow may be instantiated without supplying any data, in which case an empty string is used for the show's title as well as the network's name, and 0 is used for the Kneelson rating. There are public member functions: void title(char s[]) which copies the show's title into the array s, as a null- terminated string. void network(char s[]) which copies the network's name into the array s, also as a null-terminated string. int rating() which returns the Kneelson rating for the show. As well, two tvshows may be compared using >, which returns a true value if the Kneelson rating of the tvshow on the left side is higher than the Kneelson rating of the tvshow on the right side. Similarly, a tvshow can be compared to an int value using >, where the tvshow's Kneelson rating is compared to the int. Finally, there is a non-member function tvshow best(tvshow x[], int count) which is passed an array of tvshows along with the number of elements in the array, and returns the element with the highest Kneelson rating. For example tvshow shows[3], top; shows[0] = tvshow("This Hour Has 22 Minutes", "CBC", 15); shows[1] = tvshow("Frasier", "NBC", 30); shows[2] = tvshow("Beavis and Butthead", "MuchMusic", 1); top = best(shows, 3); char s1[51], s2[21]; top.title(s1); top.network(s2); printf("%s on %s tops ratings at %d\n",s1,s2,top.rating()); displays Frasier on NBC tops ratings at 30 Appendix A - Sample Problems Page 96

Page 99: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 13. Walkthrough. #include <iostream> using namespace std; class blob { int *nums; int size; int pos; public: blob() { size = 5; nums = new int [size]; pos = 0; } blob(int sz) { size = sz; nums = new int [size]; pos = 0; } ~blob(); void operator=(blob &b); void operator=(int n) { nums[pos] = n; pos++; if (pos == size) pos = 0; } }; void blob::operator=(blob &b) { for (int i = 0; i < b.size; i++) *this = b.nums[i]; // note: calls the other = operator! } blob::~blob() { for (int i = 0; i < size; i++) { cout << nums[i]; if (i < size - 1) cout << ','; else cout << " is now gone\n"; } delete [] nums; } int main() { blob x; for (int i = 1; i <= 7; i++) x = i * 2 ; blob y(3); y = x; return 0; } Appendix A - Sample Problems Page 97

Page 100: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 14. Given the following public part of a class declaration: class charfile { //...there is some private stuff, but you can't use it! public: charfile(char name[]); // opens text file named "name" ~charfile(); // closes the file virtual int read(char *ptr); // reads next character // from the file into the memory location // pointed to by ptr. Returns 1 if it // read OK, 0 if not (e.g. end of file) }; derive a class, called a "linefile", that is a charfile, except that the read member function reads an entire line into the memory location pointed to by ptr, rather than just one character. (A "line" is defined to end with a newline character, although if some non-newline characters immediately precede the end of the file, they are also considered a line.) Once the newline character is read, it is not placed in memory at the end of the rest of the data; it is simply discarded. A null terminating byte, however, is placed in memory at the end of the data. Hint: the new class will need (1) a constructor and (2) a new version of read. Have this new version of read call the base version in a loop that ends when the base version fails or reads a newline. It is very important for you to note that YOU DO NOT NEED TO KNOW how a charfile works, you only need to understand what the parts of a charfile do. Appendix A - Sample Problems Page 98

Page 101: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 15. Walkthrough. #include <stdio.h> #include <string.h> class field { char *data; int size; int justify; public: field(const char s[], int sz); field(const char s[], int sz, int just); field(const field &); ~field(); void operator=(const field &); void out() const; }; void showem(field &a, field b); int main() { field w("12345", 10), x("678", 6, 2); showem(w, x); field y = x, z("", 8, 1); z = w; showem(y, z); return 0; } field::field(const char s[], int sz) { data = new char[sz]; strcpy(data, s); size = sz; justify = 0; printf("(done with A)"); } field::field(const char s[], int sz, int just) { data = new char[sz]; strcpy(data, s); size = sz; justify = just; printf("(done with B)"); } field::field(const field &f) { data = new char[f.size]; strcpy(data, f.data); size = f.size; justify = f.justify; printf("(done with C)"); } field::~field() { delete [] data; } void field::operator=(const field &f) { delete [] data; Appendix A - Sample Problems Page 99

Page 102: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition data = new char[f.size]; strcpy(data, f.data); size = f.size; printf("(done with D)"); } void field::out() const { int i, extra = size - strlen(data); if (justify == 2) for (i = 0; i < extra; i++) printf("*"); else if (justify == 1) for (i = 0; i < extra/2; i++) printf("*"); printf("%s", data); if (justify == 0) for (i = 0; i < extra; i++) printf("*"); else if (justify == 1) for (i = extra/2; i < extra; i++) printf("*"); } void showem(field &a, field b) { a.out(); printf("\n"); b.out(); printf("\n"); } Appendix A - Sample Problems Page 100

Page 103: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 16. Create a class, called a Buffer, that can store up to 4 strings destined for output. Each string has a maximum size of 81 bytes. A Buffer has the following characteristics: - when a Buffer is created, it is empty. - the operator, <<, may have a Buffer on the left side and a character string on the right side. The string is stored in the Buffer. If the Buffer is full (i.e. this is the fourth string that has been sent to an empty Buffer) then all the strings in the Buffer are displayed on the screen (with a new-line after each one) and the Buffer is made empty. - there is a member function void flush() that displays all the strings in the Buffer (with a new-line after each one) and the Buffer is made empty. - when a Buffer disappears, any strings stored in it are displayed with a new-line after each one). For example, the following code: produces this output: { Buffer x; 123hi x << "hi"; hello printf("1"); morning x << "hello"; greetings printf("2"); 45howdy x << "morning"; yo! printf("3"); 6hey x << "greetings"; bye printf("4"); x << "howdy"; printf("5"); x << "yo!"; x.flush(); x << "hey"; printf("6"); x << "bye"; } Appendix A - Sample Problems Page 101

Page 104: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 17. Walkthrough. #include <stdio.h> #include <string.h> class folder { char s[31]; int pos; int len; int inc; public: folder(char []); char next(); void operator+=(folder); int size() { return len; } }; folder::folder(char str[]) { strcpy(s, str); len = strlen(s); pos = 1; inc = 2; } char folder::next() { char c = s[pos]; pos = pos + inc; if (pos >= len) { inc = -2; if (pos == len) pos--; else pos = pos - 3; } else if (pos < 0) { inc = 2; pos = pos + 3; } return c; } void folder::operator+=(folder a) { int i = 0; while (i < a.len) { s[len] = a.s[i]; i++; len++; } s[len] = '\0'; } Appendix A - Sample Problems Page 102

Page 105: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition void operator!(folder a) // hint: this is no longer not! { for (int i = 0; i < a.size(); i++) printf("%c", a.next()); } int main() { folder x("tgnee"), y("tb*"); x += y; !x; printf("\n"); return 0; } Appendix A - Sample Problems Page 103

Page 106: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 18. Write the code to implement a class called an occasion, which stores information about an up-coming special occasion. An occasion has the following publicly accessible characteristics: - a member function int date(int day, int month, int year) which sets the date on which the occasion will occur. This function performs a SIMPLE check of the supplied data (to make sure that day is 1-31, month is 1-12 and year is 1900-9999). If the data is OK, the occasion's date is set as requested and a true value is returned. Otherwise, the occasion's date is set to January 1, 1980 and a false value is returned. - a member function void set(char s[]) which sets the description for the occasion to (the null- terminated string) s. - a member function long date() which returns the occasion's date in the format yyyymmdd (e.g. 19960315 for March 15, 1996). - a member function char *tell(char s[]) which extracts the occasion's description and places it in s as a null-terminated string. For convenience, this function also returns s. - when instantiated, the default date is January 1, 1980, and the default description is an empty string. Additionally, three ints (day, month, year) and a char string (description) may be supplied, in which case the occasion is initialized according to the rules in date(int, int, int) and set(char []) above. - the == operator may be used to compare two occasions. Two occasions are considered equal if they occur on the same date. (I.e. the descriptions for the occasions have nothing to do with whether they are equal or not). For example, the following program reads a file of occassions, and displays the description for every occasion that occured on November 5, 1996: int read(FILE *fp, occasion *p) // read an occasion from file { int rc = 0, d, m, y; // returns true if successful char s[81]; if (4 == fscanf(fp, "%d/%d/%d %80[^\n]", &d, &m, &y, s)) if (p->date(d, m, y)) { p->set(s); rc = 1; } return rc; } Appendix A - Sample Problems Page 104

Page 107: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition int main() { FILE *fp = fopen("occasion.dat", "r"); occasion today(5, 11, 1996, "Today"), x; char desc[81]; while (read(fp, &x)) if (x == today) printf("%s\n", x.tell(desc)); fclose(fp); return 0; } Appendix A - Sample Problems Page 105

Page 108: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 19. Walkthrough. #include <iostream> using namespace std; #include <string.h> class a { char str[21]; public: a() { strcpy(str, "ordinary"); cout << str << '\n'; } a(char s[]) { strcpy(str, s); cout << str << '\n'; } void swap(int i, int j) { char t = str[j]; str[j] = str[i]; str[i] = t; } virtual void switchem() { swap(2, 5); } virtual void out(ostream &os) const { os << str; } }; ostream &operator<<(ostream &os, const a &x) { x.out(os); return os; } class b: public a { public: b() {} b(char s[]): a(s) { cout << "extra extra!\n"; } void switchem() { swap(3, 1); a::switchem(); } }; void doit(a one, a &two) { one.switchem(); two.switchem(); cout << one << ',' << two << '\n'; } int main() { a x, y("baneose"); b r, s("dieevrd"); doit(x, y); doit(r, s); cout << x << ',' << y << ',' << r << ',' << s << '\n'; return 0; } Appendix A - Sample Problems Page 106

Page 109: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 20. Write a class called apartment that simulates the income available from an apartment building. An apartment stores one double, containing the monthly rent, for each unit in the building. When instantiated, an apartment may be supplied the number of units in the building. If the number of units is not supplied, then 50 units are to be assumed. Regardless of the number of suites, each unit should be initialized to have $500 monthly rent. The number of units should be limited only by available memory, not by some arbitrary maximum size (i.e. use dynamic memory allocation). There are member functions: void set(int unit, double rent) - changes the rent for unit "unit" (where 0 is the first one, 1 is the second, and so on) to be "rent" dollars. If "unit" is not in range, considering the actual size of the building, then nothing is changed. void increase(double percent) - increase the rent for all units by "percent" percent. For example, if "percent" is 7.5, the rent for each unit will be raised by 7.5%. (Note that the percent may be negative, even though the owners never plan to use that feature). double rent(int unit) - returns a copy of the rent for unit "unit" (where 0 is the first one, 1 is the second, and so on). If "unit" is not in range, considering the actual size of the building, then zero is returned. double rent() - returns the total monthly rental income for the entire building. Furthermore, apartments may be copied (e.g. in parameter passing or using the = operator) without causing memory allocation problems (i.e. you need to write a proper copy constructor, etc.). The following shows some ways you might use an apartment (to help illuminate the specs above): apartment fifty, onetwenty(120); // different # of units for (int i = 80; i < 115; i++) onetwenty.set(i, 1000); // some units are $1000 fifty.set(70, 400); // invalid unit - nothing changed cout << onetwenty.rent() << '\n'; // rent for whole building cout << fifty.rent(40) << ',' << fifty.rent() << '\n'; // line above should show 500,25000 Appendix A - Sample Problems Page 107

Page 110: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 21. Walkthrough. #include <iostream> using namespace std; #include <string.h> class what { char s[81]; public: what() { strcpy(s, "nothing"); cout << "what\n"; } virtual void set(char str[]) { int i; for (i = 0; str[i] && str[i] != ' '; i++) s[i] = str[i]; s[i] = '\0'; } friend ostream &operator<<(ostream &, what); }; ostream &operator<<(ostream &os, what x) { return os << x.s; } // note: this operator has nothing to do with // iostreams, and it calls a virtual function void operator<<(what &x, char s[]) { x.set(s); } class hey: public what { public: hey() { cout << "hey\n"; } void set(char str[]) { int i = 0; while (str[i] && str[i] != ' ') i++; while (str[i] == ' ') i++; char temp[81]; int j = 0; while (str[i]) { temp[j] = str[i]; i++; j++; } temp[j] = '\0'; what::set(temp); } }; int main() { what is, that; hey really, confusing; confusing << "Everything gained from hard work is good except"; that << "ventured but not completed is valueless"; cout << is << ' ' << that << ' ' << really << ' ' << confusing << '\n'; return 0; } Appendix A - Sample Problems Page 108

Page 111: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 22. Write a class called a reader, which reads an entire (text) file into memory, and allows the program to access the data by position. When instantiated, a reader is passed a character string which contains the name of the file to read. The file is then read into the reader and closed (i.e. the file is NOT left open throughout the reader's existence). Also, there are member functions: long size() - returns the size of the data read from the file (in bytes). char position(long i) - returns character number i from the data read from the file, where character number 0 is the first character from the file. If an invalid index is given (e.g. less than 0 or bigger than the size of the data read), then the null byte is returned. If there is not enough memory to read the entire file in, or the file can't be read, then size() should return 0 (and then position() will consequently always return the null byte). For example, the following program displays the first 100 bytes from the file "abc.txt": int main() { reader f("abc.txt"); for(long i = 0; i < f.size() && i < 100; i++) cout << f.position(i); cout << '\n'; return 0; } Hint: read through the file once to see how big it is. Then dynamically allocate an array big enough to hold the data, and re-read the file, copying it into the array. Don't forget to provide a destructor, as well as a copy constructor and = operator to duplicate the array. Appendix A - Sample Problems Page 109

Page 112: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 23. Walkthrough. #include <iostream> using namespace std; #include <string.h> class stats { int *pwdcnts; int cntcnt; public: stats() { cout << "new default stats\n"; cntcnt = 0; pwdcnts = NULL; } stats(const char s[]) { cout << "new stats for " << s << '\n'; cntcnt = 0; pwdcnts = NULL; int i, newwd = 1; for (i = 0; s[i] != '\0'; i++) { if (s[i] != ' ' && newwd == 1) { cntcnt++; newwd = 0; } else if (s[i] == ' ' && newwd == 0) newwd = 1; } if (cntcnt > 0) { // assume this allocation will succeed: pwdcnts = new int[cntcnt]; int wd = 0; for (i = 0; s[i] != '\0'; i++) { pwdcnts[wd] = 0; while (s[i] == ' ') i++; while (s[i] != '\0' && s[i] != ' ') { pwdcnts[wd]++; i++; } wd++; } } } ~stats() { cout << cntcnt << " stats gone\n"; if (pwdcnts != NULL) delete [] pwdcnts; } friend ostream &operator<<(ostream &, const stats &); }; Appendix A - Sample Problems Page 110

Page 113: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition ostream &operator<<(ostream &os, const stats &x) { int t = 0; os << x.cntcnt << " words\n"; for (int i = 0; i < x.cntcnt; i++) { os << ' ' << i + 1 << ". " << x.pwdcnts[i] << '\n'; t += x.pwdcnts[i]; } return os << t << " characters\n"; } int main() { char string[41] = "this is a test"; string[15] = '\0'; // extra null (kludge!) stats strstats(string); cout << string << ":\n" << strstats << '\n'; cout << stats(" Another test ") << '\n'; return 0; } Appendix A - Sample Problems Page 111

Page 114: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 24. Code three classes: publication, periodical and book, where periodical and book are both derived from publication. A publication stores the following information, which is common to any publication (including periodicals and books): - Title (char string, max length 60 characters) - Publisher (char string, max length 40 characters) A periodical additionally stores a string (max length 20 characters) which contains a description of how often the periodical is published, such as "Monthly" or "Daily" or "Every Three Weeks". A book additionally stores: - Author (char string, max length 80 characters) - ISBN number (char string, max length 20 characters) When instantiating one of these classes, data for each of the fields (specified above) must be supplied. (See example below for the order). The only public feature of these classes, other than construction (and destruction, if your implementation requires it), is that instances of each of these classes may be sent to any ostream using the << operator (in a manner similar to the built-in types). Additionally, if a periodical or book is sent to an ostream using << in a publication context, the additional information pertinent to the periodical or book is shown, in a format exactly matching that of the example below. [Hint: this last specification implies that the display action must be "virtual". But the << operator cannot be virtual, since it cannot be a member. Therefore, your << operator will need to call one or more virtual functions to do the actual display work. This function, or set of functions, should be private or protected, to fully meet the requirements outlined above. Look carefully at ALL the output below before designing the code to perform output in the base class.] For example, the following program: Appendix A - Sample Problems Page 112

Page 115: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition int main() { publication *p[3]; p[0] = new publication("OOP244 Notes", "Seneca College"); p[1] = new periodical("Toronto Sun", "Southam News", "Daily"); p[2] = new book("21 Days to a Better You", "Pretice Hall", "Joseph Noodlemeyer", "1-234-5546-3"); for (int i = 0; i < 3; i++) { cout << *p[i] << '\n'; delete p[i]; } return 0; } would output: Publication: OOP244 Notes Published by Seneca College Periodical: Toronto Sun Published Daily by Southam News Book: 21 Days to a Better You Published by Prentice Hall Written by Joseph Noodlemeyer ISBN: 1-234-5546-3 Appendix A - Sample Problems Page 113

Page 116: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 25. Walkthrough. #include <iostream> using namespace std; class base { int *x; int sz; public: base(int cnt) { x = new int [sz = cnt]; seed(2); } ~base() { if (x) delete [] x; } void seed(int val) { for (int i = 0; i < sz; i++) x[i] = val + i; } void seed(const int v[]) { for (int i = 0; i < sz; i++) x[i] = v[i]; } int term(int n) const { int *p = new int [sz]; int i, tmp; for (i = 0; i < sz; i++) p[i] = x[i]; tmp = p[0]; for (int k = 0; k < n; k++) { for (i = 1; i < sz; i++) { tmp += p[i]; p[i - 1] = p[i]; } p[i - 1] = tmp; tmp = p[0]; } delete [] p; return tmp; } }; ostream &operator<<(ostream &os, const base &x) { for (int i = 0; i < 7; i++) os << (i > 0 ? ", " : "" ) << x.term(i); return os; } class deranged: public base { public: deranged(int n, int m): base(2) { int sval[2]; sval[0] = n; sval[1] = m; seed(sval); } }; int main() { base x(3); deranged fib(4, 5); cout << x << '\n' << fib << '\n'; return 0; } Appendix A - Sample Problems Page 114

Page 117: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 26. Given the following class: class person { char name[41]; char relation[41]; public: person() { name[0] = relation[0] = '\0'; } person(const char s1[], const char s2[]) { strcpy(name, s1); strcpy(relation, s2); } void getname(char s[]) const { strcpy(s, name); } void getrelation(char s[]) const { strcpy(s, relation); } }; which stores the information about a person and that person's relationship to someone else, create a class called family, which stores a number of person instances, representing an entire family. When instantiated, a family may be given an int, specifying the maximum number of persons which will be stored in the family, and a person instance representing the central person in the family. Alternatively, either or both of these may be omitted. Use a maximum of 10 persons, if the maximum is omitted, and use the first person put into the family (using the 2nd = operator described below) as the central person, if the central person is omitted. The following operators can be used with a family: - the = operator with a family on both sides makes the family instance on the left be a duplicate of the family on the right. All data which had been in the (left side) family is destroyed. As well, this operator returns a reference to the family on the left. - the = operator, with a family on the left side and a person on the right side, adds the person to the end of the family as long as the maximum for the family will not be exceeded. If the family already has its maximum number of members, no change is made to the family. As with the other = operator, a reference to the family on the left is returned. - the << operator, with an ostream on the left side and a family on the right side, displays the family (one person per line, showing the name, a colon, a space, and that person's relationship) on the ostream. This operator returns a reference to the ostream. The first person shown should be the central person in the family. Appendix A - Sample Problems Page 115

Page 118: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition For example: family simpsons(5); // Homer is the central figure simpsons = person("Homer Simpson", "The Man"); simpsons = person("Marge Simpson", "Homer's wife"); simpsons = person("Bart Simpson", "Homer's son"); cout << simpsons; displays: Homer Simpson: The Man Marge Simpson: Homer's wife Bart Simpson: Homer's son Tips: To do this right, you need to use dynamic memory allocation. There will be 5 ways to construct a family, including the copy constructor. Don't forget to write a destructor. Appendix A - Sample Problems Page 116

Page 119: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 27. Walkthrough. #include <iostream> using namespace std; class last3 { int nums[3]; int cnt; int st; public: last3() { cnt = st = 0; } void store(int); void show(); }; class whazzit: public last3 { int skipped; public: whazzit() { skipped = 0; } void store(int); }; void last3::store(int n) { cout << "In first store (" << n << ")\n"; if (cnt == 3) // Hint: st = (st + 1) % 3; // recall that x%3 else // is between 0 and 2 cnt++; nums[(st + cnt - 1) % 3] = n; } void last3::show() { for (int i = 0; i < cnt; i++) cout << i + 1 << ':' << nums[(st + i) % 3] << ' '; cout << '\n'; } void whazzit::store(int n) { if (n >= 0 && n <= 15) last3::store(n); else skipped++; cout << "In second store (" << n << "): " << skipped << " skipped so far\n"; } int main() { last3 a; whazzit b; for (int i = -3; i < 30; i += 7) { a.store(i); b.store(i); } cout << "The last3 variable is "; a.show(); cout << "And the whazzit variable is "; b.show(); return 0; } Appendix A - Sample Problems Page 117

Page 120: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 28. A. The Unwanted Animals Shelter (UAS) wants you to do some programming to help keep track of the pets they have available for adoption. Declare and define a class, called a Pet, with the following publicly accessible characteristics: - A constructor that is passed the following pieces of data (in this order): 1. a character string, up to 15 characters long, with the type of Pet (e.g. "cat", "dog", "bird", etc.) 2. a character string, up to 20 characters long, with the breed of Pet (e.g. "doberman", "Russian Blue", etc.) 3. a character string, up to 20 characters long, with the Pet's name (e.g. "Worf", "Rover", etc.) 4. an integer with the approximate age of the pet in years. - a member function void waiting(int num_weeks) which specifies how many weeks the Pet has been waiting to be adopted. (If this function is never called, assume the Pet has been waiting 0 weeks). - a member function int waiting() which returns the number of weeks the Pet has been waiting to be adopted (see the other waiting() function above). - a member function int adopt() which specifies that the Pet has been adopted and is no longer available. If this function is called for a Pet that has already been adopted, the message "NNN is already adopted" (where NNN is the name of the Pet) is displayed and a false value is returned, otherwise nothing is displayed and the function returns a true value. (Until this function is called, the Pet is assumed to be available for adoption). Note that this function does not change the number of weeks the Pet was waiting to be adopted. - a member function int adopted() which returns a true value if the Pet has been adopted (see the adopt() function above), and a false value otherwise. Appendix A - Sample Problems Page 118

Page 121: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition - a friend operator friend ostream &operator<<(ostream &os, Pet p) which displays (on os) Pet p's type, breed, name, age and a message either (1) saying that the Pet has been adopted or (2) telling how long the Pet has been waiting to be adopted. The operator returns a reference to the ostream it was given. Note that you have to decide what private members you need in order to implement these specifications. B. Since most Pets the UAS deals with are cats and dogs, derive two classes, Cat and Dog, from Pet. A Cat works the same as a Pet, except that only three values (breed, name and age) are specified when a Cat is created, and the "Cat" is shown as the type in the output of the << operator. A Dog is similar, except that the assumed type is "Dog" rather than "Cat". Hint: when calling the base class constructor, supply a constant string value for the Pet's type. Appendix A - Sample Problems Page 119

Page 122: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 29. Walkthrough. #include <iostream> using namespace std; #include <string.h> class q1 { char s[31]; public: q1(const char str[]) { strcpy(s, str); } int size() const { return strlen(s); } char position(int i) const { return s[i]; } friend ostream &operator<<(ostream &, const q1 &); }; ostream &operator<<(ostream &os, const q1 &x) { return os << '<' << x.s << '>'; } class q1a: public q1 { public: q1a(const char str[]):q1(str) { /*empty!*/ } int size() const; }; int q1a::size() const { int i = 0, count = 1; while (position(i) != '\0') { if (position(i) == ' ') count++; i++; } return count; } int main() { q1 x("This is a test"); q1a y("This is also a test"); cout << x.size() << ':' << x << '\n'; cout << y.size() << ':' << y << '\n'; return 0; } Appendix A - Sample Problems Page 120

Page 123: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 30. Write a class called a pile, which stores a pile of integers. A pile has the following publicly accessible characteristics: - when instantiated, a pile is passed the maximum number of integers that it can store. (This is used to dynamically allocate an array to store the numbers). - when destroyed, any memory dynamically allocated to the pile is deallocated. - the << operator can be used with a pile on the left side and an integer on the right. The integer is added to the end of the pile, unless the pile is already full, in which case the number is ignored. (When first created, a pile starts out empty). - the << operator can be used with an ostream on the left side and a pile on the right. The numbers stored in the pile are displayed on the ostream, with a comma separating each number from the next. - there is a member function int length() which returns the number of numbers currently in the pile. (A new pile starts out with length zero. The maximum for the length will be the number specified when the pile was created). For example, int main() { pile x(5); x << 1; x << 3; x << -1; x << 99; x << x.length(); // This should put 4 on the pile x << 10; // ignored, since the pile is full cout << "The pile is " << x << '\n'; return 0; } outputs The pile is 1,3,-1,99,4 Appendix A - Sample Problems Page 121

Page 124: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 31. Walkthrough #include <iostream> using namespace std; class q1 { char ch; public: q1() { cout << "Made one: a\n"; ch = 'a'; } q1(const q1 &x) { ch = x.ch + 2; cout << "Made another: " << ch << '\n'; } ~q1() { cout << "Bye: " << ch << '\n'; } void show() { cout << ch << '\n'; ch++; } }; q1 foo(q1 a) { q1 b; b = a; b.show(); return b; } int main() { q1 n, m; m = foo(n); return 0; } Appendix A - Sample Problems Page 122

Page 125: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 32. A. Create a class, called a "transaction", designed to store a customer's credit card transaction. A transaction has the following characteristics: - when a transaction is created, a dollar amount (double) and a transaction type (char: 'c' for a new charge, 'p' for a payment, 's' for service charge) must be specified (by the program creating the transaction). - a transaction has two member functions, double amount() char type() which return the dollar amount and the transaction type, respectively. - a transaction may be displayed using the << operator to write to an ostream, in which case something in the same format as the following appears: New Charge: $ 300.24 or Payment: $ 1000.00 or Service Charge: $ 0.50 depending, of course, on the type of transaction and its amount. - a transaction may be added to a double, or vice versa, using the + operator, where the result of the addition is a double. The result is the original double minus the transaction amount, if the transaction is a payment, or the original double plus the transaction amount otherwise. B. Create another class, call an "account", which simulates a credit card account. An account has the following characteristics: - when an account is created, a 10-character long account "number" and a dollar amount, specifying a "credit limit", must be specified. A new account always has a balance of zero. - an account has three member functions, double balance() void acctno(char s[]) double limit() which return the current balance of the account, the account number, and the credit limit, respectively. In the acctno function, the account number is sent back by copying it into the parameter, s, as a null-terminated string. Appendix A - Sample Problems Page 123

Page 126: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition - an account also has a member function, void limit(double x) which resets the credit limit to be the amount specified in x. - an account can have a transaction (see the previous question for a description of what a transaction is) added to it using the += operator (the account is the left argument and a transaction is the right argument), in which case the account's current balance gets the transaction added to it, as long as the new balance does not exceed the credit limit. If the new balance would exceed the credit limit, the current balance does not get changed. Note that for this part of the question, you are to assume that the transaction class has been written properly according to its specifications. Your marks for this question do not depend on your answer for the previous question. - an account can be displayed to an ostream using the << operator, with the following format (where in this example the account number is 1123111234, the current balance is 1400.25 and the credit limit is 2500): Account 1123111234: $ 1400.25 (remaining: 1099.75) C. Write a program that uses the account class (from part B) and the transaction class (from part A). This program has the user enter the initial information for an account, and then repeatedly gives the user the following choice: 1. Charge something new to the account 2. Apply a service charge to the account 3. Make a payment to the account 4. Change the credit limit 5. Quit the program. After making selections 1-4, the user is asked to enter the amount for the transaction, which is then appropriately applied to the account. The account is displayed after each change. The program finally stops when selection 5 is chosen. Appendix A - Sample Problems Page 124

Page 127: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 33. Walkthrough. #include <iostream> using namespace std; class q5 { protected: int n; public: q5() { cout << "Here I am\n"; n = 0; } void show() {n++; cout << n << " 1\n";} virtual void display() {n+=2; cout << n << " 2\n"; } }; class q5a: public q5 { public: void display() {n-=3; cout << n << " 3\n"; } }; void doboth(q5 &x) { x.show(); x.display(); } int main() { q5 a; q5a b; doboth(a); doboth(b); b.q5::display(); return 0; } Appendix A - Sample Problems Page 125

Page 128: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 34. Create a class called "converter" which is used to do conversions between systems where a simple multiplication can be used to perform the conversion. A converter has the following publicly accessible characteristics: -When constructed, a converter must be passed two strings (each up to 30 characters in length) and a double containing the conversion factor. The first string is the name of the units in the system we are converting from, and the second string is the name of the units in the system we are converting to. -If the = operator is used with a converter on the left side and a double on the right side, then the converter stores the number for a future conversion. If a number had previously been stored using =, it is replaced. -If the << operator is used with an ostream on the left side and a converter on the right side, then the number previously stored in the converter using = is displayed on the ostream, followed by the name of the "from" units, followed by the word "is", followed by the result of converting the number to the "to" system (by multiplying it by the conversion factor), followed by the name of the "to" units. If the = has not been used to store a value, zero is used. For example, converter km_to_miles("km", "miles", 0.62); km_to_miles = 100.0; cout << km_to_miles << '\n'; would output: 100 km is 62 miles and converter us2cdn("$ US", "$ CDN", 0.74); us2cdn = 100; cout << us2cdn << " and "; us2cdn = 200; cout << us2cdn << '\n'; would output: 100 $ US is 74 $ CDN and 200 $ US is 148 $ CDN Appendix A - Sample Problems Page 126

Page 129: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 35. Walkthrough. #include <iostream> using namespace std; class one { int n; int m; public: one() { n = 5; m = 6; cout << "one one made\n"; } one(int a, int b) { n = a; m = b; cout << "made one one\n"; } friend ostream &operator<<(ostream &, one); }; ostream &operator<<(ostream &os, one a) { return os << a.n << '/' << a.m << '=' << (a.n/a.m) << '\n'; } class two { one x; one y; public: two() { cout << "one two made\n"; } two(int a, int b, int c, int d) { x = one(a, b); y = one(c, d); cout << "made one two\n"; } friend ostream &operator<<(ostream &, two); }; ostream &operator<<(ostream &os, two a) { return os << a.x << a.y; } int main() { two t1, t2(4, 2, 8, 3); cout << t1 << t2; one t3(5, 10), t4; cout << t3 << t4; return 0; } Appendix A - Sample Problems Page 127

Page 130: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 36. Create a class called "usedcar" which is used to store information about a used car. The usedcar constructor must be passed the following data, which will be stored in the usedcar: (1) the model year of the car (int, e.g. 1997) (2) the make of the car (char string, max 20 characters long, e.g. "Ford") (3) the model of the car (char string, max 20 characters long, e.g. "Mustang") (4) the original purchase price of the car when bought new (double, e.g. 28385.00) (5) a comment describing the car (char string, max 60 characters long, e.g. "Exceptionally clean car, one owner only") (6) the year in which the used car is to be sold (int, e.g. 2001) There are member functions: int sellyear() - returns the year in which the car is to be sold. void sellyear(int year) - changes the year in which the car is to be sold to the value passed in "year". double price() - returns the asking price for the car in the year in which it is sold. The scheme to be used to calculate the asking price for the car is: - if the model year for the car is the same as the selling year, then the asking price is 10% less than the original purchase price of the car when new. - if the model year for the car is the year before the selling year, then the asking price is 20% less than the original purchase price of the car when new. - if the model year for the car is more than one year before the selling year, then the asking price is 20% plus an additional 5% per year (after the first year) less than the original purchase price. For example, a 4 year old car would sell for 20 + 3x5 = 35% less than the original price. If this calculation results in a number less than 500, however, then the asking price is 500. If the model year for the car is later than the selling year (an impossible situation), then the asking price returned is 0. The << operator can be used to display, on an ostream, all the stored values of the used car as well as the asking price for the car, attractively laid out. Finally, there is a + operator that takes two usedcar arguments, and returns a double, where the value returned is the sum of the two cars' asking prices. Appendix A - Sample Problems Page 128

Page 131: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition The following code shows how *some* of the parts of a usedcar should work: usedcar x(1997, "Mazda", "Protege", 19500.00, "Nice car",2000); // Display details of x cout << "Car number 1:\n" << x << '\n'; // increase selling year to 2001 x.sellyear(x.sellyear() + 1); // then show what it will sell for in 2001 cout << "Next year this car will sell for " << x.price(); Appendix A - Sample Problems Page 129

Page 132: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 37. Walkthrough. #include <iostream> using namespace std; #include <string.h> class test { char s[21]; public: test() { strcpy(s, "ate"); show(cout); } test(char c) { s[0] = c; s[1] = '\0'; show(cout); } test(char str[] ) { strcpy(s, str); show(cout); } ~test() { cout << '~'; show(cout); } void show(ostream &os) const { os << s << '\n'; } test &operator*(test x) { int i = 0; while (s[i] != '\0') i++; s[i] = '*'; i++; int j = 0; while ((i < 20) && (x.s[j] != '\0')) { s[i] = x.s[j]; i++; j++; } s[i] = '\0'; return *this; } }; ostream &operator<<(ostream &os, test x) { x.show(os); return os; } int main() { test a('i'), b, c("ter"); (a * c) * b; cout << a << b << c << "the end\n"; return 0; } Appendix A - Sample Problems Page 130

Page 133: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 38. Walkthrough. #include <iostream> using namespace std; class abc { double n; public: abc() { n = 67.5; cout << "1\n"; } abc(double num) { set(num); cout << "2\n"; } double get() const { cout<<"3\n"; return n; } virtual void set(double num) { if (num < 10) n = 10; else if (num > 100) n = 100; else n = num; cout << "4\n"; } }; class def: public abc { double m; public: def() { m = 6.2; cout << "5\n"; } def(double num1, double num2): abc(num1) { set(num2 - abc::get()); cout << "6\n"; } double get() const { cout << "7\n"; return m + abc::get(); } void set(double num) { if (num < 10 || 100 < num) m = num; else m = 55; cout << "8\n"; } }; void do_it(abc &var, double num) { cout << var.get() << '\n'; var.set(num); cout << var.get() << '\n'; } int main() { abc x(45); def y(2, 340); cout.setf(ios::fixed); cout.precision(3); do_it(x, 200); do_it(y, 253); cout << x.get() << '\n'; cout << y.get() << '\n'; return 0; } Appendix A - Sample Problems Page 131

Page 134: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 39. A. Code a class named polygon, which stores the coordinates for a (two dimensional) polygon. In mathematics, a polygon is a shape composed of points connected with lines. For the purposes of this question, a polygon is defined by the x- and y-coordinates of each of the points, also called vertices, which make up the polygon. The order of the vertices matters, and must be maintained. When a polygon is instantiated, the program must pass (1) an int specifying the number of vertices for the polygon. This also is the size of the next two arrays. (2) an array of x-coordinates (int values). (3) an array of y-coordinates (int values). There are also the following public member functions: int size() const - returns the number of vertices void point(int index, int &x, int &y) const - sets x to the value of the x-coordinate of vertex i of the polygon, and sets y to the value of the y-coordinate of vertex i of the polygon (where vertex number 0 is the first vertex in the polygon). For example, the program #include <iostream.h> int main() { int xs[5] = {1, 2, 3, 4, 5}; int ys[5] = {5, 6, 7, 8, 9}; polygon a(5, xs, ys); for (int i = 0; i < a.size(); i++) { int x, y; a.point(i, x, y); cout << "Vertex " << i << ": (" << x << ',' << y << ")\n"; } return 0; } would output: Vertex 0: (1,5) Vertex 1: (2,6) Vertex 2: (3,7) Vertex 3: (4,8) Vertex 4: (5,9) Appendix A - Sample Problems Page 132

Page 135: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Note that to do this question properly, you must use dynamic memory allocation to allocate arrays of the proper size when instantiating the polygon. This means you will also need to code a destructor, copy constructor and = operator for a polygon. B. Code the << operator so that a polygon can be sent to any ostream (just like all the built-in types can), to display the polygon's coordinates. The output should have the exact same output format as the sample main() program supplied in part A. In other words, the for-loop in part A's sample main could be replaced with cout << a; and it would still produce the same output. Your operator should allow subsequent output operations, so that, for example, if a and b were both polygons, the following line: cout << "First\n" << a << "Second\n" << b; would display both a and b with the headings "First" and "Second", respectively. C. Derive a class, shape, from polygon. A shape is just like a polygon, except that it also has an int value which identifies the colour of the interior of the polygon. The constructor for a shape has four, instead of three, parameters, where the fourth parameter is an int containing the colour of the shape. A shape also has an additional member function: int colour() const - returns the colour of the shape. As well, the << operator which sends a shape to an ostream, should display a line like: Colour: 56 (where 56 would be replaced by the shape's actual colour value) before displaying all the vertices of the shape. HINT: because of inheritance, it is possible to cast a derived instance to a base class instance without having to write any special code to do this. So, if x is a shape, then polygon(x) would be just the polygon portion of the shape, which could be sent to an ostream... Appendix A - Sample Problems Page 133

Page 136: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 40. Walkthrough. #include <iostream> using namespace std; class point { double x1; double y1; double z1; public: point() { x1 = y1 = z1 = 0; } point(double n1, double n2, double n3) { x1 = n1; y1 = n2; z1 = n3; } double x() const { return x1; } double y() const { return y1; } double z() const { return z1; } }; ostream &operator<<(ostream &o, point p) { return o <<'('<<p.x()<<','<<p.y()<<','<<p.z()<<')'; } point operator+(point a, point b) { return point(a.x() + b.x(), a.y() + b.y(), a.z() + b.z()); } point operator*(point a, point b) { return point((a.y()*b.z()) - (b.y()*a.z()), (a.z()*b.x()) - (a.x()*b.z()), (a.x()*b.y()) - (b.x()*a.y())); } point operator*(double a, point b) { return point(a*b.x(), a*b.y(), a*b.z()); } point operator*(point a, double b) { return b * a; } point operator-(point p) { return point(-p.x(),-p.y(),-p.z()); } point operator-(point a, point b) { return a + (-b); } int main() { point one(2, 1, 3), two(1, -4, -2), three(4, 5, 6); cout << one << " X " << two << " = " << one * two << '\n'; one = two + three; cout << one << '\n'; one = one * 5; cout << one << '\n'; one = two - (3 * three); cout << one << " and " << three << '\n'; return 0; } Appendix A - Sample Problems Page 134

Page 137: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 41. Walkthrough. // assume all dynamic memory allocation succeeds #include <iostream> using namespace std; #include <string.h> class artist { char name[31]; char desc[31]; public: artist() { name[0] = desc[0] = '\0'; } artist(char n[], char d[]) { strcpy(name, n); strcpy(desc, d); } virtual void display(ostream &os) const { os << name << ", " << desc; } }; ostream &operator<<(ostream &out, const artist &a) { a.display(out); return out; } class group: public artist { artist *members; int size; int count; public: group(char n[], int sz): artist(n, "Group") { members = new artist[sz]; size = sz; count = 0; } ~group() { delete [] members; } void operator+=(artist x) { if (count < size) { members[count] = x; count++; } } void display(ostream &os) const { artist::display(os); os << ':'; for (int i = 0; i < count; i++) os << "\n - " << members[i]; } }; int main() { artist one("Leonard Cohen", "good poet, bad singer"); group two("Spice Girls", 4); two += artist("Mel B.", "scary singer"); two += artist("Mel C.", "sporty singer"); two += artist("Emma", "baby singer"); two += artist("Victoria", "posh singer"); two += artist("Geri", "ginger singer"); cout << one << '\n' << two << '\n'; return 0; } Appendix A - Sample Problems Page 135

Page 138: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 42. A. Teaching at Seneca is a union job, with union imposed limits on the amount of work a teacher may perform. The hours a teacher supposedly puts in are tallied to make sure that the limits are not exceeded. Code a class named task, which stores the information needed to compute the number of hours per week that it should take a teacher to perform a particular task. Specifically, a task instance will store: - a character string description of the task. (Maximum length 40 characters, Default value: the empty string). - a double value storing a base quantity, which could be something like the number of students in a class, the number of hours per week that a class meets, or simply the number of hours per week that a task should take. (Default value: 0). - a double value storing a "workload factor", which depends on the sort of task this is. The workload factor is multiplied against the base quantity to arrive at the number of hours per week the task should take. (Default value: 1). When instantiated, the constructor may be passed a character string and two doubles, specifying initial values for the description, quantity and factor, respectively. Alternatively, there is a default constructor, which sets the various data members to the default values specified above. There are publicly accessible member functions: void quantity(double d) - resets the base quantity to be the value passed in d. double quantity() const - returns the base quantity. void factor(double d) - resets the workload factor to be the value passed in d. double factor() const - returns the workload factor. double hours() const - returns the number of hours the task should take per week (which is simply the base quantity multiplied by the workload factor). As well, the following operators may be used with a task: << with a char array on the left and a task on the right copies the task's description into the char array. + with a task on both sides returns a double containing the total weekly hours that the two tasks together should take. + with a double on one side and a task on the other Appendix A - Sample Problems Page 136

Page 139: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition side returns the sum of the double and the number of hours the task should take per week. (Either side could have the double, so code two versions). For example, if we declared the following tasks: task classes("Classroom hours", 16, 1), prep("Lecture preparation", 16, 0.5), students("Student contact", 120, 0.05), marking("Prepare and mark evaluations", 16, 0.25), meetings("Meetings", 1.5, 1); then char desc[41]; desc << students; cout << desc << ": " << students.hours() << '\n'; cout << "Total hours: "; cout << classes+prep+students+marking+meetings << '\n'; would display Student contact: 6 Total hours: 35.5 B. Code a class named swf (Seneca's name for its "Standard Workload Form"), used to store a list of the tasks (as described in question 3) a teacher is to perform, along with the teacher's name (maximum length: 40 characters). When instantiated, the swf constructor may be passed an integer specifying the maximum number of tasks will appear in the swf and a character string containing the teacher's name. If no data is supplied to the constructor, then assume that up to 20 tasks will appear on the swf, and make the teacher's name empty. [Since there is no preset limit for the number of tasks a teacher may be asked to perform, you should use dynamic memory allocation to make an array of tasks large enough to suit the particular swf being created. Don't forget to code a destructor. You are NOT required to code a copy constructor nor an = operator to properly copy swf objects. However, you can earn some bonus marks if you correctly do so.] The += operator, with a swf on the left and a task on the right, should append the specified task to the end of the list of tasks currently stored on the swf, if there is room for it. If the number of tasks that have already been appended to the swf exceeds the originally specified limit, Appendix A - Sample Problems Page 137

Page 140: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition however, then nothing is done. There are publicly accessible member functions: task item(int i) const - returns a copy of task number i (where task number 0 is the first task) from the swf. If i is not the number of a task on the swf, then a default task (with base quantity 0) is returned. void item(int i, task t) - if i is the number of a task currently on the swf (where task number 0 is the first task), then that task is replaced by t. Otherwise t is appended to the swf, just as if it had been passed to the += operator. int count() const - returns the number of tasks currently on the swf. double total() const - return the total weekly hours of all tasks on the swf. void getteacher(char s[]) const - copies the teacher's name into the array s. For example, swf evan(5, "Evan Weaver"); evan += task("Classes", 16, 2); evan += task("Marking", 20, 1); char temp[41]; evan.getteacher(temp); cout << temp << "\n----\n"; for (int i = 0; i < evan.count(); i++) { temp << evan.item(i); cout << temp << ": " << evan.item(i).hours() << '\n'; } cout << "Total hours: " << evan.total() << '\n'; would output: Evan Weaver ---- Classes: 32 Marking: 20 Total hours: 52 Appendix A - Sample Problems Page 138

Page 141: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 43. Walkthrough. #include <iostream> using namespace std; #include <string.h> class scary { char is[31]; char sez[31]; public: scary() { strcpy(is, "Creep"); strcpy(sez, "boooo"); } scary(const char i[], const char s[]) { strcpy(is, i); strcpy(sez, s); } virtual void sayis(ostream &os) const { os << is; } void saysez(ostream &os) const // this is NOT virtual!! { os << sez; } }; ostream &operator<<(ostream &os, const scary &x) { x.saysez(os); os << ", said the "; x.sayis(os); return os; } class ghost: public scary { public: ghost():scary("Ghost", "Boo!") {cout<<"made a ghost\n";} }; class witch: public scary { char extra[31]; public: witch(): scary("Witch", "Toil and Trouble") { cout << "which wicked witch?\n"; strcpy(extra, "Double, Double"); } void saysez(ostream &os) const { os << extra << " "; scary::saysez(os); } }; void trythis(scary g) { cout << g << '\n'; } int main() { scary s; ghost g; witch w; cout << s << '\n' << g << '\n' << w << '\n'; trythis(s); trythis(g); trythis(w); s.saysez(cout); cout << '\n'; g.saysez(cout); cout << '\n'; w.saysez(cout); cout << '\n'; return 0; } Appendix A - Sample Problems Page 139

Page 142: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 44. Walkthrough. #include <iostream> using namespace std; #include <string.h> class spinner { char *s; int n; void init(const char str[]) { s = new char[strlen(str) + 1]; // Assume this succeeds if (s) strcpy(s, str); n = 0; } public: spinner() { init("xyz"); } spinner(const char s[]) { init(s); } ~spinner() { if (s) delete [] s; } void operator++() { if (s) { char t = s[n]; s[n] = s[strlen(s) - (n + 1)]; s[strlen(s) - (n + 1)] = t; n++; if (((2 * n) + 1) == strlen(s)) n++; if (n >= strlen(s)) n = 0; } } friend ostream &operator<<(ostream &, spinner &); }; ostream &operator<<(ostream &os, spinner &f) { for (int i = 0; i < strlen(f.s); i++) { os << f.s << '\n'; ++f; } return os; } int main() { spinner f1, f2("YzZuF"); cout << f1 << "-----\n" << f2; return 0; } Appendix A - Sample Problems Page 140

Page 143: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 45. A. The Grendel Supercomputer Project has developed software that allows computers to be connected together (or "clustered") to make what looks like a single, more powerful, computer. Such computers are useful for extremely compute-intensive applications, where different parts of the logic can be executed on different processors at the same time. The two crucial statistics for determining the capabilities of a "Grendel cluster" are MB (megabytes) of memory and MIPS (millions of instructions per second). Every time two computers (each of which could be a Grendel cluster) are joined, the MB of the resulting cluster is the sum of the MB of the two pieces, less 1MB (which gets used as buffer space for managing the connections). Similarly, the MIPS of the resulting cluster is the sum of the MIPS of the two pieces, less 5 MIPS overhead. Make a class, called grendel, which represents a Grendel cluster. When instantiated, a grendel is given three ints: the number of computers in the cluster (1 or more), the MB (16 or more), and the MIPS (10 or more), in that order. If the values supplied are less than the minimum values shown, then the minimum values are used. Alternatively, a grendel may be constructed with no data, in which case the minimum values are used for all three values (1, 16, and 10, respectively). A grendel has the following public member functions: int CPUs() - returns the number of computers in the cluster. int MB() - returns the MB of memory in the cluster. int MIPS() - returns the MIPS of the cluster. Also, two grendels may be added with the + operator, resulting in a grendel representing the cluster you get when you combine the two operands. (See above for what the statistics for the combined cluster should be). For example, grendel x, // 1 CPU, 16 MB, 10 MIPS y(2, 128, 30), z(1, 32, 50); x = x + y; // 3 CPUs, 144-1=143 MB, 40-5=35 MIPS x = x + z; // 4 CPUs, 175-1=174 MB, 85-5=80 MIPS cout << x.CPUs() << " CPUs: " << x.MB() << " MBs and " << x.MIPS() << " MIPS\n"; would output Appendix A - Sample Problems Page 141

Page 144: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 4 CPUs: 174 MBs and 80 MIPS B. A crucial concern with running a multiple-processor capable program on a Grendel cluster is the amount of memory available on the smallest computer in the cluster. This would limit the size of the program that could distributed among the various processors. Derive a class from grendel, named xgrendel, which works like a grendel but also keeps track of the MB of the smallest machine in the cluster. When instantiated, a xgrendel is supplied either (1) nothing, in which case the minimum values (as described in the grendel specs) are used, with the MB for the smallest machine being the same as the MB (since there is only one machine), or (2) four ints (number of CPUs, total MB, smallest machine's MB, and MIPS, in that order, where the smallest machine's MB must be between 15 and the total MB or else 15 will be used; the other fields follow the same rules as with a grendel), or (3) a grendel, in which case the smallest machine's MB will have to be estimated: divide the total MB by the number of machines. A xgrendel has the additional public member function: int minMB() - returns the MB on the smallest machine in the cluster. As well, when two xgrendels are added, using +, the result is a xgrendel which is like the sum of two grendels with the additional stipulation that its minMB is the lesser of minMBs of the two operands, except in the following circumstance: when an operand has the lesser minMB and also has just one CPU, then the result's minMB will be that operand's minMB minus 1 MB (to account for the buffer space that gets used when computers are added to a cluster). For example, xgrendel x(1, 32, 32, 30), y(2, 128, 47, 50); xgrendel z = x + y; // 3 CPUs, 159 MB, 32-1=31 minMB, 75 MIPS xgrendel w = z + y; // 5 CPUs, 286 MB, 31 minMB, 120 MIPS cout << w.CPUs() << " CPUs: " << w.MB() << '(' << w.minMB() << ") MB and " << w.MIPS() << " MIPS\n"; displays 5 CPUs: 286(31) MB and 120 MIPS Appendix A - Sample Problems Page 142

Page 145: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 46. Walkthrough. #include <iostream> using namespace std; class foo { int n; public: foo() {cout << "A one\n"; n = 1;} foo(int num) {cout << "A " << (n = num) << '\n';} ~foo() { cout << "(Lawrence Welk imitation?)\n";} int tick() {cout << "ana " << ++n << '\n'; return n;} friend void tock(foo &a) { cout << a.n - 1 << ' ' << a.n << '\n'; } }; int main() { foo x, *p; x.tick(); p = new foo(x.tick() + 1); p->tick(); tock(*p); delete p; return 0; } 47. Write the code to implement a class called a "display". When a display is created, the width (in characters) of the display may be specified, though a default value of 80 should be used when it is not. The display has three publicly callable member functions: void centre(char *s) which displays the null terminated string s on a line of the standard output device, centred within the width of the display. void left(char *s, int indent) which displays the null terminated string s on a line of the standard output device, indented indent spaces to the right. void right(char *s) which displays the null terminated string s on a line of the standard output device, with enough leading spaces added so that the last character of the string is displayed in the last column, according to the width of the display. Bonus: In all of the above cases, if the string is too long to fit on a line (because it is longer than the display's width), only display as much as will fit. For example, |would output: display x(10); | x.left("1234567890", 0); |1234567890 x.centre("Hi"); | Hi x.left("test",2); | test x.left("1, 2, 3", 2); | 1, 2, 3 x.right("Bye!"); | Bye! Appendix A - Sample Problems Page 143

Page 146: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 48. Write the code to implement an object called a "SafeArray", which is used to store a bunch of ints, accessible by position. When a SafeArray is created, the number of integers that can be stored in the SafeArray is passed, and all the elements of the Safearray are initialized to 0. (Note: you will have to use dynamic memory allocation to grab enough space; for simplicity, you may assume that you will have enough memory available). When a SafeArray is destroyed, any memory allocated to it is freed. There is a public member function, int& elem(int position) which returns the corresponding element of the SafeArray, if "position" is at least 0 and is less than the number of ints in the SafeArray. If "position" is out of range, elem() returns a private "junk" int, so that invalid use of elem() will not cause a mysterious crash. The following program uses a SafeArray, as an example to help you understand the specs: main() { SafeArray x(15); // make array of 15 ints x.elem(6) = 77; // puts 77 into position 6 x.elem(-14) = 88; // puts 88 somewhere harmless // this loop outputs 0 0 0 0 0 0 77 0 0 0 0 0 0 0 0 for (int i = 0; i < 15; i++) cout << x.elem(i) << ' '; return 0; } Appendix A - Sample Problems Page 144

Page 147: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 49. A. Walkthrough. #include <iostream> using namespace std; class tig { char* data; int sz; int pos; public: tig(int n) { if (n > 80) n = 80; cout << "getting " << n << " bytes\n"; data = new char[sz=n]; pos = 0;} tig() { cout << "making default tig\n"; data = new char[sz=7]; pos = 0;} ~tig() { delete [] data; cout << "returning " << sz << " bytes\n"; } void store(char c) { if (pos < sz) data[pos++] = c; } void store(char *s, int p) { if (p != -1) pos = p; for (int i = 0; s[i]; i++) if (i%2 == 0) store(s[i]); } void store(char *s) { store(s, -1); } void read(char *s) { int i; for (i = 0; i < pos; i++) s[i] = data[i]; s[i] = '\0'; } }; main() { tig x(8); x.store("Two roofs to fix"); tig y; y.store("Obnoxious leaks..."); y.store("the rain", 2); y.store("ah", 3); y.store("oh no!", 6); x.store("not on my head! Now I'm soaked!", 4); char buffer1[9], buffer2[9]; x.read(buffer1); y.read(buffer2); cout << buffer1 << buffer2 << '\n'; return 0; } Appendix A - Sample Problems Page 145

Page 148: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition B.~Write the code to create an object called a tigio, derived from a tig (see question 49A). A tigio is exactly like a tig, except that it offers the following additional capabilities: - a tigio can be the recipient of << style output of characters and character strings. For example, the following code works according to the comments provided: tigio x(15); x << "Hi there"; // same effect as x.store("Hi there"); x << 's' << "ummer"; // same effect as x.store('s'); // and then x.store("ummer"); - a tigio can deliver >> style input to characters and character strings, so that the following code (continued from the example above) works in keeping with its comments: char s[16], c; x >> s; // same effect as x.read(s); x >> c; // sets c to value in the first position of // x.data unless x is empty in which case c // is not touched. - a tigio can be sent to an <iostream> ostream (such as cout) using the << operator, in which case all characters in "data" up to, but not including, position "pos" are sent to the ostream. For example, cout << "x contains " << x << '\n'; would work as expected. Hint: to do the extraction (>>) from a tigio to a character, and to do the insertion (<<) of a tigio into an ostream, you will have to get the data from the tigio. Unfortunately, "data" and "pos" are private to a tig, so you will have to use "read" to get the data. Fortunately, the tig constructors ensure that no tig data array will be bigger than 80 bytes. Appendix A - Sample Problems Page 146

Page 149: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 50. Walkthrough. #include <iostream> using namespace std; class a { char ach; public: a(char c) { ach = c - 1; } ~a(); // defined below virtual void out(ostream &os) { if ('m' < ach) os << ach << char(ach+7) << char(ach+6) << ' '; else os << ach << ach << ach; } }; class b: public a { char bach; public: b(char c1, char c2) : a(c1) { bach = c2-1; } void out(ostream &os) { a::out(os); os << ' ' << bach << char(bach + 11); } }; ostream &operator<<(ostream &os, a &x) { x.out(os); return os; } a::~a() { cout << *this; // calls above operator } int main() { b var1('n', 'e'); a var2('o'); cout << "Homer says: " << var1 << '\n'; return 0; } Appendix A - Sample Problems Page 147

Page 150: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 51. Write the code to implement a class, called a distribution, which stores the distribution of a bunch of integers that will be sent to it. When a distribution is created, the lower and upper limits for the range are specified. A program may use the << operator to send integers to the distribution (the left argument of << is a distribution and the right argument is an integer). A program may also send a distribution to an ostream (the left argument of << is an ostream and the right argument is a distribution), in which case, for each number in the distribution's range a count of how many times that number has been sent to the distribution is shown on the ostream (unless the value shown would be zero, in which case nothing is shown on the ostream for this number). Also, a count of how many times any number outside the distribution's range has been sent to it is shown on the ostream, unless that count is zero. For example, the following program shows a typical use of a distribution int main() { // create a distribution of numbers from 5 to 10 distribution x(5, 10); // send a bunch of numbers to the distribution x << 6 << 9 << 2 << 7 << 9 << 15; x << 6 << 6 << 17 << -2 << 6; // display a distribution of those numbers cout << x; return 0; } and would output 6: 4 times 7: 1 times 9: 2 times error: 4 times Although it might be appropriate to do so, you do not need to worry about making a copy constructor or the assignment (=) operator. Hint: You'll need to write a constructor, a destructor, a member << operator that is passed an int, and a << operator for an ostream that is passed a distribution. In the constructor, which is passed two ints, dynamically allocate an array big enough to hold one counter for each number in the range. (In our example, this would be an array of 6 counters, one for each of the numbers 5, 6, 7, 8, 9, and 10). Each counter should be initialized to zero. There Appendix A - Sample Problems Page 148

Page 151: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition should also be a counter for the errors which is also initialized to zero. The destructor should deallocate this memory. In the member << operator, increment the appropriate counter, based on the integer value sent. In the ostream << operator, "display" a line for any counter that is non-zero. You'll need to figure out for yourself what private variables you'll need. Appendix A - Sample Problems Page 149

Page 152: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition 52. A. Create a class called a "formatter", which is designed to display, on the standard output device, any text that is given to it. The text is displayed with no more than a specified number of characters per line (default 50), wrapping around to the next line at the start of any word that would otherwise cause the output line to be too long. A "word", for the purpose of this question, is defined to be any string that is given to the formatter at one time. A formatter has the following publicly available members: void width(int n) - sets the width for subsequent output lines to n. If more than n characters have already been output on the current line, a newline is output. void display(char s[]) - outputs the string s, if it will fit on the current line. If it will not fit, a newline is output, thus starting another line, and then the string is output (unless the string is longer than the maximum width of a line, in which case only as many characters as will fit are displayed and the rest are ignored). After outputting the string, if the current line is not already full, a space is output. Note that this function does not output a newline after outputting the string. Also note that you may assume that only normal printable characters will be part of the string, and not oddball characters such as tabs or newlines. Hint: keep track of how many characters have been output on the current line, so that future calls to display() can accurately determine how much room is left on the current line. void newline() - outputs a newline, thus beginning another line of output. Also, when a formatter is destroyed, a newline is output, unless no characters have been output on the current line, in which case nothing happens. For example, the following program: int main() { formatter x; x.display("12345678901234567890"); x.width(15); x.display("Now"); x.display("we"); x.display("finally"); x.display("understand"); Appendix A - Sample Problems Page 150

Page 153: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition x.display("what"); x.display("the"); x.display("heck"); x.display("is"); x.display("happening."); x.newline(); x.display("It"); x.display("has"); x.display("only"); x.display("taken"); x.display("the"); x.display("whole"); x.display("semester!"); return 0; } outputs (beginning at the left edge of the screen): 12345678901234567890 Now we finally understand what the heck is happening. It has only taken the whole semester! B. Derive a class from a formatter, called a wrapper, that can be sent character string data using the << operator. (A wrapper appears as the left argument to <<, and a character string appears on the right side). The << operator takes each "word" from the string, and displays it using the display() function described in the previous question. For the purpose of this question, a "word" is defined to be any series of non-whitespace characters. Any tab or space characters (which simply separate one word from the next) in the string are skipped. Whenever a newline character is encountered, the newline() function is called to start a new line. The << operator returns its left argument, so that << operations can be chained. For example, the program: Appendix A - Sample Problems Page 151

Page 154: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition int main() { wrapper x; x << "12345678901234567890"; x.width(15); x << "Now we finally understand"; x << "what the heck is happening.\nIt"; x << "has" << "only taken" << "the"; x << "whole semester!"; return 0; } produces the same output as the program in the part A. For simplicity, you may assume that no word will ever be longer than 255 characters (so you can make a buffer array 256 bytes big to hold the next word to be displayed). A few paltry bonus marks will be given if instead you dynamically allocate an array big enough to hold the largest word, as long as you do it right (and remember to properly deallocate the memory). Appendix A - Sample Problems Page 152

Page 155: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition APPENDIX B. Operator Precedence Precedence Operator Grouping ---------- -------- -------- 1 highest :: scope resolution left-to-right 2 [] array index left-to-right 2 . member left-to-right 2 -> member left-to-right 2 type() construction left-to-right 2 ++ -- post-in/decrement left-to-right 3 ++ -- pre-in/decrement right-to-left 3 + (unary) identity right-to-left 3 - (unary) negation right-to-left 3 & (unary) address right-to-left 3 * (unary) pointer resolution right-to-left 3 ! logical not right-to-left 3 new (memory allocation) right-to-left 3 delete (memory deallocation) right-to-left 3 (type) cast right-to-left 4 * (binary) multiplication left-to-right 4 / division left-to-right 4 % remainder left-to-right 5 + (binary) addition left-to-right 5 - (binary) subtraction left-to-right 6 < less than left-to-right 6 <= less than or equal to left-to-right 6 > greater than left-to-right 6 >= greater than or equal to left-to-right 7 == equality left-to-right 7 != inequality left-to-right 8 && logical and left-to-right 9 || logical or left-to-right 10 ?: conditional right-to-left 11 lowest = assignment right-to-left 11 +=, -=, *=, /=, %= right-to-left Operators with the same precedence level number have the same precedence regardless of their order in this table. "Grouping" refers to the direction of precedence for multiple consecutive operators of the same level. Appendix B - Operator Precedence Page 153

Page 156: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition Appendix B - Operator Precedence Page 154

Page 157: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition APPENDIX C. Reserved Words The following C++ keywords should be avoided when creating identifier names: asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit export extern false float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch Appendix C - Reserved Words Page 155

Page 158: Foundations of Object Oriented Programming Using C++evan.weaver/oopfoundations.pdf · 2014-04-16 · Foundations of Object Oriented Programming Using C++ . by . Evan Weaver . School

Foundations of OOP using C++ November 2003 Edition template this throw true try typedef typeid typename union unsigned using virtual void volatile while Appendix C - Reserved Words Page 156