You are on page 1of 161

C++ notes

Roger Mitton's preface: In the first year that we ran the C++ course, one of the first-year part-time students, Stephen Connolly, put all his notes on the web for the benefit of his fellow students. He has now kindly provided them to me. His notes provide the basis for what is presented here, though I have made corrections, additions and changes to reflect developments of the course. Full-time students do the course at double speed oddnumbered "weeks" on Mondays, even-numbered "weeks" on Wednesdays. As well as the notes for the C++ course, there is some recommended pre-course reading. The first three items are the introductory material on which applicants take a test at the admissions interview. Students are assumed to be familiar with this by the start of the MSc/PGDip course. The fourth is an extra item of optional pre-course reading on C++. The other two are for students whose maths is rusty. For other C++ course materials (assignments, lab worksheets etc), go to the Module outlines. Stephen Connolly's preface: Since there aren't any handouts for the course and we're all likely to miss one or two lectures, here's a stab at providing absentees with what they've missed. No responsibility is accepted for eventual errors and omissions (though I've tried to be as accurate as I can). Most of the sample code has been tested for accuracy under CCN or g++. Clicking on some links will bring the code up in a separate browser window so you can copy and paste it into a text editor and tweak it yourself. If nothing happens, it may be that the window with the code in it is behind the current browser window. I can't fix this bug according to the books it ought to work but it looks like the browsers I've tested it with don't support JavaScript's Focus() method properly. Pre-course reading Pre-course introduction to C++, 1 (1, 2 and 3 are required pre-interview reading) Simple input/output with integers, outputting words and ends of lines, assignment and initialization Pre-course introduction to C++, 2 Arithmetic expressions, identifiers and comments, string variables, conditionals, two-way branches, ASCII Pre-course introduction to C++, 3 Loops, booleans, #include, using namespace, main Pre-course introduction to C++, 4 (optional pre-course reading) Functions, procedures, return types, parameter passing A lesson on octal, hex, and binary A refresher on basic arithmetic C++ course notes Week 1 Program structure, #includes, variables, assignments, input and output, expressions, operator precedence and associativity, type conversion, strings Week 2 Use of objects, member functions. Conditionals, booleans, order of evaluation of operands, lazy evaluation, conditional operator, the switch statement Week 3 Loops - while, for, and do, Handling filestreams for input and output, stringstreams Week 4 Procedures and functions, parameter passing and return values, error handling and assert, nested loops Week 5 More on functions, prototypes, value and reference parameters, side-effects, scope and lifetimes (statics, globals and extern), the system stack

Week 6 Vectors, strings as vectors of char Week 7 Introduction to classes: a simple Date class Week 8 More classes - Stack and Queue, static data members, representation of integers, static_cast Week 9 Dynamic data structures - pointers and linked lists, heap storage Week 10 More on linked lists: inserting and deleting nodes. Stack and Queue as singly-linked lists Week 11 Implementing a Deque class with singly- and doubly-linked lists; a first look at binary trees Week 12 A sample halfway test Week 13 Two-dimensional vectors, typedef, arrays and pointers, C-strings, character-level I/O, precision of doubles Week 14 const data members, overloading the output operator with a non-member function, a Safevec class Week 15 Recursion, and binary trees again Week 16 More recursion: building a binary search tree, the Towers of Hanoi, recursion in language Week 17 Objects containing pointers: copy constructor, overloaded assignment operator, destructor Week 18 Inheritance and polymorphism Week 19 Exception handling Week 20 Function overload resolution Week 21 Templates, header files and preprocessor commands, the STL Week 22 Sample end-of-term tests

Notes on R. Mitton's lectures by S.P. Connolly, extensively edited and extended by R. Mitton, 2000 -

A short introduction to computer programming, using C++


Roger Mitton, Birkbeck, University of London

Chapter 1
To get a computer to do something, you have to tell it what you want it to do. You give it a series of instructions in the form of a program. You write a program in a programming language. Many programming languages have been devised; well known ones include Fortran, BASIC, Cobol, Algol, Lisp, Ada, C++ and Java. Programming languages vary a lot, but an instruction in a typical programming language might contain some English words (such as while or return), perhaps a mathematical expression (such as x + 2) and some punctuation marks used in a special way. The following three lines are in BASIC, Cobol and C++ respectively:
FOR I% = 1 TO 100 STEP 5 DO PERFORM RECORD-TRANSACTION UNTIL ALL-DONE = "T" if (x > 100) cout << "Big"; else cout << "Small";

Programs can vary in length from a few lines to thousands of lines. Here is a complete, short program written in C++:
// Given a series of words separated by spaces, // this program finds the length of the longest word. #include <iostream> #include <string> using namespace std; int main() { string s; int max = 0; while (cin >> s) if (s.length() > max) max = s.length(); if (max == 0) cout << "There were no words." << endl; else { cout << "The longest word was "; cout << max << " characters long." << endl; } }

When you write a program, you have to be very careful to keep to the syntax, ie to the rules of the language. For example, the above program would behave incorrectly if we wrote if (max = 0) instead of if (max == 0) or if we put a semi-colon immediately after while (cin >> s). The computer would refuse to accept it if any of the semi-colons were missing or if any of the parentheses or curly braces were missing, or if we wrote min instead of main or even Main instead of main.

Input and output


To get the computer to take some input and to store it in its memory, we write, for example:
cin >> num; cin (pronounced "see-in"), is a word which has a special meaning to the computer. The combination of cin with the symbol >> means "Take some input and put it into the memory." num, by contrast, is a word that I (the programmer) have chosen. I could have used number or widget or foodle or just n or almost

any word I wanted. (There are some restrictions which I will deal with in the next chapter.) The semi-

colon signals the end of this instruction. Computers can take in all sorts of things as input numbers, letters, words, records and so on but, to begin with, we will write programs that only handle numbers (whole numbers like 0, 2, 15, 10025, 1, 2465). We'll also assume that the computer is taking its input from the keyboard, ie when the program is executed, you key in one or more numbers at the keyboard and these are the numbers that it puts into its memory. You can imagine the memory of the computer as consisting of lots of little boxes. Programmers can reserve some of these boxes for use by their programs and they refer to these boxes by giving them names. cin >> num; means "Take a number and put it in the box called num." When the program runs and this instruction gets executed, the computer will take a number which you type at the keyboard (8 or 25 or 9999 or whatever you like) and will put it into the box which has the (temporary) name of num. Each of these boxes can hold only one number at a time. If a box is holding, say, 26, and you put a 44 into it, the 44 replaces the 26. In computer parlance these boxes are called variables because the number inside the box can vary; you can put a 10 in it to start with and later change it to a 20 and change it again to 555 and so on as often as you want. In C++ you have to tell the computer that you want to use a variable with a certain name before you use it. You can't just pitch in with cin >> num; without telling it what num is. You also have to tell it what type of variable it is, ie what sort of thing you are going to put into it. In this case we are going to put whole numbers into it. Whole numbers, or integers as they are called in programming, are known in C++ as int. To tell the computer that we want to use a variable of type int called num, we write
int num;

If we wanted more than one variable, we could use two lines:


int num; int total;

or we could declare them both on one line, separated by a comma


int num, total;

Your program can have as many variables as you want. In C++ you don't have to declare all your variables at the start of the program, as is the case in some other languages. You can declare them in the middle of a program, but you mustn't try to use a variable before you've declared it. If you want the computer to display (on the screen) the contents of one of its boxes, you use cout << (pronounced "see-out") followed by the name of the box. For example
cout << num;

If the contents of the box called num happened to be 876 when the computer came to execute cout << num; then the number 876 would appear on the screen. Note that the arrows go >> with cin >> num; and << with cout << num; Think of the arrows in cin >> num; as showing you that something is going from the input into num, whereas in cout << num; something is being taken out of num and sent to the output. Arithmetic expressions such as num + 5 can also appear in a cout line. For example, if num had the value 7, then cout << num + 5; would output 12. To put these instructions into a valid C++ program, we need some more lines. I'll explain what they mean in a later chapter, but for now just copy them down (carefully). We have to begin the program with:
#include <iostream> using namespace std; int main() {

and we finish it with a closing curly brace:


}

So we can now write a program in C++ (not a very exciting program, but it's a start):
#include <iostream> using namespace std; int main() { int num; cin >> num; cout << num; }

This program takes a number as input from the keyboard and displays it on the screen. It is customary to lay out programs like this, but putting each part on a line of its own is actually for the benefit of human readers, not for the computer. Except for the part beginning with a "#", which must be on its own line, this entire program could be on one line, thus:
#include <iostream> using namespace std; int main() { int num; cin >> num; cout << num; }

I am not suggesting that you use this style. On the contrary, you should use the style of the previous version. I am just making the point that the layout of a C++ program on the page separate lines for separate parts, blank lines, indentation of certain lines is for the benefit of the human reader. With a few exceptions, it is of no significance to the computer.

Compiling and running a program


You can learn the rudiments of C++ from these notes just by doing the exercises with pencil and paper. It is not essential to run your programs on a computer. However, if you have a computer and are wondering how you run the programs, you will need to know the following, and, even if you don't have a computer, it will help if you have some idea of how it's done. First of all you type your program into the computer using a text editor, but before you can run the program you have to compile it. This means that you pass it through a piece of software called a compiler. The compiler checks whether your program is acceptable according to the syntax of C++. If it isn't, the compiler issues one or more error messages telling you, in a more or less unhelpful fashion, what it objects to in your program and where the problem lies. You try to see what the problem is, correct it and try again. You keep doing this until the program compiles successfully. You now have an executable version of your program, ie your program has been translated into the internal machine instructions of the computer and the computer can run your program. Now you issue a command (or click on an icon) and the computer executes your program. If you are lucky, your program does what it is supposed to do first time. Often it doesn't. You look at what your program is doing, look again at your program and try to see why it is not doing what you intended. You correct the program, recompile and run it again. You might have to do this many times before the program behaves in the way you wanted. As I said earlier, you can study this introduction without running your programs on a computer. However, it's possible that you have a PC with a C++ compiler and will try to run some of the programs given in these notes. If so, do not be dismayed if your compiler objects to them. The ISO standard for C++ was only established in 1998 and there are many pre-standard compilers in circulation. The most likely problems are: your compiler might prefer #include <iostream.h> to #include <iostream> it may well insist on you using &&, || and ! instead of and, or and not (I explain this in a later chapter) it might not accept the string data type at all. If you have a PC but you don't have a compiler, I attach a few notes telling you how you can obtain one.

Outputting words and ends of lines


Let's suppose that you managed to compile your program in one way or another and that you then ran it. Your running of the above program would produce something like this on the screen:

1234 1234

The first line is the result of you keying in a number. The system "echoes" the keystrokes to the screen, in the usual way. When you hit RETURN, the computer executes the cin line, ie it reads the number. Then it executes the cout line and the number appears on the screen again. We can also get the computer to display words by putting them in quotes after the cout <<, for example:
cout << "Hello";

We can use this to improve the above program:


#include <iostream> using namespace std; int main() { int num; cout << "Please key in a number: "; cin >> num; cout << "The number was "; cout << num; }

A run of this program might appear on the screen thus:


Please key in a number: 9876 The number was 9876

Note the spaces in the cout lines after number: and was. This is so that what appears on the screen is number: 9876 and was 9876 rather than number:9876 and was9876. It's possible to output more than one item with a single cout line. For example, we could combine the last two lines of the program into one:
cout << "The number was " << num;

and the output would be exactly the same. Let's suppose that we now added three lines to the end of our program, thus:
#include <iostream> using namespace std; int main() { int num; cout << "Please key in a number: "; cin >> num; cout << "The number was " << num; cout << "Now please key in another: "; cin >> num; cout << "And this one was " << num; }

The screen would look something like this:


Please key in a number: 9876 The number was 9876Now please key in another: 543 And this one was 543

Which is probably not what we wanted. If we want a new line after the output of the first number, we have to include this in the cout line. We do it by putting an endl into the line, thus:
cout << "The number was " << num << endl;

Now we would get:


Please key in a number: 9876 The number was 9876 Now please key in another: 543 And this one was 543

We can input two or more numbers with a single cin. The numbers can be on the same line, separated by one or more spaces, or on separate lines. The following lines:

cout << "Please key in two numbers: "; int x, y; cin >> x >> y;

would read 5 into x and 6 into y in any of the following ways:


Please key in two numbers: 5 6

or
Please key in two numbers: 5 6

or
Please key in two numbers: 5 6

Exercise 1A Now pause and see if you can write (a) a line of C++ which would output a blank line. (b) a single line of C++ which would output
Hickory Dickory Dock

(c) a program which reads in two integers on one line and displays them in reverse order. If the input was 3 99, the output should be 99 3. To check your answers, click on Answers to the exercises.

Assignment and initialization


There is another way to get a number into a box apart from using cin >>. We can write, for instance:
num = 22;

This has the effect of putting the number 22 into the num box. Whatever was in num before is obliterated; the new number replaces the old one. In programming, this is called assignment. We say that the value 22 is assigned to the variable num, or that the variable num takes the value 22. The "=" symbol is the assignment operator in C++. We are not testing whether num has the value 22 or not, nor are we stating that num has the value 22; we are giving the value 22 to num. If we want, we can have arithmetic expressions on the right-hand side of the "=", for example:
num = count + 10;

This instruction means, "Take whatever number is in count, add 10 to it and put the result into num." An assignment instruction such as this:
num = num + 5;

looks a little strange at first but makes perfectly good sense. Let's suppose the current value of num (the contents of the box) is 14. The instruction says, "Take the value of num (14), add 5 to it (19) and put the result into num." So the effect is to put 19 into num in place of the earlier 14. The "=" operator is also used to initialize variables. When the computer allocates a portion of memory to store one of your variables, it does not clear it for you; the variable holds whatever value this portion of memory happened to have the last time it was used. Its value is said to be undefined. Using undefined values is a common cause of program bugs. Suppose a program uses the variable num

without giving it an initial value and suppose that, on the computer the programmer is using, the initial value in num happens to be zero and that, by happy chance, zero is just what the programmer wants it to be. The program seems to work fine. Then the program is compiled and run on a different computer. On this second computer, the initial value of num does not happen to be zero. The program, which has worked OK on the first computer, does not work on the second one. To prevent yourself from using undefined values, you can give a variable an initial value when you declare it. If you wanted num to begin with the value zero, you should declare it thus:
int num = 0;

This is very like assignment since we are giving a value to num but this is a special case where num did not have any defined value before, so it is known as initialization. Finally a word about terminology. I have used the word "instruction" to refer to lines such as cout << num; and num = 5; It seems a natural word to use since we are giving the computer instructions. But the correct word is actually "statement". cout << num; is an output statement, and num = 5; is an assignment statement. The lines in which we tell the computer about the variables we intend to use, such as int num; or int num = 0; are called variable definitions. They are also referred to as variable declarations. When you learn more about C++ you will find that you can have declarations which are not definitions, but the ones in these introductory notes are both definitions and declarations. Exercise 1B Now see if you can write a program in C++ that takes two numbers from the keyboard and outputs the sum, eg if you keyed in 6 and 8 it would reply with 14. A run of the program should look like this:
Please key in a number: 6 And now key in another: 8 The sum is 14

To check your answers, click on Answers to the exercises.

A short introduction to computer programming, using C++


Roger Mitton, Birkbeck, University of London

Chapter 2 Arithmetic expressions


C++ uses the following arithmetic operators: + addition subtraction * multiplication / division

% modulo The last one is perhaps unfamiliar. The result of x % y ("x mod y") is the remainder that you get after dividing the integer x by the integer y. For example, 13 % 5 is 3; 18 % 4 is 2; 21 % 7 is 0, and 4 % 6 is 4 (6 into 4 won't go, remainder 4). num = 20 % 7; would assign the value 6 to num. How does the computer evaluate an expression if there is more than one operator in it? For example, given 2 + 3 * 4, does it do the addition first, thus getting 5 * 4, which comes to 20, or does it do the multiplication first, thus getting 2 + 12, which comes to 14? C++, in common with other programming languages and with mathematical convention in general, gives precedence to *, / and % over + and . This means that, in the example, it does the multiplication first and gets 14. If the arithmetic operators are at the same level of precedence, it takes them left to right. 10 5 2 comes to 3, not 7. You can always override the order of precedence by putting brackets into the expression; (2 + 3) * 4 comes to 20, and 10 (5 2) comes to 7. Some words of warning are needed about division. If the operands are both of type int, the result of a division is also of type int. For example, the result of 7 / 2 is 3, not 3.5. If num, which is an int, had the value 7, then num / 2 would be 3, and 18 / num would be 2. Computers are perfectly capable of handling floating-point numbers such as 3.5, but the type of division carried out depends on the type of the operands. In order to get a result of 3.5, it would be necessary for at least one of the operands to be a floating-point number, such as 7 / 2.0. But if they are both of type int, you get integer division with an integer result. I'm not going to say much about floating-point numbers in this introduction. A computer would get into difficulty if it tried to divide by zero. Consequently, the system makes sure that it never does. If a program tries to get the computer to divide by zero, the program is unceremoniously terminated, usually with an error message on the screen. Exercise 2A Write down the output of this program:
#include <iostream> using namespace std; int main() { int num = 5; cout << num << endl; num = num + 2; cout << num << endl; num = num / 3 * 6; cout << num << endl; cout << 7 + 15 % 4 << endl; num = 24 / 3 / 4; cout << num << endl; num = 24 / (num / 4); cout << num << endl; }

To check your answers, click on Answers to the exercises.

Identifiers and comments


I said earlier that you could use more or less any names for your variables. I now need to qualify that. The names that the programmer invents are called identifiers. The rules for forming identifiers are that the first character can be a letter (upper or lower case) and subsequent characters can be letters or digits or underscores. (Actually the first character can be an underscore but identifiers beginning with an underscore are often used by system programs and are best avoided.) Other characters are not allowed. C++ is case-sensitive, so Num, for example, is a different identifier from num. The only other restriction is that you cannot use any of the language's keywords as an identifier. You couldn't use int as the name of a variable, for example. There are 74 keywords but most of them are words that you are unlikely to choose, such as reinterpret_cast or xor_eq. Ones that you might accidentally hit upon are break, case, catch, class, const, continue, delete, double, export, float, friend, long, new, return, short, switch, this, throw, try and union. You should also avoid using words which, though not technically keywords, have special significance in the language, such as cin, cout and string. Programmers often use very short names for variables, such as i, n or x for integers. There is no harm in this if the variable is used to do an obvious job, such as counting the number of times the program goes round a loop and its purpose is immediately clear from the context. If, however, its function is not so obvious, it should be given a name that gives a clue as to the role it plays in the program. If a variable is holding the total of a series of integers and another is holding the largest of a series of integers, for example, call them total and max rather than x and y. The aim in programming is to write programs that are "self-documenting", meaning that a (human) reader can understand them without having to read any supplementary documentation. A good choice of identifiers helps to make a program self-documenting. Comments provide another way to help the human reader to understand a program. Anything on a line after "//" is ignored by the compiler, so you can use this to annotate your program. You might summarise what a chunk of program is doing:
// sorts numbers into ascending order

or explain the purpose of an obscure bit:


x = x * 100 / y; // x as percent of y

Comments should be few and helpful. Do not clutter your programs with statements of the obvious such as:
num = num + 1; // add 1 to num

Judicious use of comments can add greatly to a program's readability, but they are second best to selfdocumentation. Their weakness is that it is all too easy for a programmer to modify the program but forget to make any corresponding modification to the comments, so the comments no longer quite go with the program. At worst, the comments can become so out-of-date as to be positively misleading. Exercise 2B Say for each of the following whether it is a valid identifier in C++ and, if not, why not:
BBC, C++, y2k, Y2K, old, new, 3GL, a.out, remove, first-choice, 2nd_choice, third_choice, constant, UNION

To check your answers, click on Answers to the exercises.

String variables
As well as variables of type int we can have variables of type string. To do this we have to include

another of those lines at the start of the program whose purpose I have yet to explain:
#include <string>

We could declare a string variable as follows:


string s;

and also initialize it if we wanted to:


string s = "Wallace";

We can change the value of a string with assignment:


s = "Feathers McGraw";

or, if we had two strings s and t:


s = t; // (s and t do not need to be the same length)

We can also use cin >> to give a new value to a string:


cin >> s;

When this statement is executed, the computer will skip any leading spaces or tabs or blank lines until it comes to a non-space character. This will be the first character of the string s. Then it picks up characters and adds them to s until it comes to a space, tab or end-of-line, at which point it stops. So, if the input was:
Silverdale

the value in s would be "Silverdale" (without the leading spaces). If the input was:
Warton Crag

the value in s would be "Warton", since it would stop at the space. Another useful input function with strings is getline, as in
getline(cin, s);

This takes the whole of the input line and puts it into s, spaces and tabs included. Executing this statement with the input line
Silverdale, Warton Crag
(with the line ending after "Crag")

would put the whole line into s, including all the spaces, from the leftmost of the leading spaces up to the "g" of "Crag". The "+" operator has special significance with strings; it is the concatenation operator. For example, if s had the value "Wallace" and t had the value "Grommit", then
s = s + " and " + t;

would give s the value "Wallace and Grommit". If you want to know how long a string is, you can find out with the length function. If s is the string, s.length() (don't forget the brackets) is the length. You could say, for example:
string s; cin >> s; int len = s.length();

And you can obtain a substring of a string with the substr function. For example if s has the value "Silverdale", then s.substr(0,6) will give you the first six letters, ie the string "Silver". The first number in brackets after the substr says where you want the substring to begin, and the second number says how long you want it to be. Note that the initial character of the string is at position 0, not position 1. If you leave out the second number, you get the rest of the string. For example, s.substr(6) would give

you "dale", ie the tail end of the string beginning at character 6 ('d' is character 6, not 7, because the first one is character 0). You can output substrings with cout << or assign them to other strings or combine them with the "+" operator. For example:
string s = "software services"; s = s.substr(0,4) + s.substr(8,1) + s.substr(13); cout << s << endl;

will output "soft ices". Exercise 2C Say what the output of the following program fragment would be:
string s = "artificial reality"; cout << s.substr(11,4) + " " + s.substr(0,3) << endl; cout << s.substr(11).length() << endl;

To check your answers, click on Answers to the exercises.

Conditionals (if statements)


To write anything more than very straightforward programs we need some way of getting the computer to make choices. We do this in C++ with the keyword if. We can write, for example,
if (num == 180) cout << "One hundred and eighty!" << endl;

When the computer executes this, it first sees whether the variable num currently has the value 180 or not. If it does, the computer displays its message; if it doesn't, the computer ignores the cout line and goes on to the next line. Note that the conditional expression (num == 180) has to be in brackets. Note also that, to test whether num has the value 180 or not, we have to write if (num == 180) and not if (num = 180). We have to remember to hit the "=" key twice. This is a serious nuisance in C++, especially for beginners. It comes about because the language uses the "=" operator for a different purpose, namely assignment. num = 180 does not mean, " num is equal to 180." It means, "Give the value 180 to num." You may feel that it is obvious that assignment is not what is intended in if (num = 180), but unfortunately the rules of the language allow an assignment at this point, and that is how the computer will interpret it. The line if (num = 180), with one "=" sign, will actually give the value 180 to num. You have been warned. The following program takes two numbers and displays a message if they happen to be the same:
#include <iostream> using namespace std; int main() { int num1, num2; cout << "Please key in a number: "; cin >> num1; cout << "And another: "; cin >> num2; if (num1 == num2) cout << "They are the same" << endl; }

Conditional expressions the kind that follow an if can be formed using the following operators: == is equal to
!= > <

is not equal to is greater than is less than

>= <=

is greater than or equal to is less than or equal to

When these operators are used with integers, their meaning is obvious, but they can also be used with strings. Here their meaning corresponds to something like alphabetical order. For instance, if (s < t), where s and t are strings means "If s comes before t in alphabetical order." So it would be true if s had the value "Birkbeck" and t had the value "College". All the upper-case letters come before the lowercase, so (s < t) would still be true if s had the value "Zebra" and t had the value "antelope" (uppercase 'Z' comes before lower-case 'a'). But what about strings that contain non-alphabetic characters? Would s come before t if s had the value "#+*" and t had the value "$&!"? To find the answer we have to consult the ASCII table the American Standard Code for Information Interchange. ASCII, pronounced to rhyme with "Askey", defines a particular ordering of all the characters on the keyboard. (There are other orderings in use, notably EBCDIC which is used on IBM mainframe computers, but, since ASCII was adopted by PCs, it has become the general de facto standard.) The ASCII table tells us that the character '#' comes before the character '$', for instance. There is a listing of the printable characters in the ASCII set at the end of this chapter. Some points worth remembering are: The space character comes before all the printable characters. Numerals come in the order you'd expect, from '0' to '9'. Letters come in the order you'd expect, from 'A' to 'Z' and from 'a' to 'z'. Numerals come before upper-case letters and upper-case letters come before lower-case. Exercise 2D Say, for each of the following, whether s < t would be true or false, assuming that s had the value on the left and t had the value on the right: "A" "9" "Zurich" "Abba" "acapulco" "ABBA"

"long_thing_with_a "long_thing_with_a _$" _&" "King's College" "King Kong"

To check your answers, click on Answers to the exercises.


Two-way branches (if .. else) The following program fragment tells students whether they have passed their exam: int exammark; cout << "Please key in your exam mark: "; cin >> exammark if (exammark >= 40) cout << "A satisfactory result" << endl; What happens, in the case of this exam mark program, if a student's mark is < 40? The program does nothing. This kind of if statement is a one-way branch. If the condition is true, we do something; if not, we do nothing. But in this case this seems unsatisfactory. If the exam mark is < 40, we would like it to display "I'm afraid you have failed." We could arrange this by including another test if (exammark < 40) or, better, we could do it by using the keyword else, thus: if (exammark >= 40) cout << "A satisfactory result" << endl; else cout << "I'm afraid you have failed." << endl; The else turns a one-way branch into a two-way branch. If the condition is true, do this; otherwise, do that.

Exercise 2E Write a program that takes two numbers, one representing a husband's salary and the other representing the wife's, and tells them whether or not their combined income makes them due for tax at the higher rate (exceeding 40000). To check your answers, click on

Answers to the exercises.

Let's suppose we want to extend the exam mark program so that candidates who have passed get two lines of output, thus: if (exammark cout << cout << else cout << >= 40) "A satisfactory result" << endl; "You may proceed with your project." << endl; "I'm afraid you have failed." << endl;

Unfortunately the compiler will object to this. It will say that it has encounted an else in an unexpected place. What is the problem? Although the layout of this program suggests that the "satisfactory" line and the "proceed with project" line go together, there is nothing to indicate this to the compiler. As I pointed out earlier, the compiler pretty much ignores the layout. So far as the compiler is concerned, we have a one-way if statement (the kind without an else) which ends at the first endl; Then there is another cout line (proceed with project) which is not part of the if statement; it's just the first line of the rest of the program. And then, unexpectedly, appears an else. We need some way of bracketing together the "satisfactory" line and the "proceed with project" line so as to make it clear to the compiler that both of these lines come under the if. This is how we do it in C++: if (exammark >= 40) { cout << "A satisfactory result" << endl; cout << "You may proceed with your project." << endl; } else cout << "I'm afraid you have failed." << endl; The curly braces have the effect of grouping all the statements inside them into a programming unit called a block. If the exam mark is >= 40, the whole of the block is executed. If the mark is < 40, the computer skips to the else and executes the "I'm afraid" line. You will find that different programmers, and different textbooks, have different ideas about the precise placement of the curly braces. Some would set out the above fragment as: if (exammark cout << cout << } else cout << >= 40) { "A satisfactory result" << endl; "You may proceed with your project." << endl; "I'm afraid you have failed." << endl;

and there are other variations. Personally I like to see the opening and closing curly braces lined up vertically. It helps the reader to see how they match up. Exercise 2F Extend the above program fragment so that all the candidates get two lines of output, the unsuccessful ones getting "I'm afraid you have failed," and "You may reenter next year." To check your answers, click on

Answers to the exercises.

Suppose now that we wanted to give a different message to candidates who had done exceptionally well. Our first thought might be as follows: if (exammark >= 70) { cout << "An exceptional result!" << endl; cout << "We expect a first-class project from you." << endl; } if (exammark >= 40) { cout << "A satisfactory result" << endl; cout << "You may proceed with your project." << endl; } else cout << "I'm afraid you have failed." << endl; But this would not work quite right. It's OK for candidates with marks < 70, but candidates with marks >= 70 would get the following output: An exceptional result

We expect a first-class project from you. A satisfactory result You may proceed with your project. The problem is that if a mark is >= 70, it is also > 40. The first condition is true, so we get the "exceptional" part, but then the second condition is also true, so we get the "satisfactory" part. We want to proceed to the >= 40 test only if the mark is < 70. We need another else: if (exammark >= 70) { cout << "An exceptional result!" << endl; cout << "We expect a first-class project from you." << endl; } else if (exammark >= 40) { cout << "A satisfactory result" << endl; cout << "You may proceed with your project." << endl; } else cout << "I'm afraid you have failed." << endl;

Exercise 2G Write a program which takes two integers as input. If the first is exactly divisible by the second (such as 10 and 5 or 24 and 8, but not 10 and 3 or 24 and 7) it outputs "Yes", otherwise "No", except when the second is zero, in which case it outputs "Cannot divide by zero." Exercise 2H Write a program which takes an integer as its input representing the time using the 24-hour clock. 930 is 9.30 am; 2345 is 11.45 pm. Midnight is zero. The program responds with a suitable greeting for the time of day. If you want to make this a bit harder, make the program respond with a "?" if the time represented by the number is impossible, such as 2400, 5 or 1163. To check your answers, click on The ASCII character set The portion of the ASCII table dealing with printable characters is as follows. The numbers indicate the character's position in the ASCII character set; upper-case 'A', for example, is character number 65. Character number 32 is a space.

Answers to the exercises.

32 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' 40 ( 41 ) 42 * 43 + 44 , 45 46 .

48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 56 8 57 9 58 : 59 ; 60 < 61 = 62 >

64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G 72 H 73 I 74 J 75 K 76 L 77 M 78 N

80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^

96 97 98 99

` a b c

112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w 120 x 121 y 122 z 123 { 124 | 125 } 126 ~

100 d 101 e 102 f 103 g 104 h 105 i 106 j 107 k 108 l 109 m 110 n

47 /

63 ?

79 O

95 _

111 o

A short introduction to computer programming, using C++


Roger Mitton, Birkbeck, University of London

Chapter 3 Loops
How would you write a program to add up a series of numbers? If you knew that there were, say, four numbers, you might write this program:
#include <iostream> using namespace std; int main() { int num1, num2, num3, num4; cout << "Please key in four numbers: "; cin >> num1 >> num2 >> num3 >> num4; int total = num1 + num2 + num3 + num4; cout << total << endl; }

But a similar program to add up 100 numbers would be very long. More seriously, each program would be tailor-made for a particular number of numbers. It would be better if we could write a program to handle any series of numbers. We need a loop. One way to create a loop is to use the keyword while. For example:
#include <iostream> using namespace std; int main() { int num = 0; while (num < 100) { num = num + 5; cout << num << endl; } }

Having initialized the variable num to zero, it checks whether the value of num is less than 100. It is, so it enters the loop. Inside the loop, it adds 5 to the value of num and then outputs this value (so the first thing that appears on the screen is a 5). Then it goes back to the while and checks whether the value of num is less than 100. The current value of num is 5, which is less than 100, so it enters the loop again. It adds 5 to num, so num takes the value 10, and outputs this value. It goes back to the while, checks whether 10 is less than 100 and enters the loop again. It carries on doing this with num getting larger each time round the loop. Eventually num has the value 95. 95 is less than 100 so it enters the loop again, adds 5 to num to make it 100 and outputs this number. Then it goes back to the while and this time sees that num is not less than 100. So it stops looping and goes on to the line beyond the end of the loop. (In the case of this program, there are no more lines, so it finishes.) The output of this program is the numbers 5, 10, 15, 20 and so on up to 95, 100. Note the use of curly braces to mark the start and end of the loop. Each time round the loop it does everything inside the curly braces. When it decides not to execute the loop again, it jumps to the point beyond the closing brace. What would happen if the while line of this program was while (num != 99)? The value of num would eventually reach 95. The computer would decide that 95 was not equal to 99 and would go round the loop again. It would add 5 to num, making 100. It would now decide that 100 was not equal to 99 and would go round the loop again. Next time num would have the value 105, then 110, then 115 and so on. The value of num would never be equal to 99 and the computer would carry on for ever. This would be an example of an infinite loop. Note that the computer makes the test before it enters the loop. What would happen if the while line of

this program was while (num > 0)? num begins with the value zero and the computer would first test whether this value was greater than zero. Zero is not greater than zero, so it would not enter the loop. It would skip straight to the end of the loop and finish, producing no output. It is not essential to indent the lines inside the loop, but it makes the program easier to read and it is a good habit to get into. Exercise 3A Write a program that outputs the squares of all the numbers from 1 to 10, ie the output will be the numbers 1, 4, 9, 16 and so on up to 100. To check your answers, click on Answers to the exercises.

Booleans (true/false expressions)


So far we have just used integer and string variables. But we can have variables of other types and, specifically, we can have boolean variables, which in C++ are variables of type bool. (The word "boolean" was coined in honour of an Irish mathematician of the nineteenth century called George Boole.) A variable of type bool does not hold numbers; it can hold just the values true and false. We might declare and initialize a boolean variable thus:
bool positive = true;

Note that we do not have quote marks around the word true. true, without quote marks, is not a string; it's the name of a boolean value. Contrast it with:
string stringvar = "true";

is a string variable which is being initialized with the four-character string "true". We could assign any other string to stringvar. positive, by contrast, is a boolean variable. We cannot assign strings to positive. It can hold only the values true or false.
stringvar

You have already met boolean expressions. They are also called conditional expressions and they are the sort of expression you have in brackets after if or while. When you evaluate a boolean expression, you get the value true or false as the result. Consider the kind of integer assignment statement with which you are now familiar:
num = count + 5;

The expression on the right-hand side, the count + 5, is an integer expression. That is, when we evaluate it, we get an integer value as the result. And of course an integer value is exactly the right kind of thing to assign to an integer variable. Now consider a similar-looking boolean assignment statement:
positive = num >= 0;

The expression on the right-hand side, the num >= 0, is a boolean expression. That is, when we evaluate it, we get a boolean value (true/false) as the result. And of course a boolean value is exactly the right kind of thing to assign to a boolean variable. You can achieve the same effect by the more long-winded:
if (num >= 0) positive = true; else positive = false;

The variable positive now stores a simple fact about the value of num at this point in the program. (The value of num might subsequently change, of course, but the value of positive will not change with it.) If, later in the program, we wish to test the value of positive, we need only write
if (positive)

You can write if (positive == true) if you prefer, but the == true is redundant. positive itself is either true or false. Once the computer has evaluated positive (established whether it is true or false)

there is nothing more to do. We can also write


if (not positive) // (! positive) on some compilers

which is the same as if (positive == false) If positive is true, then not positive is false, and vice-versa. (If you are using a compiler which hasn't caught up with the current standard, such as g++ or Microsoft Visual C++, you have to use "!" instead of not, ie you have to write if (!positive) instead of if (not positive). These compilers do not understand the word not.) Boolean variables are often called flags. The idea is that, leaving aside subtleties such as half-mast, a flag has basically two states either it's flying or it isn't. So far we have constructed simple boolean expressions using the operators introduced in the last chapter (x == y), (s >= t) and so on now augmented with not. We can make more complex boolean expressions by joining simple ones with and and or. For example, we can express "if x is a non-negative odd number" as if (x >= 0 and x % 2 == 1) We can express "if the name begins with an A or an E" as if (name.substr(0,1) == "A" or name.substr(0,1) == "E"). The rules for evaluating and and or are as follows: and or . left right left right 1 true 2 true 3 false 4 false true false false false true false true false true true false false true true true false true false true false

Taking line 2 as an example, this says that, given that you have two simple boolean expressions joined by and and that the one on the left is true while the one on the right is false, the whole thing is false. If, however, you had the same two simple expressions joined by or, the whole thing would be true. As you can see, and is true only if both sides are true, otherwise it's false; or is false only if both sides are false, otherwise it's true. As with not, you will find that some compilers do not understand the words and and or. For those compilers, you have to use && for and and || for or. Exercise 3B Given that x has the value 5, y has the value 20, and s has the value "Birkbeck", decide whether these expressions are true or false:
(x == 5 and y == 10) (x < 0 or y > 15) (y % x == 0 and s.length() == 8) (s.substr(1,3) == "Bir" or x / y > 0)

To check your answers, click on Answers to the exercises.

Back to loops
Returning now to the problem of adding up a series of numbers, have a look at this program:
#include <iostream> using namespace std; int main() { bool finished = false; int total = 0; while (not finished) { int num; cin >> num; if (cin.fail()) finished = true; else total = total + num; } if (cin.eof()) cout << "Total is " << total << endl;

else cout << "Invalid input" << endl; }

If we want to input a series of numbers, how will the program know when we have put them all in? That is the tricky part, which accounts for the added complexity of this program. The bool variable finished is being used to help us detect when there are no more numbers. It is initialized to false. When the computer detects that there are no more numbers to input, it will be set to true. When finished is true, it means that we have finished reading in the input. (More precisely it means that the input has failed, as I will explain shortly, but the reason for this is usually that there is no more input.) The while loop begins by testing whether finished is true or not. If finished is not true, there is some more input to read and we enter the loop. If finished is true, there are no more numbers to input and we skip to the end of the loop. The variable total is initialized to zero. Each time round the loop, the computer reads a new value into num and adds it to total. total holds the total of all the values input so far. Actually it only adds num to total if the input has been successful. There are two main reasons why the input might fail. The first is that there are no more numbers to input. The user will signal that there are no more numbers by keying in a special character. (On Unix this is a Control-D; on PCs it is usually a Control-Z. If this is gobbledygook to you, just imagine that the user strikes a special key on the keyboard.) The other reason why the input might have failed is that what was entered was not an integer perhaps the user entered a letter or a punctuation mark. If the program is expecting an integer and instead receives something like "abc" or "W" or "**!", the input will fail. We can test whether the input has failed, for whatever reason, with the line if (cin.fail()). If this is true, the input has failed and we set finished to true in order to terminate the loop. When we exit from the loop, we know that the input has failed for one reason or another, but we do not know exactly why. Perhaps there were no more numbers or perhaps the input was invalid. We can test whether we had reached the end of the input with the test if (cin.eof()). eof stands for "end of file". If this is true then we know there were no more numbers to input and we output the total. If it is false, then the input must have failed for some other reason. A real-life program ought not to respond to a user error by aborting with a terse error message, though regrettably many of them do. However, dealing with the problem properly would make this little program more complicated than I want it to be at this stage. You have to take some care in deciding whether a line should go in the loop or outside it. This program, for example, is only slightly different from the one above but it will perform differently:
#include <iostream> using namespace std; int main() { bool finished = false; int total; while (not finished) { total = 0; int num; cin >> num; if (cin.fail()) finished = true; else total = total + num; } if (cin.eof()) cout << "Total is " << total << endl; else cout << "Invalid input" << endl; }

It resets total to zero each time round the loop. So total gets set to zero, has a value added to it, then gets set to zero, has another value added to it, then gets set to zero again, and so on. When the program finishes, total does not hold the total of all the numbers, just the value zero. Here is another variation:
#include <iostream> using namespace std; int main() { bool finished = false;

int total = 0; while (not finished) { int num; cin >> num; if (cin.fail()) finished = true; else { total = total + num; cout << "Total is " << total << endl; } } }

This one has the cout line inside the loop, so it outputs the value of total each time round the loop. If you keyed in the numbers 4, 5, 6, 7 and 8, then, instead of just getting the total (30) as the output, you would get 4, 9, 15, 22 and then 30. Exercise 3C Write a program that reads a series of numbers and then tells you how many numbers you have keyed in. For example, if you keyed in the numbers 5, 10, 50, 22, 945, 12, it would output 6. To check your answers, click on Answers to the exercises.

#include, using namespace, and main


A final word about those mysterious lines that you have to put into your program. When you send your program to the compiler, it actually goes first to another piece of software called the preprocessor. The preprocessor handles the lines beginning with "#". When it comes to a #include line, it removes the line and replaces it with a file of C++ which is stored as part of the system software. This file is called a library header file. The line #include <iostream> is replaced by the iostream library header file. If you intercepted your program after it had left the preprocessor but before it reached the compiler, you would find that there were large amounts of C++ that you didn't recognize and then the program that you had written, tiny by comparison, on the end. The purpose of these libraries is to provide the programmer with extensions to the language which can be pulled in, as and when needed. If you want to use the string data type, you have to include the string library. If you wanted to use the mathematical functions (square-root, cosine, logarithms etc), you would include the cmath library, and so on. The iostream library contains what's needed for doing input and output. If you didn't include the iostream library, you couldn't use cin and cout. Some libraries, known as the standard libraries, are provided with every C++ implementation. Other libraries can be created for special purposes. One team of programmers might produce a library of routines for another team of programmers to use. When teams of programmers divide up the work on a large program in this way, it is all too easy for programmers in team A to choose a name for some item in their library and for programmers in team B to choose the same name for some quite unrelated item elsewhere in the program. This can be a serious nuisance. To help contain the problem, C++ provides namespaces. Team A declares one namespace and team B declares another. Now it doesn't matter if they accidentally choose the same name. Team B can still use names from A's namespace, but they have to tell the compiler specifically which names they are going to use. All the names in the standard libraries are declared in a namespace called std. So, if you are going to use names from these libraries, such as cin and cout, you have to tell the compiler that you are going to use names from the std namespace. It is possible to do this separately for each name, but it's a lot easier to say, just once, that you want your program to be able to use any of them. Hence the line using namespace std; If the compiler seems not to know what you mean by perfectly ordinary bits of C++ such as cin and cout, check that you have not forgotten the using namespace std; Finally, what is int main()? This is the first line of a program unit, known as a function, which extends from the int to the final "}". A program ordinarily consists of many functions. The main function is the one that gets executed first. Every C++ program must have a main function. You'll understand why the word main is preceded by the word int and followed by () when you have learnt about functions. This topic is covered in chapter 4; you don't have to read this before the interview, but you are recommended

to read it before starting the MSc.

Exercises
Exercise X1 What does this program do?
#include <iostream> using namespace std; int main() { bool finished = false; int m = 0; while (not finished) { int num; cin >> num; if (cin.fail()) finished = true; else if (num > m) m = num; } cout << m << endl; }

Exercise X2 If you have worked out what the above program does, can you see that, for certain series of numbers, it will not produce the correct output? In what circumstances will it not work correctly, and how could you change the program to make it work properly? Exercise X3 Write a program that takes a series of numbers and counts the number of times that the number 100 appears in the list. For example, for the series 2, 6, 100, 50, 101, 100, 88, it would output 2. Exercise X4 Write a program that takes a series of lines and, at the end, outputs the longest line. You may assume there is at least one line in the input. Exercise X5 (this one is a bit harder) Write a program that takes a series of numbers. If the current number is the same as the previous number, it says "Same"; if the current number is greater than the previous one, it says "Up", and if it's less than the previous one, it says "Down". It makes no response at all to the very first number. For example, its output for the list 9, 9, 8, 5, 10, 10, would be Same, Down, Down, Up, Same (comparing, in turn, 9 and 9, 9 and 8, 8 and 5, 5 and 10, 10 and 10). You may assume there are at least two numbers in the input. To check your answers, click on Answers to the exercises.

A short introduction to computer programming, using C++ Summary of the language features mentioned in this introduction
#include <iostream> using namespace std; int main() {

Start and end your programs like this.

Your program goes here

} #include <string> // int i; int x, y; bool b; string s; int num = 0; cin >> num; cin >> x >> y; cin >> s getline(cin,s); cout << num; cout << "The answer is " << x; cout << s << x; cout << endl; x = 99; +, , *, /, %

Extra line if you are going to use strings Introduces a comment Defines an integer variable called i Defines integer variables called x and y Defines a boolean variable called b Defines a string variable called s Defines and initializes an integer variable called num Takes a value from the input and puts it into num Takes values from the input and puts them into x and y Takes a string from the input (ignoring leading spaces) and puts it into s Takes a whole line from the input and puts it into s Outputs the value of num Outputs "The answer is " followed by the value of x Outputs the value of s followed by the value of x Outputs an end-of-line Assigns the value 99 to x Arithmetic operators. *, / and % take precedence over + and Division with integers gives an integer (not floating-point) result.

s.length() s.substr(x, y)

Gives length of string s Gives substring of s starting at character x and of length y (The first character of the string is character zero.)

if (A) B; else C; (x == y) !=, >, <, >=, <= { } while (A) B; true, false not, and, or (cin.fail()) (cin.eof())

If A is true, do B, else do C. Tests whether x is equal to y. Note double "=" Not equal to, greater than, less than, etc Used to bracket together separate statements to make them into a block While A is true, do B. B can be a block. The boolean values true and false. The boolean operators not, and and or (on some compilers !, && and ||). Tests whether the most recent attempt to input something failed. Tests whether the program has reached the end of the input.

Obtaining, installing and running a C++ compiler on a PC


If you intend to use your own PC for doing assignments on the MSc course, we want you to use the Borland C++ compiler. You can download a free copy of this from: www.borland.com/products/downloads/download_cbuilder.html (You may find you have to create an "account" even though no money is changing hands.) Documentation is provided there to help you to install it. You can find more help with installation on the departmental website: www.dcs.bbk.ac.uk/support/borlandfreecpp.pdf

Compiling and running programs


The Borland is a command line compiler. You first type your program in a text editor. Notepad will do. If you use a word-processor, make sure you save the file as text. Give the filename a .cpp extension. Open a DOS Window (Command Prompt from the Start Menu.) Change to the directory (folder) where you have saved your program. (You don't have to do this but it's easier if you do, otherwise you have to type the full path for your source code file.) Suppose your program is in a file called myprog.cpp If compiling with the Borland compiler, you would type
bcc32 myprog.cpp

to compile the program. If compilation was successful you would be returned to the prompt. The compiler would make an executable file called myprog.exe, and you would type myprog to run your program. If there were errors you would get error messages and you would need to go back to the text editor, correct and save the program again, then recompile. Remember that, with the Borland compiler (and many others), you need to use &&, || and ! for and, or and not.

A short introduction to computer programming, using C++


Roger Mitton, Birkbeck, University of London

Chapter 4 Functions
Suppose you were writing a program that needed to use a square root. (For readers whose maths is rusty, the square of a number is what you get when you multiply the number by itself. So, for example, the square of 4 is 16. A square root is the opposite. The square root of some number x is that number which, when multiplied by itself, gives you x. So the square root of 16 is 4.) If the square root is an integer, as in my example, the arithmetic is not too hard. But if the square root is a number with a decimal point in it (a "floating-point number"), it's not so easy. What is the square root of 15? Three point something, but what exactly? The data types we have looked at so far include integers, strings and booleans (in C++ int, string and bool). For doing calculations involving decimal points, we use a data type called a double. There is also a data type called a float (for "floating-point number"), which we could also use. A double occupies twice as much memory as a float, hence its name. In the days when a computer's memory was very limited, you would use a double only if you needed the extra precision, but, now that computer memories are so much larger than they used to be, the double is more often used. In C++ it is the default for representing floating-point numbers. That is to say, if you had an assignment such as d = -0.23; the -0.23 would, technically, be a double, not a float. Fortunately, we do not have to invent our own algorithm for calculating square roots since C++ provides a square-root function. If we #include <cmath> then we can use a function called sqrt. For example, we could say double d = sqrt(15.0); or cout << sqrt(15.0); (It's 15.0, a double, rather than 15, an int, since the sqrt function expects to be given a double.) The sqrt function is one of the functions in the cmath library. That is to say, it is a self-contained unit of program code, placed in one of the standard libraries, which is there for us to use if we want to. It takes number (as a double) and returns a double representing the square root of that number. In the example, we give it (or pass it) the number 15.0, and it gives us back (or returns) 3.87298. Note that we do not know how it does what it does, and we do not need to know. To us, it is a "black box". To use it, we just need to know its name (sqrt), what it takes (a double) and what it returns (also a double). (We also need to know that it is in the cmath library and to remember to #include <cmath>. This is one of the nuisances of the library system.) If we had two variables of type double, d1 and d2, we could say d1 = sqrt(d2); In this case the value of d2 is passed to the sqrt function. Or we could say d1 = sqrt(d2/3.7); or d1 = sqrt(d2/3.7 * 5.4 + 8.913);. The expression in the brackets (which is known as the argument) is evaluated, and its value is passed to the sqrt function. Using a function is known as calling a function; sqrt(d2) is an example of a function call.

Writing your own functions


With square-roots, we were in luck; the C++ language designers had decided to include a square-root function in the standard library. But what if we need a function that is not provided in the library? Then we write our own. Suppose we want a square function; square(x) will return x * x. You may feel that we hardly need a function for this since writing, say, double d2 = square(d1); is no easier than writing double d2 = d1 * d1; But suppose it's

double d3 = square(d1/3.706 * (d2/9.854 + 4.6));

That is, actually, easier, clearer and less error-prone than writing
double d3 = (d1/3.706 * (d2/9.854 + 4.6)) * (d1/3.706 * (d2/9.854 + 4.6));

So, as well as being a simple example, the square function might actually be useful. The first thing we have to decide, in writing the square function, is what type of function it is, i.e. what type of thing it is going to return. It's clear from the examples above that the square function is going to return a double, so that is the first thing we write down when defining the function. Next we have to think of a good name for it. I've already done that in calling it square. Now we have to decide what we are going to pass to it when we call it the things that it needs to be given (if any) to do its job. In the case of square, this is simple; it needs a double. We can now write the first line of the function definition:
double square(double dx)

This says, "We are defining a function called square that takes a double [that's the part in brackets] and also returns a double [that's the first word on the line]." The thing in brackets, that I have called dx, is a parameter. It is declared like a variable a type name followed by an identifier and it behaves like a variable within the function. Its purpose is to receive the value that gets passed when the function is called. I'll come back to that in a second. First let's complete the function definition. We have written the first line, sometimes called the function header. Now we write the body of the function; this is where we specify what the function does. Since the purpose of a function is to return a value, we need to calculate the value to be returned. We do this in an ordinary block of code and return it with the keyword return. (A block, you will recall, is some lines of program code between curly braces.) We could do it like this:
double square(double dx) { double lv; lv = dx * dx; return lv; }

is an example of a local variable i.e. a variable that is accessible only within the function. The parameter dx also behaves like a local variable. The difference is that, whereas the value of the local variable lv is initially undefined (unless we choose to initialize it), the parameter dx will already have been given a value by the time this code executes.
lv

Suppose that our function is called in the following way:


int main() { double d1 = 1.5; cout << "The square of " << d1 << " is " << square(d1) } << "." << endl;

When the computer executes the call square(d1) it evaluates the argument, i.e. it works out that the current value of d1 is 1.5, and then passes this value to the function. This value is then used to initialize the parameter dx. So, by the time the body of the function begins to execute, dx already has the value 1.5. The function body then evaluates dx * dx (i.e. multiplies 1.5 by 1.5), assigns the result (2.25) to be the value of lv and then returns the value of lv. So the value of the expression square(d1) is 2.25, and the program's output is, "The square of 1.5 is 2.25." The process by which a value is passed to the function and becomes the initial value of the parameter is known as parameter passing. This is the standard term though actually it is slightly odd since it's really the argument, or rather the value of the argument, that gets passed; the parameter is on the receiving end. When writing a function, bear in mind that, when your function code executes, the parameters will already have values. You don't have to do anything (for example with cin >>) to put values into them; they already have values thanks to the parameter passing.

Note that the argument does not have to have the same name as the parameter. Equally, it would not matter if it did. The argument and the parameter are two different entities. It is the mechanism of parameter passing that links them, not their names. Returning to our square function, we can simplify it. The local variable is not really necessary, and we could write the function more simply as follows:
double square(double dx) { return dx * dx; }

It only remains to say where the function definition goes in relation to the other parts of the program. For now, it will be simplest to put it after the using namespace std; and before the int main(). So, the whole program looks like this:
#include <iostream> using namespace std; double square(double dx) { return dx * dx; } int main() { double d1 = 1.5; cout << "The square of " << d1 << " is " << square(d1) << "." << endl; }

In this example, we are calling the function only once, but we could call it many times if we wanted to, and we could, if we wanted, give it a different argument each time. For example:
#include <iostream> using namespace std; double square(double dx) { return dx * dx; } int main() { double cout << cout << cout << endl; cout << "." << endl; } d1 = "The "The "The 1.5, d2 = square of square of square of 10.1; " << d1 << " is " << square(d1) << "." << endl; " << d2 << " is " << square(d2) << "." << endl; " << d1 + d2 << " is " << square(d1 + d2) << "." <<

"The square of " << square(d1) << " is " << square(square(d1)) <<

Exercise 4A Write a function, called triplus (just the function definition, not the call or any of the rest of the program) that takes an int as its parameter and returns that number times three plus one. If the call was triplus(3) it would return 10; for triplus(8) it would return 25. To check your answer, click on Answers to the exercises.

More parameters, and of different types


We can also have strings, both as parameters and as return types. The following is a function that takes a string as its parameter and returns the last character. For example, if place was a string with the value "Hornby", last(place) would return the single-character string "y".
string last(string s) { return s.substr(s.length()-1); } // or return s.substr(s.length()-1, 1);

Sometimes the function needs more than one thing to do its job. If, for example, we wanted a function that returned the longer of two strings (or either if they are the same length), it would have to have two parameters, and we would write them in a comma-separated list, each one with its data type:
string longer(string a, string b)

{ }

if (a.length() > b.length()) return a; else return b;

The arguments, similarly, are presented in a comma-separated list:


string string one = "Quernmore", two = "Abbeystead"; three = longer(one, two);

The parameters do not all have to be of the same data type. For example, if we wanted a function that returned the first n characters of a string, we would have to pass both the string and the value of n:
string firstpart(string s, int n) { return s.substr(0, n); }

If our place variable held the string "Ulverston", then firstpart(place, 5) would return the value "Ulver". Note that the order of the arguments has to match the order of the parameters (in this case, a string first followed by an int). Note also that you have to state the data type for each parameter in a parameter list, even if they are all the same. For example, a function that took three integers as its parameters and returned an integer would have a header something like this:
int func(int a, int b, int c)

In all the examples so far, the return type has been the same as the type of the parameters, or of one of the parameters, but the return type can be different. The following function, very like longer above, returns the length of the longer string:
int longer_length(string a, string b) { if (a.length() > b.length()) return a.length(); else return b.length(); }

Exercise 4B Change our last function into a lastpart function that returns the last n characters of a string. For example if place held the value "Cartmel", lastpart(place, 3) would return "mel". See if you can modify it further so that, if the value of n is longer than the string (such as lastpart(place, 10) for "Cartmel") it returns the full string. To check your answer, click on Answers to the exercises.

Procedures (void functions)


All the functions we have seen so far return a value. That is what they are for. But it is possible, in C++, to write a function that returns no value. You call such a function, not for what it returns (it returns nothing), but for what it does. Some other languages draw a distinction between functions (that return values) and procedures (that don't). In C++, both are called functions; it's just that some functions return no value. But I find it helpful to keep the term "procedure"; by "procedure" I simply mean "void function". You begin the definition of one of these with the word void as the return type. For example, we could write a procedure (void function) to take a string and to write it out with a few stars to left and right:
void stars(string s) { cout << "*** " << s << " ***" << endl; }

We might call it as follows:


stars("Urgent");

and it would output the following line:


*** Urgent ***

A (non-void) function call is an expression, something that gets evaluated and returns a value, such as d2 = square(d1); or if (sqrt(d) > 20.0) or cout << lastpart(place, 3); By contrast, a call to a void function is a statement, such as stars("Urgent"); This is how it might look in a program:
#include <iostream> #include <string> using namespace std; void stars(string s) { cout << "*** " << s << " ***" << endl; } int main() { string message = "This is important."; stars(message); }

Note the difference between that and this alternative way of producing the same result:
#include <iostream> #include <string> using namespace std; string starred(string s) { return "*** " + s + " ***"; } int main() { string message = "This is important."; cout << starred(message) << endl; }

The same work is being done but it is divided between the parts of the program in a different way. In the first, main simply hands over a message to stars, and stars both adds the stars and outputs it. In the second, main asks starred to return a new version of the message, with added stars, and then main itself takes care of the outputting. Which is better? For these tiny programs, it hardly matters. In the context of a larger program, it would depend on what else the program wanted to do with these starred messages. If it had various messages and sometimes output starred versions but sometimes did something else with a starred version, such as assigning it to be the value of another string:
string urgent_message = starred(message);

then the second would be better. But if the starring was only ever needed in conjunction with the outputting, the first might be simpler. In the examples we have seen so far, it has always been main that did the calling. But in fact it is possible for one function to call another. This enables us to have a third version:
#include <iostream> #include <string> using namespace std; string starred(string s) { return "*** " + s + " ***"; } void out_starred(string s) { cout << starred(s) << endl; } int main() { string message = "This is important."; out_starred(message); }

Given what we have covered so far, it would be necessary to lay out the program in that order the starred function first, then the out_starred procedure, and then main. Although procedures and functions generally have parameters, they don't have to. In other words, the parameter list can be empty. This procedure just outputs the string "Ouch!" whenever it is called:

void ouch() { cout << "Ouch!"; }

Note that you still need the brackets for the parameter list, even if there is nothing inside them. You would call it thus:
ouch();

Note, also, that you still need the brackets for the argument list, even though there are no arguments. The most obvious difference between a procedure definition and a (non-void) function definition is that the former begins with the word void. But there is another. A (non-void) function always contains a return whereas a procedure does not have to. If there is no return in a procedure, it will simply execute and then the flow of program control will return to the line after the call. For example:
#include <iostream> using namespace std; // Assume that the first and last functions are defined here void test_and_display(string s) { cout << "The string is " << s << endl; cout << "The length is " << s.length() << endl; cout << "The first character is " << first(s) << endl; cout << "The last character is " << last(s) << endl; // Control is returned from here. } int main() { string userstring; cout << "Please key in a string: "; cin >> userstring; test_and_display(userstring); // Control is returned to here. cout << "Thank you and goodbye" << endl; }

But you can have an explicit return if you want. Of course, this is not like return dx * dx; or return a.length(); because a void function does not return anything. It is simply return; You might use this if, in some circumstances, you did not want the procedure to execute in full. For example, the test_and_display procedure would crash or produce peculiar output if it was given an empty string (a string of length zero, i.e. with no characters at all). We could prevent this by inserting some lines:
void test_and_display(string s) { if (s.length() == 0) { cout << "Empty string." << endl; cout << "Zero length, no characters." << endl; return; // Either control is returned from here } cout << "The string is " << s << endl; .... // Or control is returned from here. }

Or, if you preferred, you could achieve the same effect with an else:
void test_and_display(string s) { if (s.length() == 0) { cout << "Empty string." << endl; cout << "Zero length, no characters." << endl; } else { cout << "The string is " << s << endl; .... } // Control is returned from here. }

Exercise 4C Write a procedure (void function) that takes two integers and displays the sum, the difference (the larger

minus the smaller), the product, the quotient (the larger divided by the smaller) and the remainder. For example, if given 13 and 5 (or 5 and 13), it would output the following:
The The The The sum is 18. difference is 8. product is 65. quotient is 2, remainder 3.

Be careful not to divide by zero. To check your answers, click on Answers to the exercises.

The main function


In most respects, main is an ordinary function. As you can see from its header int main() it has an empty parameter list and returns an int. (Actually, it can have parameters, but that takes us beyond this introductory lesson.) But, if it's a function, where is the function call? Where does main get called from? It gets called from the operating system, the program (Windows, Unix or whatever) that is running all the time a computer is switched on and which gives it its basic repertoire of behaviour maintaining the screen display, managing the file storage system and so on. When main calls, for example, test_and_display, it hands over control to the function and waits till it returns. In much the same way, when the operating system calls main, it hands over control to your program and waits till main returns, i.e. until your program terminates. Since main is an int function, it returns an int at the end, to the operating system. In some operating systems, Unix for example, it is possible to chain a series of programs together, each one passing on the results of its work to the next one. When one has finished, the operating system calls the next. In such a system, it is very useful for the individual programs to be able to signal to the operating system whether they were able to do their part successfully or whether something went wrong, in which case the whole chain of processing should be aborted. The integers returned from main can be used for that. By convention, a return value of zero from main means "Everything OK" and any other value means "Something went wrong." Since things can go wrong in different ways, it is possible to arrange for different return values to have different meanings. It will not have escaped your notice that not one of the main functions that we have had so far in these notes appears to return anything. Should we not be returning an int since it's an int function? We could, if we wanted, put a return 0; as the last line of main (if we were programming in C, rather than C++, it would be obligatory), but we don't have to. If we leave it out, the compiler puts it in for us. This is another respect in which main differs from other functions. With all other (non-void) functions, an explicit return is essential; with main, you can leave the compiler to put it in for you. Returning from main allows us to terminate the program in a simple way. If something goes wrong such that there is no point in continuing the program (if, for example, we tried to read in a file of data but could not find it), we can terminate simply with, say, return 1; (1 for "something's wrong" rather than 0 for "all's well"). Note, however, that it is only when we are in main that we can terminate the program with a return. If we are in any other function, a return will simply terminate that particular function. Exercise 4D Write a function that takes a string as its parameter. If the string has fewer than three characters, it returns an empty string. Otherwise it returns the middle section of the string, i.e. the string minus its first and last characters. For example, if it was passed the string "Kendal", it would return "enda". Exercise 4E Write a procedure (void function) that takes a string as its parameter. If the string is empty, it does nothing. If the string has only one character, it outputs that character followed by a newline character. Otherwise it outputs the characters separated by spaces, with a newline on the end. For example, if it was passed the string "Carnforth", it would output:
C a r n f o r t h

Exercise 4F Copying the firstpart function (above) into your program in the right place, write a program that takes a string from the user and then, using a loop and using firstpart, writes out a triangle of letters based on that string. For example, if the user keyed in the name "Morecambe", the computer would display the following:
M Mo Mor More Morec Moreca Morecam Morecamb Morecambe

To check your answers, click on Answers to the exercises.

C++ Week 1 Elements of a C++ Program


What is the shortest C++ program you can write?
main() { }

There is no need to use #include <iostream>, as there is no input or output so we don't need the iostream library There's no need to specify a return value for main(). However, although C and C++ implicitly assume an integer return value for main() you should normally indicate the return type explicitly, with int main() You do not need put any code inside main() You can specify return 0; to indicate to the operating system that everything is working OK, but it is not essential to put this in explicitly.

#includes
These are libraries. When you send the file to the compiler, the preprocessor substitutes the #include with the contents of the specified library file. In this way you can create and use modules that define functions that you only add to your code when you need them. There are lots of different libraries available for many different purposes. Note that input/output is defined in a library, so if you want your program to be able to do any simple I/O, which you normally do, you need to use #include <iostream>.

Namespaces
A namespace allows you to use your own names without worrying about conflicts with names that other programmers may have used for their own variables etc. However you need to make it explicit that you are using a standard namespace by adding using namespace std; If you forget to add this line to your code, the compiler will be unable to recognise any of the keywords defined in the libararies you are using, such as cout.

Elements of a program
Output using cout <<
cout is

the name of the standard output stream, normally the monitor. The << operator evaluates the argument that follows it and places it in the output stream that precedes it. Here are some valid cout statements:
cout << "Hello"; cout << 27;

// "Hello" is a string literal

// 27 is a value // 2+3 is an expression // you can concatenate << operators on a line

cout << 2+3;

cout << "result" << 5+7;

To end a line of output you can use << "\n" or << endl.

Variables
A variable is a data item stored in a chunk of memory that has been reserved for it. Integer variables are

numbers without decimal points. Declare them using e.g. int x; Or you can declare several on one line, e.g. int x, y, z; When you declare a variable in C++, you have to state what type of variable it is - what sort of value it is going to store. Double variables are for floating-point numbers. Declare them using the double keyword. float variables are like doubles but occupy less memory. (A double occupies twice as much memory as a float, hence its name.) Since memory is no longer in such short supply they are falling into disuse.
int and double are

examples of types that are part of the core language, sometimes known as "built-in" types. Other types are defined in libraries and can be linked in if required. string is an example of one of these. If you want to use variables of type string, you include the line #include<string> Variables of built-in types are not initialized for you. If you declare simply int x; the compiler reserves a portion of memory for your program to use as an integer variable, but the contents are whatever happens to be there from the last time this portion of memory was used. We say that the variable's value is undefined. You can initialize a variable when you declare it (more precisely, when you define it), for example int x = 0; If you don't initialise a variable when you declare it, be sure that, by the time you try to use its value, it does indeed have a value for you to use! If you are declaring several variables in one line and you want to initialize them all, you have to initialize each one separately. For example:
int x = 1, y = 3;

Strings
If you want to use strings remember to use #include<string>. Nowadays, storage is not the scarce resource that it used to be, and some implementors of C++ choose to include several of the other standard libraries in the iostream library. So you might find, depending on which system you are using, that, just by putting in #include <iostream> you are able to use such things as strings, character functions, mathematical functions and so on which, strictly, require the string, cctype and cmath libraries respectively. But if you want to be sure that your C++ programs will compile and run correctly on any C+ + system, you should make a point of including all the library files that your program requires. Enclose string literals in " ". Strings are not built-in types. If you don't initialize a string explicitly, it will be initialized for you as an empty string "" (that's two double-quote marks together, with no space between).

Reserved keywords
The C++ compiler uses words like int, double, float etc and they are therefore reserved. You cannot use them as variable names.

Identifiers
Identifiers are the names that the programmer invents, for example for variables. They must always begin with a letter; subsequent characters may be a letter, number or underscore _ but not a space or other character. Note that C++ is case-sensitive (i.e. it distinguishes between upper-case and lower-case) so that you could use, for example, num1 and NUM1 in your code as the names of two different variables (the compiler won't get them confused, though you might!); you could use Int as an identifier since it is different from the reserved word int.

Input using cin >>


cin is

the name of the standard input stream. By default, this will be the keyboard. The >> operator reads data from the input stream that precedes it and places it in the argument that follows it. Take note of the following characteristics.

the >> operator ignores leading white space (<space>, <newline> and <tab> characters) and takes as its input the first non-space characters it encounters in the input stream (from the keyboard or a file). Thereafter its behaviour depends on the type of variable it is being asked to put data into.
string s; cin >> s;

When reading into a string, >> skips leading white space and reads everything into the string up to but not including the next white space. Remember that "white space" includes <space>, <newline> and <tab> characters.
int x; cin >> x;

When reading into an integer, >> will skip leading spaces and will then accept a '+' or '-' or a digit ('0' to '9') as a leading character and thereafter continues to read in characters, so long as they are digits, until it encounters some other sort of character (white space, non-numeric or decimal point). In the above example, typing +492A will place 492 into x.
double d; cin >> d;

When reading into a double, >> proceeds much as it does for integers except that it will also accept a decimal point if it has not had one already. In the above example, typing .67a will place 0.67 into d (it will accept, but does not require, a leading zero in the input). Here is an example of how >> behaves when asked to read input into an int, a double and then a string.
int i; double d; string s; cin >> i >> d >> s;

input
12 5.9 London -3 6.6 Paris 33.54 Hong Kong

s Londo n

12 5.9

-3 6.6 Paris 33 0.54 Hong

In the last of these examples, note how the decimal point terminates the reading of the integer (cin >> i) and is then the first character to be read when it tries to read a double (>> d). If the wrong sort of data is placed in the input stream then it will fail - for example, if you type a letter when >> is expecting integer data. Once the input stream fails you can use cin.clear() [i.e. input_stream.clear()] to clear the input stream's fail state, but of course you must also do something about the input that caused it to fail; simply trying to read the input again with the same input statement will cause it to fail again. For example, if it failed when you were asking it to read into an integer variable, then, having cleared the fail state, you might ask it to read into a string variable instead. (The Borland implementation of the string read has an annoying quirk. It consumes the white-space character that terminated the string - it does not put it into the string variable, but it removes it from the input stream. It does not do this when reading into an int or a double. For example, if the input were 245 678 and you read it into an int with cin >> i; you'd get the value 245 into i and the next character to be read would be the space immediately after the 5. If, however, you read it into a string with cin >> s; then s would contain the string "245" but the next character to be read would be the 6.)

Assignment statements
Assignments take the form Variable = Expression Note: Do not confuse the assignment operator = with the test-for-equality operator ==. You can combine the = operator with other mathematical operators. For example, x += 5; means x = x + 5; while x /= 20; means x = x / 20; If you are adding 1 to a variable, or subtracting 1 from it, there is an even shorter shorthand. x++ or ++x

will increase x by 1. Similarly x-- and --x will decrease x by 1. However when you use these auto-increment (++) or auto-decrement (--) operators within expressions, the two versions are not equivalent. When they are used as expressions, we are also interested in the value that they return. ++x and --x return the new value of x while x++ and x-- return the old value of x
y y n n = = = = ++x; // x++; // 0; while 0; while x x ( ( = x = x ++n n++ + + < < 1; 1; 5) 5) y = new But y = cout << cout << value of x; as you'd expect old value of x; n << " \n"; // outputs 1 to 4 n << " \n"; // outputs 1 to 5

Arithmetic Operators
Arithmetic operators are used in expressions. + addition - subtraction * multiplication division - if both arguments are integers, the result is truncated to / an integer, otherwise the result is a floating point number. % modulo - the remainder of the division of two integers.

If you have an expression without explicit parentheses, you need to know where the implicit parentheses go. For example, if you have
2 + 3 * 4

you need to know whether this means


(2 + 3) * 4

or
2 + (3 * 4)

This is decided by reference to a table of operator precedence. Every operator in the language has its place in the precedence table. For the arithmetic operators, the operators *, / and % take precedence over + and -. So
2 + 3 * 4

means
2 + (3 * 4)

If the operators are of equal precedence, we need to know their associativity, i.e. whether we take them left to right or right to left. These five arithmetic operators are all left-associative, meaning that, for example,
8 - 5 - 3

is taken as
(8 - 5) - 3

and not
8 - (5 - 3)

We can apply these rules to the following expression:

1 + 2 1 + 2 1 + 2 1 + 2 1 + 2 1 + 2 1 + 2 (1 + 2) 3 - 2 1

3 * 4 / 5 % 6 (3*4) / 5 % 6 12 / 5 % 6 (12 / 5) % 6 2 % 6 (2 % 6) 2 - 2

When using the shorthand forms of assignment, note that the expression on the right-hand side is implicitly parenthesised, because these operators (+=, *= and so on) occupy a lowly place in the precedence table. So, for example,
x *= y + 2

means
x = x * (y + 2)

Type conversion
If you assign an integer value to a variable of type double, the value is cast (type-converted) into a double. Conversely, if you assign a double to an integer variable the value is truncated. An expression that contains an int paired with a double is evaluated to a double. For example:
int i = 16; double d; d = i; // d = 16.0 d = 10.7; i = d; // i = 10 (truncation, not rounding) cout << 2 * 3.4 + 7 / 2; // 6.8 + 3 = 9.8 (a double)

Note that integer division is performed between 7 and 2 in this example, because both operands are integers. (A numeric constant with no decimal point is of type int, and one with a decimal point is a double.)

String manipulation
If you try to use a >> operator to input a string that consists of several words, you will get only the first word - it stops at the first space. If you want to input more than one word into a string variable, you should use the getline procedure - getline(stream, variable); This places the whole line up to the <EOL> character into the string variable and consumes the <EOL> character (i.e. places you at the start of the next line). If you are at the start of a line when you execute a getline, you get the whole of the line. If you are already some way through the line, for example as a result of having used the input operator >>, then getline will give you the rest of the line. You have to be a bit careful if you use both the >> operator and a getline(cin, s); For example, suppose the input were:
234 Chipping Sodbury

You might think that the following would read the number into x and the string into name:
cin >> x; getline(cin, name);

But, though you'd get the number 234 into x, you actually wouldn't get "Chipping Sodbury" into name. In fact, name would contain an empty string. You have to remember that the newline character counts as white space. The integer read stops immediately after the 4, i.e. before the newline character. The getline(cin, s); then puts all the characters from there to the end of the line (there are none) into name and consumes the newline character, leaving you immediately before the C of "Chipping". In order to get past the newline character, you could do an extra getline, as in:

cin >> x; getline(cin, junk); getline(cin, name);

// just consumes the newline character

or you could use the ignore function, as in


cin >> x; cin.ignore(); // skips one character, in this case the newline character getline(cin,name);

Substrings
You can extract substrings from a string using the substr() member function.
s.substr(start_position, substring_length);

For example if the string s is "department", the code:


cout << s.substr(2, 4);

outputs the substring "part". The letter 'p' is at position 2 because the first character of a string is in position zero. You can leave out the second argument to the substr function, in which case it will return the whole of the rest of the string. For example, s.substr(6) will return "ment". Note that you cannot assign to a substring. You cannot say s.substr(6,4) = "ures" and expect to get "departures".

Concatenation
Strings can be concatenated using the + operator. The use of the "+" operator to do two quite different tasks (arithmetic addition and string concatenation) is an example of operator overloading. You can concatenate string variables and literals, on condition that the concatenation expression contains at least one string variable.
string s = "abc"; string t = "def"; string z = s + t; // z = "abcdef" z = "Letters " + s + " More letters"; // Valid code z = "Letters " + " More letters"; // Invalid code (no string variable)

Finding the length of a string


The function length() returns the length of a string. Note that the index of the last character of a string is one less than the length of the string. For example, to determine the last character of the string s, use the following code:
string z = s.substr(s.length() - 1, 1);

Note that you cannot assign to length() It might be tempting to think that you could change the length of a string by saying something like s.length() = 5 but you can't.

Formatting the output stream


To specify that an item in the output stream should be allocated five columns, insert << setw(5) before the item in the output statement. For example:
cout << "x" << setw(5) << 133 << "x";

produces the output


x 133x

The 133 is right-justified in a five-character field. setw() only affects the next item in the output stream. If you want to specify the number of decimal places, use setprecision() with fixed. This is often

required if you need to specify that trailing zeroes are used (e.g. when displaying currency). fixed and setprecision() affect all subsequent items in the output stream, so you only need to use them once.
double d = 6.0; cout << d; // (without fixed and setprecision) puts 6 (not 6.0) in the output stream cout << fixed << setprecision(1) << d; // puts 6.0 in the output stream cout << fixed << setprecision(3) << 123.456789; // 123.457 (3 dec places, with rounding)

To use these output stream manipulators, you need to #include <iomanip>


Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 2 Using objects. Conditionals.


As well as being something simple like an int or a double a variable can also be an object. Typically an object holds structured data and exhibits certain behaviour. The data access can be restricted; the data inside the object is said to be encapsulated. The definition of what data an object will contain and what functionality it will have is specified in a class. An object variable is said to be an instance of a class. A string is an example of an object. the string library contains the definition of the string class.

Classes
Consider a possible Student class, which defines objects that contain the following three data items: Registration number, of type int Name, of type string Fees, of type int Assuming that the class Student has been defined (we see later in the course how to do that), we can create an instance of the Student class using either of the following definitions:
Student st(1234, "Mary Smith", 2420); Student st = Student(1234, "Mary Smith", 2420);

We can also use the default constructor (if there is one defined for the class) as follows to create an instance of Student that contains default data:
Student st;

Note that we do not write Student st();. You can use assignment with objects. If st1 and st2 were both objects of type Student, we could do the following:
st1 = st2;

The following statement changes the contents of the already-existing object:


st = Student(5678, "Bill Brown", 1320);

Encapsulation
The data stored in an object is not generally immediately accessible to the outside world. Access is defined by the object's member functions which expose the data through the object's interface. For example the Student class could have an accessor member function get_number() that defines access to the student's ID number as follows:
st.get_number();

// returns the ID number of the Student object st // assigns the ID number to the variable reg_no.

reg_no = st.get_number();

To access the properties and member functions of an object, use the dot operator as shown above. Objects can also have mutator member functions that allow the properties to be modified. For example, the Student class might have a set_name() function which you could use to change the name property of a student:
st.set_name("Betty Jones");

changes the name of st from Bill Brown to Betty Jones. What you can do with an object depends on how the class has been defined. For example, whether you can say cout << st; or if (st1 == st2) (where st1 and st2 are objects of type Student), depends on

whether Student has been defined in such a way as to allow you to do this. If it has, you can; if it hasn't, you can't.

Non-standard libraries
You include the standard C++ libraries by using a #include<library> statement. However, if you want to include non-standard libraries (Horstmann's for example), you use a statement of the form #include "filename". If the libraries contain names defined in the std namespace, you may need to put the #include after the using namespace std; statement.

Conditional statements
Conditions take the form:
if (condition) {statements executed if the condition is true }

or
if (condition) {statements executed if the condition is true } else {statements executed if the condition is not true } Execution resumes here

Note that the first line does not end with a semicolon. The curly brackets are necessary only if there are several statements. If you are only executing one statement as a result of the condition, you can place it on the same line or the next. For example:
double first, second; // first and second serves, in tennis cin >> first >> second; if (first /second < 1.2) cout << "Practise more serves"; // Statement on the next line, but indented else cout << "Serve is OK"; // Statement on the same line if (first / second < 1.2) { cout << "How many double faults?"; double dfault; cin >> dfault; if (dfault / second > 0.25) cout << "Could do better"; } // curly brackets to enclose the compound statement after the if

Relational operators
Relational operators allow you to compare data items. == is equal to != is not equal to > is greater than < is less than >= is greater than or equal to <= is less than or equal to The < and > operators can be used with strings to determine alphabetical order. For example, if s has the value "abc" and t has the value "def"
(s < t)

is true. When dealing with non-alphanumeric characters, you need to know which character set you are

using before you can determine the collating order of a pair of strings. The most common character set is the ASCII (American Standard Code for Information Interchange). The EBCDIC (Extended Binary Coded Decimal Interchange Code) is used in IBM mainframes. The first 32 characters of the ASCII character set (numbered 0 to 31) are non-printable control codes. Features of ASCII worth remembering are that the space character (number 32) comes before all the printable characters, the digits come before the letters, and the upper-case alphabet comes before the lower-case alphabet.

Boolean operators
Boolean operators are used to compare true (1 or non-zero) and false (0) values pre- 1998 '98 standard
! && || not and or

The order of precedence is not, then and, then or. For example A or not B and C means A or ((not B) and C). The and operator evaluates to true only if both operands are true; or evaluates to true if either operand is true; not evaluates to the complement of the operand (if A is true, not A is false and vice versa).

Common pitfalls
Be very careful to write "==" rather than "=". If you write if (x = 0), this is an assignment, not an equality test, and the effect will be to assign 0 to x. How does it evaluate an assignment as true or false? If the value assigned is zero, it evaluates it as false; if it is anything else, it evaluates it as true. When expressing "If x or y is greater than zero", you must write:
if (x > 0 or y > 0)

rather than
if (x or y > 0)

which are both correct code but logically different. The second expression literally means "if x is true or y is greater than zero", and x is always true for non-zero values of x. When expressing "If x is between 0 and 10 inclusive", you must write:
if (x >= 0 and x <= 10)

rather than
if (0 <= x <= 10)

To evaluate the second, it first decides whether zero is less than or equal to x. Either it is (so the expression is true) or it isn't (so the expression is false). It then decides whether this result (true, or false) is less than or equal to 10. How can true (or false) be less than or equal to 10? It makes no sense. But it resolves the problem by treating true as 1 and false as 0. Both of these are less than 10, so the expression is true regardless of the value of x.

Order of evaluation of operands


In general, in C++, there is no guarantee of the order in which the operands of an expression will be evaluated. For example, given the expression (x + 2) * (y - 1), does it first evaluate the (x + 2) and then the (y - 1) and then multiply them together, or does it first evaluate the (y - 1) and then the (x + 2) and then do the multiplying? The answer is that it might do either - the language does not specify. Note that this is a different matter from deciding where the brackets go in an unbracketed expression; we

have already seen that that is decided by the precedence and associativity of the operators. Precedence and associativity decide where the implied brackets go - they decide what the expression means. The above expression is fully bracketed; operator precedence is not an issue here - there is no doubt about what the expression means. What we are talking about now is the temporal order in which the different operands of an expression are evaluated. Most of the time, it does not matter which way round it does it, but it might. If the evaluation of one operand affected the value of the other, it might make a difference. Suppose an integer variable x had the value 3, and you had ++x * (10 - x). Doing the left side first changes the value of x from 3 to 4, so the right-hand side evaluates to 6 and the result is 24. But if it did the right side first, (10 - x) comes to 7, + +x comes to 4, so the result is 28. This would apply also to a cout statement. For example, in
cout << expressionA << expressionB << endl;

while you can be sure that the value of expressionA will be output before the value of expressionB, you cannot be sure that expressionA will be evaluated before expressionB. There is no guarantee that the following will output 1234:
int x = 0; cout << ++x << ++x << ++x << ++x << endl;

In general you should be careful not to write expressions that rely on a particular order of evaluation. The boolean operators, however, are an exception to this general rule. The operands of and and or are always evaluated left to right. For example, given the expression x < 3 and y > 10, x < 3 will always be evaluated first.

Lazy evaluation
Given a pair of conditional expressions joined by a boolean operator, C++ always evaluates the left-hand one first. It also uses lazy evaluation, in which boolean expressions are evaluated only until their result is clear. Since false and anything is always false, then, if it is evaluating an and expression and it evaluates the left side to false, it can (and does) stop evaluating since it already knows the result for the whole expression. Likewise with evaluating the left side of an or expression to true. This can be useful. For example:
if (second > 0 and first / second > 1.2) {... }

In this case if second is indeed zero, then the potentially fatal calculation of first / second is never executed.

Boolean variables
A boolean variable is a variable that can hold only the values true and false. Suppose we are processing applications for theatre tickets and suppose that a discount applies if the performance is on a Monday or the customer is under 15 or the ticket is for one of the back rows. Rather than evaluate this condition repeatedly in the program, we might evaluate it just once and store the outcome in a boolean variable:
bool half_price = day == "Mon" or age < 15 or row >= "w";

You might like to put the boolean expression in brackets, but you don't have to:
bool half_price = (day == "Mon" or age < 15 or row >= "w");

Then, when we want to say "If the discount is applicable," we just say if (half_price). (For "If the discount is not applicable," we just say if (not half_price).) Note that there is no need to say if (half_price == true). Having established whether half_price is true, it already has the result it needs. Asking it now to test whether true is equal to true (or whether false is equal to true) is redundant. Languages that don't have boolean variables have to make do with some other type of variable for this

job, such as:


string half_price; if (day == "Mon" or age < 15 or row >= "w") half_price = "T"; else half_price = "F";

And then later to say if (half_price == "T"). But it's all too easy to write if (half_price == "t") or if (half_price == "true") or if (half_price == "yes") or ...

Conditional operator, ? :
It is possible to abbreviate some condition statements using the ? : operator as follows:
conditional_expression ? expression_1 : expression_2

in which, if conditional_expression is true, expression_1 is returned, otherwise expression_2 is returned. Note that the expressions must be of the same type.
max = x > y ? x : y; // Assigns to max the larger of x and y cout << (s.length() < t.length() ? s : t); // prints the shorter of s and t

The conditional operator doesn't do anything that an if statement couldn't do, but it sometimes enables you to express something more succinctly.

The switch statement


The switch statement is C++'s multi-way branch, allowing you to specify a number of different cases, rather than simply true or false. Here is an example:
int x = 12; int z; cin << z; switch (z) { case 1: cout << x + 1; case 2: cout << x - 2; case 3: cout << x * 3; case 4: cout << x / 4; default: cout << "Invalid value"; }

The switch statement requires an expression after the word switch and then it jumps to the statement whose case matches the expression. All subsequent statements are executed as well, so that if z were 3 in this example, the output would not just be 36, but 363Invalid value. You can avoid this state of affairs by placing a break keyword at the end of each case statement, to go to the statement after the curly brackets, thus:
switch (z) { case 1: cout << x + 1; break; case 2: cout << x - 2; break; case 3: cout << x * 3; break; case 4: cout << x / 4; break; default: cout << "Invalid value"; }

switch statements are of limited use: the expression must always evaluate to an char (not double or string); you must specify constant values after the case -

integral value, e.g. int or you can't have expressions here - and you can have only one value per case and not, for example, a range of values, though you can implement a (small) range rather clumsily as follows:
switch (z) { case 1: case 2: case 3: cout << "One, two or three"; break;

which will output "One, two or three" in the event that z has the value 1 or 2 or 3.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 3 Loops, Reading from files, istringstreams


Pseudo-random numbers
The C++ random number generator is only pseudo-random. Successive calls to the random number generation function rand() will produce the same series of numbers. You can use the srand() function to seed the random number generator; seeding it with a different number will produce a different series, but always the same series for a given seed number. If you want to make the function less predictable, you need to specify a different seed each time you execute your program. You can do this by setting the seed using srand() and supplying an argument based on the time. To use the random number generation functions you need to #include the cstdlib library. To access the current time, you need the time(NULL) function which is in the ctime library. For example the following code implements a simple fruit machine:
#include<iostream> #include<cstdlib> #include<ctime> #include<string> using namespace std; int main( ) { srand(time(NULL)); cout << "Do you want to play? (y or n)"; string resp; cin >> resp; if (resp == "y") { int x = rand() % 4 + 1; int y = rand() % 4 + 1; cout << x << " " << y << endl; if (x == y) cout << "prize\n"; } }

Loops
The above program plays the fruit machine sequence only once before exiting. Ideally we would want it to keep on asking the question Do you want to play and repeating as long as the answer y is given. We can do this by using a while loop based on the condition that resp is equal to y:
... cin >> resp; while(resp == "y") { int x = rand() % 4 + 1; int y = rand() % 4 + 1; cout << x << " " << y << endl; if (x == y) cout << "prize\n"; cout << "Play again (y/n)?"; cin >> resp; }

If the while condition is true the statement after the while (it might be a block of statements enclosed in { } ) is executed. Then the condition is tested again; if it is still true, the statement is executed again, and so on. If and when the test is false, the loop is skipped and execution continues with the statement after the loop. Another variation on this theme is to specify in advance how many times the loop is to be executed. You can do this as follows:
int i = 0; while (i < 100) { cout << i << endl;

i++; }

This code outputs the numbers 0 to 99. Be careful not to put a semi-colon immediately after the condition of the while. An empty statement is a valid statement in C++. If we added a semi-colon to the above example, thus:
int i = 0; while (i < 100); { cout << i << endl; i++; } // Note the semi-colon

it would change the meaning completely. The body of the loop now consists of the empty statement between the (i < 100) and the semi-colon. In other words, that single line while (i < 100); is now the complete while loop. The line cout << i << endl; is just the start of the rest of the program. So what would it do? It tests the condition and decides that 0 is less than 100. Then it executes the body of the loop, i.e. the empty statement, so it does nothing. Then it returns to the start of the loop and again tests whether 0 is less than 100. And so on. It's in an infinite loop, apparently doing nothing, but actually comparing the value of i (0) with 100 over and over again.

for loops
The for loop provides a more succinct way of writing the kind of while loop we've just seen. The for statement contains three items: some initialisation, the continuation condition and the action(s) performed at the end of each iteration. A typical for loop takes the following form:
for (n=0; n < 100; n++) iterated statement

You can declare the loop driver in the for statement:


for (int i = 0; ... )

in which case the loop driver's scope is the loop itself - the variable is destroyed when the loop is exited. If you want to use the loop driver elsewhere you need to declare it before the loop. The loop driver does not have to be be an int; it can be a double you can increment or decrement at the end of the loop you can increment or decrement by any number For example:
for (int n = 100; n >= 0; n-=5) cout << n << endl;

This is the standard pattern of the three elements in the definition of a for loop, but the three parts do not have to have any obvious relationship to each other - from a programmer's point of view, it's better if they do, but the compiler doesn't care. It's also not essential to have all three parts: you do not need to specify the initial value of the loop counter you can omit the action to be performed at the end of each loop iteration you can even omit the loop continuation condition Here are some typical for loops:
int nz = 0; for (int i = 0; i < s.length(); i++) if (s.substr(i, 1) == "z") nz++; cout << "There are " << nz << " zs in the string.\n";

or:
for (int i = s.length() -1; i >=0; i--) cout << s.substr(i, 1);

There are a few patterns that you often need:

To go from zero to some maximum - 1, for (i = or, in the opposite direction, for (i = max - 1; To go from 1 to some maximum, for (i = 1; i or, in the opposite direction, for (i = max; i >

0; i < max; i++) i >= 0; i--) <= max; i++) 0; i--)

You might sometimes need a combination of elements from more than one of these patterns, such as for (i = 0; i <= max; i++), but if you write something unusual like this, make sure that it really is what you want. You can initialize more than one variable and perform more than one action at the end of each loop. For example:
for (i = 1, j = 0; ...; i++, j++) for (i = 0, j = s.length() - 1; i < s.length() / 2; i++, j--) if (s.substr(i, 1) == s.substr(j, 1) ...;

You can have a for loop where all the work is done by the loop header and there is no need for a body at all. For instance, the following finds the first occurrence of the letter "z" in a string:
int i; for (i = 0; i < s.length() and s.substr(i, 1) != "z"; i++) ; // loop terminates when i is the position of the first "z" in the string s // if i is equal to s.length(), it means that there was no "z" in the string

Handling files
There are two ways to use a file, rather than the keyboard, as the source of the input stream - you can: provide the name of the input file as an argument in the command line when calling the executable file put the name of the input file in the program itself

Redirecting I/O from the command line


To call a program from the command line you type the name of the executable file, e.g. prog (if your program file was called prog.cpp), and by default it reads input from the keyboard and sends output to the monitor. However, you can specify instead that the program reads its input data from a file and writes the output to another file. You do this by typing the following at the command prompt:
prog < input_file // instructs the program to read from input_file

Now, any statements in the program that take input from cin will take input from this input_file instead of from the keyboard. Similarly you can redirect the standard output:
prog > output_file // instructs the program to write to output_file

Now, any statements in the program that send output to cout will send output to this output_file instead of to the monitor. If you do this on Unix, then, if there is no file of this name, it will create one; if there is a file of this name, the new one will replace the old one. You can combine these arguments to read from one file and write to another:
prog < infile > outfile

Using a filestream
Filestreams allow you to hardcode the names of the files that will be read and written when the program is executed or to supply the filenames as strings when the program runs. To use filestreams you must #include the fstream library. Once a file has been opened for reading, you can use the name of the filestream in the same way as you would use cin to read characters and lines from the file rather than from the keyboard. For example:
#include <fstream> #include <string>

using namespace std; int main() { ifstream infile; // ifstream is the typename of an input file stream called "infile" infile.open("datafile.dat"); // opens datafile.dat for reading when stream infile is accessed if (infile.fail()) // detects failure in opening file (usually because it couldn't find it) { cout << "Error: input file not opened" << endl; return 1; // exit program } else cout << "File successfully opened" << endl; // This is just an example; you don't normally output such a message. int n; string s; infile >> n; getline(infile,s); // use "infile" like "cin" infile.close(); // you can close an ifstream and reopen it to read a different file string fname; cout << endl << "What file should I open?" << endl; cin >> fname; infile.open(fname.c_str()); // c_str converts the filename into a form the open function can handle if (infile.fail()) { cout << "Error: file " << fname << " not opened." << endl; return 1; } else cout << "File successfully opened" << endl; }

Reading until the end of a file


When you get to the end of a file and try to read more input, the input stream goes into a fail state. This means that to test for the end of a file you have to try to read beyond the end of the file and then test for failure. The test is always retrospective; you are asking, "Has the input failed?" Let's assume that we have successfully opened a file by opening an ifstream called input_stream, that we have a string variable called s and that we are reading the file one line at a time. It is tempting to try to read to the end of this file as follows:
while (not input_stream.fail()) { getline(input_stream, s); ... process s } // tempting but WRONG!!

However, this approach won't work. Having read the last line of the file, the ifstream is not (yet) in a fail state. The answer to the question, "Has the input failed?" is No we read the last line of the file successfully. So it enters the loop again and tries to read beyond the end of the file. The ifstream now does go into a fail state. s is unchanged, and we now try to process s as if we had just read a line, but we haven't; we will be processing the last line for a second time. Any of the following will work:
while (true) { getline(input_stream, s); if (input_stream.fail()) break; // break terminates the current loop; this works, but exiting a loop from the middle is not good style ... process s } getline(input_stream, s); while (not input_stream.fail()) { ... process s getline(input_stream, s); } while (getline(input_stream, s)) { .... process s } // Read the first line before entering the loop // Read the next at the end of the loop

// Looks a bit strange, but works.

The last works because, if getline(input_stream, s) succeeds, it returns a value which is treated as true; if it fails, it returns a value which is treated as false. So we read the last line of the file successfully; the condition is true; we enter the loop and process this final line. We then try to read beyond the end of the file; this fails and the condition is false, so we exit the loop. The evaluation of the while condition is both reading a line of input and returning a value indicating success or failure; this one line of program code is doing a lot of work, which is why this version is so succinct. In all the above examples, I could have used the input operator >> instead of getline. Suppose we were reading a file of integers, one at a time. Any of these would work (assume we've declared an integer

variable n):
while (true) { input_stream >> n; if (input_stream.fail()) break; // break terminates the current loop; this works, but exiting a loop from the middle is not good style ... process n } input_stream >> n; while (not input_stream.fail()) { ... process n input_stream >> n; } while (input_stream >> n) { .... process n } // Read the first integer before entering the loop // Read the next at the end of the loop

One of the advantages of using the first of the two methods described earlier for reading a file the redirection of standard input is that you can test your program with input from the keyboard and then run exactly the same program taking input from a file. When keying in data from the keyboard, you simulate the end of file by keying in CTRL+Z (for DOS) CTRL+D (for UNIX). (Annoyingly, on Borland, this works properly only if the control character comes at the start of a new line and only if it is the first thing the program encounters when it attempts a read. Suppose you are keying in integers, one per line, and reading them with the input operator. The input stream could look like this: 45\n67\n^Z\n (where \n indicates where you hit RETURN, and ^Z is where you key in a CTRL-Z). And suppose you are reading it with while (cin >> x) One read will take in the 45 but stop at the newline (without consuming it). The next read will skip the newline (because that's what >> does) and take in the 67. Now, in order for the next read to detect the ^Z properly, you have to consume the newline that precedes it. So arrange that each cin >> x is followed immediately by a getline(cin, junk); or a cin.ignore();, so each newline gets consumed immediately after a read, so each read begins at the start of a line. This only applies to simulating EOF from the keyboard; it has no problem detecting EOF in a file.)

Istringstreams
The input stream goes into a fail state when you try to read beyond the end of a file, but it also goes into a fail state if you are reading with >> straight into an int or a double and it encounters unacceptable data; for example, it would fail if it were trying to read a value into an int and encountered a letter. Suppose the file contained the following:
123 -456 891 XYZ 543 876

Any of the methods recommended above, using input_stream >> n, will read only the first three lines; they will fail on XYZ and exit. But we would probably prefer that the program skip over XYZ, perhaps reporting that it had encountered some unaccceptable input, and carry on to the end. We can do this by using an istringstream. This is an object which is a cross between a string and an input stream. It's like a string but you can do the sort of things with it that you can do with an input stream. To use one, you have to #include <sstream>. You would use it like this:
string s; while (getline(input_stream, s)) { istringstream is(s); // read int n; is >> n; // if (is.fail()) // { cerr << "Bad input data: continue; // } process n // } initialize a new istringstream with the string s you've just you can use >> with an istringstream i.e. if the attempt to extract an int just failed " << s << endl; // Error message to cerr (or to cout) go on to next line an int was successfully extracted from is

Since we have declared the istringstream local to the while loop, we get a new one each time round the loop. (This is not special to istringstreams; we also get a new int n each time round the loop.)
cerr is

an output stream intended for error messages; it defaults to the monitor.

is a relative of break. It terminates the current iteration through the loop and goes back to the beginning, i.e. it begins the next iteration with the evaluation of the while loop condition. Some programmers disapprove of explicitly changing the behaviour of a loop with break or continue. I am using it here in order to deal briskly with the bad data and to focus on the processing of n, which is the important part. Having the processing of n hanging off an else can look a bit unwieldy, especially if it's long and complicated.
continue

It is possible, though unlikely, that the getline function will fail before the end of the file, perhaps due to a hardware glitch. Once you have exited the loop, you can check whether you are indeed at the end of the file by using the input_stream.eof() boolean function. This returns true if you have attempted to read beyond the end of the file. By the way there is no advantage in using it as the loop condition:
while (not input_stream.eof()) { getline(input_stream, s); ... process s } // tempting but still WRONG!!

This will not work properly for the same reason that while (not input_stream.fail()) does not work, as explained above.

do ... while loops


It is possible that a while loop will not execute even once if the termination condition is met on the first iteration (in other words a while loop executes zero or more times). This is generally what you want (consider trying to read from an empty file, for example). But sometimes you need to execute a loop at least once. You can force a while loop to behave in this way, but it is more elegant to use a construct specially provided for this a do ... while loop:
string move; do { cout << "Make your move or Q to quit."; cin >> move; if (move != "Q") process move } while (move != "Q");

Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000-2010

C++ Week 4 Procedures and functions


Consider the following lines of code that print out a line of characters:
for (int i = 0; i < 20; i++) cout << "+-+"; cout << endl;

This is quite a useful procedure, and it would be nice to be able to re-use it throughout a program, rather than keep writing out the same code. This would have several advantages: if you want to change it, you only need change the procedure writing code once means you only debug once it would save a lot of typing separating out this chunk of code and giving it a name makes its purpose clearer

Such reusable pieces of code are known as functions. A procedure in C++ is a void function. This is how you define one:
void printline( ) // declares the printline procedure { for (int i = 0; i < 20; i++) cout << "+-+"; cout << endl; }

The function printline() is declared as being void (it has no return value) and it takes no parameters. This is how printline() is called:
int main( ) { ... ... printline(); // calls the printline procedure ... printline(); // calls it again ... }

The procedure always prints a line of twenty groups of +-+, and it would be more useful if we could pass an argument to the procedure to specify the number of groups to be printed. The definition would now look like this:
void printline (int len) // define the parameter len { for (int i = 0; i < len; i++) cout << "+-+"; cout << endl; }

and the call to print a line of twelve of the strings would look like this:
printline(12);

When printline() is called, we pass it the argument which the function uses as the value of len. This process is called parameter passing. (Though this is the standard term for it, it's slightly odd as the parameter is on the receiving end - it's the argument, or more strictly the value of the argument, that gets passed.) The argument is an expression which is evaluated and the value is passed to the function. The expression can be a simple integer constant (printline(12)), or a variable (printline(x)) or a more complex expression (printline(x % 3 + y / 5)). We can now add another parameter that specifies the string of characters that we want to print:
void printline(int len, string s) { for (int i = 0; i < len; i++) cout << s; cout << endl; }

The call to printline would now include a comma-separated list of arguments like:

printline(15, "*-*");

Nested loops
Loop statements can be compound, that is, they may contain several discrete statements bracketed by { } so that they are treated as a single statement. It is possible for one of the statements inside a loop to be another loop. For example:
void printsquare( ) { for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) cout << j << " "; // prints the numbers: 0 1 2 3 4 cout << endl; // prints a line return after the 4 } }

The outer for loop (the i loop) executes five times. Each time round, the inner for loop (the j loop) is executed followed by a newline, so the result is five lines with five numbers on each line. We can modify the procedure so that it takes a parameter that defines the size of the square:
void printsquare (int size) { for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) cout << j << " "; cout << endl; } } // goes round the outer loop size times // prints the numbers: 0 1 2 3 ... size-1 // prints a line return after the last digit

Modifying it to print out a square of stars would be easy. If we also wanted to make the square hollow, we could modify the nested loop as follows:
void printsquare (int n) { for (int i = 0; i < n; i++) // goes round the outer loop n times { for (int j = 0; j < n; j++) if (i == 0 or i == n-1 or j == 0 or j == n-1) cout << "*"; else cout " "; cout << endl; // prints a line return after the last star } }

Note that we don't need curly brackets for the body of the j loop. The if .. else is a single statement. The reason we need them for the body of the i loop is that it is composed of two statements - the inner for loop and the statement that outputs the newline. Finally let us make the procedure even more flexible by using parameters for height and width:
void printrectangle(int rows, int cols) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) cout << "*"; cout << endl; } }

Generally speaking we use the term procedure to refer to a routine, like the ones above, that simply carries out some task (in C++ its definition begins with void). A function is like a procedure but it returns a value; its definition begins with a type name, e.g. int or double indicating the type of value it returns. Procedure calls are statements that get executed, whereas function calls are expressions that get evaluated.

The hailstone function H(x)


The hailstone function takes an integer parameter and returns an integer. it is defined as follows For odd values of x, H(x) = (x*3) + 1 For even values of x, H(x) = x/2. We can define the function in C++ as follows:
int hailstone(int n)

{ }

if (n % 2 == 1) return n * 3 + 1; else return n / 2;

Note that a function should always return a value for every valid argument. A call to the hailstone function might look like this:
cin >> x; while (x > 1) { x = hailstone(x); cout << x; }

You can see that, although the definition used n as the parameter name, we used the variable x in the function call. The value of the argument (the value of x) is passed to the function and becomes the initial value of the parameter (n). We can use different names for them (x and n, as here) or we could use the same name for both; it would make no difference. Either way, they are different entities. Let's look at a function to calculate a percentage. It takes the dividend and divisor and returns the fraction expressed as a percentage:
double percentage (double x, double total) { return x / total * 100; }

Consider the function finalchar() that takes a string argument and returns its last character as a onecharacter string:
string finalchar(string s) { return s.substr(s.length()-1, 1); }

Now consider the following boolean function (or predicate) that determines whether the first number is exactly divisible by the second:
bool isdivby (int i1, int i2) { if (i1 % i2 == 0) return true; else return false; }

Or, more succinctly:


bool isdivby (int i1, int i2) { return i1 % i2 == 0; }

Although return is normally used to specify the return value of a function, you can still use it inside a procedure to exit the procedure; you just don't specify a return value. For example:
void procedure( ) { if (condition) return; else ...; }

The reason that you often see procedures without a return is that, in the absence of an explicit return, the procedure returns when it has finished executing, which is generally what you want anyway.

Error handling
A common problem in program design is to decide whether a function should check that the values of the parameters are acceptable or whether it should assume that any such checking has been done before the call and simply accept them. For example, isdivby will crash if the second parameter is zero. Should it check for this or simply assume that it will never be called with zero as the second argument? There is no simple answer to this question; it depends on how the program's work is divided among the separate parts, so it needs to be resolved in the context of the program as a whole. When programming, it often happens that you can see that a certain problem might arise, but you are not expecting it to arise and you don't want to spend time writing validation routines for it, but you would like to know if it unexpectedly did arise.

C++ has a feature which can help here called assertion: to use it you should #include <cassert>. Assertions perform a test at runtime and if the test is false the program fails and issues a message stating where the failure occurred. To use it with isdivby we would insert a line as follows:
bool isdivby (int i1, int i2) { assert (i2 != 0); return i1 % i2 == 0; }

If i2 is non-zero, the function proceeds as normal. If, because of some oversight somewhere else in the program, isdivby is called with i2 equal to zero, the program will terminate abruptly with a message telling you that this assertion failed. A typical use is when you are opening files, for example:
assert (not infile.fail()); // issues an error and terminates the program if the file won't open.

is useful to give the programmer some helpful feedback in the event that something unexpected occurs which makes it pointless for the program to continue. It is obviously not to be used in general as a method of error handling since it simply halts the program. Exception handling is covered much later in the course.
assert
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 5 Procedures and Functions II


Declaring Functions
Suppose you had the following:
void funcA() { .... funcB(); .... } void funcB() { .... }

As it stands, this would not compile. When the compiler encounters the call to to funcB(), it doesn't yet know that funcB() exists. To overcome this problem you have to declare funcB() before the call to funcB(), so that the compiler knows what to do when it encounters the call. The declaration of a function is known as a function prototype. It means that the function is in scope and can be called. A prototype should contain: The function's return type The function's name The function's parameter list, containing the type of each parameter (you can include the identifiers, but it is not necessary) In essence the prototype looks the same as the first line of the function's definition, but it ends in a semicolon. For example:
bool is_div_by(int, int);

or
bool is_div_by(int dividend, int divisor);

A prototype can be global. For example,


void funcB(); // global prototype of funcB. // funcB is now in scope till the end of the program, so can be called from anywhere after here void funcA() { ... funcB(); ... }

// Compiler can cope with this call, since it now knows about funcB

Or a prototype can be local:


void funcA() { void funcB(); funcA, but not outside ... funcB(); ... } // local prototype makes funcB available within the definition of

Parameter passing by value and by reference


Given a procedure header such as void display(int a, int b) and a call such as display(x, y), the arguments remain unchanged - whatever display does, it will have no effect on x and y. Only the values of x and y are passed to the procedure - the arguments are passed by value. If you wish the procedure to modify the arguments, you can pass the arguments by reference. To specify a pass by reference, place an

ampersand & after the typename in the function's parameter list. For example:
void swap (int& a, int& b)

Given the call swap(x, y), the aim of this procedure is to exchange the values of variables x and y, so the procedure needs to have access to the variables x and y - getting just their values would be no help - hence the use of passing by reference. When parameters are passed by value, the arguments are expressions, as in display(20); or display(x); or display(x + y); but the arguments for reference parameters are variables. To summarize: By Value Arguments are expressions Parameters are like local variables in the function By Reference Arguments are variables Parameters are like alternative names for the arguments

Changing the parameter inside Changing the parameter the function does not change inside the function does the argument change the argument A value parameter occupies as much memory space in the function as is necessary to accommodate its type of value, and the value gets copied into this space every time the function is called. If the value being passed occupies a lot of space (such as a large object taking up thousands of bytes), the space and time overhead of pass-by-value might be significant. You might then use pass by reference for efficiency. If you are worried about the possibility that you might modify the argument by mistake, you can use the const keyword to protect the argument:
int func (const Bigclass& b)

This is known as a "const ref" parameter.

Constants
Numbers often appear scattered throughout code, but they are a disaster from a maintenance point of view. Numbers do not lend themselves to uniqueness and it is not always clear what a number is supposed to represent or how it is related to another number. Consider the following example that shows how the relationship between two numbers could be overlooked:
if (x < 200) { ... ... for (i = 199; i >= 0; i--)

For this reason, you should avoid these "magic numbers" and use constants instead. Constants are defined using the const keyword, and constant identifiers are often written in upper case to distinguish them from variables. For example:
int const SAMPLE = 200; if (x < SAMPLE) { ... ... for (i = SAMPLE - 1; i > 0; i--)

Example - Box procedure, and default arguments


In this example we will create a box() procedure to create a box around a string. It does this by calling the line() procedure to create the top of the box, then it adds characters before and after the string and finally it creates the bottom of the box. The end result should be a procedure call and output as follows:

box ("Hello!", "*");

******** *Hello!* ********

First we must define the line() procedure


void line(string s, int n) { for (int i = 0; i < n; i++) cout << s; cout << endl; }

Now we can define the procedure box() that will actually print the box around the string argument. The character used to create the box is also passed to the procedure:
void box(string message, string ch) { line(ch, message.length() + 2); cout << ch << message << ch; line(ch, message.length() + 2); }

In order for box() to call line(), either line() has to be defined before box(), or a global prototype of line() has to appear before the definition of box() or box() has to contain a local prototype of line(). It would be nice if we could specify a default character to be used as the box outline, but which we could override if desired. This can be done by assigning a value to the parameter in the definition as follows:
void box(string s, string ch = "*") {... }

In calls to this procedure, the second argument is optional (because a default argument is provided in the procedure definition), and both of the following calls are valid:
box("abc", "+"); box("abc");

A limitation of this feature is that mandatory parameters must all come before optional parameters in the parameter list. The compiler will not accept calls like proc(a,,c);

Side-effects
A function that does something else apart from just returning its value, such as taking some input or generating some output or changing the values of variables (other than its own local ones) is said to have a side-effect. Side-effects are generally best avoided; they make programs hard to debug. Consider the following:
int silly(int& a) { a *= a; return a * 2; } // Because this is a reference parameter, // this line changes the value of the argument.

int main() { int x = 0, y = -2; x = silly(y); // This looks as though it changes x, which it does, but it also changes y !

In general, the functions you write should not have side-effects. Side-effects can occasionally have their uses, however, and some of the standard functions are actually functions that have side-effects. For example, getline not only reads in a line of input, but also returns something. Though we think of the reading in as the main thing that it does, it is technically a side-effect of the evaluation of the function. What it returns is a reference to the input stream. If the call is getline(cin, s); it returns a reference to cin. So you could, if you wanted, use the returned reference as the first argument to another getline, as in
getline(getline(cin,s1), s2);

which would read in two lines, the first into s1, the second into s2. If it fails, it returns a NULL reference. A valid reference can be type-converted to true and a NULL reference to false, which is why we can use it in expressions such as while (getline(cin,s)) The same is true of the standard I/O operators (>> and <<). When cin >> is executed, the taking of input is actually a side-effect; it also returns a reference to cin. This is why you can concatenate several of them in one statement - cin >> x >> y >> z. When the cin >> x is evaluated, it pulls some input into x and also, if successful, returns a reference to cin. This now forms the left-hand argument for the second >>, and so on.

Global variables
Variables defined within a block are local to that block. For example,
{ int x; for (int y = 0; y < 5; y++) { int z; // x, y and z accessible here } // x accessible here, but neither y nor z

} // neither x, nor y, nor z accessible here

Variables defined outside any block are global. They are accessible from the point of definition to the end of the program. By default variables in a procedure or function only have local scope. If a local variable is declared using the same identifier as a global variable, the local variable is the one used. For example:
int x; // global

void procedure1 ( ) {int x; }

// local to procedure1; its existence masks the global one

void procedure2 ( ) {int x; }

// local to procedure2

Globals should be used only sparingly, if at all. They lead all too easily to mistakes like the following:
int count; while (...) { dosomething(); count++; } ... void dosomething() { ... count++; }

// we think we're counting something

// but this procedure is using the same variable!

It's easy to imagine how mistakes of this kind in a large program (thousands of lines) could be very difficult to track down.

The extern keyword


In general you want to grant access (i.e. access to variables) to just those parts of the program that need it and to deny it to the rest. For example, suppose that main calls procedures a and b and that a and b both use the same variable x (I don't mean that each has its own local variable called x but that they need to access one and the same variable x). We can make it local to main and pass it to a and to b, but then main has access to it, which we don't want. Or, as another example, consider that main shares access to some variable z with procedure d, but that procedure d is called from within procedure c. main calls c and c calls d. If z is local to main, main has to pass it to c to pass on to d. So then c has access to z, which we

don't want. We could make our variables global, defined early in the program, but then the whole program has access to them, which we certainly don't want. One solution to this problem is to make x global but to put its definition at the end of the program. We now make it accessible to those functions that need it by means of extern declarations, thus:
void a( ) { extern int x; //procedure a can access the (global) variable x but the definition of x is elsewhere ... } ... // x not accessible here

void b( ) { extern int x; //procedure b can also access x ... } ... int x; // x not accessible here // global definition of x on the last line of the program

Lifetimes
Look at the following code:
bool three_in_a_row (bool win) { int nwins = 0; // nwins is local to the function if (win) nwins++; else nwins = 0; return nwins == 3; } { if (user_wins_game) { win = true; prize(); } else win = false; if (three_in_a_row(win)) jackpot();

The three_in_a_row function won't work because a new nwins variable is created every time the function is called and destroyed when the function returns. Local variables are auto by default, meaning that they exist only so long as the function is executing. An alternative is to define nwins as a static as follows:
bool three_in_a_row (bool win) { static int nwins;

A static variable comes into existence at the start of the program and exists until the program ends. It keeps its value from one call to the next - it provides a function with a memory. Note that lifetimes are different from scope. A static local variable is still a local variable, and therefore only accessible from within the function - being static does not affect its scope. But it exists (albeit inaccessibly) from one call to the next. In addition, static variables are initialized, by default, to zero (the initial value of an auto variable is undefined, as you know). If a static variable is explicitly initialized, the initialization takes place only once - when the execution of the function first reaches the initialization. After that, it retains its value from the previous call. All global variables are static - they exist from the start of the program to the end and they are initialized by default to zero. As another illustration of the static keyword, let's look at the following code:
#include <iostream> using namespace std; int glob; // global, therefore static, therefore initialized to zero

void proc( ) { static int statloc = glob; int autoloc = 3; cout << autoloc << " " << statloc << endl; statloc++; autoloc++; // incrementing of autoloc is pointless; it is about to disappear } int main( ) { cout << glob << endl; glob = 5; cout << glob << endl; proc(); glob = 8; cout << glob << endl; proc(); } // glob is zero // glob is 5 // statloc is 5, autoloc is 3, statloc increased to 6 // glob is 8 // statloc still 6 - it is not reinitialized

The output of this program is:


0 5 3 5 8 3 6

declarations and static variables provide a partial solution to the problem of protecting data (by restricting access to it) within the context of procedural programming, such as programming in C. Objectoriented programming, supported by C++, provides a much more satisfactory solution, with data and the functions that act on it being packaged up inside objects, as we will see later in the course.
extern

Function calls and the system stack


When a program is running, the computer reserves a portion of the memory for the system stack. When a function (or procedure) is called, an activation record for that function is created and placed on the top of the stack. This activation record will contain the function's parameters and local variables and also the return address, so that control returns to the right place in the (executable) program when the function has finished. The activation record stays on the stack until the function has finished, at which point the record is effectively destroyed. Consider the structure of the following program:
int func1(int x) { .... } int func2(int x) { int z; .... } void proc(int x) { int y; .... y = func1(x); .... y = func2(x); .... } int main() { int m; .... proc(m); .... } // C

// E

// B // D // F

// A // G

Just after main has begun (at point A), the stack will contain the activation record for main:
main's local variable m main's return address (where control returns to when the program terminates)

Then, after main has made a call to proc (so execution is at point B), the stack will contain this:
proc's proc's proc's main's main's parameter x local variable y return address local variable m return address

Then, after proc has called func1 (we're now at point C), the stack will contain this:
func1's parameter x func1's return address proc's parameter x proc's local variable y proc's return address main's local variable m main's return address

After func1 has returned and we are at point D, the stack will contain this:
proc's proc's proc's main's main's parameter x local variable y return address local variable m return address

After we've called func2, so we are now at E, we have this:


func2's parameter x func2's local variable z func2's return address proc's parameter x proc's local variable y proc's return address main's local variable m main's return address

At F we are back to this:


proc's proc's proc's main's main's parameter x local variable y return address local variable m return address

And at G we are down to this:


main's local variable m main's return address

So you can see why it is that local, auto variables come into being and disappear in the way that they do. Globals and statics are not stored on the stack. They are held in a separate part of memory and stay throughout the program.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 6 Vectors


A one-dimensional array is a row of data items, all of the same type. The word "array" is used in programming in general to describe this type of structure, but, in C++, the word "array" is used to refer rather more specifically to the implementation of this structure in C. Since C++ subsumes all of C, you can have C-style arrays in a C++ program. But C-style arrays are inflexible and in some ways awkward to use, so we will use the C++ implementation of the structure, which is a vector. Just as a C++ string is more than a mere row of characters - it can also do things for you, such as telling you how long it is or providing a substring - so a vector is more than a mere row of, say, integers or doubles. Like a string, a vector is an object and has a number of member functions. To use vectors in your code, you need the appropriate library - #include <vector>. Vectors are declared as in this example:
vector<int> ivec(4);

which declares a vector called ivec, containing four integers. In memory it looks like this: 0 0 0 0 By default the data items are initialised to zero (if they are numbers, or empty strings if they are strings). If you want to initialize the vector to a different value, add an argument to the declaration as follows:
vector<int> ivec(4, 3);

This would create a vector of four elements, each one initialized to 3. ivec looks like this: 3 3 3 3 The elements of a vector are numbered, starting from zero. So the first element of ivec would be ivec[0], the second element would be ivec[1], and so on. These numbers (the 0 and the 1) are called subscripts. To refer to individual elements of the vector you use the element's subscript inside square brackets. Note that the first element of the vector has subscript zero. In other respects, the elements of an integer vector behave just like ordinary integer variables, as in these examples:
ivec[2] = 95; ivec[0] = ivec[2] + ivec[3];

After these operations, ivec looks like this: 98 3 95 3 You can use the elements of the vector in the same way as you would any other variable of the same type, for example:
x = ivec[0] - ivec[2]; if (ivec[0] < ivec[3]) return;

The thing that goes into the square brackets is actually an expression (integer constants and single variables are simple expressions). So, as well as having things like ivec[2], you can also have ivec[x], where x is an integer variable, or ivec[x-2] or ivec[x+y-z]

Array bounds checking


When you use vectors you must take care that you do not try to reference an element beyond the bounds of the vector by using a subscript outside the valid values for that vector - for example, trying to access ivec[-1] or ivec[4] or ivec[99] given ivec as defined above. The operating system will not check this for you and you may find yourself using values from undefined portions of memory or, worse, overwriting data. At best the o/s might issue a segmentation fault error if you try to use a subscript that is

way out of range. You might also consider using the at function in preference to the square brackets. ivec.at(92) is the same as ivec[92] except that it will terminate the program if ivec[92] does not exist. The square bracket notation is widely used in other programming languages and in writing about programming generally.

Manipulating vectors
Let us create a vector v of eight integers and initialize it so that each element's value is the same as its index.
vector<int> v(8); for (int i = 0; i < 8; i++) v[i] = i;

looks like this: 0 1 2 3 4 5 6 7

The following code causes any negative elements to be set to zero:


for (int i = 0; i < 8; i++) if (v[i] < 0) v[i] = 0;

The size() member function


The size() member function for vectors, like the length() member function for strings, returns the number of elements in the vector. Using v from the previous example, v.size() returns a value of 8. Note that, because subscripts begin at zero, v.size() is always one more than the highest subscript of v.

The = operator
You can use the = (assignment) operator to assign one vector to another, for example v1 = v2, so long as they are vectors of the same type (eg both vector<int> or vector<double>). In this case the contents of v1 are overwritten with the contents of v2 and v1 is truncated or extended to be the same length as v2.

The push_back() member function


This function allows you to add elements to the end of a vector. Suppose you create an uninitialised vector as follows:
vector<int> evec;

so that evec.size() is zero. evec is effectively useless until you add some elements to it. The following statement will add an element to the end (the back) of evec and initialise it with the value 21.
evec.push_back(21);

At this point, evec[0] is 21 and evec.size() is one. Now we can add another element and initialise it with the value 33:
evec.push_back(33);

Now evec[0] is 21, evec[1] is 33 and evec.size() is two.

The pop_back() member function


This function removes the last element from the vector. A typical call looks like this:
evec.pop_back();

Unfortunately pop_back() doesn't return the value of the popped element. If you need it, you have to copy it to somewhere else before you pop, for example:
to_be_popped = evec[evec.size()-1];

evec.pop_back();

Using vectors as parameters


Let us consider a function that returns the sum of the elements of a vector of integers:
int total(vector<int> v) { int total = 0; for (int i = 0; i < v.size(); i++) total += v[i]; return total; }

Note that the vector here is passed by value, meaning that the vector's data will be copied for use locally. If the vector is small, there is no problem, but if it is large, then the parameter-passing itself could consume a lot of resources. For this reason it is a good habit to pass vectors by reference, and use the const keyword to ensure that the data doesn't get inadvertently modified. Here is a function that takes as its arguments a vector of integers and an integer. It returns true if the integer is in the vector.
bool isin(const vector<int>& v, int a) { for (int i = 0; i < v.size(); i++) if (v[i] == a) return true; return false; }

Let us look at the following code fragment for loading words into a vector:
string s; vector<string> v; while (cin >> s) v.push_back(s);

The system will continue to populate the vector v until the input stream fails. The following procedure also adds new words to a vector but does not store duplicates:
void add_word(vector<string>& vs, string s) // // { for (int i = 0; i < vs.size(); i++ ) if (s == vs[i]) return; // // vs.push_back(s); } the procedure modifies the vector, so we pass by reference (without const) don't add the word as it's already in the vector

We could call this procedure thus:


string s; vector<string> v; while (cin >> s) add_word(v,s);

C++ strings behave (to some extent) like vectors of char


In C++ a string behaves in many ways like a vector of char elements; in particular, you can use the square-bracket operator to access a single character. char is a datatype; a char is a single character. For example:
string s = "eyrie"; s[1] = s[0]; // s is now "eerie" string t = "cat"; char ch = t[0]; // ch is the letter 'c'; note that ch is of type char, not type string t[0] = t[1]; t[1] = ch; // t is now "act"

literals are enclosed in double quotes "Z" is a string of length one whereas char literals are enclosed in single quotes 'Z' is a single character value.
string

You can also define strings using the same sort of constructor function that you use for vectors. For example:

string s(20, '*'); // creates a string of length 20, initialised to asterisks; note that the second argument is a char, not a string

The library cctype contains a number of useful predicate functions for returning information about char data, for example:
isupper(ch) islower(ch) isdigit(ch) isalpha(ch)

returns true if ch is upper case returns true if ch is lower case returns true if ch is a digit returns true if ch is a letter

Many languages draw a clear distinction between the types char and int but C and C++, to a large extent, treat them as interchangeable, with implicit type conversions from one to the other. For example, you could do the following:
int char n = 'A' ch = n; cout << n; ch; + '!'; n << " " << ch << endl;

and the output would be 98 b. That is, you get the ASCII value of 'A' added to the ASCII value of '!', giving 98, and ch takes the value of the char with ASCII value 98, which is 'b'. This kind of thing is generally confusing and best avoided, but it can occasionally be useful. If, for example, you had a char variable with a digit as its value, you could convert the digit to the corresponding integer value simply by subtracting '0'. For example:
char int ch = '9'; n = ch - '0'; // 57 - 48, so n is initialized to the value 9

or vice-versa:
int char n = 9; ch = n + '0'; // ch is initialized to the value '9'

C++ strings are different from C strings. C strings are arrays of char, not vectors (more about arrays and C strings later in the course). If you needed to cast a C++ string into a C string, you would use s.c_str() (where s is a string). You can use the at function with strings, as you can with vectors, as an alternative to square brackets so as to get the benefit of array bounds checking. It can be used as follows:
string s = "hello, world!"; cout << s.at(7) << endl; // returns letter w s.at(12) = '?'; // changes ! to ? cout << s.at(12) << endl; cout << s.at(s.length()) << endl; // aborts the program - there is no s[13]

However, a string is not exactly the same as a vector <char>. For example, a string has push_back (you could have s.push_back(ch); to add a char to a string) but it does not have a pop_back. But don't overuse vectors Finally a word of warning. Because vectors in C++ are so flexible and versatile, novice programmers are inclined to overuse them. Use a vector where it's needed or where it will really help. Don't use one just because you can. Say you are writing a program that reads in a file of numbers and outputs the largest. Some programmers, especially those who have just discovered vectors, will read the entire file of numbers into a vector and will then search through the vector looking for the highest. But a moment's thought will show that the vector here is not serving any purpose. You only need to inspect the numbers one at a time, and you can do that when you read them from the file in the first place. Unnecessary vectors, apart from being a waste of computer storage, tend to clutter a program and, often,

to confuse the programmer. The vector is a very useful tool, but don't use it always and for everything.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 7 Classes I


Suppose a program begins like this:
int string Employee x; s; e;

This defines three variables. The first is just an item of data. You can do things to it, but it has no functions of its own; you can't say x.something() It might be useful if you could test whether it was an odd number with x.odd(), but you can't. It's not an object in the C++ sense; it has no member functions. The other two, however, are objects. One is defined as an object of the class string, which is one of the standard C++ classes. The other is an object of the class Employee, defined by Horstmann. Each of these has a repertoire of member functions, which we call with the dot notation, as for example in s.length() and e.get_name() If we want objects of our own design, we have to define a class. A class definition is a complete specification of what an object of that class contains (its data members) and what it can do (its member functions, sometimes called "methods").

A basic Date class


Suppose that we want to handle dates in our program. Each date will consist simply of a day and a month. We won't bother about leap years. At the most basic level we could simply use two int variables to represent the day and the month, but, so far as the compiler was concerned, there would be no more connection between them than between any other two variables. It would be up to the programmer to remember that some changes to the day variable (such as adding a day to 31 Jan) would need to be accompanied by changes to the month variable, and so on. Any linkage between the two variables would be in the code; the variables would just be separate variables. We want the two numbers to be treated as a single unit - a date. We want to have objects in our program of type Date so that we could do things like this:
int main( ) { Date d1; Date d2(18, 1); d1.display(); d2.display(); d1.add_day(); d1.display(); }

To get there we first need to define a class. We use the class keyword and a name. By convention, class names begin with a capital letter. The class definition follows inside curly braces, and ends with a semicolon after the closing brace (easy to forget). The definition specifies the interface and the data that the class will hold. It defines the access (read, write or none) you want the outside world to have to an object's data and methods. The public part comprises the methods (and occasionally data) you want to expose to the outside world; this is known as the interface. The private part comprises the data (and often also) methods you want to encapsulate or hide from the outside world. Encapsulated data and methods are not accessible from outside the objects. Before we define the class, we must decide what we want to store in it and how we want to access the data: It should hold integers representing the month and the day of the month We want to be able to create an object and specify the date it represents We want to be able to increment the date by one day We want to be able to print the date, as for example "25 Dec"

The class definition looks like this:


class Date { public: Date(); Date(int, int); void add_day( ); void display( ) const; private: int day; int month; };

The public part (the interface)


The first thing we usually declare for any class is its constructor. It's the constructor that initialises the data members. Often a class has a default constructor, which creates a default instance of the class, initialising its data to some default values. In this case the declaration takes the form Date(); This is the method that will be called by code such as Date today; the date represented by the object it creates will be the default. Next we define a constructor that allows us to create a Date object and specify the date it represents: Date(int, int); This is the method called by an initialization such as Date d(25, 12); or an assignment such as today = Date(25, 12); The add_day() method will be used to add one day to the date. It takes no arguments and returns nothing. This procedure will change the data and is known as a mutator. The last method declared, display(), also takes no arguments and returns nothing. It will be used to print the date. Since the method doesn't modify but only accesses data, it is known as an accessor function. We add the keyword const to the declaration so the compiler knows the function should not change the data fields.

The private part


Next we turn to the private part of the class declaration. Here we declare the data fields and methods we want to encapsulate. We only have two data items, day and month, both integers.

Defining the class's methods


We now have to define these functions. Since different classes might have member functions with the same name (you can see that each of several classes might have its own display procedure, for example), we have to explicitly identify the class whose member function we are defining. To do this, use the scope operator (::). We can write the member function definitions in any order. Here we begin by defining the default constructor.
Date::Date() { day = 1; // we decide what the default is going to be month = 1; }

A constructor always has the same name as the class, and it returns nothing, not even void. Note that a member function has access to all the data (and functions) in the class. We just refer to day, for example, as if it were a global variable. Next we define the add_day function
void Date::add_day( ) { if (is it the last day of the month?) { day = 1; month = month % 12 + 1; } else day++; }

This code is incomplete because we have no way yet of knowing whether it is the last day of the month. What we need is a list of integers representing days of the month 31, 28, 31 etc (remember that we are ignoring leap years). A vector of integers would do, but it is tedious to initialise the individual elements of

a vector one by one. Fortunately there is an alternative structure, called an array that is very like a vector but which can be initialised with a list of differing values all at one go. I'll say more about arrays in a later lecture. For now, all we need to know is that we can define and initialize an array of 13 constant integers, each element containing the number of days in a month, as follows:
const int mdays[ ] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

Why 13 elements rather than 12? As with vectors, the first element of an array is element zero, not element one. Since I want to consider January as month 1 rather than month 0, I've just put a dummy value (0) into the first element. mdays[1] now holds the number of days in January, mdays[2] February and so on to mdays[12]. We use the square-bracket notation, as with vectors, to refer to individual elements of an array. Similarly we can define an array of constant strings to hold the names of the months (again putting a dummy value this time the empty string into element zero):
const string mnames[ ] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

For now, we'll declare these two arrays as global. We'll see in a later lecture how to improve on this. We are now in a position to test for the last day of the month and can amend the add_day() function as follows:
void Date::add_day( ) { if (day == mdays[month]) // is it last day of the month? { day = 1; month = month % 12 + 1; } else day++; }

The display() procedure is easily defined:


void Date::display( ) const { cout << day << " " << mnames[month] << endl; }

Finally we come to the second constructor that allows us to specify the date. It could be as simple as this:
Date::Date(int d, int m) { day = d; month = m; }

However, it's possible that the parameters might be given impossible values, such as the 50th day of the 13th month, so we might decide to test the validity of the arguments supplied. We can do so either by including the <cassert> library and an assert statement or by writing some output to the cerr output stream and calling the exit function (requires the <cstdlib> library)
Date::Date(int d, int m) { assert(m >= 1 and m <= 12 and d >= 1 and d <= mdays[m]); // assert requires the <cassert> library /* alternatively we can use the exit function as follows: if(m < 1 or m > 12) { cerr << "Invalid month: " << m << endl; // Goes to the cerr output stream exit(1); // Requires <cstdlib> library } if(d < 1 or d > mdays[m]) { cerr << "Invalid day: " << d << endl; exit(1); } */ day = d; month = m; }

Note that exit is very different from return. Whereas return terminates the procedure or function and returns control to the place where it was called from, exit terminates the whole program. The class and all its member functions have now been defined. We now have everything in place to compile and run our main() function:
int main( )

Date d1; Date d2(18, 1); d1.display(); d2.display(); d1.add_day(); d1.display();

Note the effect of encapsulation. It would not be possible to add a line like this to main():
d1.day = 31; the day data-item. // Can't be done. From outside the class there is no access to

Click here to see what our class now looks like.

Copying objects
With simple classes (where the objects do not contain explicit pointers - a topic we deal with later in the course), there are two operations that are provided and which we therefore do not have to define. We can use a copy constructor. That is, we can create a new object and initialize it with an object of the same class as follows:
Date d1(14, 10); Date d2(d1); // Create d2 as a copy of d1; this is called a copy constructor

And we can use assignment we can assign one object to have the value of another:
Date d3; d3 = d1; d2 = Date(17,8); // Give d3 the same value as d1 // Give d2 the value of a temporary Date object with value 17th Aug

Overloading operators
We now have a basic Date class that works, but say we wanted to use an expression of the form if (thisdate == thatdate) ... If we tried this with the current Date class the compiler would complain: the operator == is not defined for the Date class. One way round this would be to define a public member function equals() as follows:
bool equals (Date) const; // prototype in the public part of the class definition, const because accessor ... bool Date::equals (Date dx) const { return month == dx.month and day == dx.day; } ... if (thisdate.equals(thatdate)) ...

This works fine. There are a couple of things to note about this code: You need to use the dot operator to access the members of those objects passed as parameters (the explicit parameters) but not for the members of the object whose member function is being called (the implicit parameter). That is to say, if we just use the word month in this function, it refers to the month variable of the Date thisdate - the one on the left of the dot in if (thisdate.equals(thatdate)) - whereas if we want to refer to the month variable of the explicit parameter dx (which corresponds to thatdate in the call) we have to say dx.month. If you prefer, you can make it obvious when you are referring to the implicit parameter by writing this->month and this->day instead of just month and day, so you would write return this>month == dx.month and this->day == dx.day; This is not necessary, but some people feel it makes it clearer. We will see in a later lecture what this "this->" actually is. It looks at first sight as though the references to dx.month and dx.day are breaking the rules about encapsulation. Effectively, thisdate is directly accessing the private data of thatdate. In fact it's OK because the parameter dx happens also to be a Date. Encapsulation is enforced at the level of the class, rather than at the level of the object. It's as if the compiler says, "Are we at a place in the program that has access to the private section of Date?" Since we are inside a member

function definition belonging to the Date class, the answer is obviously "Yes". However, we can go one step better than this; we can overload the == operator in the public interface so that it will give us the functionality of the equals() method.
bool operator== (Date) const; ... bool Date::operator== (Date dx) const { return month == dx.month and day == dx.day; define a special == for Dates }

// We are using the ordinary == operator to

We have now overloaded the == operator for Dates, i.e. given it a special meaning in the context of Dates, so we can go ahead and say:
if (thisdate == thatdate) ..

An overloaded operator is a function that we are allowed to call like an operator. But it is still a function and, if we wanted, we could call it in the more familiar way - if
(thisdate.operator==(thatdate)) ...

In the same way we can overload the < operator and use it to test whether one date is earlier than another. Here we are passing the explicit parameter as a const ref parameter; this is customary with objects since objects are likely to be large structures, for which call-by-value would be inefficient.
bool operator< (const Date&) const; //public declaration ... bool Date::operator< (const Date& dx) const { return month < dx.month or (month == dx.month and day < dx.day); }

We need to have const twice because they state different things. In if (thisdate < thatdate), the const inside the parameter list (const Date& dx) promises that this function will not change thatdate while the const at the end of the function header promises that this function will not change thisdate. Note that it is only existing operators that we can overload; we cannot take any old character and turn it into an operator. Nor can we change the number of arguments that it takes or its place in the precedence table. Code of the Date class
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000/8

C++ Week 8 Classes II, and integer representation


More on arrays
A one-dimensional array is like a one-dimensional vector; it's a row of items of the same type in which you can refer to individual items with the square-bracket notation. You could define an array of, say, seven integers as follows:
int harry[7];

The compiler would reserve enough storage for seven contiguous integers, and you could refer to these as harry[0], harry[1] and so on up to harry[6] (the first element is always element zero). Note that, when you are defining an array, as above, harry[7] does not mean "element seven of the array harry", but rather, "I want an array of seven elements, called harry." Given the above definition, the individual integers in harry would have undefined values. You could initialize the values and this is a feature of arrays that vectors don't have as follows:
int harry[7] = {42, -5, 68, 91, 3, 1002, 12};

Or you might want to initialize just the first few elements:


int harry[7] = {42, -5, 68, 91};

in which case the remaining elements would be initialized to zero. If you don't do any initialization, you get undefined values, but, if you initialize any of the elements, even if it's only the first one, then all the rest get initialized to zero. So you could initialize the whole lot to zero with just:
int harry[7] = {0};

If you initialize all the values explicitly, you don't have to specify the length of the array. You could define harry as follows:
int harry[ ] = {42, -5, 68, 91, 3, 1002, 12};

In this case, the compiler calculates the length of array on the basis of the initialization. Arrays are not objects in the C++ sense there is no array class (as there is a vector class and a string class). So they don't have any functionality. We cannot say things like harry.size() or harry.push_back(77) The length of an array is fixed at compile time; you cannot grow it or shrink it during the run of the program, as you can with vectors.

A simple Stack class


A linear list is, conceptually, just a series of items one next to another. A stack is a linear list where you are limited to putting an item on the stack (known as "pushing") or taking an item off the stack (known as "popping") and where both these operations take place at the same end of the list. It's sometimes called a LIFO list (Last In First Out). It is one of the fundamental data structures widely used in programming. The following is one possible implementation of a stack, which uses an array to store the items (here integers, but, in principle, items of any type):
const int MAX = 10; class Stack { public: Stack(); void push(int); int pop(); private:

int int };

a[MAX+1]; top;

// we won't be using a[0] // "stack pointer"

Stack::Stack() { top = 0; } void Stack::push(int x) { if (top >= MAX) { cerr << "Stack overflow!" << endl; return; } top++; a[top] = x; } int Stack::pop() { if (top == 0) { cerr << "Stack underflow!" << endl; return 0; } int returnval = a[top]; top--; return returnval; }

A simple Queue class


The queue, another fundamental data structure, is also a linear list with restricted access, but, this time, items are added to the queue at one end but removed at the other a FIFO (First In First Out) list.
class Queue { public: Queue(); void add(int); int remove(); private: int a[MAX+1]; // we won't be using a[0] int front, rear, n; // n = number of items in the queue }; Queue::Queue() { n = 0; front = rear = 1; }

// any value >= 1 and <= MAX

void Queue::add(int x) { if (n == MAX) { cerr << "Queue overflow!" << endl; return; } n++; rear = (rear == MAX ? 1 : rear + 1); a[rear] = x; } int Queue::remove() { if (n == 0) { cerr << "Queue underflow!" << endl; return 0; } n--; front = (front == MAX ? 1 : front + 1); return a[front]; }

This implementation of the queue is perhaps less intuitively obvious than that of the stack. When people are in a queue (for tickets, say) and someone at the front gets their ticket and leaves, the whole queue shuffles forwards. In this array implementation, however, there is no shuffling forwards, so the whole queue, in a sense, moves backwards through the array, as items are added to the rear and removed from the front. When the rear has reached a[MAX], the next addition is at a[1] (assuming it has been vacated by items leaving the queue). The effect is that the queue, so long as it does not overflow, moves round and round the array.

static const int


In the above, I defined the const int as a global. Although this will work, it seems unsatisfactory since the length of the array is a feature of the data structure used by the objects of type Stack or Queue. In other words, this const int ought to be private to these classes, just as the array definitions are. We can make it private to our Stack definition as follows (and we could do the same for the Queue):
private: static const int MAX = 10; int a[MAX+1]; // we won't be using a[0] int top; // "stack pointer"

Defining it as "static" means that it belongs to the class as a whole rather than to individual objects of that class. If we had several objects of type Stack in our program, then each of them would have its own array and its own top variable, but they would all share a single const int. You can have static variables, as well as static constants. For example, a class that dealt with currency exchange might have an exchange_rate variable. This would be a variable it would change from time to time but you would not want different objects of the class to have different values of it. You would want them all to share the same, current value, and you could ensure this by making it static. With variables, you can decide whether to make them static or not, but with constants you have no choice they have to be static. (It is actually only constants of integral type that you can define within the class definition, as in the example above; you can have private, static constants of other types but defining them is more long-winded, as we'll see in a later lecture.)

Representation of integers
When you compile programs containing expressions such as i < s.length() or i < v.size(), the (Borland) compiler gives you a warning about comparing signed and unsigned values. What is it warning you about? The unsigned integers are all the integers >= 0; there are no negative ones, so we don't need to worry about a minus sign, hence the name "unsigned". The internal representation of unsigned integers is very straightforward; the computer simply stores the integer in binary. (If you are unclear about binary numbers, have a look at "A lesson on octal, hex, and binary".) Supposing we had six bits to represent an integer (32 bits would be more realistic), then the range would go from 000000, representing 0, to 111111, representing 63. Storing signed integers, which include negatives as well as positives (signed integers are denoted by int in C++), is not so easy. One way, known as sign and magnitude, reserves one bit for the sign, usually using 0 to mean "positive" and 1 for "negative", and the remaining bits for the magnitude. Given our six bits, then 9 would be represented as 001001 (0 for the sign, then 01001), while -9 would be 101001. Though conceptually simple, this arrangement does not lend itself to easy implementation in hardware, and it has the peculiar feature of having two representations for zero: 000000 and 100000, the latter being a mysterious "negative zero". More common is a system called two's complement. Continuing with our six bit example, the numbers 0 to 31 would be represented exactly like their unsigned versions: 000000 to 011111. All the patterns beginning with 1, however, represent negative numbers, from -32 (100000) to -1 (111111). (If you forget everything else about two's complement, remember at least that all ones is minus one, and you can probably work out the rest from that.) To get the two's complement representation of a negative number, say -9, you first write down the positive version (001001) then flip all the ones to zeros and all the zeros to ones (110110) and finally add 1 (110111). (To get minus one, you start with positive one (000001), then flip (111110), then add 1, giving 111111.) A great advantage of this system is that simple addition of two numbers, regardless of whether they are positive or negative, gives the right answer, provided that the answer is within our range (i.e. from -32 to +31). For example:
-9 + -20 = 110111 101100 100011 which is -29

(Actually, adding 110111 and 101100 gives a seven-bit number 1100011, but the "carry" bit falls off the left-hand end of our six-bit range and we just ignore it.)
+ = -9 20 110111 010100 001011 which is 11

Subtraction is easy. To calculate x - y, we just calculate x + -y. For example


9 4 001001 000100

converts to + = 9 -4 001001 111100 000101

which is 5

But what happens if the answer lies outside our range? Let's try 20 + 15, which gives 35, which is higher than our maximum of 31:
+ = 20 15 010100 001111 100011 which is -29

If the answer lies outside our range, our calculations will simply yield a wrong answer. This is known as "integer overflow". It would be possible for the run-time system to detect this and to terminate the program, as it does with division by zero, or at least output a message. If adding two positive numbers yields a negative result, or adding two negative numbers yields a positive one, then we have overflow. (We are never going to have overflow from adding a positive and a negative.) In fact C++ does not do this. A C++ program will cheerfully generate integer overflow and produce nonsense results without a hint of warning. For example, the largest signed integer that can be represented in 32 bits is 2,147,483,647. If you set an int variable equal to this value, then added, say, 2,100,000,000 to it, this would obviously cause overflow since the correct result of 4,247,483,647 is too large to be represented. But the program would give you no warning about this and, if you then output the result, you would get -47483649. However, C++ does at least enable the programmer to prevent it. If you include the climits library, you can use the values INT_MAX and INT_MIN, being the largest and smallest signed integer values that can be represented in your implementation. (It can vary between implementations depending on how many bytes are allocated to the storage of an integer.) Suppose you have two int variables big and x, and you are about to add x to big, but you think that big might be close to INT_MAX. You might deal with this as follows:
if (INT_MAX - big >= x) big += x; else cerr << "Integer overflow adding " << x << " to " << big << endl;

Note that it is no use to write, as students are sometimes tempted to:


if (big + x > INT_MAX)

Consider what would happen in our six-bit world, where INT_MAX is 31, if big was 20 and x was 15. big + x would be -29, not 35 (see earlier example). In fact signed_int_value > INT_MAX can never be true.

Mixing signed and unsigned values


To return to the compiler warning about comparing signed and unsigned values, the first thing we need to know is that s.length() and v.size() return unsigned integers. This is reasonable since the length of a string, or the size of a vector, can never be negative. The second thing we need to know is that, if you do arithmetic using a signed and an unsigned integer, the result is unsigned. This is probably not what you would expect. Suppose you had an expression such as s.length() - 1 and suppose that the string was empty. Let's do it with our six-bit integers:

s.length() - 1, which converts to =

0 + -1

000000 111111 111111

which is -1 as a signed int, but 63 as an unsigned int

So, if this was part of a for loop, such as for (int i = 0; i < s.length() - 1; i++) and the string s was empty, the condition being evaluated would not be 0 < -1 (therefore false, which is what you'd expect), but 0 < 63 (therefore true). The same applies when you are simply comparing a signed value with an unsigned one; the signed value gets converted to unsigned before the comparison takes place. Suppose you had the following:
cout << (-1 < v.size() ? "Yes" : "No") << endl;

You might expect this to output "Yes" whatever the size of v, since -1 is obviously less than zero or any positive integer, but the reverse is the case. Because v.size() returns an unsigned int, the value -1 is converted to unsigned for the comparison. Since -1 is all ones in two's complement, this converts to a very large unsigned int. So, the compiler warning is telling you to be careful. If all parts of the expression are always going to evaluate to zero or more, as in, for example for (int i = 0; i < s.length(); i++), you will not have a problem. But, if there is any chance that any part of the expression will yield a negative value (as a signed int), which will therefore be interpreted by the computer as a very large unsigned int, you might be in trouble. The solution to this problem is to cast the unsigned value into a signed one, with static_cast<int>(unsigned value), as in this example:
for (int i = 0; i < static_cast<int>(s.length()) - 1; i++)

The effect of this is to type-convert the zero returned by s.length() from an unsigned int to a signed int, so that the evaluation of 0 - 1 yields -1, which is what you want. There is an old-style form of cast, inherited from C, which looks like this:
for (int i = 0; i < (int)s.length() - 1; i++)

However, this is deprecated. That is to say, it is retained in the language in order to preserve compatibility with previous versions, but it is considered an undesirable feature and programmers are urged not to use it. The reasons for its undesirability are that ill-considered type casts are a cause of subtle bugs and they don't stand out clearly to someone reading the program. Typing out the static_cast rigmarole, by contrast, forces a moment of reflection, and it stands out on the page like a sore thumb. One final point. Some students take to writing for (unsigned int i = 0; i < s.length() - 1; i+ +) and, because the compiler stops giving them a warning, they think they have dealt with the problem. But of course they haven't. The compiler stops giving its warning because now both i and s.length() 1 are unsigned int, so they are not mixing the types. But, if s was empty, s.length() - 1 would still be a very large number. It's the type of s.length() that needs to be modified, not the type of i.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000/2008

C++ Week 9 Dynamic Data Structures I


So far in the course we have only been looking at variables and objects defined at the start of the program. Now we are going to be considering objects that are explicitly created (and destroyed) during the running of the program. When we declare an int we reserve a portion of memory (a few bytes) that will be used to store an item of integer data. This piece of memory has an address. We can declare an integer pointer, int* p, which can contain the address of an integer - where it is found in memory. A pointer in C++ is always a pointer to a particular type of thing - a pointer to an int (int*), a pointer to a double (double*), a pointer to a string (string*), or whatever. The type of a pointer depends on the type of thing that it points at. When you declare a pointer its value is initially undefined, that is to say, it holds some address but you don't know what address (you don't know where it's pointing), so at some stage you should assign an address to it. You do this by assigning it an already existing address or by using the keyword new as follows:
int z = 25; int* p; p = &z; // declare an integer and initialise it // declare an integer pointer // make p point to z by assigning it the address of z (&z)

Note that the int value is held in memory in the space assigned to z. p does not contain an integer data value, but the address of the integer z.
int* p; p = new int; data // creates an integer pointer // makes it point at an uninitialised piece of memory that can hold integer

Or we can do these two things in one line:


int* p = new int; // creates an integer pointer and makes it point at an uninitialised // piece of memory that can hold integer data

This code: Declares an integer pointer (int* p). Allocates enough memory to hold an integer variable (new int) and returns its address. Initializes the pointer p with the address of that piece of memory (=). The value stored in this portion of memory is as yet undefined. The only way to get at it is through the pointer, and if you lose the pointer, you can't get the memory back. More about this later.

So how do we put a value in the portion of memory that p points to? We apply the dereferencing operator * on p and then we can assign a value to it:
*p = 25;

If p is a pointer, *p is the thing it is pointing at. We can treat *p in exactly the same way as we would any other integer variable, because that is what it is!

A note about declaring more than one pointer


Normally declarations of pointers place the asterisk next to the typename, as in
int* p;

Here we are declaring p as a pointer of type int*. However, when you declare more than one pointer, you must use a * operator on each subsequent variable in the declaration. If you don't you will declare the wrong type of variable:
int* p, *q; // p and q are both integer pointers int* p, q; // p is an integer pointer, but q is a plain old integer

If you find yourself forgetting this, you can always declare pointers one per line.

Using pointers
Consider the situation created by the following code:
int* p = new int; *p = 25; cout << *p << endl; int* q; q = p; cout << *q; // // // // // // // declare an integer pointer p, create an integer in memory and make p point to it assign the value 25 to the integer p points to print the value stored in the segment that p points to (= 25) declare another integer pointer q make it point to the same place as p points to print the value stored in the segment that q points to (= 25)

Note that on its own, p = new int; declares an integer storage space in memory and makes pointer p point at it. It doesn't create p. You do that in the ordinary way by declaring a variable of type int*. The line int* q; only declares a pointer, assigning memory space for the address of an integer, but it doesn't point anywhere (it's uninitialised) and the statement doesn't assign any memory space for the integer data. It is also worth noting that q = p; means that q is pointing to the same place p is pointing at - it does not mean that q is pointing at p. (If we wanted q to be a pointer to an integer pointer we would have to declare it as type int**.) We can write a slightly condensed version of the above code fragment as follows:
int* cout int* cout p = new int(25); << *p << endl; q = p; << *q; // initialize the new int when creating it // initialize the pointer q when defining it

What is now the effect of assigning a new value to *q ?


*q = 34;

Since p and q are currently pointing at the same thing, any changes to *q will also affect *p.

But p and q do not have to point at the same thing for evermore. If there was another int, we could make q point at it, or we can create a new int for q to point at:
q = new int(56); // creates a new integer in memory, // assigns the value 56 to it and then makes q point at it

Finally, we can create another integer = 78 in memory and make p point at it:
p = new int(78);

Unfortunately by moving p from 34 to 78 we have no way of getting back hold of 34. We have in effect 'let go of the balloon' which is now floating in memory, taking up space which we can't re-use. This effect is known as memory leakage, and the piece of memory containing the 34 is known as garbage. The runtime systems of some languages have garbage collection built in, but C++ doesn't and you have to be careful. If you leak too much memory, the system will run out of RAM and crash! So what do you do when you want to make a pointer point to something else? Before you let go of the object you are pointing at, you have to delete it. e.g.
delete p; // Release the space that p is pointing to so it can be re-used // by the operating system p = new int(78); // make p point to a new variable

If you delete the object that is being pointed at, then, unless you immediately give the pointer a new value, it is a good idea also to set the pointer to NULL - undefined pointers are a common source of program bugs.
q = NULL;

Objects that contain pointers to other objects


If you create classes that contain pointers you can set their pointers to point to other objects that in turn point to other objects, creating long chains called data structures. For example, consider this simple Node class that contains a data field that can point to another Node object:
class Node { public: int n; Node* link; };

This is a simple form of class, containing only data items, and all public. It could have all the things you usually find in a class, such as a private section and a constructor, but in this case we are choosing not to use those features. Objects of this class are simply aggregations of data, without any functionality. Most programming languages have the facility to define such data types, often called something like a record or, in C, a struct. Having defined a Node class, we can define a variable of type Node*:
int main( ) { Node* p = new Node; } // declare a Node pointer, p, and assign space for a new // Node and make p point at it

This creates the following structure in memory:

Now, how can we initialise the data members of the Node *p? Code like
*p.n = 5;

looks a good bet, but unfortunately the rules of precedence mean that this is interpreted as:
*(p.n) = 5;

which makes no sense as we do not have an object called p with a pointer member called n. So we have to use brackets to force the order of evaluation:
(*p).n = 5;

In C++ this is a very common operation so there is an operator that performs the same action, ->:
p->n = 5; p->link = NULL; // sets the integer member of *p to 5 // sets the pointer member of *p to NULL

ptr->mem

picks out the member mem of the object pointed at by ptr.

Now let's create a new Node q and make p point at it:


Node*q = new Node; q->n = 3; q->link = NULL; p->link = q;

// *p's link field now points to *q

This is the situation in memory:

By stringing many of these Node objects together we can create a structure called a singly-linked list. Click here to see the code for such a list. Singly-linked lists are chains of nodes, each containing a pointer to the next item in the list. Assuming the input A B C D to the above program, in memory we will have the following structure after the input, and the output will be D C B A, leaving q pointing at node D and p set to NULL (i.e. not pointing anywhere).

If we now perform the action cout << q->link->ch; on the above structure, we get the output C, the ch member of the node pointed at by the link member of the node that q is pointing at.

A circular linked list


If we add the following procedure to the end of the previous code, the output is ADCBADCB...in an unending loop. The pointer p moves to q (i.e. points to node D) and thence scrolls along to node A. Then it sets the link of node A to point at node D (making the list circular). Finally, it prints A, followed by the next node in the list (D) followed by C and so on...
p = q; while (p->link != NULL) p = p->link; p->link = q; do { cout << p->ch; p = p->link; } while (p != NULL);

Click here for a full program listing.

The heap
We have in previous weeks seen three areas of memory that a C++ program makes use of. The first, obviously, is the area where the program's executable code resides. Then there is the part of memory reserved for static (as opposed to auto) variables; these are globals and local statics that remain throughout the life of the program. Then there is the system stack that holds parameters and local auto variables - the ones that are local to functions and which come into existence when a function begins and disappear when it finishes. Dynamic data structures are supported by yet another area of memory, the heap. This is a large area of

currently unused memory that the system can allocate to your program when you call the new function. The word "heap" is chosen to indicate that there is no particular relationship between one piece of allocated memory and another - they might be next to each other in memory or they might be miles apart. It's as though there is a big heap of memory and you just get given a chunk. When you do a delete, it just gets thrown back on the heap, and could be reallocated in response to another new. The system keeps track of which chunks have been allocated and which are still on the heap, but, if you lose all your pointers to a chunk that has been allocated to your program, it does not return to the heap. Hence the problem of memory leak. (There is also a data structure called a heap, notably used in a sorting algorithm called heapsort. This is a quite different use of the word "heap".)
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 10 Dynamic Data Structures II


Adding nodes to a list
Let's go back to our basic Node class and create three Node* pointers head, x and y.
class Node { public: int n; Node* link; }; ... Node* head, *x, *y;

At this point head, x and y are not pointing anywhere as they have not been initialised. Let us imagine now that we have created a linked list, where head points to the head of the list and x at the last item in the list (i.e. the one with the null pointer).

Creating a new node


To create a new node, make y point at it, and initialise the node as follows:
y = new Node; y->n = 33; y->link = NULL;

Adding at the head of the list


We can add this new node at the head of the list as follows:
y->link = head; head = y;

This works perfectly well even if the list is empty (i.e. the head pointer is NULL).

Adding at the tail of the list


If we have a pointer to the tail node of the list, then adding a new node at the tail end is easy:
x->link = y;

However, quite often the only list pointer we have access to is the head pointer. To get to the tail we have to scroll along the list until the end. We want a pointer that will stop while still pointing at the last node. Thus our termination condition is that the node's link field is NULL. Once we have a pointer to the end of the list, we can make it point to the node we want to add:
x = head; while (x->link != NULL) x = x->link; x->link = y; // assumes that head itself is not NULL

Removing the head node


When removing a node, beware of memory leak; remember to give yourself a pointer to the node that is about to be removed before you lose your pointer to it:
Node* zap = head; head = head->link; delete zap; // zap and head both point at the head node // move head along // No memory leak

Stack and Queue again


We now have the operations we need to implement a stack as a singly-linked list:
class Stack { public: Stack() {top = NULL;} void push(int); int pop(); private: Node* top; // assuming the Node class has already been defined }; void Stack::push(int x) { Node* p = new Node; p->n = x; p->link = top; top = p; } int Stack::pop() { if (top == NULL) deal_with_underflow(); else { Node* zap = top; top = zap->link; int return_value = zap->n; delete zap; return return_value; } }

If we keep a pointer (here called rear) to the tail of the list, we can implement a queue also:
class Queue { public: Queue() {front = NULL;} // if front is NULL, the queue is empty, whatever the value of rear may be void add(int); int remove(); private: Node* front, *rear; }; void Queue::add(int x) { Node* p = new Node; p->n = x; p->link = NULL; if (front == NULL) // putting the first node into an empty queue front = p; else rear->link = p; rear = p; } int Queue::remove() { if (front == NULL) deal_with_underflow(); else { Node* zap = front; front = zap->link; // if removing the final node, front becomes NULL int return_value = zap->n; delete zap; return return_value; } }

Note that, in these implementations, all the data is kept on the heap. The only thing that a Stack object itself contains is a pointer (or two pointers in the case of the Queue). This has implications for the assignment operation and the copy constructor, which we will address in a later lecture.

Adding nodes in the middle of the list


The next trick is to add a node between two others. We have to make one node point to the new one, and the new node point to the next in the list. Assuming that x is pointing at the '99' node, then to add a node after 99 we use the following code:
y->link = x->link; x->link = y; // make the new node point at the 27 node // make the 99 node point at the new node

To perform the same operation, but starting from the head node, we have to stop at the node before the insertion point. Remember that a singly-linked list is a one way street!
x = head; while (x->n != 99) x = x->link; y->link = x->link; x->link = y; // assumes that head is not NULL and that there is a 99 in the list

Removing nodes from the middle of a list


To remove a node from a list we have to do three things: 1. use a handle pointer to keep hold of the unwanted node 2. make the node before the unwanted node point to the node after the unwanted node 3. delete the unwanted node The order here is VERY important. If you don't use the 'handle' pointer, the unwanted node will 'float away' when you close the gap, causing a memory leak. if you delete the unwanted node before closing the gap, you will lose the pointer to the bottom end of the list. Let's assume that we want to delete the '99' node. To make the example easier, let's assume that there is a '99' node in the list, that it is not the head node, and that there are at least two nodes in the list. (In a real program, you cannot make such assumptions, of course.) The code for removing the '99' node is as follows:
x = head; while (x->link->n != 99) x = x->link; Node* zap = x->link; x->link = zap->link; delete zap; // stop when you get to the // node before 99 // avoid memory leakage

The above code will work, but expressions such as x->link->n are not recommended. It is hard to keep track of what exactly you are doing if you reference the node beyond the one you are pointing at. This would be better:
Node* x = head, *zap = head->link; // we're assuming at least two nodes and 99 is not the head node while(zap->n != 99) // we're assuming there is a 99 node { x = zap; zap = zap->link; } // Now we've found it. zap points at it, x at the one before. x->link = zap->link; delete zap;

Pointers as parameters
Let's take one of the examples we began with - adding a node to the tail of the list - and turn it into a procedure. If we had a list with a pointer variable called head pointing at the head node, the call might be addnode(head, 33); The procedure might look like this:
void addnode(Node* h, int val) { Node* y = new Node; y->n = val; y->link = NULL; Node* x = h; while (x->link != NULL) x = x->link; x->link = y; } // Create a new node to put on the end // Find the tail node // Link the new node to the tail node

What would happen if the list was empty, i.e. if the head pointer was NULL ? The procedure would get as far as x->link and crash, because x would be NULL - not pointing at anything. Presumably, if head were NULL, we would want it to be pointing at a new node by the end of the procedure. How about this?

void addnode(Node* h, int val) { Node* y = new Node; y->n = val; y->link = NULL; if (h == NULL) { h = y; return; } Node* x = h; while (x->link != NULL) x = x->link; x->link = y; }

// Create a new node to put on the end // special case - empty list

// Otherwise, find the tail node // Link the new node to the tail node

This looks OK, but it won't work. This is because h is a value parameter. Recall that a value parameter is like a local variable, so h is like a variable local to the addnode procedure, separate from the argument. When the procedure gets called, as in addnode(head, 33); the value of head is passed over and becomes the initial value of h. So, if head is NULL, h also becomes NULL. In that case, addnode will build a new node, but will attach it to h, not to head. head will remain NULL (and you'll also have some memory leak since, when addnode returns, you lose your pointers to the new node). If you want addnode(head, 33); to have the ability to change the value of the head pointer itself (not the thing it might be pointing at), you have to pass it by reference:
void addnode(Node*& h, int val)

Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 11 Dynamic Data Structures III


A Deque class
A deque (double-ended queue) is a linear list where we can add items and remove them at both ends. We already have definitions of functions to add-at-the-rear and remove-from-the-front (see last week's definition of the Queue class). It remains to add-at-the-front and remove-from-the-rear. The first of these is fairly straightforward:
void Deque::add_at_front(int x) { Node* p = new Node; p->n = x; p->link = front; if (front == NULL) rear = p; front = p; }

The second is more tricky:


int Deque::remove_from_rear() { if (front == NULL) deal_with_underflow(); else { int return_value; if (front == rear) // only one node in the queue { front = NULL; return_value = rear->n; delete rear; } else { Node* p = front; while(p->link != rear) // find the next-to-the-last node p = p->link; p->link = NULL; // and detach the rear node return_value = rear->n; delete rear; rear = p; } return return_value; } }

Remove-from-the-rear is not only complicated; it is also inefficient. Every time it is called, it has to traverse the full length of the list looking for the next-to-the-last node. This is because of the one-way nature of the singly-linked list.

Doubly-linked lists
An alternative implementation, which removes this particular inefficiency, is to use nodes that have two pointers rather than one, so as to produce a doubly-linked list:
class Binode { public: int n; Binode* l, *r; };

// "l" and "r" for "left" and "right"

And, rather than go through the process of initializing a new Binode explicitly every time we create one, let's have a constructor in the class:
class Binode { public: Binode(int x) { n = x; l = r = NULL; } int n; Binode* l, *r; };

Now, to create a new Binode we do something like this:

Binode* p = new Binode(5); initialize it

// creates a new Binode and calls the constructor to

and we get: A doubly-linked list looks like this: Adding or removing nodes is going to be equally easy at either end of the list.
class Deque { public: Deque() { left = right = NULL; } void add_left(int x); void add_right(int x); int remove_left(); int remove_right(); private: Binode* left, *right; // assuming the above definition of Binode }; void Deque::add_left(int x) { Binode* p = new Binode(x); p->r = left; if (left == NULL) // putting first binode into empty deque right = p; else left->l = p; left = p; } int Deque::remove_left() { if (left == NULL) deal_with_underflow(); else { Binode* p = left; left = p->r; if (left == NULL) // removing the last binode from the deque right = NULL; else left->l = NULL; int return_value = p->n; delete p; return return_value; } }

For the operations at the other end, just switch left and right, and l and r.

public or private data?


A possibly unsatisfactory feature of our Binode class (like our Node class) is that all of its data fields are public. At first sight this seems a bit fishy, as you don't want anything messing with the contents of your deque. As it happens, because the only way to get at the Binode list is through left or right, and since these are private, the data is effectively encapsulated. If we wanted to make quite certain that nothing except a Deque object could make any use of a Binode, we could make the Binode class private to the Deque class by adding the following code to the declaration of Deque:
private: class Binode;

and modifying the declaration of Binode:


class Deque::Binode {... };

It would also now be necessary to define the Deque class before the Deque::Binode class. Now, other parts of the program could define their own Binode classes, but this particular Binode class would be private to the Deque class. If we were really determined not to have public data in our Binode class some people would never have public data, as a matter of principle we could declare the data members as private, but then we would have to arrange for Deque objects to be able to access them. We could do this, obviously, by providing accessor and mutator functions or we could fix it at a stroke by making the Deque class a friend of the

Binode

class, by modifying the definition of Binode:

class Binode { public: Binode(int x) { n = x; l = r = NULL; } private: int n; Binode* l, *r; friend class Deque; };

This friend line does not define a member of the Binode class, so it is neither public nor private. It declares that the Deque class has special privileges with respect to the members of the Binode class: the member functions of the Deque class can access the private members of a Binode. Some programmers do not approve of the friend feature of C++. The idea behind object-oriented programming, they would say, is that we can see, from inspecting the interface in a class definition, how the private data members of an object can be altered; we do not need to fear that data will be changed in a way that is not obvious from the class definition. Yet this friend feature, they point out, provides a backdoor route into an object; private data can be changed in a way that is not at all obvious from the object's own interface but is only to be found in the function definitions of a different class. On the other hand, it provides a convenient way of permitting close collaboration between objects of two intimately related classes.

Binary trees
Another structure that we can create with Binodes is a binary tree. In the context of binary trees, a Binode* may be: NULL, in which case it represents an empty binary tree, or pointing to a Binode, which is the root of a binary (sub)tree Pointers from one Binode down to Binodes at the next level are called branches. Binodes that don't have any branches are known as leaf nodes. We can also define a binary tree recursively by saying that a binary tree is either empty or consists of a Binode with a binary tree on each side. The nodes are all related to one another as parents, children and siblings (brothers and sisters). Let's look at how to build a binary tree. We always begin with a NULL pointer and add Binodes:
Binode* root = new Binode; root->l = root->r = NULL; // initialise the member pointers to NULL root->n = 5;

Or we could use the Binode constructor as follows:


Binode* root = new Binode(5);

We can now start populating the tree:


root->l = new Binode(6); root->r = new Binode(7); Binode* p = root->l; p->l = new Binode(8); p->r = new Binode(9);

This creates the following structure in memory:

Binary search trees


The nodes in the above tree are in no particular order. However, if we arrange the values so that the values in all the nodes in the subtree to the left of the parent are less than the parent's value (note: all the values in the whole subtree, not just the values in the child nodes) and all the values in the subtree to the right of the parent are greater than the parent's value, it becomes very easy to decide whether a particular value is in the tree. If we rearrange our tree as follows, we turn it into a binary search tree: Note that, in order for this to be a binary search tree, it is not sufficient merely that the left child of the

root has a lower value (6) than the root node (8) but that all of the left child's children (5 and 7) also have values less than the root.

Building a binary search tree


Suppose you have a series of numbers and you want to put them into a binary search tree. You take the first number, whatever it may be, and make it the root node of the tree. Then you take the second number. If it's less than the root number, you hang it on the left of the root; if not, you hang it on the right. With each subsequent number, you start by comparing it with the root number. If it's less, you follow the left pointer; if not, the right. And you repeat this all the way down the tree until you find a NULL pointer. This is where the new number belongs in the tree.
Binode* root = NULL; int x; while (cin >> x) addnode(root, x); // tree initially empty // take next value of x from somewhere // put it in its right place in the tree

And this is one possible way of writing the addnode procedure:


void addnode(Binode*& t, int x) { Binode* newnode = new Binode(x); if (t == NULL) { t = newnode; // first number becomes the root value return; } Binode* a = t, *b; while (a != NULL) { b = a; if (x < a->n) a = a->l; else a = a->r; } // We've found where it goes; b points at its parent if (x < b->n) b->l = newnode; else b->r = newnode; }

We'll see a neater way of writing the addnode procedure when we've done recursion.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000-8

C++ Week 12 Sample halfway test


Question 1
There is something wrong with each of the following. (The first one is meant to be a complete program; the rest are program fragments.) In each case, explain briefly what is wrong: 1. #include <iostream>
int main( ) { cout << "1234"; }

2. int x, 2x = 2;
cin >> x; cout << x * 2x;

3. int x, y = 0;
cin >> y; if (y > x) cout << "Bigger";

4. if (s[1] == 'A') cout << "Starts with A"; 5. int totlen = 0;


while (not infile.fail()) { getline(infile, s); totlen += s.length(); }

6. if (x || y > 0) cout << "At least one of them is positive"; 7. for (int i = v.size(); i >= 0; i--)
v[i] = v[i-1];

8. bool isthere (const vector<int>& v, int q)


{ for (int i = 0; i < v.size(); i++) if (v[i] == q) return true; else return false;

} 9. ifstream infile;
string filename = "a2f.txt"; infile.open(filename);

Question 2
Give the output of each of the following: 1. string s = "Who's Who?", t = "Who is Who?";
cout << (s < t ? s: t);

2. int x = 12, y = 5;
switch (x % y) { case 1: s = "One"; case 2: s = "Two"; default: s = "Neither"; } cout << s;

3. int x = 9, y = 3;
if (y > 0) if (x % y > 0) cout << "x not divisible by y"; else cout << "y is zero";

4. char s[] = "ZANZIBAR";


int notZ = 0; for (int i = 0; i <= 8; i++) if (s[i] != 'Z') notZ++;

cout << notZ;

5. int i = 4, j = 10;
double d = 2, e = 8.8; cout << fixed << setprecision(1) << j/i << " " << e/d << " "; cout << e/i << " " << d + j / i << " "; d = i; j = e; cout << d << " " << j << endl;

6. int n = 0;
while ( ++n < 5); cout << n << " ";

7. (Assume this input: 35 46.2 I.C.I. 49.3 65 De Beers )


int i; double d; string s; cout << fixed << setprecision(1); while (cin >> i >> d >> s) cout << i << " " << d << " " << s << endl;

Question 3
Write down the output of this program:
#include <iostream> #include <string> using namespace std; int int int { a = 0; x = 0; main( ) int a = 0, b = 0; extern int c; void f1(int x); void f2(int& x); cout << "m1: " << a << " " << b << " " << c << " " << x << endl; f1(a); cout << "m2: " << a << " " << b << " " << c << " " << x << endl; f2(b); cout << "m3: " << a << " " << b << " " << c << " " << x << endl; f1(int x) int b = 0; extern int c; a++; x++; b++; c++; cout << "f1: " << a << " " << b << " " << c << " " << x << endl; f2(int& x) int b = 0; extern int c; a += 2; x += 2; b += 2; c += 2; cout << "f2: " << a << " " << b << " " << c << " " << x << endl; c = 0;

} void {

} void {

} int

Question 4
A firework company keeps records of its sales in a text file. Part of the file might look like this:
Mount Vesuvius 80965 Emerald Spray 46589 Crackerjack 65890 Snow Storm 64578 Golden Rain 63865 Giant Catherine Wheel 76354 Air Bomb 34672 Firestorm 9685 Standard sparklers 153876 Coloured sparklers 84750 Giant sparklers 120853

Write a program to read this file and to output the number of the best-selling firework sold and the number of the worst-selling. (For the above file, the output would be 153876 and 9685.) You may assume there is only one best-selling firework and only one worst-selling. Your program should read the file only once. You may read the file using standard input or by opening an

ifstream,

as you wish.

Question 5
Write a function that takes a vector of integers as its parameter and which returns true if every odd number is followed by an even number and every even one by an odd one (except for the last, obviously). If there are fewer than two numbers in the vector, the function should return true.

Question 6
Write a procedure that takes an integer as its parameter and which prints out a hollow square of stars (square in the sense that each side has the same number of stars) with a diagonal line going from top right to bottom left. For example, if given 6, it would produce the following:
****** * ** * * * * * * ** * ******

Question 7
An experimental psychologist has designed the following class in C++ to perform a (very) crude simulation of short-term memory:
class Brain { public: Brain(); void add_an_item(string s); void bump( ); void display( ) const; private: static const int MAX = 7; vector<string> memory; };

A Brain is initially empty. add_an_item adds a string to the memory. The memory can only contain up to seven strings. The addition of another string causes the first (oldest) string to be forgotten. A bump on the head causes the most recently added string to be forgotten. display displays the current contents of memory. Write the definitions of the interface functions.

Some more sample questions


Question 8
A text file contains lines varying in length; you may assume that no line is longer than 100 characters. Write a program that reads this file and outputs the number of blank lines and the length of the longest line. The program should read the file only once. You do not know in advance how long the file is. You may read it from standard input or by opening an ifstream, as you wish.

Question 9
Modify the program of the previous question so that it also outputs the most frequently occurring line length. (If, for example, there were more lines of 62 characters than of any other single length, it would output 62.) You may assume that just one line length is the most frequent. Just write the modifications; there is no need to write out the whole program again.

Question 10
Write a function that takes a vector of first-names (such as "Audrey", "David") as its parameter and which returns true if the names are in alphabetical order. If there are fewer than two names in the vector, the function should return true.

Question 11
Write a procedure that takes a string as its parameter and which prints out a pattern made out of the string. For example, given the string "XMAS", it would output the following:
X MM AAA SSSS

Question 12
Given the following:
class Weekday { public: Weekday(int); Weekday(string); private: int day; static const string dayname[]; friend ostream& operator<<(ostream&, const Weekday&); }; ......... int main() { Weekday wd(1); Weekday wd2("Fri"); Weekday wd3(8); cout << wd << " " << wd2 << " " << wd3 << endl; }

the output would be:


Sun Fri ?

i.e. any invalid value for a constructor gives rise to some dummy value for day which appears as a question mark on the output. Write the definitions of the array and of the member functions.

Question 13
Write a complete definition (class and functions) for an Ivec class. An Ivec is like a vector of int with the following functionality: 1. It has a constructor that takes as its parameters an array of int and an integer giving the length of the array. 2. If an array-bounds error occurs, the program terminates with the message "Invalid subscript: " followed by the offending subscript value. 3. It has a there function that takes an integer and returns true if that integer is in the Ivec; otherwise false. 4. The "+" operator denotes concatenation. For example, given ivec1 with values 4, 6, 22, -5 and ivec2 with values 33, 1, 12, the value of ivec3 after ivec3 = ivec1 + ivec2; would be 4, 6, 22, -5, 33, 1, 12. 5. It has a remove procedure that takes an integer and removes the first occurrence of that integer from the Ivec, closing up the gap without changing the order of the values in the Ivec. If the integer does not occur in the Ivec, it does nothing. 6. It has a zap procedure that takes an integer and removes all occurrences of that integer from the Ivec. 7. The output operator can take an Ivec as an argument. The effect is to output the values of the Ivec, in order (first iv[0], then iv[1] and so on) on the same line with a space between one value and the next.

Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 13 Two-dimensional vectors, typedef, C-style arrays and Cstrings, character-level I/O, precision of doubles
Two-dimensional vectors
A two-dimensional vector in C++ is just a vector of vectors. For example, you could define a twodimensional vector of integers as follows:
vector<vector<int> > v2;

Note the space between <int> and the second >. If you have them next to each other, as in >>,the compiler interprets it an operator and flags it as an error. This definition gives you an empty two-dimensional vector. If you wanted to grow it with push_back, you would have to push back a one-dimensional vector, not a single int. For example:
vector<int> v(5); v2.push_back(v);

To initialize a two-dimensional vector to be of a certain size, you can first initialize a one-dimensional vector and then use this to initialize the two-dimensional one:
vector<int> v(5); vector<vector<int> > v2(8,v);

or you can do it in one line:


vector<vector<int> > v2(8, vector<int>(5));

You can picture v2 as a two-dimensional vector consisting of eight rows with five integers in each row. You refer to individual elements of a two-dimensional vector by using two subscripts. (Since v2[0], for example, is itself a vector, you can subscript it v2[0][0].) The following sets the third element of the fifth row of v2 to 99:
v2[4][2] = 99; // remember that the first element of the first row is v2[0][0]

The following procedure would display a two-dimensional vector of int:


void display (const vector<vector<int> >& vy) { for (int i = 0; i < vy.size(); i++) // loops through each row of vy { for (int j = 0; j < vy[i].size(); j++) // loops through each element of each row cout << " " << vy[i][j]; // prints the jth element of the ith row cout << endl; } }

Defining datatypes with typedef


You can give your own names to datatypes using the typedef keyword. The following code creates a datatype called Words, which is a vector of string.
typedef vector<string> Words; //note the capitalisation: this is a convention

This looks just like a definition of a variable called Words except that it has the word typedef on the front. This means that we have not defined a variable - we do not now have a vector called Words. We have defined a datatype. As well as being able to define variables of type int or string or vector and so on, we can now also define variables of type Words. Once you have defined a datatype in this way you can use it in the same way as any other datatype to declare variables of that type. For example:

Words w; // creates a vector of strings, called w

Defining new types can help to simplify the notation of a multidimensional vector. Let's use a typedef to create a 2-D vector of integers:
typedef vector<int> Vint; Vint v(5); vector<Vint> vx(4, v); vx[1][2] = 99; // creates the datatype Vint // creates a vector v of five integers, all zero // creates a vector of 4 Vints called vx

// vx[1] is a vector, so we can subscript it

v[0] v[1] v[2] v[3] v[4] vx[0] 0 vx[1] 0 vx[2] 0 vx[3] 0


vector<Vint> vy(10); Vint v(5); for (int i = 0; i < 10; i++) vy[i] = v; vectors vy.push_back(v); off) vy[3].push_back(66); component Vints vy[4].pop_back(); have to be the same length

0 0 0 0

0 99 0 0

0 0 0 0

0 0 0 0

// creates a vector vy of 10 // Vint vectors, all empty // makes each vy a vector of // 5 zeroes - note that you can use assignment with // You can push a Vint onto the end of vy (or pop one // You can push an int onto the end of one of the // (or pop one off) - the component Vints do not all

Arrays
C-style arrays are less flexible than C++ vectors, but they are still occasionally useful. An array can be declared as follows:
int a[6];

creates an array of six integers, all uninitialized (like ordinary int variables). Arrays are not objects in the C++ sense, so they do not have member functions. If a is an array, you cannot have things like a.size() or a.push_back(99) The size of an array is calculated by the compiler and cannot be set or changed at runtime. Note one consequence of the second point the size must be known at compile time. People are often tempted to write something like this:
void proc(int len) { int a[len]; ... } // Can't be done!

The array is local to the procedure and the programmer is trying to set the length of the array with a parameter. But this value is only passed to the procedure at run time. The compiler cannot set aside the right amount of storage for this array at compile time. This cannot be done. A curious feature of arrays in C (and therefore in C++) is that the name of an array is really the name of a pointer to the first element of the array. One consequence of this is that, when you pass an array as parameter, you can define it like this:
int somefunc(int a[])

or like this:
int somefunc(int* a)

The two are equivalent. Then, inside the function, you could refer to a[0], a[5] etc, or, if you preferred, to

*a, *(a+5) etc. (In fact, since a[5] is equivalent to *(a+5), which is equivalent to *(5+a), you could actually write 5[a], which looks very weird.) Since what is passed is the address of the first element, it follows that arrays are always passed by reference (even though there is no &). Another consequence of this is that the function knows only what type of thing the array consists of and where (in memory) it begins; it does not know how long it is. So, when you pass an array to a function, you need also to pass its length. When we passed vectors as parameters, the procedures or functions that used them typically contained lines like this:
int func(const vector<int>& v) { for (int i = 0; i < v.size(); i++) ... }

But we can't do that with an array since there is no a.size() function. So, if we want to pass an array as a parameter, we also have to pass the length as a separate parameter, like this:
int func(int a[], int alen)

We have to pass the array length, as there is no other way for the function to know how long it is. (You can put a number inside the square brackets if you like, for example int func(int a[6], int alen), but the compiler ignores it.) Since arrays are always passed by reference (even though you don't include an &), a procedure or function that takes an array as a parameter can potentially make changes to the array. Sometimes you don't want this to happen and you can prevent it by adding a const to the parameter, like this:
int func(const int a[], int alen)

Now the function should treat a as though it were composed of const int. (At the least you should get a warning from the compiler if the function contains code that might change a value in a.) sizeof There is a sizeof operator in C and C++ (yes, it's an operator, not a function, despite its name). sizeof some_variable gives you the size of some_variable in bytes (a reminder that C is a relatively low-level language). For example, if d is a double, sizeof d gives 8 on most current machines. sizeof can also take a type as its argument, in which case you put the argument in parentheses, as in, for example sizeof (double). When applied to an array, sizeof gives you the size of the array in bytes. So, if ax was an array of six integers, sizeof ax would give 24 (four bytes per int, times six). It is therefore possible to calculate the number of elements in an array ax (giving us something like the size function for vectors) with the rather cumbersome formula sizeof ax / sizeof ax[0]. Can we not use this to calculate the number of elements in an array that has been passed to a function, thus avoiding the necessity of passing the length as a separate parameter? Sadly no. Recall that, in a function that begins int func(int ax[]), what gets passed to the function is actually a pointer to the first element, and, if you get sizeof ax inside the function, you will get the size of a pointer usually four bytes.

Initialising arrays (and, indirectly, vectors)


One advantage of arrays over vectors is that you can initialize them with a set of different values as follows:
int a[6] = {2, 5, -9, 14};

which produces an array of 6 elements as follows:

-9

14

The last two elements are initialized to zero as a by-product of the initialization. (If you explicitly initialize any elements, even just the first, the rest get initialized to zero.) The argument in square brackets is optional, and if omitted, the array is populated only with the elements specified in curly brackets. In this example, if you left out the 6, you'd get an array of 4 elements. This feature of arrays provides a back-door route to initializing vectors. One of the constructors in the vector class takes two pointers (memory addresses) as its parameters. The first of these is to the element we want first in our vector, the second is to the element after the last one that we want. Since a, a+3 and so on are pointer values, we can use an array to initialize a vector as follows:
int a[6] = {2, 5, -9, 14}; vector<int> v(a, a + 6);

This code will populate the vector with the first 6 elements of the array. Note that the second argument of the initialization (a + 6) is the address of a (here non-existent) element after the end of the array. As another example,the following code:
int a[] = {1, 5, 10, 50, 100, 500, 1000}; vector <int> v (a + 1, a + 5)

populates the vector with the values 5, 10, 50 and 100.

C-strings
Just as C++ strings are like vectors of char, C-strings are arrays of char. For example:
char cs[4] = "C++"; // four characters to allow for the null byte at the end

C-strings always occupy one byte more than their apparent length because a C-string always ends with a null byte. (As a literal, a null byte is written as '\0'.) All the functions in C that manipulate strings rely on the presence of this null byte. If you are providing a string literal as initialization, you need not specify how long the array is to be - you can leave it to the compiler to work it out from the literal:
char csx[] = "mary smith";

The compiler determines how long the array has to be to hold the string and the null byte. C-strings, like other arrays, are always passed by reference. However, we do not need also to pass the length in the case of C-strings because we can find where it ends by looking for the null byte. Consider the following procedure that receives an array containing a C-string and converts all the spaces it contains into asterisks:
void stars (char cs[]) { for (int i = 0; cs[i] != '\0'; i++) if (cs[i] == ' ') cs[i] = '*'; }

or, thinking of the parameter explicitly as a pointer (which is what it is):


void stars (char* cs) { for (char* p = cs; *p != '\0'; p++) if (*p == ' ') *p = '*'; }

Converting strings to C-strings


C-strings are not equivalent to C++ strings. For example:
char ch[] = "E"; // an array of two bytes, an E and a \0 string s = "E"; // a vector-like container of character data, containing an E

In some C++ compilers, certain functions cannot handle C++ strings as arguments. In particular the

open()function

for an ifstream on many compilers takes a C-string as its argument (it can also take a string literal, but that is because a string literal is a C-string). If, for example, you ask a user for the name of a file to be opened and store the filename as a string, you must convert it before passing it to the open() function as follows:
string filename; cin >> filename; infile.open(filename.c_str());

Similarly, the function atoi() (from the cstdlib library) only takes C-strings. This function is used to convert a string (holding the character representation of an integer) into an integer. For example:
string s = "1234"; int n = atoi(s.c_str()); cout << "string " << s << " is integer " << n << endl;

Or we can use an istringstream for the same purpose:


string s = "1234"; int n; istringstream iss(s); iss >> n;

Input and output of characters


get() and put()
If we want to analyse files on a character-by-character basis, we use the input_stream.get(char) function, to read the next character from the specified input stream. Similarly you place char data into the output stream using the output_stream.put(char) function. The following code reads in character data from the input stream and places a copy of it in the output stream:
int main ( ) { char ch; while (cin.get(ch)) cout.put(ch); }

ignore()
If for any reason you need to ignore a character in the input stream, for example because you know it will send the stream into a fail state, you can use the input_stream.ignore() function. If you need to ignore several characters, you can pass arguments to ignore() to specify a character to act as a delimiter (ignore all characters up to and including the delimiter) and the maximum number of characters to ignore. For example:
//assume cin contains abcde$fgh cin.ignore(10, '$'); string s; cin >> s; // s is now "fgh": characters up to and including $ ignored // again assume cin contains abcde$fgh cin.ignore(4, '$'); string s; cin >> s; // s is now "e$fgh": maximum of 4 characters ignored

You know that you can effectively skip the rest of a line with a getline(instream, junk); You could also do it with instream.ignore(INT_MAX,'\n');

peek()
input_stream.peek() returns the next character in the buffer without getting it, so you can have a look

at the next character that's coming up before you have committed yourself to reading it. This could be useful if you wanted to avoid sending a stream into a fail state, e.g. by reading char data into an int variable.

unget()
input_stream.unget() replaces the last character you obtained (with a get()) and pushes it back into the input stream, so that it will be the next character to be read. You can only unget() one character.

Suppose you had two procedures - A and B - taking it in turns to read from the same input stream. A reads a series of characters until it hits one that marks the start of B's section, whereupon A hands over to B, and vice-versa. When A stops, perhaps it has already read the character that marks the start of B's section, in which case it might unget that character before handing over to B.

Precision of doubles (round-off errors)


Are computers good at arithmetic? The following code seems to suggest not:
#include <iostream> using namespace std; int main( ) { double d = 7; for (int i = 0; i < 10 ; i++) d -= 0.7; if (d == 0.0) cout << "Yes"; else cout << "No"; }

The representation of doubles is necessarily approximate. Consider the decimal representation of one third - 0.3333333.... No matter how many 3's you stick on the end, you haven't quite got it exactly. Computer representation of doubles is subject to the same sort of problem. In this example, after subtracting 0.7 from 7.0 ten times, we ought to be at 0.0, but, because of these approximations, a tiny amount of error creeps in so that the final value of d is not exactly zero. In computations involving huge numbers of calculations, typical of many applications in science and engineering, these cumulative errors can render the final result wildly inaccurate and so completely useless. Consequently great effort is devoted in such applications to devising algorithms than minimise these errors, and the results are not presented as being completely accurate, but rather as accurate to within certain limits. An important lesson to take from this is that a test for absolute equality with a double is dangerous. If you wanted to fix the example program so that it output "Yes" as it ought to, you would decide what level of accuracy you were prepared to accept, and write a near_enough function that took two doubles and returned true if they were within your accepted distance of each other. For example:
bool near_enough(double x, double y) { const double ACCEPT = 0.000001; return x <= y ? x >= y - ACCEPT : y >= x - ACCEPT; }

Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 14 Classes II


Enhancing the Date class A private array of const Month
In our first version of the Date class, we had a couple of global arrays, one to hold the number of days in each month, the other to hold the names of the months. There are two unsatisfactory aspects of this arrangement. One is that we have parallel arrays, and the other is that our Date class depends on global structures defined outside it. Arrays ax and bx are said to be parallel when there is some relationship one-to-one between the elements ax[0] to bx[0], ax[1] to bx[1], ax[2] to bx[2] and so on. The reason that this arises is often, as here, that they are holding different pieces of information about the same items. It is an unsatisfactory arrangement because there is nothing in the program code to tie them together; the connection is too flimsy. Someone who was modifying the program without appreciating how the two arrays went together might insert a new element into the middle of ax without inserting a corresponding one into bx. The solution is to create an array of objects that hold the mdays and mnames data for each month. Let us define a Month class to hold the data:
class Month { public: char name[4]; int days; }; // every month name has three letters, plus one for the null byte

Note that the class is entirely public and has no methods. It serves only as a structure in which to store data of different types. It's not really a fully-fledged C++ class at all but rather the sort of simple aggregation of data that you would find in C (in C it would be called a struct). Let's call it a C-style class. We have chosen to use an array of char rather than a string to make the initialization easier. It is possible to initialise an object of a C-style class using the same curly bracket system that you can use to initialize an array. So we can initialize a Month as follows:
Month m = {"Dec", 31};

Note that the order of the arguments is the same as in the class definition. Any data fields left uninitialized in the curly brackets are initialized to zero, or an empty string in the case of strings or C-strings. We now declare an array months of Month objects. Since we don't want the data to be changed, we declare it as an array of const Month:
const Month months[ ] = {{"", 0}, {"Jan", 31}, {"Feb", 28}, .... {"Dec", 31}};

We can address the second problem of the arrays being global by making the array of const Month private to the Date class. We saw in an earlier lecture that, if you want a const data item as a member of your class, it has to be declared as static. This means that every instance of Date shares the same months array, rather than each Date object having its own copy; the array belongs to the class rather than to objects of the class. It is only for const data members of integral type that you can put the full definition of the const inside the class definition. For const data members of other types, you have to include a declaration inside the class and then provide the full definition outside the class. This is what it finally looks like:
class Date { public: .... private: .... static const Month months[ ]; // declare an array of Month objects in the private part

of the Date class .... }; .... // Then put the definition of Date::months outside the class, using C-style initialization for a C-style array of C-style objects const Month Date::months[ ] = {{"", 0}, {"Jan", 31}, {"Feb", 28}, {"Mar", 31}, {"Apr", 30}, {"May", 31}, {"Jun", 30}, {"Jul", 31}, {"Aug", 31}, {"Sep", 30}, {"Oct", 31}, {"Nov", 30}, {"Dec", 31}};

Overloading the minus operator for Dates


Suppose we want a - (minus) operator, which will return the difference (in days) between two dates. If, say, date1 had the value 5 Feb and date2 had the value 27 Jan, date1 - date2 would be 9. (date2 date1 would be -9 we're assuming that the two dates are in the same year.) It would help a lot with this if we had a method that returned the day-number of a date (1 Jan = 1, 1 Feb = 32, 31 Dec = 365).
int daynum( ) const; // prototype in the class definition ... int Date::daynum( ) const { int num = 0; for (int i = 1; i < month; i++) num += months[i].days; num += day; return num; }

Now it's trivial to overload the - (minus) operator to return the difference between two dates:
int operator- (const Date&) const; ... int Date::operator- (const Date& dx) const { return daynum() - dx.daynum(); // or return this->daynum() - dx.daynum(); }

This operator, obviously, has to be public, but does the daynum() function also have to be public? Not necessarily. If you wanted to include it in the interface, you could, but you might decide to make it private, in which case other functions of the Date class could call it (as here) but it would not be accessible from outside.

Overloading the output operator


It would be useful to be able to write the contents of a Date to an output stream. One way of doing this is to call display(), but what about overloading the << operator so we can use code like cout << d1; ? There are several difficulties in overloading the << operator. One is that the date is on the wrong side of the operator. Remember that, if we have overloaded some operator OP so that we can write things like x OP y, we are really calling a function called operatorOP and the call looks like x.operatorOP(y). So the overloaded function clearly has to belong to the thing on the left of the operator; the thing on the right is just an argument in the function call. So the overloaded << operator cannot be a member function of the Date class. It is possible to define an overloaded operator function that is not a member function of any class, just a free-standing function. The free-standing version of a function that will be called as x OP y will begin with a line of this form:
return_type operatorOP(type_of_x x, type_of_y y)

The argument to the left of the operator in the call x OP y corresponds to the first of the parameters in the definition. So we can define a non-member function, that will take an output stream and a Date as its parameters. Another problem is that the output operator takes as its argument an output stream of type ostream (e.g. cout) and a data item, and it returns the modified output stream. (This is why you can use code of the form cout << x << y; the second << is sending y to the output stream returned by the first <<.) So it has to have the output stream both as a reference parameter and as a reference return type. Our prototype

for the overloaded << operator is going to look like this:


ostream& operator<< (ostream&, const Date&); parameters rather than value parameters // objects generally passed as const ref

Note that, since this overloaded operator function is not a member of any class, the first parameter corresponds to the argument on the left of the operator, and the second corresponds to the argument on the right. The next hurdle to overcome is that, because the operator isn't a member function of Date, the operator has no access to Date's private data. We have to define accessor functions to expose day and month:
int get_day( ) const; // public string get_month( ) const; // public ... int Date::get_day( ) const { return day; } string Date::get_month ( ) const { return months[month].name; }

Now at last we can define the << operator:


ostream& operator<< (ostream& os, const Date& d) { os << d.get_day() << " " << d.get_month(); return os; }

This works, but you might take the view that we have been forced into creating accessor functions that we perhaps never intended should be part of the interface. There is an alternative solution. By declaring the << operator as a friend of the Date class we grant it access to the private section. It is still a non-member function but it now has the same access to the private section as a member function does. (We encountered friend in an earlier lecture where we made one class a friend of another, eg Stack a friend of the Node class, so that any function of the Stack class had privileged access to the private items of Node. Here we are using it to grant this privileged access just to one function.) The operator<< function is not a member of Date, so it is neither public nor private.
// In the class declaration friend ostream& operator<< (ostream&, const Date&);

We can now do without the accessor functions, so that the operator definition becomes:
ostream& operator<< (ostream& os, const Date& d) { os << d.day << " " << d.months[d.month].name; Date::months[d.month].name return os; } // or, if you prefer,

As I noted when I introduced friend, some people feel strongly that the friend feature of C++ should never be used, on the grounds that one of the main reasons for using an object-oriented language is to gain the benefits of encapsulation and that the purpose of friend is precisely to circumvent the encapsulation. Another variation, which avoids the need either for extra accessor functions or a friend function, is, first, to change the display procedure of Date so that it takes an ostream and returns it:
ostream& Date::display(ostream& os) const { os << day << " " << months[month].name; return os; }

and then to write a trivial non-member function for the overloaded output operator whose sole purpose is to call the display procedure:
ostream& operator<<(ostream& os, const Date& d) { return d.display(os); }

This is better because the functionality remains within the member functions. This non-member function

does not require access to the private section of Date and so does not need to be a friend. If you want, you can rename the display function to operator<< , just to underline the point that these two functions - the member and non-member versions - work closely together. The compiler will not be confused by them having the same name; they have different parameter lists, and one is a member function of the Date class, the other is not a member of any class.
ostream& Date::operator<<(ostream& os) const { os << day << " " << months[month].name; return os; } ostream& operator<<(ostream& os, const Date& d) not a member function { return d.operator<<(os); } // This one is not a const function as it is

One parameter or two?


Suppose we had an ordinary free-standing function (i.e. not a member of the Date class) called print_date which output a date. Its prototype might look like this:
ostream& print_date(ostream&, const Date&);

Assuming we had a Date variable called d1, we would call it like this:
print_date(cout, d1);

(You are familiar with calls like this, as in, say, getline(cin, s);) Now, if we choose to give this function the special name operator<<, we can call it either in the same way as print_date:
operator<<(cout, d1);

or like an operator:
cout << d1;

So we provide two arguments in the call - an output stream and a Date - and they match up with the two parameters in the function definition. But if we think back to one of the overloaded operators that we defined earlier, say the overloaded minus, the call has much the same form as cout << d1;:
d1 - d2

but, if we look at the prototype, we see that it has only one parameter:
int operator-(const Date&) const;

Since the minus operator requires two Dates, shouldn't we have two parameters? We can see why we have only one if we put the call into its alternative form:
d1.operator-(d2)

This reminds us that, in contrast to the overloaded output operator, the overloaded minus is a member function of the Date class. When we call this function, we will already be providing one argument - the Date on the left of the dot (sometimes called the implicit parameter); there will only be one argument in the actual argument list (inside the brackets), so we only need one explicit parameter. Code of the Date class

A Safevec class
As an illustration of some aspects of classes that we haven't dealt with yet, we build a Safevec class. A Safevec is very like a vector of integers its only data member is a vector<int> but with a few variations. Its pop_back returns a value, and terminates the program if the Safevec is empty. Its size

function returns an int, not an unsigned int. Its [ ] operator does array-bounds checking and terminates the program if the subscript is invalid.
class Safevec { private: vector<int> v; public: Safevec() { } Safevec(int len, int init = 0) : v(len, init) { } void push_back(int x) { v.push_back(x); } int pop_back(); int size() const; int& operator[ ](int); };

Note that the default constructor is empty. This is because the only data item is a vector, which is itself an object that has its own default constructor. The default constructor for the vector will be called automatically, which will provide a correctly initialized empty vector. We don't need to do anything. There are various ways you could write the second constructor. Either of these would work:
Safevec::Safevec(int len, int init = 0) { vector<int> temp(len, init); v = temp; } Safevec::Safevec(int len, int init = 0) { v = vector<int> (len, init); }

The version I've given in the class definition uses a field initializer list. After the parameter list, you put a colon and then you put the data items in a comma-separated list and provide the values for initializing them in parentheses. Since all the initializing has been done by the field initializer list, there is nothing to do in the body of the constructor, so it is empty. This is slightly more efficient than the other methods but mainly, at present, it is just a stylistic alternative. We will see a situation in a later lecture where this method is obligatory. A version that looks plausible but is in fact incorrect is the following:
Safevec::Safevec(int len, int init = 0) { vector<int> v(len, init); } // No!!!

A constructor is a function, and, like other functions, it can have local variables. In this version, we have defined a local variable which happens to have the same name as the vector that is a data member of the class. But the Safevec's vector and this local vector are different objects. This local one will be created and then, being a local (therefore auto) variable, will promptly disappear when the function ends. The Safevec's v will be the same as it was before, i.e. empty. Using a default argument for the init parameter allows the constructor to be called with either one argument or two, as for vectors. If we preferred, we could use a default argument for the first parameter also Safevec(int len = 0, int init = 0) .... We could then dispense with the default constructor. (In fact we would have to dispense with it since, if we left it in, a call such as Safevec sv; could be a call to either the first or the second constructor; the compiler would complain of an ambiguous call.)
int Safevec::pop_back() { if (v.size() == 0) { cerr << "Cannot pop empty Safevec" << endl; exit(1); } int x = v[v.size() - 1]; v.pop_back(); return x; } int Safevec::size() const { return v.size(); }

returns an unsigned int, but, since Safevec::size() is an int function (it returns a signed int), the value returned by v.size() will be type-converted when Safevec's size() function returns it. The
v.size()

rules for type-conversion in such cases are the same as for assignment. (If, for example, you had return 5.432 in a function that returned an int, the value returned would be 5.)
int& Safevec::operator[ ](int sub) // if the call was sv[5], sub would have the value 5 { if (sub < 0 || sub >= v.size()) { cerr << "Subscript " << sub << " out of range" << endl; exit(1); } return v[sub]; }

Why are we returning an int&? Why not the more usual int? Suppose we had defined a Safevec thus: Safevec sv(10); If expressions such as sv[4] were only ever used in places where we simply wanted the value of sv[4], such as on the right-hand side of assignment statements, as in x = sv[4]; then a function that returned int would be adequate. But sv[4] is also going to be used in contexts where we need to know where sv[4] is (in order to put something in it), such as on the left-hand side of assignment statements, as in sv[4] = x;. For those uses, we need a reference, not just a value, hence the int&. A reference return type is very much like a reference parameter, except that we are passing something back instead of passing something in. These two aspects of a variable are sometimes called its r-value - the value that it has on the right-hand side of an assignment, often called simply "the value" - and its l-value - the value that it has on the lefthand side of an assignment, i.e. its position or address. Given a variable's l-value, the computer can always find its r-value (it can go to that address and see what's there) but not vice-versa a variable's rvalue gives no clue to where it is located. Note that it would be dangerous to return a local (auto) variable by reference. Local variables go out of existence when the function ends. The part of the program that received the returned reference would have a reference to something that no longer existed, or, to be more precise, to something whose continued existence could not be relied upon. This overloaded [ ] function cannot be declared as a const function. Even it you don't actually use it to change the contents of the Safevec, you could do if you chose to, so it cannot be guaranteed to leave the object unaltered.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000-8

C++ Week 15 Recursive procedures and functions I


A recursive function is one that calls itself. We will begin by looking at the calculation of a factorial.

Calculating a factorial
Factorials are often used in statistical calculations. The factorial of n, written as n! is equal to the product of n(n-1)(n-2)...(1). For example 4! is equal to 4 3 2 1 = 24. There is an obvious way to do this calculation using a loop, but we can also do it recursively. Let's consider the definition of a factorial in a slightly different way: if n = 0, n! is equal to 1. if n > 0, n! is equal to n ((n-1)!) Let's look at an implementation of our recursive factorial function.
int fact(int n) { if (n == 0) return 1; else return n * fact(n - 1); }

There are some key properties that all recursive functions exhibit: Recursive functions always call themselves at some point. Recursive functions must have at least one special case, i.e. there must be some condition that stops the function being called yet again. This is known as bottoming out. The recursive calls must converge on the bottoming out condition, i.e. each call must be nearer the bottoming out condition than the previous one.

The rev() function


This function takes a series of characters and outputs them in reverse order.
#include <iostream> using namespace std; void rev( ) { char ch; cin.get(ch); if (ch != '\n') { rev(); cout.put(ch); } } int main( ) { rev(); }

Assume that the input is a single line of characters ABCD The first call to rev reads the letter A (so we are now positioned between A and B in the input stream), decides it is not the newline character, and calls rev. It then waits until this call to rev has completed. The second call to rev begins by reading a letter; this time it's B. The first call and the second call are separate calls, so each one has its own local variable ch. So the first call is holding A in its local ch, and the second call is holding B in its local ch. (Think of them as separate people following the same instructions rather than as one person.) The second call decides that its char is not the newline character and it calls rev. The second call now waits until the third call has finished. You can picture successive calls like this: 1. ch = A 2. ch = B

3. 4. 5.

ch = C ch = D ch = \n

When we get to call number 5, the recursion bottoms out because the character read by call 5 is the newline character, so it does not make a call to rev; in fact it does nothing at all except terminate, i.e. return. Call 4 then wakes up and carries on from where it left off, i.e. it goes on to do its cout.put(ch); Its char is D so we get a D in the output. It then finishes. Call 3, which has been waiting for call 4 to finish, now wakes up and outputs its character, which is C, so the next thing we get in the output is a C. And so on. The eventual output is DCBA

Recursing with vectors


The next examples are just for illustration - you would probably not choose to use recursion for these. Later we will see examples where recursion, if not the only way to do it, is easily the best.

Summing the contents of a vector of integers


This function takes as its arguments a vector<int> and the index of the last element it needs to bother with. A typical function call will be: total = sum(v, static_cast<int>(v.size()) - 1);
int sum(const vector<int>& v, int top) { if (top < 0) return 0; // empty vector else return v[top] + sum(v, top - 1); }

Testing for negative integers in the vector


bool anyneg(const vector<int>& v, int top) { if (top < 0) return false; // no negatives in empty vector else return v[top] < 0 or anyneg(v, top - 1); }

Testing whether a number is in the vector


bool there (const vector<int>& v, int top, int val) { if (top < 0) return false; else return v[top] == val or there(v, top - 1, val); }

Testing whether a vector is a palindrome


A palindrome vector would be one where one half was a mirror-image of the other, such as 7 6 9 6 7 or -5 66 66 -5. We will say that a vector of zero elements or of just one element is a palindrome. The algorithm starts from the extreme ends of the vector and terminates when it reaches the middle of the vector (the high and low indexes pass one another). The call might be mirror(v, 0, static_cast(v.size()) 1);
bool mirror (const vector<int>& v, int low, int high) { if (high <= low) return true; else return v[low] == v[high] and mirror(v, low + 1, high - 1); }

Testing whether a string is a palindrome


Rather than using two indexes, this algorithm returns the inner substring, minus its first and last elements, until the string is of length one or less.
bool mirror(string s) { if (s.length() <= 1) return true; return s[0] == s[s.length() - 1] and mirror(s.substr(1, s.length() - 2); }

Note that in this example I haven't bothered to use an else. (I didn't actually need one in the other examples either.) The reason we can do without one here is that a return terminates the function immediately. So, if the bottoming-out condition is true and the first return is executed, the second return will not be reached.

Recursive linked list functions


The beauty of recursive programming is that the functions tend to be very small and elegant. The looping is taken care of by the recursion, rather than the programmer having to write the loop explicitly.

The sum of the items in a linked list


int sumlist(Node* head) { if (head == NULL) return 0; // empty list else return head->val + sumlist (head->link); }

The number of nodes in a linked list


int no_nodes (Node* head) { if (head == NULL) return 0; return 1 + no_nodes (h->link); }

Printing the contents of a linked list


void print_num(Node* head) { if (head != NULL) { cout << h->n << " "; print_num(head->link); } }

Note that a procedure, unlike a function, does not have to do anything. (A function must always return a value.) So if, as here, we do not want to do anything for an empty list, it is neater to state the condition as if (head != NULL) rather than if (head == NULL)

Binary trees
Consider the way some groups of people organise themselves to make sure that everyone in the group is informed while sharing the burden of the phone bill. Each person only has to phone two others, and the news eventually filters down to everyone. This works fine when the information is only travelling in one direction, but what happens when we need to find something out from someone in the group. Imagine Mary needed to get hold of a book. She could just put out a general call to everyone, but then she might end up with three copies of the book at the next meeting, or none. It would be better to have a way to ask each person in turn, then when she got through the whole group she would be sure of having only one copy of the book, or none. The algorithm Mary would use in this case would be: 1. 2. 3. 4. Have I got a copy of the book? If not, does anyone on the left branch have a copy of the book? If not, does anyone on the right branch have a copy? If not, we don't seem to have one.

When Mary rings Sarah, Sarah will use the same algorithm herself, checking her bookshelf, then ringing Fiona and Cathy before getting back to Mary to say whether anyone on her branch has a copy. In a sense, Fiona and Cathy also use the same algorithm except that, since they have no-one to ring, they can skip lines 2 and 3. If we adapt this analogy to a binary tree of Binodes, e.g.

we can use a function call of the form if (there(root, 91)) ... to search the tree to see if it contains the value 91.
bool there(Binode* { if (troot->n == if (troot->l != has a copy if (troot->r != a copy return false; } troot, int x) x) return true; // Mary has a copy NULL and there(troot->l, x)) return true; NULL and there(troot->r, x)) return true; // No-one has a copy // Someone down Sarah's side // Someone down Jane's side has

This is fairly straightforward code, but it is weak on two counts: It is not very elegant There's a bug - if you try to search an empty tree by passing a NULL pointer, you'll get a segmentation fault. Let's deal with the NULL pointer problem - here, as often, getting that right actually simplifies the rest:
bool there (Binode* troot, int x) { if (troot == NULL) return false; return (troot->n == x) or there(troot->l, x) or there(troot->r, x); }

Remember that booleans are evaluated left to right and a value is returned as early as possible. If (troot->n == x) is true, then (there(troot->l, x)) and (there(troot->r, x)) are not evaluated. (If Mary has the book, she won't bother Sarah or Jane.) If (troot->n != x) , then (there(troot->l, x)) is evaluated; if it comes back with true, then (there(troot->r, x)) is not evaluated. (If Mary does not have the book but Sarah comes back with a Yes, she won't bother Jane.)
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 16 Recursive Procedures and Functions II


Building a binary search tree
Consider a list of numbers 5, 3, 8, 7, 2, 10, 6 If we put them in a binary search tree in order, we want each number (and its node) to be added to the left tree of a node if it is less than the node's own value, otherwise we add it to the right hand tree of that node. The resulting tree will look like this: The code to do the insertion of the numbers is as follows:
void insert(Binode*& troot, int x) { if (troot == NULL) troot = new Binode(x); else if (x < troot->n) insert (troot->left, x); else insert (troot->right, x); }

If you were reading the numbers from a file, say, then you would have a loop to read a number and insert it into the tree with a call such as insert(root, num);

The Towers of Hanoi


This is a famous puzzle. You have three towers on which you can stack a series of discs of different sizes. The task is to move the discs one at a time from the first tower to the third, but: You can move only one disc at a time At the end of each move you must always replace a disc on top of one of the towers, i.e. no putting them on the floor You may not put a larger disc on top of a smaller one. When you have only one disc, the solution is trivial: move the disc from tower A to tower C. When you have two discs, you move the small one onto tower B, leaving just one disc on tower A and a space on tower C. Then we can reuse the solution for one disc by moving the bigger disc from A to C, and finally move the small disc from B to C. Believe it or not we have our solution already! When we come to three discs, we can break down the solution into its constituents. We use the solution for two discs to move all of the discs except one onto tower B so we can move the biggest disc across to from tower A to tower C. Then we use the solution for two discs to move the remaining discs from tower B to tower C. With four discs we apply the three-disc solution to move the top three onto the spare tower, then we move the bottom one across, then we apply the three-disc solution again to move the three from the spare tower to the final tower. Each procedure is based on the one before - Move discs onto the 'spare' tower until you have only one disc left, move that across to the destination tower, then move the discs from the spare tower to their final destination. No matter how many discs there are, we will eventually be able to move all the discs across. The solution for n discs is based on the solution for n-1 discs, and the solution for 1 disc is trivial. Let's look at some code. We need a procedure that will take a start position and end position and the number of discs to be moved:
void hanoi (char start_pos, char end_pos, char spare_pos, int n) { if (n > 1) hanoi(start_pos, spare_pos, end_pos, n-1); cout << "Move disc from " << start_pos << " to " << end_pos << endl; if (n > 1)

hanoi(spare_pos, end_pos, start_pos, n-1); }

or, perhaps neater,


void hanoi (char start_pos, char end_pos, char spare_pos, int n) { if (n > 0) { hanoi(start_pos, spare_pos, end_pos, n-1); cout << "Move disc from " << start_pos << " to " << end_pos << endl; hanoi(spare_pos, end_pos, start_pos, n-1); } }

Recursion in language
Software that processes language tends to be recursive, because language is recursive. You can start with a sentence such as "She was going skiing." Then you can embed this in another sentence such as "You said that she was going skiing." This longer sentence now contains something which is itself a sentence. And we can do it again - "She told me that you said that she was going skiing." And so on. If we keep doing it, the sentence might become unwieldy and hard to follow, but not ungrammatical. If you had a sentence() procedure that analysed sentences like these, you would expect it to contain a call to sentence(). The same goes for programming languages. Think of the structure of an if statement in C++:
if ( boolean_condition ) statement ;

The statement here could be any statement, including, possibly, an if statement. An if statement can contain an if statement which can contain an if statement which can contain ... And it's not just if statements. A while loop can contain a while loop; a for loop can contain a for loop, and so on. Actually, languages exhibit something called indirect recursion. As well as having an if statement which contains an if statement (direct recursion), you can have an if statement which contains a while loop which contains an if statement (recursion at one remove) or an if statement which contains a while loop which contains a for loop which contains a switch which contains an if statement (recursion at several removes). The part of the compiler that analyses the syntax of a program (it's called the parser) is likely to have a procedure to analyse a statement - let's call it analyse_statement(). It will decide, on the basis of the first word, what kind of statement it is and will then call another procedure to handle that kind of statement - analyse_if_statement() or analyse_while_statement() or whatever. Let's suppose it's dealing with an if statement. analyse_if_statement() will check that the parentheses are there around the boolean expression, perhaps calling yet another procedure to check the boolean expression itself, and will then call analyse_statement(). So procedure A has called procedure B which has called procedure A. The (here indirect) recursion in the parser mirrors the recursion in the language.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 17 Objects containing pointers


Assigning and copying objects that contain pointers
When we built our Date class, there were some operations that we did not need to program explicitly; the system provided default versions of these operations and these were adequate for our purposes. Specifically, we were able to use one Date object to create another. Assuming we had already defined a Date d1 - as in, say, Date d1(25,12); - we could write Date d2(d1); We were using the default version of the copy constructor. And we did not need to program the assignment operation. Assuming we had already defined Dates d1 and d3, we could simply write d3 = d1; and the assignment would take place as we wanted. And we could pass Dates as value parameters, and they would behave as we would expect value parameters to behave. If a function began int func(Date dx) and we called it as int x = func(d1); then dx would be a copy of d1 and the function could do what it liked to dx without having any effect on d1. The default versions of these operations simply copy data items from one object to another, which is fine if the items are simple things like integers. But, if the objects contain pointers, this is almost certainly not what we want. Take assignment as the example. If some object o1 contains, as its only data member, a pointer to a linked list, then the default version of the assignment o2 = o1; will do no more than copy the value of the pointer in o1 to the pointer in o2. So now, the pointer in o1 and the pointer in o2 are pointing at one and the same linked list. But that is not what we wanted. We wanted o2 to be pointing at a linked list of its own, an exact copy of the one that o1 is pointing at. We have to do some programming to make this happen. We'd have the same problem if we used the default copy constructor to create o2 as in Objtype o2(o1); And we'd have the same problem if we passed o1 as a value parameter, say in int x = func(o1); to a function beginning int func(Objtype ox) - the pointer in ox would be pointing at the same linked list as the one in o1.

An implementation of a stack
We will illustrate the technique with a simple stack class, which we will implement with a singly-linked list. A stack is a list structure in which insertions and deletions take place at the same end. The class definition, before the additions we are about to make, looks like this:
class Stack { public: Stack() : top(NULL) {} void push(int); int pop(); void display() const; bool empty() const; private: class Node; Node* top; }; class Stack::Node { public: int n; Node* link; Node(int x) : n(x), link(NULL) {} definition };

// constructor defined inside the Node class

Click here for a full listing of the code This implementation of the stack is fairly straightforward, consisting of push() and pop() functions. Note that, in the full listing, we define a constructor for the Node class outside the class definition, so we have to write:

Stack::Node::Node(int x) { ... }

Node(int x)

is the constructor, and the one we are talking about here belongs to the Stack::Node class.

Overloading assignment and the copy constructor


Let's suppose we have populated a stack, S1 and want to make a copy of it, S2. A simple assignment of the form
S2 = S1;

will only copy the top pointer stored in S1 to S2, so if you did anything to the stack S2 you would actually be making changes to S1 (and vice-versa). The same would happen if you used the copy constructor
Stack S2(S1);

So we have to overload both of these operations to make them work properly. The easiest way to do this is to create two private functions called copy() and free(). The first of these builds a new list, pointed at by tnew and copies the values from an existing list pointed at by texist. Since we've just done recursion, let's do it recursively (non-recursive would also be OK):
void Stack::copy(Node*& tnew, Node* texist) { if (texist == NULL) tnew = NULL; else { tnew = new Node(texist->n); copy(tnew->link, texist->link); } }

If we are assigning S2 = S1; and S2 already exists, we have to make sure that we make S2 empty before we do anything, otherwise we will leak memory. For this we define a free() function. Again I'll do it recursively (the non-recursive version is equally straightforward):
void Stack::free(Node*& ts) { if (ts != NULL) { free(ts->link); delete ts; ts = NULL; } }

Now we are in a position to implement the copy constructor and the assignment operator.

The copy constructor


The copy constructor can simply call copy() because we know that the one that's being constructed does not already exist:
Stack::Stack(const Stack& sx) { copy(top, sx.top); }

The overloaded assignment operator


The assignment operator first has to call free() because the stack being assigned to might already exist:
Stack& Stack::operator=(const Stack& sx) { if (this != &sx) { free(top); copy(top, sx.top); } return *this; }

The condition in the function is required as we have to deal with the special case of an assignment of the

form stk = stk; It would be a pointless assignment, but not an invalid one. If we didn't guard against it, we would first destroy stk, with free(), and then try to copy the nodes from the Stack we had just destroyed. To prevent ourselves from doing that, we check whether the Stack we are copying from is exactly the same Stack as the one we are copying to. this is a pointer to the implicit parameter - the Stack we are copying to - and &sx is the address of the explicit parameter - the one we are copying from. The reason why the assignment operator returns something is that assignment in C++ is a function, not a procedure. You will recall that you can have x = y = 10; This works because y = 10 not only assigns a value to y but also returns the value (more precisely it returns a reference to y, as we'll see later), so that it can be assigned to x also. (Assignment is right-associative, so if we put in the implicit brackets, we would have x = (y = 10);) If we are to do the same with Stack objects, our overloaded assignment also has to return a Stack. We return it as a non-const ref. (For more on this point, see the end of this lesson.)

Value parameters
Providing our Stack class with a copy constructor also solves the problem of value parameters. Say we had a function that began int func(Stack sx) and we called it with int x = func(s1); If Stack did not have a copy constructor, the default copy constructor would be used and we'd have the pointers in sx and s1 pointing at the same stack. But, if the Stack class has a copy constructor, then it's Stack's own copy constructor that will be used for the parameter passing, so sx will be a separate stack from s1. (In practice, you'd be more likely to use a const ref parameter than a value parameter, since that's how you generally pass objects to functions, so you shouldn't have this problem anyway, but it's nice to know that the value parameter would work as it ought to, if you wanted to use one.)

The destructor ~Stack


When we create variables with local scope, they are destroyed automatically when the procedure is finished. In the case of pointers, and structures that use them, we have to take care to destroy them explicitly, otherwise we will leak memory. For example, consider the following fragment:
for (int i = ...) { Stack st; ... }

Every time the loop iterates, we create a new stack and presumably push items onto it, so, by the time the loop ends, we will have created a new singly-linked list. At the end of each iteration, the private variable top of st will be destroyed, but NOT the structure it points to. We need a procedure that will do this at the end of each loop. In C++ this procedure resides in the so-called destructor member function of the class:
Stack::~Stack() { free(top); }

Its name is just the name of the class, preceded by a tilde, ~. You don't have to call the destructor explicitly - it happens automatically. The destructor would also be called if you had a pointer to a Stack and did a delete on it:
Stack* sptr = new Stack; sptr->push(99); ... delete sptr; // calls Stack constructor // use the Stack that sptr is pointing at // calls Stack destructor

Now you can see why it is convenient to have the private functions copy and free. The copy constructor just calls copy, the destructor just calls free, and the assignment operator calls both. Our class declaration now looks like this:
class Stack { public: Stack(); void push(int x); int pop( ); void display( ) const; bool empty( ) const; Stack(const Stack&);

~Stack(); Stack& operator=(const Stack&); private: class Node; Node* top; void copy(Node*&, Node*); void free(Node*&); };

Should assignment return a const ref or a non-const ref?


Given that the assignment operator has to return something, should we return a const ref or a non-const ref? Have a look at this simple class (which is pointless as well as pointer-less, but it will serve as illustration):
class Number { private: int n; public: Number(int x) : n(x) {} int get() const { return n; } Number& operator=(const Number& x) { n = x.get(); return *this; } };

Now suppose we have three objects of type Number and a simple assignment:
int main() { Number a(1), b(2), c(3); a = b = c; // or, equivalently, a = (b = c); cout << "a is " << a.get() << ", b is " << b.get() << ", c is " << c.get() << endl; // outputs "a is 3, b is 3, c is 3" }

This works because assignment is right-associative. b takes the value of c, and a reference to b is returned; a then takes the value of b because b is what was returned by (b = c). Now remember that, if you return a reference, then the part of the program that receives the reference can now make changes to the thing that was returned. (If you cast your mind back to the overloaded [] operator in the Safevec class, we returned an int& because this was exactly the effect we wanted to achieve.) What would happen if we changed the above program with some brackets?
(a = b) = c;

The compiler does not object to this strange-looking line. a takes the value of b, and a reference to a is returned; then the value of c is assigned to whatever is returned by (a = b) i.e. to a. So, a, having just taken the value 2, now takes the value 3, and the output is
a is 3, b is 2, c is 3

Wouldn't it be better to block such odd goings-on by making our return ref a const?
const Number& operator=(const Number& x)

Now, a takes the value of b and returns a const reference to a. We are now trying to assign the value of c to whatever (a = b) returned, i.e. effectively to a, but, because of the const, the compiler prevents us from doing that. You might well feel that, unlikely though such a scenario might be, you'd like to prevent it, by returning a const ref. Weirdly, however, programmers are in fact allowed to do things exactly like that to the built-in types such as int. For example:
int main() { int x = 5, y = 6, z = 7; (x = y) = z; cout << x << " " << y << " " << z << endl; }

// output is 7 6 7

So, if you want your overloaded assignment operator to emulate the behaviour of the regular assignment operator and, in general, you do want your overloaded operators to behave "as normal" then you

return a non-const ref.


Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 18 Inheritance


Reusing code
Often when designing a program you need to add new objects that resemble objects that already exist. For example in your library management software you have collections of books. Then someone decides that you are going to start renting out CDs, DVDs etc. You could easily just copy the book code and tweak it for the new requirements, but that means that: you've re-invented the wheel if you have to add a new feature to the original book object, you will have to repeat the changes down the line, and probably introduce bugs in the process C++ allows you to define classes that are variations on existing classes, so that you can reuse the basic methods of the Book class (date lent out, IDnumber), and add other data fields and methods that are specific to CDs, DVDs etc. (artist, composer, conductor, and so on). In C++ terminology the book class is the base class, and the CD class is a derived class. The derived class inherits from the base class. It's possible to have a base class which doesn't correspond to any real world object and which is never instantiated (e.g. LendableObject), i.e. you never have an object in your program of type LendableObject - the class only exists so that you can derive other classes (Book, CD etc) from it. This is known as an abstract class.

Defining a base class


We'll develop a very simple system for an estate agent. The agent deals with properties for rent as well as properties for sale, so the system has a base class Property and derived classes ForRent and ForSale. The Property class is simple:
class Property { private: string address; int rooms; // number of rooms char condition; // 'A' = very good condition, 'B' not so good etc public: Property(string, int rms = -1, char cond = 'U'); // defaults indicate "unknown" void set_rooms(int); void set_condition(char); void print() const; }; Property::Property(string addr, int rms, char cond) : address(addr), rooms(rms), condition(cond) { }

I have used here a field initializer list. I introduced this in an earlier lecture (with the Safevec class). We don't have to use this method. If you preferred, you could use assignments:
Property::Property(string addr, int rms, char cond) { address = addr; rooms = rms; condition = cond; }

Now the member function definitions:


void Property::set_rooms(int rms) { rooms = rms; // you might want to include some validation } void Property::set_condition(char cond) { condition = cond; } void Property::print() const { cout << "Address: " << address << endl; cout << "Number of rooms: " << rooms << endl;

cout << "Condition: " << condition << endl; }

Derived class definitions


Now we can define a ForRent class derived from our Property class:
class ForRent : public Property { private: int rent; int max_occupants; public: ForRent(string, int rms = -1, char cond = 'U', int rnt = -1, int nocc = -1); void set_rent(int); void set_occupants(int); void print() const; };

Note the word "public" in class ForRent : public Property I'll come back to that later.
ForRent::ForRent(string addr, int rms, char cond, int rnt, int nocc) : Property(addr, rms, cond) { rent = rnt; max_occupants = nocc; }

Here the use of the field initializer for Property(addr, rms, cond) is obligatory. It is the only way to initialize the inherited data (the Property part) of a ForRent object. If we called the Property constructor inside the definition of the ForRent constructor, we would create a local Property variable, i.e. local to the ForRent constructor. If we tried to initialize separately the data items of the Property part of the ForRent object with statements such as address = addr; we would find that we were denied access to these data items. This last point seems surprising. ForRent has inherited address, rooms and condition from Property yet it has no access to them. This is because these data items are private to the Property class. Access control is enforced at the level of the class, and the Property class is a different class from the ForRent class, even though one is derived from the other. We could, if we wanted, do the rest of the initialization in the field initializer list also:
ForRent::ForRent(string addr, int rms, char cond, int rnt, int nocc) : Property(addr, rms, cond), rent(rnt), max_occupants(nocc) { }

The other thing to notice about the ForRent class definition is that it contains a prototype for the print function that is identical to the one in Property. This means that ForRent does not want to simply inherit the print function from Property but will override it with a print function of its own.
void ForRent::set_rent(int rnt) { rent = rnt; }

Let's suppose, just for the sake of an example, that we decide to include some validation in the set_occupants function; if the number of occupants exceeds the number of rooms, we output an error message. We might try to write it like this:
void ForRent::set_occupants(int nocc) { if (nocc > rooms) cerr << "Too many occupants for the rooms" << endl; else max_occupants = nocc; }

But we can't do that. As we've just discussed, we have no access to rooms from inside a ForRent function definition. There are two things we can do. One is to go back to our definition of the Property class and add a public accessor function get_rooms. Another is to move rooms out of the private section and into a protected section, thus (and we might as well move condition while we are about it):
class Property { private: string address;

protected: int rooms; // number of rooms char condition; // 'A' = very good condition, 'B' not so good etc public: Property(string, int rms = -1, char cnd = 'U'); // defaults indicate "unknown" void set_rooms(int); void set_condition(char); void print() const; };

Items in the protected part of a base class are accessible to classes derived from that base class, but not to the rest of the world. So, in this example, ForRent and ForSale can now access rooms and condition. Finally the print procedure:
void ForRent::print() const { Property::print(); cout << "Max n of occupants: " << max_occupants << endl; cout << "Monthly rent: " << rent << endl; }

Since the Property class already has a print procedure, it makes sense to use it to output the first few lines of data, but if we simply made a call to print(), this would be a recursive call to ForRent's print. So we have to specify that we mean the print procedure in the Property class Property::print(). We can define the ForSale class along the same lines.

Using derived-class objects


Having done all that, we can now use objects of these classes:
int main( ) { Property xp("1, Malet Street, Bloomsbury"); base class for the other data items) ForRent xr("The Penthouse, Park Drive, Mayfair"); the derived classes, as you would expect ForSale xs("23 Railway Cuttings, Cheam"); xr.set_rent(3000); function, obviously xs.set_price(65000); xp.set_condition('B'); function on a base class object xp.set_rooms(12); xr.set_condition('A'); class function on a derived class object xs.set_rooms(8); xp.print(); xr.print(); xs.print(); } // we can have objects of the // (we're using the defaults // we can also have objects of

// we can call a derived class

// we can call a base class

// and we can also call a base

// calls Property::print // calls ForRent::print // calls ForSale::print

Note the lines xr.set_condition('A'); and xs.set_rooms(8); The ForRent class does not have a set_condition function of its own, nor does ForSale have a set_rooms. These are functions defined in the Property class and they have been inherited by the derived classes. What is public in the base class is also public in the derived classes. This is, of course, the whole point of this inheritance, so that is what you would expect. But it only works because of the word "public" in the first line of the ForRent (and ForSale) class definitions:
class ForRent : public Property

It is possible to specify private inheritance (just replace "public" with "private"), in which case what is public in the base class is private in the derived classes. If we did that here, it would mean that ForRent could make use of, say, set_condition within its own function definitions, but we could not say xr.set_condition('A') from within main. It is hard to think of a plausible reason why you would want this, and examples in text books are pretty strained. Annoyingly, private is the default (if you just write "class ForRent : Property", private is what you get), so you have to remember to specify public inheritance.

Dynamic binding and polymorphism


Suppose we wanted a procedure that printed out the details of a property with a header and a footer, and suppose that we wrote a non-member function to do this, taking a property as its parameter:
void fancy_print(Property x) { cout << "Pricey and Ripoff, Estate Agents" << endl; x.print(); cout << "For more information see www.exorbitant.com" << endl; }

If we supplied an object of the base class Property as the argument, as, for example:
fancy_print(xp);

this would work OK. But, if we supplied an object of one of the derived classes, say a ForRent object:
fancy_print(xr);

it would be only the Property part of the ForRent object that would be printed out the extra items, special to ForRent, would be sliced off in the process of parameter passing. But we can arrange for it to print derived class objects, as well as base class ones, by the following: Go back to the base class definition and insert the word virtual before the print prototype:
class Property { private: string address; int rooms; // number of rooms char condition; // 'A' = very good condition, 'B' not so good etc public: Property(string, int rms = -1, char cond = 'U'); // defaults indicate "unknown" void set_rooms(int); void set_condition(char); virtual void print() const; };

Pass the argument by reference rather than by value:


void fancy_print(const Property& x) { cout << "Pricey and Ripoff, Estate Agents" << endl; x.print(); cout << "For more information see www.exorbitant.com" << endl; }

Normally, it is the compiler that decides, when given a function call, which function it is that is being called (and, since you can have several functions with the same name, this is not always a trivial matter, as we will see in a later lecture). This process, of linking up the call with the definition, is known as binding, and, if it is performed at compile time, it is static binding. What we have just done, however, is to delay the process of choosing the appropriate function till run-time. When it encounters x.print(); it is now the run-time system, rather than the compiler, that decides which function to call. This is known as dynamic binding. So, if we call fancy_print and supply a ForRent object as the argument say, fancy_print(xr); it will be the ForRent::print() function that will be called. If we supplied a ForSale object fancy_print(xs); it would be the ForSale::print() that would be called. If you want, you can put the word virtual in front of the print prototypes in the derived class definitions also, but this would only be for documentation, a reminder that the corresponding function in the base class is virtual. It is the one in the base class that is important. Just as a base-class reference parameter can accept an argument of a derived class, so a base-class pointer can point at a derived-class object, and, again, you get dynamic binding:
Property* ppt = &xr; ppt->print(); ppt = &xs; ppt->print(); // Note Property*, not ForRent* // calls ForRent::print() // calls ForSale::print()

//

vector<Property*> vp; vp.push_back(new ForRent("10 Downing Street")); vp.push_back(new ForSale("Dome, Greenwich")); and so on, filling vp with pointers to, variously, ForRent and ForSale objects

for (int i = 0; i < vp.size(); i++) vp[i]->print(); of *(vp[i])

// calls the appropriate print() for the type

This feature of the language the ability to treat objects of different but related types in the same way, without worrying precisely which type each one is is known as polymorphism; objects of the same basic class can take on different forms. It can happen that you want the derived class objects to have a function (like print in our example) that has this character but there is no function in the base class for you to label virtual. For example, you might want a commission function that calculates the commission when a client buys a property or rents one, perhaps 2% of the sale price for a ForSale and six months' rent for a ForRent. But there is no commission function in the Property class. In that case, you have to include one anyway, even if the body is empty. (Some compilers complain if a function (as opposed to a procedure) has an empty body, in which case you can include return 0; or something.) So the public part of our Property class would contain the following line:
virtual int commission() const {} // or { return 0; }

A Shape class: pure virtual functions and abstract base classes


Let's look at a different example. Suppose we wanted to manipulate shapes circles, rectangles etc. We might define a Shape base class and define Circle, Rectangle and so forth as derived classes:
class Shape { public: virtual double area() const {} virtual void draw(int x_coord, int y_coord) const {} graphics library .... }; class Circle : public Shape { private: double rad; static const double PI; public: Circle(double r) : rad(r) {} double area() const { return rad * rad * PI; } void draw(int x, int y) { draw_circle(x, y, rad); } circle of radius rad at co-ordinates x, y .... }; const double Circle::PI = 3.14159; class Rectangle : public Shape { private: double hei, wid; public: Rectangle(double h, double w) : hei(h), wid(w) {} double area() const { return hei * wid ; } void draw(int x, int y) { draw_rectangle(x, y, hei, wid); } .... };

// imagine we have use of some

// imagine this draws

As it stands, it would be possible to define an object of type Shape. However, Shape has no constructor (since it has no data, it has no need of one), and, if you called one of its functions, it is not clear what would happen. In any case, it hardly makes sense to have a shape that is not any particular shape. We might decide to rule out objects of type Shape, and we can do this by making the functions into pure virtual functions, as follows:
class Shape { public: virtual double area() const = 0; virtual void draw(int x_coord, int y_coord) const = 0; .... };

Note the addition of = 0 after the function prototypes. This makes a function a pure, virtual function. A pure, virtual function does not need to have a body, though it may do. Its purpose is solely to provide a basis for it to be overridden in the derived classes.

The addition of this = 0 is not only to make the functions pure virtual, but it also makes the class an abstract base class. That is to say, we cannot now have objects of type Shape. The purpose of the Shape class is solely to provide a basis for the derivation of Circle, Rectangle etc. Essentially it defines the interface that will be common to all the derived shapes. It is enough to make just one function pure virtual to make the entire class abstract. If we made just, say, the commission function in our Property class pure virtual, then the Property class would become abstract and we could no longer define objects of the base type Property. Generally a pure virtual function has no body, but it may do. Suppose that the print procedure in the Property class was made pure virtual. It would no longer be possible to call it on a Property object since Property would now be an abstract base class, but it would still be possible to call it in the derived classes as Property::print(); (If a pure virtual function has a body, the function definition has to be outside the class definition; the class definition contains just the prototype with "= 0" on the end.) Our Shape class has no data and so no constructor, but, if we made our Property class abstract, it would still have the data items address, rooms and condition and would still have a constructor for them. But, since this constructor could only ever be called from inside the derived classes, it could be protected rather than public. Does a derived class have to override all the functions of an abstract base class? No, but if it fails to override a pure virtual function, then it will itself become an abstract class. Click here for a full listing of the Shape example showing the use of an abstract base class.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000/08

C++ Week 19 Exception handling


An exception is something that may happen in your program but which shouldn't really, and which makes it unsafe or impossible for the program to continue. Typical examples of exceptions are: trying to divide by zero trying and failing to open a file (maybe it's not in the place it ought to be) trying to pop an empty stack There are various ways of dealing with these problems. At one extreme, you can just do nothing; the C++ standard stack class, for example, produces undefined behaviour if you try to pop an empty stack object, and the subscript operator([]) for strings and vectors makes no attempt to trap array-bounds errors (though the at function is provided as an alternative, which does trap these errors). At the other extreme, you can crash the program, perhaps with an error message, e.g.
int Stack::pop() { if (empty()) { cerr << "Cannot pop empty stack" << endl; exit(1); } ..... }

Between these two extremes are ways of signalling the error in the hope that the problem can be sorted out elsewhere. One approach is to appeal to a user to sort out the problem. For example:
do { cout << "Please key in file name: "; cin >> filename; infile.open(filename.c_str()); if (infile.fail()) { cout << "Cannot open " << filename << endl; cout << "Please check file name and try again" << endl; } } while (infile.fail());

Or you might return an error code so that the calling program can take appropriate action. If the problem is liable to arise in a procedure, you can turn it into a boolean function; returning true indicates that all went well, while false indicates a problem. Now, instead of calling a procedure as, say, r.get_record(); you call it as a function:
if (!r.get_record()) // getting a record is now a side-effect { Deal with problem } else { Process record }

If you have a (non-void) function, you might be able to commandeer one of the possible return values as an error flag. If, for example, your int function always returns values >= 0, you might use -1 to indicate that an error has occurred. This assumes, of course, that -1 could never be returned as a genuine return value. Giving this role to a value that is merely unlikely (though not impossible) is asking for trouble. An extra (reference) parameter is sometimes used, like flag in this example:
bool flag; x = f(y, z, flag); // f returns int but also sets flag as side-effect if (!flag) { there is a problem ....

Or a global might be used. There is in fact a standard global int variable supplied for this purpose, called errno (you have to #include <cerrno>). But it obviously suffers from the same problems as global variables in general - several functions might be setting and clearing the same variable and confusing each other in the process. A further variation is to have a function return a pointer, say an int* rather than an int. Now the calling

program can test for a NULL return value:


int* p = f(x, y); if (p == NULL) { there is a problem .... } else { use *p }

But be careful when writing the function. Can you see what is wrong with this?
int* { } f(int x, int y) int n; assign some value to n return &n;

The problem is that n is an auto variable - it ceases to exist once the function has completed. By the time the calling program comes to try to use the returned value (as *p), the part of memory that p is pointing at may no longer hold the value that f computed. It would work if n were static (remember to place an initial value in it with an assignment, unless you actually want it to start with the value it ended up with last time) or if you returned a pointer to a new int, though beware of memory leaks:
int* { } f(int x, int y) int* ptr = new int; // calling program responsible for deleting it assign some value to *ptr return ptr;

Yet another variation is to create a class that contains both returned value and error code, such as this:
class Interr { public: .... private: int x; bool error_flag; };

and have your function return an Interr rather than an int. The problem with all these error-code systems is that they assume that the writer of the calling program knows what system the function is using and what the significance of this or that error code is. If the writer of the program and the function are one and the same person, this may be acceptable, though even there the resulting program exhibits what software engineers call "tight coupling", which is when one part of a program depends overmuch on the details of another part, so that the two are not easily disentangled.

The C++ exception handling system


Consider, however, using a class whose internals are hidden from you, such as a library class. Which of the above methods would you like it to use for coping with exceptions? You probably don't want it to ignore them and behave unpredictably, and you probably don't want it to crash the whole program for reasons invisible to you. So it has to have some way of telling you that something has gone wrong in a way that enables you, possibly, to deal with it. This is the situation you should keep in mind when considering the C++ exception handling mechanism. Briefly, it works by: declaring a named class for each kind of exception we expect to have to handle. This class may be empty, though it does not have to be, e.g.
class Poponempty {};

throwing an object of this class at the point where the exception actually occurs
int Stack::pop( ) { if (empty()) throw Poponempty(); default constructor .... } // throws an (unnamed) object created by the Poponempty

Note the brackets in throw Poponempty(); If you left them out - throw Poponempty; - you would be trying to throw a type, which is not possible. If you preferred, you could give the exception object a name, as in { Poponempty ppe; throw ppe; } defining a try block in which the exception might arise. Often this will be the body of a function, perhaps main(), for example
int main( ) try { .... x = mystack.pop(); // but mystack might be empty! .... }

putting a catch clause at the end of the try block to handle the exceptions, e.g.
try { .... x = mystack.pop(); // but mystack might be empty! .... } catch (Poponempty) { cerr << "Can't pop an empty stack\n"; exit(1); }

You could give the Poponempty parameter a name if you wanted - catch (Poponempty x) - but there would be no point in doing so since the object is empty and there is therefore no reason to refer to it within the catch clause; the object has served its purpose just by being thrown and caught. (The above syntax is correct, but on the Borland compiler you may find that you have to put in an extra '{' before the try and a matching '}' after the catch clause.) Unwinding the stack In the above example, the exception is thrown from a function call made directly within the try block. But it would also work if the exception was thrown from, say, a function called by a procedure called within the try block:
int func(int a) { .... x = mystack.pop(); .... } void proc() { .... y = func(z); .... } int main() try { .... proc(); .... } catch (Poponempty) { .... }

If pop threw Poponempty, the exception-handling mechanism would look for a catch clause to handle it. Perhaps the call in func occurs inside a try block with a suitable catch clause at the end. If not, perhaps the call to func occurs inside a try block in proc with a suitable catch clause at the end. If not, perhaps the call to proc occurs inside a try block in main with a suitable catch clause at the end. This process of moving up the hierarchy of calls is known as "unwinding the stack". throw/catch is not exactly like parameter passing The thing that gets thrown is usually a specially defined exception object, as in this example (Poponempty), but it can be a variable of some other type, such as an int. Be careful, however. Although the throw and catch mechanism looks like parameter passing, it is not the same thing. In particular, no

type conversion takes place. catch (double d) would not catch the -1 from throw -1; and catch (string s) would not catch the "Stack empty" from throw "Stack empty"; (a string literal is, strictly, a C-string, not an object of the C++ string class). Declaring exceptions in the interface You can declare in the interface that a function might throw a particular exception (or give a list of exceptions that it might throw):
class Stack { public: ... int pop() throw (Poponempty); ... };

You put this in the function definition also:


int Stack::pop( ) throw (Poponempty) { if (empty()) throw Poponempty(); ... }

Even if the programmer who is using this class has no access to the function definitions, he/she can still see that the pop function might throw a Poponempty exception and can incorporate a try block and a catch clause to handle it. In fact, if a function has an exception list of this kind, it is not only warning you that it might throw one of these exceptions; it is also promising not to throw any other exceptions. A function without such a list is not making any promises - it might throw any exception. Exception objects containing data If your calculations increment or multiply data you may inadvertently use a value in excess of INT_MAX. To catch this you have to test the possible overflow before the calculation. It is also possible to pass arguments to the exception class to help you track errors, e.g.
if (x < INT_MAX - sum) sum += x; else throw Integeroverflow(x, sum); // uses a two parameter constructor for the exception object

To make this code work, we need an appropriate constructor. For example:


class Integeroverflow { public: Integeroverflow(int ix, int iy): x(ix), y(iy) {} int get_x() const {return x;} int get_y() const {return y;} private: int x; int y; };

Also the catch clause will now look like:


catch (const Integeroverflow& iobj) // iobj is just an identifier { cerr << "Integer overflow caused by " << iobj.get_x() << " and " << iobj.get_y(); exit(1); }

Standard exceptions There are some standard exceptions, mostly defined in <stdexcept>. They are all derived from the same base class. They all take a C-string (e.g. a string literal) as argument to the constructor and they all have a member function called what that returns it. A useful one to know is bad_alloc which is thrown if a call to new fails (i.e. your program requests some more memory and the operating system cannot oblige). Another is logic_error, versions of which are thrown by the STL (the Standard Template Library that contains vector etc).

You can catch these exceptions:


catch (const bad_alloc& ba) { cerr << ba.what() << endl; Deal with it, eg release some storage by deleting some parts of a structure }

You can throw one of them with your own argument:


if (month < 1 || month > 12) throw logic_error("Impossible value for month"); which inherits from logic_error // or could use invalid_argument,

Or you can write your own exception class that inherits from one of them:
class Myerror : public logic_error { public: Myerror (string s, int x) : logic_error(s), mynumber(x) {} Data and functions specific to Myerror };

More than one exception More than one exception might be thrown from within a given try block, in which case you might have more than one catch clause at the end. The exceptions are tested in the order you place the catch clauses:
try { Various exceptions might be thrown from in here } catch (Exception1) { Deal with Exception 1 } catch (Exception2) { Deal with Exception 2 }

The order of the catch clauses - and therefore the order in which the exceptions are tested - might be important if, for example, Exception1 was a special form of Exception2 (i.e. derived from Exception2) requiring special treatment. You can also have a "catch-all" catch clause to deal with any exceptions that you have not explicitly mentioned (the "..." is not my shorthand - it is the correct syntax for this):
catch (...) { Deal with any other exception }

A catch clause is not a procedure In the earlier examples, the only action taken in the catch clauses was the rather unexciting one of issuing an error message and exiting the program. You are not restricted to this - the catch clause can do anything you like. If your catch clause is at the end of main, then there is perhaps not a great deal more you can do except release resources, close files and generally tidy up. If the catch clause is at the end of a try block elsewhere, you might be able to take remedial action and continue with the program. The normal flow of control in a program is that a procedure call transfers control to the procedure; at the end of the procedure, control returns to the point where the procedure was called and the program carries on from there. But a catch clause is not a procedure. At the end of the catch clause you do not return to the point where the exception was thrown and carry on from there - exception-handling in C++ is nonresumptive. At the end of the catch clause you are at the end of the try block. For example:
int f(int y) { .... if (some problem) throw logic_error("Some problem"); //A .... } void proc() try { .... x = f(y); //B

.... } catch(const logic_error& le) { .... } int main() { .... proc(); //C .... }

After the catch clause has executed, then, unless something in the catch clause directs otherwise, the program will resume at point C, not points A or B. Or consider this example, where the try block is the body of a non-void function (imagine that exceptionA and exceptionB are derived from logic_error):
int f(int x) try{ if (x == 1) throw exceptionA("A"); if (x == 2) throw exceptionB("B"); return 9999; } catch (const exceptionA& a) { cout << a.what() << " "; return 1111; // this is the return from f } catch (const exceptionB& b) { cout << b.what() << " "; // no return, so f's return value is undefined } int main( ) { cout << "f(1) " << f(1) << endl; cout << "f(2) " << f(2) << endl; cout << "f(3) " << f(3) << endl; }

The output is
f(1) A 1111 f(2) B 396200 f(3) 9999 [a garbage number - could be anything]

If the exception is something you can fix, you might be able to deal with the exception and carry on by putting the whole try block inside a loop:
while(some_condition) { try { Something here might throw Some_exception } catch (Some_exception) { Fix the problem } }

Perhaps, however, you can only partially deal with the problem in the catch clause and do not want the program to resume at its default position. In this case you might rethrow the exception (simply with throw;) for another catch clause further up the hierarchy to handle:
void proc( ) try { Something here might throw Some_exception } catch (Some_exception) { Do some of the handling here throw; // Rethrow the exception } int main( ) try { Contains, directly or indirectly, a call to proc( ) } catch (Some_exception) { Complete the handling }

Stack unwinding and memory management Can you see the problem with the following?
void procD() { Node* head; // Creates a linked list // Some_exception thrown here } void procC() try { .... procD(); .... } catch(Some_exception) { .... }

Presumably one of the things that the catch clause ought to do is to delete the linked list, but it can't; it has no access to head (which, being an auto variable, has already ceased to exist anyway). So we have a potential memory leak. One solution is as follows:
void procD() try { Node* head; // Creates a linked list // Some_exception thrown here } catch(Some_exception) { // Delete list throw; // rethrow exception for procC to deal with }

This would work, but you can imagine that it is tiresome to write catch clauses for every block of code that might produce a memory leak. A better solution would be to package the linked-list code into a class (this is better object-oriented programming anyway):
void procD() try { Linked_list lst; // Creates a linked list // Some_exception thrown here }

This solves the problem because, when an object goes out of scope, its destructor is called automatically. So, by the time procC's catch clause is executed, the linked list has already been deleted. And this works all the way up as the stack unwinds. If procA calls procB which calls procC which calls procD, and the exception arises in procD and the catch clause that handles it is in procA, then all the objects created in procD, procC and procB will have been destroyed by the time procA's catch clause begins to execute.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000-08

C++ Week 20 Function overload resolution


Resolving function overloads
In C++ you can have two or more functions with the same name so long as they differ in their parameter lists. This is called function overloading. The function is invoked whose parameter list matches the arguments in the call. Normally the compiler can deal with overloaded functions fairly easily by comparing the arguments with the parameter lists of the candidate functions. However, this is not always a straightforward matter. Consider the following code fragment:
void f(double d1, int i1) {... } void f(double d1, double d2) {... } ... int main( ) { cout << f(1.0, 2); }

How does the compiler know which version of f() to call? The compiler works through the following checklist and if it still can't reach a decision, it issues an error: 1. Gather all the functions in the current scope that have the same name as the function called. 2. Exclude those that don't have the right number of parameters to match the arguments in the call. (It has to be careful about parameters with default values; void f(int x, int y = 0) is a candidate for the call f(25);) 3. If no function matches, the compiler reports an error. 4. If there is more than one match, select the 'best match'. 5. If there is no clear winner of the best matches, the compiler reports an error - ambiguous function call.

Best matching
In deciding on the best match, the compiler works on a rating system for the way the types passed in the call and the competing parameter lists match up. In decreasing order of goodness of match: 1. 2. 3. 4. An exact match, e.g. argument is a double and parameter is a double A promotion A standard type conversion A constructor or user-defined type conversion

Exact matches
An exact match is where the parameter and argument datatypes match exactly. Note that, for the purposes of overload resolution a pointer to an array of type x exactly matches a pointer of type x. This is because arrays are always passed by reference, meaning that you actually pass a pointer to the first element of the array. For example:
void f(int y[ ]); // call this f1 void f(int* z); // call this f2 .... int x[ ] = {1, 2, 3, 4}; f(x); // Both f1 and f2 are exact matches, so the call is ambiguous. void sf(const char s[]); void sf(const char*); .... sf("abc"); // Same problem; both sf functions are exact matches.

Type promotion
The following are described as "promotions": A char, unsigned char or short can be promoted to an int. For example void f(int); can be a match for f('a'); A float can be promoted to a double. A bool can be promoted to an int (FALSE counts as 0, TRUE as 1).

Standard conversions
All the following are described as "standard conversions": conversions between integral types, apart from the ones counted as promotions. Remember that bool and char are integral types as well as int, short and long. conversions between floating types: double, float and long double, except for float to double which counts as a promotion. conversions between floating and integral types conversions of integral, floating and pointer types to bool (zero or NULL is FALSE, anything else is TRUE) conversion of an integer zero to the NULL pointer. All of the standard conversions are treated as equivalent for scoring purposes. A seemingly minor standard conversion, such as int to long, does not count as any "better" than a more drastic one such as double to bool.

Constructors and user-defined conversions


A certain kind of constructor can play a special role in type conversion. Suppose you had a class Bigint that was capable of storing integral numbers larger than INT_MAX, and you had a constructor for a Bigint that took a C-string, so that a declaration of a Bigint object might look like this:
Bigint b("12345678901234567890");

This constructor also provides an implicit type conversion. Having defined b as a Bigint, it would be possible to say, for example:
b = "999999999999";

or you could invoke the type-conversion explicitly:


b = static_cast<Bigint>("88888888888");

The same type-conversion would be used in parameter passing, enabling us to do this:


void f(Bigint); f("77777777777777");

We are able to pass a C-string to a function that expects a Bigint because there exists a Bigint constructor that takes a C-string. This kind of conversion can only work when the constructor can be called with just one argument. Generally this means that the constructor will have just one parameter, but it could have more if all but the first of the parameters (or, indeed, all of them) had default values. When you think about using this type-conversion in assignment, it is obvious that the constructor must be of the kind that can be called with only one argument. In the statement:
b = something;

the "something" can't be nothing, and it can't be more than one thing. In the context of parameter passing, the compiler has to be able to find a parameter for each argument. It would not consider void f(Bigint) as a candidate for f("6666666", 57); even if the Bigint class had a constructor that took a C-string and an int - the compiler needs one parameter for the "6666666" and

another for the 57. The compiler also has to find an argument for each parameter that is not given an explicit default in the function header, so it would not consider void f(Bigint) as a candidate for f(); even if Bigint had a constructor with no parameters or with all its parameters defaulted. (It would, however, consider void f(Bigint b = "0") as a candidate for f(); since the parameter here has an explicit default.) These "conversion constructors" enable us to have object parameters corresponding to arguments of other types. User-defined conversions are for going the other way. They enable us to pass objects (as arguments) to functions with parameters of other types, as in the following:
void f(int n); Bigint b("123456"); f(b); // would work if there was a Bigint::operator int() conversion function

Conversion member-functions allow you to specify how you want objects to respond if they are asked to behave as if they were objects of some other type. In this case, a Bigint is being treated as if it were an int. A conversion function that allowed the above code to work might take the form:
class Bigint { public: ... operator int(); ... };

// returns a Bigint as an int. // Note there is no return type and no parameter list.

Bigint::operator int() { If the value of the Bigint is less than INT_MAX, return the value as an int, else return -1; // or throw an exception or something }

A note on const ref parameters


Suppose that the Bigint class had a constructor that took an int. It would then be possible to pass an int to a function that expected a Bigint:
void proc(Bigint bx) { ...... } proc(54321);

The parameter here was a value parameter. Would it have made any difference if the parameter had been a const ref, i.e.
void proc(const Bigint& bx) { ...... } proc(54321);

At first sight, this looks rather strange. When you pass an argument by reference, the parameter name becomes an alternative to the argument name - two names for the same thing. But here, we are not passing anything that has a name; we are simply passing a value. To put it more technically, the argument has an r-value but not an l-value (you could use it on the right-hand side of an assignment but not on the left-hand side). There is, apparently, nothing for bx to refer to. But in fact it would work. When you pass an argument that has an l-value to a const ref parameter, you get the ordinary call-by-reference (except, obviously, that the parameter is const). When you pass an argument that has only an r-value, a temporary variable is created with the same type as the parameter, and the const ref parameter refers to this temporary variable. So, in this example, a temporary variable of type Bigint is created, the value 54321 is used to initialise it, and this is the object to which bx refers. The temporary variable disappears when proc terminates. (In other words, when the argument has no l-value, the const ref parameter behaves very like a value parameter.)

Parameter lists that include default values


Parameters with default values carry their full weight in the scoring of a function. For example:
void f (int x, double y, int z = 2); // (f1) void f (int x, int y); // (f2) ... f(3, 4.5); // matches f1 exactly, whereas f2 requires a double-to-int standard conversion

Choosing a winner
A candidate function is only as strong as its weakest match; a candidate requiring three promotions, for example, beats a candidate with two exact matches and a standard conversion. Candidates whose weakest matches are equivalently weak are compared on their next-weakest, and so on - a candidate with a standard conversion, a promotion and an exact match beats a candidate with a standard conversion and two promotions.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 21 Templates, preprocessor commands, the STL


Template classes
We have already seen how we can implement a stack of integers. It would be nice if we could reuse that code to allow us to declare a stack of strings or doubles, in the same way as we can declare vectors of different datatypes. In fact the vector is defined with a template and we will now look at how to implement this ourselves, taking the Stack class and modifying it. The steps are as follows: 1. Put the line template <typename T> in front of the class header and in front of all the member function definitions. There is nothing special about the name T any identifier will do; it's just a placeholder for the data type we will eventually put in our stack, e.g integer or string. We are, in effect, parameterizing the type, and this identifier is a bit like a parameter. 2. Add a <T> to the class identifier, i.e. change Stack to Stack<T>. Compilers seem to vary in how fussy they are about which ones you attach it to. Some are happy to have every Stack changed to Stack<T>; others get upset if you attach it to the Stack in class Stack or to the names of the constructor(s) or the destructor. The essential ones are the ones before the scope operator Stack<T>:: and any that do not follow a Stack<T>::, such as when the Stack is a return type (which therefore precedes the function name with its Stack<T>::), or when it is the parameter of a non-member function (a non-member function name is obviously not preceded by a Stack<T>::). 3. Replace all the occurrences of the data type you want to change with T, in our example you would change the instances of int to T. You should be careful to change only those occurrences of int that refer to the contents of the stack; you would not want to change, for example, an int that was the counter of a for loop. Typical changes are shown below. Click here for a full code listing.
template<typename T> class Stack //or Stack<T> on some compilers { public: Stack(); // or Stack<T>(); ... template<typename T> Stack<T>& Stack<T>::operator=(const Stack<T>& sx) // of the three occurrences of <T> in this line { if (this != &sx) // the first two are essential, the third optional { free(top); copy(top, sx.top); } return *this; }

In main() we can use syntax familiar from creating a vector, which is also a type of container class:
Stack<int> sti; Stack<string> ststr; sti.push(234); ststr.push("Cinderella");

Reusing code and creating components


Components are standard fragments of code that have been tested and are largely bug-free and stable. Code like this is very useful as it saves us re-inventing the wheel and cuts down on testing efforts. There are two ways in which you can deliver components. Source code

object code and its associated headers The first solution seems useful, but is fraught with danger. Open source software can be tweaked and broken. There are also commercial disadvantages. Someone could easily pinch your code, make a few improvements and sell it on at a large mark up. Moreover, tweaked code may end up being incompatible with other components and it is hard to maintain. For this reason components are often delivered as a source code header (.h ) file and an object file that contains all the binaries from the function definitions of your class. At final build time you can compile your other code into object files and link all the object code together to produce an executable. Under Unix CCN the command to produce an object file is, for example:
CCN -c date.C

Using Borland the command is:


bcc32 -c -odate date.cpp

Header files
A header file normally only contains the class definition. This is the source code file that you deliver and shows all the interfaces used by your class. Without this code it would be hard for anyone else to use your object code. Say we have two files Date.h and Month.h, which are the headers for the Date and Month classes respectively. To use them you will place a #include "Date.h" at the beginning of your code. Quite possibly Date.h will include a #include "Month.h" (There is nothing to prevent one header file including another.) The source code for Date.cpp will begin with the usual includes augmented by #include "Date.h", followed by the definitions of the member functions of the Month and Date classes, e.g.
#include <iostream> using namespace std; #include "Date.h" const Month Date::months = {{"",0},{"Jan",31}, {"Feb",28},....,{"Dec",31}}; Date::Date() { day = 1; month = 1; void Date::add_day() { .... } .... bool Date::operator<(const Date& dx) const { return month < dx.month || (month == dx.month && day < dx.day); } }

We can compile this into the Date.o object file (the compiler does not insist on having a main if it is compiling into object code - it is happy to compile a set of functions).

Using header files


When we come to write code that will make use of the object code, we just include the header file so that the compiler can see what the class interfaces are. It doesn't need any more than that for the time being. For example:
//file Usedate.C #include <iostream> using namespace std; #include "Date.h" int main( ) { Date D; .... D.add_day(); ... } // To use the Date class, we only need to know its interface

We compile our Usedate.C into object code and pass this file of object code, along with the file of object code generated from Date.C, to the linker. The linker links it all up to produce an executable.

Conditional compilation
Things can get complicated when we use several headers that themselves contain a #include "Date.h". Suppose we had #include "Calendar.h" and #include "Report.h" at the start of our program, and that both Calendar.h and Report.h contained #include "Date.h" The preprocessor will pull in Date.h twice. When the resulting file is handed on to the compiler, it will not accept it since it will not accept multiple definitions of the same class, even if the definitions are identical, so we are in trouble. To work around this problem, we use conditional compilation. The preprocessor accepts commands of the form #define, #ifdef and #endif. We put the command #ifndef DATE at the top of Date.h to test whether the flag DATE has been defined. If it hasn't (i.e. this is the first time that Date.h has been included in the file being processed), then we #define DATE and add the rest of the class definition. Finally we use #endif to close the conditional statement.
#ifndef DATE #define DATE #include "Month.h" class Date { public: Date(); ... }; #endif

The next time the preprocessor encounters a #include "Date.h", it will detect that DATE has already been set and skip the code up to the subsequent #endif, so Date.h will only ever get included once. #defines are also useful when you want to compile your code for different environments. You can set the preprocessor flags from the command line, or by adding a line to the top of your .C file, so that the preprocessor will only bring in code that has been flagged for a particular platform, such as Windows or Unix. For example:
#define SUN #ifdef SUN //code for SOLARIS in here #endif #ifdef WINDOWS //code for Windows in here #endif

You edit the first line to be #define SUN if you want SUN code to be included, #define WINDOWS if you want Windows code to be included. Similarly this can be a useful way to set up a debug version of your program that prints out messages. You bracket each of your debug sections between #ifdef DEBUG and #endif. If you want the debug version to be compiled, you insert the line #define DEBUG at the start; if not, you take it out. Conditional compilation can also be helpful when want to exclude part of the program, in the course of debugging. You can also achieve the same effect by putting part of the program between /* and */, i.e. pretending that part of the program is a comment and therefore to be ignored by the compiler. But this will not work if the part to be excluded itself contains a (genuine) comment because comments cannot be nested. Better is to insert a #if 0 before the part to be excluded and a #endif at the end.

The Standard Template Library


The STL contains a range of container classes. The vector class is one of them. Another is the list class. To use the list container class you should #include the <list> library. Lists are like vectors in that they are template classes and can hold collections of types, such as objects or integers. They are declared as follows:
list<int> ilist; // create a list of integers

The list class supports the member functions push_back() and push_front(), which work as you might expect. e.g.
ilist.push_back(1); ilist.push_back(2); // // 1 1 2 3 1 2 4 3 1 2

ilist.push_front(3); // ilist.push_front(4); //

Other container classes include the deque ("deque" is short for "double-ended queue", though the STL deque is not really a deque in the Data-Structures sense but a double-ended vector). Also the set, which you can think of as a binary search tree, the multiset (like a set but allowing duplicates), the map, which is a binary search tree of key-value pairs, and a multimap (a map allowing duplicates). Specialised versions of these are also available - the stack, the queue and the priority queue. This last is like a queue but the next item to get removed is not necessarily the one that has been there longest (as in a regular queue) but the one with the highest value on some attribute. For instance, if age were the attribute, then the person at the "front" of the queue would be the oldest person in the queue.

Iterators
Each of the container classes has its own type of iterator. An iterator is like a pointer, only it's a C++ object and therefore has its own functionality. For example, as with a pointer, you can apply the dereferencing operator to an iterator to get at the thing it is pointing at, but unlike a pointer, you can also use the auto-increment and auto-decrement operators meaning "Go on to the next item," or "Go back to the previous item." (You can use ++ and -- with a pointer but only if it is pointing at an element of an array. You cannot use them to move along a linked list or a binary tree. With iterators, you can.) Iterators allow you to move along lists, using the begin() and end() member functions as reference points. Note that, as with vectors, end() is actually the post-ultimate position, i.e. whereas begin() is the position of the first item in the structure, end() is a position after the last one. You declare an iterator variable and use it as an index to be moved along the list, much as you might use a pointer.
list<int>::iterator intiter; iterator type intiter = ilist.begin(); // declare an iterator; each container class has its own

To move an iterator along the list, use the ++ and -- functions. The following procedure prints the contents of a list:
list<int> ilist; ... list<int>::iterator intiter; for (intiter = ilist.begin(); intiter != ilist.end(); intiter++) cout << *intiter << " ";

insert()

takes an iterator and an item to insert, e.g.


// inserts a 5 before the item intiter is pointing at // equivalent to a push_back()

ilist.insert(intiter, 5); ilist.insert(ilist.end(), 6);

erase()

takes an iterator and removes the item from the list, e.g.

ilist.erase(intiter); // remove selected item intiter = ilist.end(); intiter--; ilist.erase(intiter); // last three lines equivalent to pop_back() // You can't do arithmetic with iterators, eg you can't say ilist.end() - 1.

Generic algorithms
There is a set of algorithms provided in the standard library that operate on container classes - you could use the same algorithm on a vector or a list, for example (or a set or a map), hence the name "generic". They are in the <algorithm> library. Consider a vector of integers v that contains the following values

5
sort(v.begin(), v.end());

If we #include the <algorithm> library, we can use the generic algorithm sort() as follows: which sorts the vector v in ascending order from start to finish. begin() and end() are iterator values corresponding to the position of the first element of the vector and a position immediately after the last element. The results of this procedure are thus: 2
reverse(v.begin(), v.end());

Similarly we can use the algorithm reverse() as follows to reverse the order of elements in v:

Another of the generic algorithms is find. find() takes three arguments, the start of the range, the end of the range, and the item being sought, and returns an iterator (sort of pointer) to the item, e.g.
intiter = find(ilist.begin(), ilist.end(), 1); // find the number 1 and return its position.

If the item sought is not in the list, find returns ilist.end(), which is not actually in the list. There are a number of these generic algorithms - see the appendix to Lippman and Lajoie. I have not said much about them in this course since the course is an introduction to programming in general, using C++, rather than a course specifically about C++. If you were going to be a serious C++ programmer, you would need to get acquainted with the generic algorithms - there are about 70 of them - and you would make extensive use of them, but I haven't expected you to do that on this course, and indeed I wouldn't want you to. Working out how to reverse a vector using a loop teaches you something about programming in general; learning how to use the reverse algorithm just teaches you something about C++.
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

C++ Week 22 Sample end-of-term tests


Test 1
Question 1
Write the output of the following main function. The boolalpha iostream manipulator makes the output of any subsequent boolean values appear as the words "true" or "false"; it does not itself produce any output. Write a few words of explanation for your answers for C to G.
int main( ) { cout cout cout cout cout cout cout cout } << << << << << << << << boolalpha; "A " << (0 < 1) << endl; "B " << ('0' < '1') << endl; "C " << ('0' < 1) << endl; "D " << ('0' < '1' - '0') << endl; "E " << ('0' < static_cast<char>(1)) << endl; "F " << (string("0") < string("1")) << endl; "G " << (static_cast<int>('0') < 1) << endl;

Question 2
If the following line were included in the above program, then, when run on the departmental Unix, it would output "true"
cout << ("0" < "1") << endl;

but the following line also outputs "true":


cout << ("1" < "0") << endl;

Why do you think this is? (Hint: think about parameter-passing.)

Question 3
Write down the output of this program fragment:
int f(int& x) { return ++x; } int main( ) { vector<int> v(4); for (int i = 1; i < v.size(); i++) v[i] = f(v[i-1]) + 2; for (int i = 0; i < v.size(); i++) cout << v[i] << " "; cout << endl; }

Question 4
Write down the output of this program fragment:
void f(int x, int y) { if (x <= 1 || y <= 1) cout << "* "; else { cout << x << " " << y << " "; if (x < y) f(x, y/x); else f(x/y, y); cout << y << " " << x << " "; } }

int main( ) { f(24, 3); }

Question 5
Write a program that reads a text file and, at the end, outputs the number of "words" that consisted entirely of strings of the same character. A word, for the purposes of this question, is just a string of nonspace characters, so strings such as "2345", "Why?" "BBC", "e.g.", "??!!" all count as words. The following would count as words consisting entirely of the same character "a", "ZZZZ", "???", "...", "********". You may read the file from standard input or by opening an ifstream, as you wish. Your program should read the file only once.

Question 6
Write a procedure that takes an integer as its parameter and which outputs a Christmas tree (well, a triangle of asterisks). If given the number 6, it would output the following (there are 11 stars in the bottom line):
* *** ***** ******* ********* ***********

If given a value below 1 or above 40, it does nothing.

Question 7
A researcher is trying to get a computer to read handwriting. Some lower-case letters, such as 'h' and 'k', have "ascenders", i.e. lines that stick up above the general height of the other letters, while others, such as 'g' and 'p', have "descenders"; many, such as 'a', 'c' and 's', have neither (no letter has both). He has the idea of recognizing a word by its overall pattern of ascenders and descenders. Write a procedure which takes a string as its parameter you may assume that the string contains a single word entirely in lowercase and writes out three lines showing the positions of the ascenders and descenders. For example, for "holly" it would produce the following:
| || ||||| |

while it would produce the following for "plumpudding":


| || ||||||||||| | | |

You may assume that the following array has been defined as a global:
const int ad[ ] = {0,1,0,1,0,1,-1,1,0,-1,1,1,0,0,0,-1,-1,0,0,1,0,0,0,0,-1,0};

The first element, set to 0, indicates that 'a' has neither ascender nor descender; the second element, set to 1, indicates that 'b' has an ascender. The third element is for 'c', the fourth for 'd', and so on. -1 indicates that the letter has a descender. You may also assume that you are able to call a function ctoi() which takes a char as its parameter and which, if the letter is lower-case, returns an int between 0 (for 'a') and 25 (for 'z').

Question 8
Write a function that takes a string containing a Christmas-present list, e.g. "Scooter CD Playstation Gloves Woolly_hat", and which returns a pointer to the head of a singly-linked list of nodes, each node containing a single item. The items in the original list are separated by spaces and any embedded spaces

are represented by underscores (as in "Woolly_hat").

Question 9
The following shows parts of the class definitions for a Phone class and a Phonebook class that, together, store the staff telephone numbers.
class Phonebook; class Phone { public: Phone(string nm, int nmb) : name(nm), number(nmb), l(NULL), r(NULL) {} string name; int number; Phone* l, *r; friend ostream& operator<<(ostream&, const Phone&); }; class Phonebook { public: Phonebook() { root = NULL; } void addphone(Phone ph) { if (not there(root, ph)) addphone(root, ph); } bool there(Phone ph) const { return there(root, ph); } void listbyname() const { listbyname(root); } int getnumber(string nm) const { return getnumber(root, nm); } bool dupname() const // a name appears more than once (someone has two or more numbers) { return dupname(root); } bool dupnumber() const // a number appears more than once (two or more people have the same number) { return dupnumber(root); } private: Phone* root; void addphone(Phone*&, Phone); bool there(Phone*, Phone) const; void listbyname(Phone*) const; int getnumber(Phone*, string) const; // returns 0 if name not found bool dupname(Phone*) const; bool dupnumber(Phone*) const; };

The entries are held as a binary search tree, ordered on name. Write the overloaded output operator function for Phone and the private functions for Phonebook. For dupnumber (which is harder than the rest), but not for the others, you may write an extra private function if you feel the need.

End of Test 1

Test 2
Question 1
When a constructor that takes arguments is called like this:
Date d(25,12);

why is a default constructor called like this:


Date d;

rather than like this:


Date d();

Question 2
Write the output of the following program:
#include <iostream> using namespace std; class B { public:

B() : bx(0) { cout << "B's default constructor" << endl; } B(int x) : bx(x) { cout << "B's int constructor" << bx << endl; } private: int bx; }; class A { public: A(); private: B b, c, d; }; A::A() : c(2) { d = B(3); cout << "A's constructor" << endl; } class C : public A { public: C() : A() { cout << "C's default constructor" << endl; } C(int cx) : A() { cout << "C's int constructor" << cx << endl; } }; C c; int main( ) { }

Question 3
Write the output of the following procedures, assuming that the call was proc(3);
void proc(int x, int y) { cout << string(x, static_cast<char>('A' + y)); } void proc(int x) { if (x > 0) { proc(x - 1); proc(x, x); proc(x - 1); } }

Question 4
A course director has given an evaluation questionnaire to his students, asking them to rate each of the ten modules of the programme on a scale from 5 ("Module was absolutely fascinating") down to 0 ("Module was incredibly dull"). He has had their responses keyed into a data file, called ratings.dat, with each student's ten ratings on one line, separated by spaces, like this:
1 3 0 2 3 1 5 2 3 0

He wants the results for the ten modules presented as ten tables, one after another, with the student numbers also presented as percentages, to one decimal place. Supposing that there were 200 students, the results for the first two modules might look like this:
Module 1 0: 45 22.5% 1: 59 29.5% 2: 42 21.0% 3: 36 18.0% 4: 14 7.0% 5: 4 2.0% Module 2 0: 22 11.0% 1: 45 22.5% 2: 40 20.0% 3: 16 8.0% 4: 33 16.5% 5: 44 22.0%

meaning that 45 of the students (22.5%) gave a rating of 0 to the first module, and so on. (The formula for x as a percentage of y is x/y*100. For example, 5 as a percentage of 20 is 5/20 (= 0.25) * 100 (= 25).)

If any line in the file has fewer than ten ratings, or more than ten, or if it contains non-numeric data, or if any of the ratings fall outside the range 0 to 5, he wants that line to be ignored - the results should be the same as they would have been if that line had been completely absent. The following are some examples of lines that should be ignored:
1 2 6 2 A 3 2 4 4 1 2 2 0 3 5 2 3 4 0 1 3 4 3 5 5 1 3 5 1 0 5 5 0 5 2 4 3 0 3 4 3 0 3 3 5 1 5 1 3 0 0 5 4 2 -3 0 2 0 5 1 A

The following are parts of a program to read this data file and to output the tables (to standard output) in the required format:
#include <iostream> #include <string> #include <vector> #include <sstream> #include <fstream> #include <iomanip> using namespace std; class Results { public: Results(); void get_results(); void show_results() const; private: vector<int> one_student; vector<vector<int> > results; int nstudents; // number of valid lines of data static const int nmods = 10; // 10 modules static const int maxresp = 5; // each response 0 to 5 bool get_next_student(istream& infile, bool& lineok); void add_to_results(); void show_module(const vector<int>& v) const; }; Results::Results() { one_student = vector(nmods); vector<int> one_module(maxresp+1); results = vector<vector<int> >(nmods, one_module); nstudents = 0; };

Function definitions here


int main( ) { Results res; res.get_results(); res.show_results(); }

(a) Draw simple diagrams to show the arrangement of elements in one_student and results. (b) Write get_next_student. It reads just one line from the data file and puts the ratings into the vector one_student. If the line is a valid one, it sets lineok to true, otherwise to false. It returns true, except at the end of file, when it returns false. (c) Write get_results, which calls get_next_student and add_to_results It opens the data file and processes it line by line. The ratings from valid lines are accumulated in the results vector. (d) Write add_to_results. It uses one_student to increment the appropriate elements of results. (e) Write show_results and show_module to display the contents of results in the required format.

Question 5
A Linkvec object behaves in some ways like a vector, but actually stores its results in a singly linked list, with the tail node corresponding to element[0]. (I suggest you read that last part again to make sure you have taken it in.) nodes keeps a count of the number of nodes. Below are some fragments of the definition and of a program that uses a Linkvec object. Write: the exception classes as much of the interface as is necessary to run the program

the definitions of the public functions and the definition of the (recursive) private function
lv.insert(2,99); lv.erase(5);

means, "Insert 99 between lv[1] and lv[2]."

means, "Erase lv[5]."

Providing a non-existent element as argument to insert or to erase causes a BadSubscript exception.


Exception classes here template <typename T> class Linkvec { public: Interface prototypes here void display(ostream& os) const { display(os, head); } private: class Node; Node* head; int nodes; void display(ostream&, Node*) const; };
template <typename T> class Linkvec<T>::Node { public: T n; Node* link; Node(T x) : n(x), link(NULL) {} }; template <typename T> ostream& operator<<(ostream& os, const Linkvec<T>& lv) { lv.display(os); return os; }

Function definitions here


int main( ) try { Linkvec<int> lv; lv.push_back(5); lv.push_back(6); lv.push_back(7); lv.push_back(8); lv.push_back(9); lv[lv.size()-1] += lv[0]; cout << lv << endl; // outputs 5 6 7 8 14 lv.insert(2,99); // lv is now 5 6 99 7 8 14 lv.insert(0,100); // lv is now 100 5 6 99 7 8 14 lv.erase(5); // lv is now 100 5 6 99 7 14 lv.erase(5); // lv is now 100 5 6 99 7 cout << lv << endl; // outputs 100 5 6 99 7 } catch (BadSubscript b) { cerr << "Subscript out of bounds: " << b.getsubscript() << endl; exit(1); } catch (EraseFromEmpty) { cerr << "Cannot erase from empty structure" << endl; exit(2); }

End of Test 2
Notes on R. Mitton's lectures by S.P. Connolly, edited by R. Mitton, 2000

A short introduction to computer programming, using C++


Roger Mitton, Birkbeck, University of London

Answers to exercises
1A
(a) cout << endl; (b) cout << "Hickory" << endl << "Dickory" << endl << "Dock" << endl; (c)

#include <iostream> using namespace std; int main() { cout << "Please key in two numbers separated by a space: "; int x, y; cin >> x >> y; cout << y << " " << x << endl; }

1B
#include <iostream> using namespace std; int main() { int x, y; cout << "Please key in a number: "; cin >> x; cout << "And now key in another: "; cin >> y; cout << "The sum is " << x + y << endl;
cout << "The sum is " << sum << endl;

int sum = x + y;

or you could have

2A 5 7 12 10 2 Arithmetic Exception

(attempt to divide by zero)

2B BBC C++ y2k Y2K old new 3GL a.out

OK No. Can't have "+" in an identifier. OK OK, and different from y2k. OK No. new is a keyword. No. Can't begin with a digit. No. Can't have a "." in an identifier.

remove first-choice 2nd_choice third_choice constant UNION

OK, though delete is a keyword. No. Can't have a "-". No. Can't begin with a digit. OK OK. const is a keyword, but constant is OK. OK, though union (lower-case) is a keyword.

2C
real art 7

2D false true false true false

Numerals come before letters Upper-case come before lower-case Upper-case 'B' before lower-case 'b' ASCII table puts '$' before '&' Space comes before apostrophe

2E
#include <iostream> using namespace std; int main() { int husband, wife; cout << "Please key in the husband's salary: "; cin >> husband; cout << "And now key in the wife's: "; cin >> wife; if (husband + wife > 40000) cout << "You are due for the higher rate." << endl; else cout << "You are not due for the higher rate." << endl; }

2F if (exammark >= 40) { cout << "A satisfactory result" << endl; cout << "You may proceed with your project." << endl; } else { cout << "I'm afraid you have failed." << endl; cout << "You may re-enter next year." << endl; }

2G #include <iostream> using namespace std; int main() { cout << "Please key in two numbers separated by a space: "; int x, y; cin >> x >> y; if (y == 0) cout << "Cannot divide by zero." << endl; else if (x % y == 0) cout << "Yes" << endl; else cout << "No" << endl; }

2H #include <iostream> using namespace std; int main() { cout << "Please key in an integer representing a 24-hour time," << endl; cout << "eg 1415 represents 2.15 pm. Midnight is zero: "; int time; cin >> time; if (time < 0) cout << "Cannot be negative" << endl; else if (time >= 2400) cout << "Cannot be >= 2400" << endl; else { int hours = time / 100, mins = time % 100; if (mins >= 60) cout << "Cannot have minutes >= 60." << endl; else if (hours >= 21) cout << "Working late?" << endl; else if (hours >= 17) cout << "Good evening" << endl; else if (hours >= 12) cout << "Good afternoon" << endl; else if (hours >= 8) cout << "Good morning" << endl; else if (hours >= 5) cout << "Up early?" << endl; else cout << "Get a life." << endl; } }

3A #include <iostream> using namespace std; int main() { int n = 1; while (n <= 10) { cout << n * n << endl; n = n + 1; } }

or int n = 0; while (n < 10) { n = n + 1; cout << n * n << endl; }

3B (x == 5 and y == 10) (x < 0 or y > 15) (y % x == 0 and s.length() == 8) (s.substr(1,3) == "Bir" or x / y > 0)

false true true false (s.substr(1,3) is "irk", and x / y gives integer division)

3C
#include <iostream> using namespace std; int main() { bool finished = false; int count = 0; while (not finished) { int num; cin >> num; if (cin.fail()) finished = true; else count = count + 1; } cout << "Number of numbers was " << count << endl; }

X1 The program takes a series of integers as its input and outputs the largest of them. If you can't see how it does it, trace through it with various possible inputs, such as 1 2 3 4, 4 3 2 1, 1 4 2 3. X2 It will not work if all the integers are negative. It will say, incorrectly, that the largest was zero. It will also output zero if there is no input at all. The following program will work correctly in both these cases: #include <iostream> using namespace std; int main() { bool empty = false, finished = false; int max, num; cin >> num; if (cin.fail()) empty = true; else max = num; while (not empty and not finished) { cin >> num; if (cin.fail()) finished = true; else if (num > max) max = num; } if (empty) cout << "No input" << endl; else if (not cin.eof()) cout << "Invalid input" << endl; else cout << "Largest was " << max << endl; }

X3 #include <iostream> using namespace std; int main() { bool finished = false; int count = 0; while (not finished) { int num; cin >> num; if (cin.fail()) finished = true; else if (num == 100) count = count + 1; } cout << "Number of times 100 occurred was " << count << endl; }

X4 #include <iostream> #include <string> using namespace std; int main() { bool finished = false; string s, longest; int maxlen = 0; while (not finished) { getline(cin,s); if (cin.fail()) finished = true; else if (s.length() > maxlen) { maxlen = s.length(); longest = s; } } cout << longest << endl; }

X5 #include <iostream> #include <string> using namespace std; int main() { bool finished = false; int curr, prev; cin >> prev; while (not finished) { cin >> curr; if (cin.fail()) finished = true; else { if (curr == prev) cout << "Same" << endl; else if (curr > prev) cout << "Up" << endl; else cout << "Down" << endl; prev = curr; } } }

4A int triplus(int x) { return x * 3 + 1; which is what we want }

// precedence of operators ensures (x * 3) + 1,

4B string lastpart(string s, int n) { if (n > s.length()) return s; else return s.substr(s.length() - n); }

4C void display(int x, int y) { int small = x, large = y; if (x > y) { large = x; small = y; } cout << "The sum is " << large + small << endl; cout << "The difference is " << large - small << endl; cout << "The product is " << large * small << endl; if (small == 0) cout << "Cannot divide by zero" << endl; else cout << "The quotient is " << large / small << << large % small << endl; }

" with remainder "

4D string middle(string s) { string empty_string; // initialised by default to empty string if (s.length() < 3) return empty_string; // or just return ""; (with no space between the quote marks) else return s.substr(1, s.length() - 2); }

4E

void stretch(string s) { if (s.length() == 0) return; cout << s.substr(0,1); int n = 1; while(n < s.length()) { cout << " " << s.substr(n,1); n = n + 1; } cout << endl; }

4F #include <iostream> #include <string> using namespace std; string firstpart(string s, int n) { return s.substr(0, n); } int main() { string sg; cout << "Please key in a string: "; cin >> sg; int n = 1; while(n <= sg.length()) { cout << firstpart(sg, n) << endl; n = n + 1; } }

School of Computer Science and Information Systems MSc/PGDip Computer Science: A short lesson on octal, hex and binary
The first thing to grasp is that there are many ways of writing the same number. When you see "15", you think "fifteen", but it is only by convention that we take the characters "1" and "5", arranged side by side in that order (not "51"), to represent the number fifteen. In Roman numerals, the characters "XV" are used to represent fifteen. Other systems use other characters. This lesson is about three of these other systems. The octal, hexadecimal ("hex" for short) and binary systems can sometimes look like the decimal system we are accustomed to, and this can cause some confusion, so I will first remind you how the decimal system works. Suppose a farmer is counting his sheep as they return to the fold. For each sheep that passes, he holds one finger (or thumb) in the air. When all his fingers-and-thumbs are in the air, he scratches a cross on the wall and begins again with his fingers. If he ends up with, say, two crosses and three fingers, then twentythree sheep have passed. Using the system of ten single numerals "0" to "9", we represent this as "23", the "2" standing for "number of crosses" (ie number of tens) and the "3" for "number of fingers" (ie units).

Octal
Imagine now that human beings had only a thumb and three fingers on each hand. And suppose that the farmer ended up, as before, holding up three fingers and with two crosses scratched on the wall. How many sheep would this represent? A cross represents all the fingers and thumbs on two hands, ie eight. So two crosses represent sixteen, which, with the extra three fingers, makes a total of nineteen. Now suppose the farmer was a bit more sophisticated and used a system of numerals "0", "1", "2" etc rather than crosses and fingers. He would represent this number as "23", i.e. two crosses (two eights) plus three. He would look at "23" and think "nineteen". (What I mean is that he would think of the number that we call "nineteen"; he would probably have a different name for it, something like "twocty-three".) It is because human beings happen to have two hands with four fingers, plus one thumb, on each hand i.e. ten things to stick up in the air to count on - that the number ten occupies such a significant place in our system of naming and writing numbers. If we had had eight fingers-and-thumbs, rather than ten, the number eight would have occupied that special place. The system based on eight rather than ten is called the octal system. Given that the following are written in octal, what numbers do they represent?
(a) 13 (b) 35 (c) 10

And how do you write the following in octal?


(d) twelve (e) sixty-three (f) eight

(Try to answer the questions before you look at the answers, which are at the end of these notes.)

10 and 100 What does "8" represent in octal? Trick question. "8" is not used in octal, nor is "9". When the eightfingers-and-thumbs farmer gets to eight, he scratches a cross on the wall and starts again, i.e. eight is

represented by one cross and no fingers, or "10". In octal, "10" is how you write the number eight. It looks like ten to us, but that is because we are so accustomed to the decimal system. On the same principle, when we reach "77" in octal (seven eights plus seven, ie sixty-three), the next number is "100" (one lot of eight-times-eight). In the decimal system, "100" is one hundred (ten times ten), but in the octal system, "100" is sixty-four (eight times eight).

Hexadecimal
In the familiar decimal system, the number ten has a special place; in the octal system, the number eight has this place. In the hexadecimal system, it is the number sixteen that occupies this place. We begin writing the numbers, as in the decimal system, from "0", "1" and so on up to "9". But we do not then go on to "10", i.e. we do not write the number ten as "10". Instead we start using capital letters, "A" for ten, "B" for eleven and so on until we have used "F" for fifteen. At this point we scratch a cross on the wall and start again, i.e. we write sixteen as "10", seventeen as "11" and so on. If we were using hex and we wrote "23", this would mean two sixteens and three units, i.e. thirty-five. "2A", which does not at first sight look like a numeral at all, would mean two sixteens and ten units, i.e. forty-two. "AB" would mean ten sixteens and eleven units, i.e. one hundred and seventy-one. Assuming that the following are in hex, what numbers do they represent?
(g) D (h) 13 (i) FF

How do you write these numbers in hex?


(j) thirty (k) thirty-three (l) forty-seven

Answers

Binary
We have looked at a base-ten system (decimal), base-eight (octal) and base-sixteen (hex). The binary system is base-two. It makes do with just two characters - "0" and "1". In a base-ten system, ten is written "10"; in a base-eight system, eight is written "10"; in a base-sixteen system, sixteen is written "10". In the binary system, two is written "10". The farmer's cross on the wall represents just two in binary, so "10" (one cross and no units) stands for one two and no units, i.e. two. Three in binary is "11", and already we have encountered the problem we reach at "99" (decimal), "77" (octal) and "FF" (hex); we need another digit, i.e. a three-figure numeral instead of a two-figure numeral. So, to represent four in binary we write "100", i.e. one lot of two-times-two. The following table goes up to eight in binary: nought one two three four five six seven eight 0 1 10 11 100 101 110 111 1000

Note that, while "1000" in decimal means "one lot of ten-times-ten-times-ten", "1000" in binary means "one lot of two-times-two-times-two", i.e. eight. When we read a number in decimal - say "9036" - we take the digits from left to right and we know, from their position, that the "9" indicates the number of thousands, the "0" the number of hundreds, the "3" the number of tens and the "6" the number of units. When we read a number in binary - say "1101" - we know, from their position, that the leftmost "1" indicates the number of eights, the next the number of fours, the "0" the number of twos and the final "1" the number of units - eight plus four plus one, i.e. thirteen. How do you write the following numbers in binary?
(m) nine (n) fifteen (o) sixteen

Is the following correct?


(p) 101 + 1 = 110

Answers

Hex and binary


We have been considering how numbers can be represented in hex and binary. But hex and binary also have another slightly different use in computing, namely to represent the contents of a computer's memory. You can think of the memory (or "main store") of a computer as consisting of an enormous number of little rows of switches. Each little row contains just eight switches and is called a "byte" of storage. Each switch, like an ordinary light switch, is either ON or OFF. To describe the state of a single byte in memory at a particular time, you could state the setting of each of the eight switches, perhaps like this:
ON ON OFF OFF OFF ON OFF ON

A more concise method would be to use "1" for "ON" and "0" for "OFF". Using this system, the same set of switches would be described as:
11000101

This may be an improvement on writing lots of "ON"s and "OFF"s but it is still far from ideal; strings of binary digits (or "bits") are tiresome to write and hard to read. Consequently it is common practice to take the eight binary digits in two groups of four and to represent each group of four by a single hex digit. The above example would be grouped into 1100 and 0101. 1100 is binary twelve and the hex representation of twelve is C; 0101 is binary for five, and hex five is 5. So 11000101 can be represented simply as C5. Note that we are not saying anything about the significance of this byte to the computer. It may represent a letter in the middle of some text, or it may be storing part of a number or it may be part of one of the computer's instructions or it may be something else again. We are simply using the hex system as a shorthand to describe the setting of the eight switches. The following table shows the binary and hex representations of the numbers nought to fifteen: nought one two three four five 0000 0001 0010 0011 0100 0101 0 1 2 3 4 5

six seven eight nine ten eleven twelve thirteen fourteen fifteen
(q) (r) (s) (t) 10000001 00010000 10101010 11111111

0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

6 7 8 9 A B C D E F

What is the hex representation of the following bytes?

And what are the contents of the bytes represented by these pairs of hex digits?
(u) (v) (w) (x) (y) (z) 55 7F 00 18 BC AE

Answers

Answers to questions on octal, hex and binary


(a) (b) (c) (d) (e) (f) eleven twenty nine eight 14 77 10

Return to text

Hexadecimal (g) thirteen (h) nineteen (i) two hundred and fifty five (j) 1E (k) 21 (l) 2F Return to text Binary (m) 1001 (n) 1111 (o) 10000 (p) Yes if the numbers are in binary (five plus one equals six). Return to text Hex representation of bytes (q) 81 (r) 10 (s) AA (t) FF (u) 01010101 (v) 01111111 (w) 00000000 (x) 00011000 (y) 10111100 (z) 10101110

School of Computer Science and Information Systems MSc/PGDip Computer Science: A short lesson on octal, hex and binary
The first thing to grasp is that there are many ways of writing the same number. When you see "15", you think "fifteen", but it is only by convention that we take the characters "1" and "5", arranged side by side in that order (not "51"), to represent the number fifteen. In Roman numerals, the characters "XV" are used to represent fifteen. Other systems use other characters. This lesson is about three of these other systems. The octal, hexadecimal ("hex" for short) and binary systems can sometimes look like the decimal system we are accustomed to, and this can cause some confusion, so I will first remind you how the decimal system works. Suppose a farmer is counting his sheep as they return to the fold. For each sheep that passes, he holds one finger (or thumb) in the air. When all his fingers-and-thumbs are in the air, he scratches a cross on the wall and begins again with his fingers. If he ends up with, say, two crosses and three fingers, then twentythree sheep have passed. Using the system of ten single numerals "0" to "9", we represent this as "23", the "2" standing for "number of crosses" (ie number of tens) and the "3" for "number of fingers" (ie units).

Octal
Imagine now that human beings had only a thumb and three fingers on each hand. And suppose that the farmer ended up, as before, holding up three fingers and with two crosses scratched on the wall. How many sheep would this represent? A cross represents all the fingers and thumbs on two hands, ie eight. So two crosses represent sixteen, which, with the extra three fingers, makes a total of nineteen. Now suppose the farmer was a bit more sophisticated and used a system of numerals "0", "1", "2" etc rather than crosses and fingers. He would represent this number as "23", i.e. two crosses (two eights) plus three. He would look at "23" and think "nineteen". (What I mean is that he would think of the number that we call "nineteen"; he would probably have a different name for it, something like "twocty-three".) It is because human beings happen to have two hands with four fingers, plus one thumb, on each hand i.e. ten things to stick up in the air to count on - that the number ten occupies such a significant place in our system of naming and writing numbers. If we had had eight fingers-and-thumbs, rather than ten, the number eight would have occupied that special place. The system based on eight rather than ten is called the octal system. Given that the following are written in octal, what numbers do they represent?
(a) 13 (b) 35 (c) 10

And how do you write the following in octal?


(d) twelve (e) sixty-three (f) eight

(Try to answer the questions before you look at the answers, which are at the end of these notes.)

10 and 100 What does "8" represent in octal? Trick question. "8" is not used in octal, nor is "9". When the eightfingers-and-thumbs farmer gets to eight, he scratches a cross on the wall and starts again, i.e. eight is

represented by one cross and no fingers, or "10". In octal, "10" is how you write the number eight. It looks like ten to us, but that is because we are so accustomed to the decimal system. On the same principle, when we reach "77" in octal (seven eights plus seven, ie sixty-three), the next number is "100" (one lot of eight-times-eight). In the decimal system, "100" is one hundred (ten times ten), but in the octal system, "100" is sixty-four (eight times eight).

Hexadecimal
In the familiar decimal system, the number ten has a special place; in the octal system, the number eight has this place. In the hexadecimal system, it is the number sixteen that occupies this place. We begin writing the numbers, as in the decimal system, from "0", "1" and so on up to "9". But we do not then go on to "10", i.e. we do not write the number ten as "10". Instead we start using capital letters, "A" for ten, "B" for eleven and so on until we have used "F" for fifteen. At this point we scratch a cross on the wall and start again, i.e. we write sixteen as "10", seventeen as "11" and so on. If we were using hex and we wrote "23", this would mean two sixteens and three units, i.e. thirty-five. "2A", which does not at first sight look like a numeral at all, would mean two sixteens and ten units, i.e. forty-two. "AB" would mean ten sixteens and eleven units, i.e. one hundred and seventy-one. Assuming that the following are in hex, what numbers do they represent?
(g) D (h) 13 (i) FF

How do you write these numbers in hex?


(j) thirty (k) thirty-three (l) forty-seven

Answers

Binary
We have looked at a base-ten system (decimal), base-eight (octal) and base-sixteen (hex). The binary system is base-two. It makes do with just two characters - "0" and "1". In a base-ten system, ten is written "10"; in a base-eight system, eight is written "10"; in a base-sixteen system, sixteen is written "10". In the binary system, two is written "10". The farmer's cross on the wall represents just two in binary, so "10" (one cross and no units) stands for one two and no units, i.e. two. Three in binary is "11", and already we have encountered the problem we reach at "99" (decimal), "77" (octal) and "FF" (hex); we need another digit, i.e. a three-figure numeral instead of a two-figure numeral. So, to represent four in binary we write "100", i.e. one lot of two-times-two. The following table goes up to eight in binary: nought one two three four five six seven eight 0 1 10 11 100 101 110 111 1000

Note that, while "1000" in decimal means "one lot of ten-times-ten-times-ten", "1000" in binary means "one lot of two-times-two-times-two", i.e. eight. When we read a number in decimal - say "9036" - we take the digits from left to right and we know, from their position, that the "9" indicates the number of thousands, the "0" the number of hundreds, the "3" the number of tens and the "6" the number of units. When we read a number in binary - say "1101" - we know, from their position, that the leftmost "1" indicates the number of eights, the next the number of fours, the "0" the number of twos and the final "1" the number of units - eight plus four plus one, i.e. thirteen. How do you write the following numbers in binary?
(m) nine (n) fifteen (o) sixteen

Is the following correct?


(p) 101 + 1 = 110

Answers

Hex and binary


We have been considering how numbers can be represented in hex and binary. But hex and binary also have another slightly different use in computing, namely to represent the contents of a computer's memory. You can think of the memory (or "main store") of a computer as consisting of an enormous number of little rows of switches. Each little row contains just eight switches and is called a "byte" of storage. Each switch, like an ordinary light switch, is either ON or OFF. To describe the state of a single byte in memory at a particular time, you could state the setting of each of the eight switches, perhaps like this:
ON ON OFF OFF OFF ON OFF ON

A more concise method would be to use "1" for "ON" and "0" for "OFF". Using this system, the same set of switches would be described as:
11000101

This may be an improvement on writing lots of "ON"s and "OFF"s but it is still far from ideal; strings of binary digits (or "bits") are tiresome to write and hard to read. Consequently it is common practice to take the eight binary digits in two groups of four and to represent each group of four by a single hex digit. The above example would be grouped into 1100 and 0101. 1100 is binary twelve and the hex representation of twelve is C; 0101 is binary for five, and hex five is 5. So 11000101 can be represented simply as C5. Note that we are not saying anything about the significance of this byte to the computer. It may represent a letter in the middle of some text, or it may be storing part of a number or it may be part of one of the computer's instructions or it may be something else again. We are simply using the hex system as a shorthand to describe the setting of the eight switches. The following table shows the binary and hex representations of the numbers nought to fifteen: nought one two three four five 0000 0001 0010 0011 0100 0101 0 1 2 3 4 5

six seven eight nine ten eleven twelve thirteen fourteen fifteen
(q) (r) (s) (t) 10000001 00010000 10101010 11111111

0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

6 7 8 9 A B C D E F

What is the hex representation of the following bytes?

And what are the contents of the bytes represented by these pairs of hex digits?
(u) (v) (w) (x) (y) (z) 55 7F 00 18 BC AE

Answers

Answers to questions on octal, hex and binary


(a) (b) (c) (d) (e) (f) eleven twenty nine eight 14 77 10

Return to text

Hexadecimal (g) thirteen (h) nineteen (i) two hundred and fifty five (j) 1E (k) 21 (l) 2F Return to text Binary (m) 1001 (n) 1111 (o) 10000 (p) Yes if the numbers are in binary (five plus one equals six). Return to text Hex representation of bytes (q) 81 (r) 10 (s) AA (t) FF (u) 01010101 (v) 01111111 (w) 00000000 (x) 00011000 (y) 10111100 (z) 10101110

You might also like