Sunteți pe pagina 1din 112

Types and Expressions

Types Expressions y Integers (int, short, long, char) y Summary: Expressions y Example: Converting to binary by o division y Floating Point (float, double, y Operator: sizeof long double) y Bitwise Operators o o Operators: Bitwise y Characters (char) o Example: convert to binary o Reading characters using bitwise ops o Character functions o Example: Convert to hex using <cctype> bitwise ops y bool y Exercises: ++, =, ?: Expressions y Enums o Enums o Enum Values and I/O

C++ Notes: Reading Characters


These notes are written using cin as an example, but they apply to all input streams.

What about whitespace?


The standard input stream (cin) does just what you want when reading integers, floating point etc. It ignores all whitespace (blanks, tabs, etc) that appears in front of a number. But when you're reading characters, you will often want to read whitespace too. For example, here's a little loop to count the number of characters in the input stream.
char c; int count = 0; // DOESN'T READ WHITESPACE!!!!! count++; } while (cin >> c) {

The fails to give an accurate count of characters because it ignores all the whitespace characters. There are two solutions.

Use the noskipws I/O manipulator to cause a further cin reads to not ignore whitespace (or until the skipws manipulator is used). Here's that same loop rewritten.
y y y y } char c; cin >> noskipws; while (cin >> c) { count++; // Stops all further whitespace skipping // Reads whitespace chars now.

This isn't a bad solution, but if you need to switch back and forth between skipping whitespace and not, I'd choose the next alternative.
y y y y }

Use the cin.get() function.


char c; while (cin.get(c)) { count++; // Always reads whitespace chars.

Read one line with cin.get(...) or cin.getline(...)


char ca[100]; . . . while (cin.get(ca)) { // ca has one input line without crlf and with terminating 0 . . . }

Ignoring input
When an error is encountered, it is often useful to skip all remaining characters on the line. You can do that with:
cin.ignore(n, c);

where n is the number of characters to skip, and c is the character to skip up to, whichever comes first. For example, to skip to the end of the line,
cin.ignore(1000, '\n');

C++ Notes: Character functions - <cctype>


Character functions - <cctype>

Here are some of the most common functions to with single characters. The types of the parameters are c=char/int. Some parameters have the const attribute, but it's not indicated below. Because these are the original C functions, bool isn't used. Instead, zero is false and non-zero is true. Type Method Description

#include <cctype> (or older <ctype.h>)

int int int int int int int

isalnum(c) true if c is a letter or digit. isalpha(c) true if c is a letter. isblank(c) true if c is a blank or tab. isdigit(c) true if c is a digit. islower(c) true if c is a lowercase letter. isupper(c) true if c is an uppercase letter. isspace(c) true if c is whitespace character (space, tab, vertical tab,

formfeed, carriage return, or newline). int


tolower(c) returns lowercase version of c if there is one, otherwise it returns

the character unchanged. int


toupper(c) returns uppercase version of c if there is one, otherwise it returns

the character unchanged.

C++ Notes: Enums


The problem: representing series of values
It is very common to have a series of values that need to be represented. For example, to simulate a traffic light requires representing three values (red, yellow, and green), but there is no built-in C++ color datatype. Use integer values to represent colors, for example red as 0, yellow as 1, and green as 2. There is nothing "green" about the value 2, and it could just as easily

be represented by some other number. However, it is common to start a series at zero and continue up by ones.

The danger of magic numbers


Use of these "magic" numbers in the source code makes the code unreadable. For example,
x = 1;

What does this do, assign the number one or the color yellow to x? Use of numbers is also very error prone - it is easy to mistakenly use the wrong one and making changes to the numbers and making updates to all references is difficult.

Use names instead of numbers


A better solution is to create named constants for each of the values. By convention, these named constants are uppercase.
const int RED const int GREEN = 0; = 2;

const int YELLOW = 1;

Now it's easy to distinguish between assignment of the number 1 and the color yellow.
int y; int x; . . . y = 1; // assigns the integer one

x = YELLOW; // assigns yellow (which happens to be 1).

There is still the problem that we declare x as an int altho it's a color.

The enum type declaration provides a solution


C++ uses the enum statement to assign sequential integer values to names and provide a type name for declaration.
enum TrafficLightColor {RED, YELLOW, GREEN}; . . . int y; TrafficLightColor x;

. . . y = 1; x = YELLOW;

The enum declaration creates a new integer type. By convention the first letter of an enum type should be in uppercase. The list of values follows, where the first name is assigned zero, the second 1, etc.

Type checking prevents some erroneous assignments


The compiler may issue an error message or warning if you try to assign one kind of enum to a different kind. It also allows some dangerous types of assignments.
enum TrafficLightColor {RED, YELLOW, GREEN}; enum Gender {MALE, FEMALE}; TrafficLightColor x; int i; . . . x = YELLOW; // good i = x; // Legal, but bad style. Assigns the integer representation.

i = (int)x; // As above, explicit casting is better style. x = (TrafficLightColor)2; // Legal, but very dangerous. No checking. x = FEMALE; // BAD, Compiler may give error or warning. x = 5; // BAD, Compiler may give error or warning.

C++ Notes: Enum Values and I/O


Setting enum values
It's possible to control the values that are assigned to each enum constant. If a value is assingned to a constant, each successive constant without a value is assigned a value one greater than the previous.
enum Day {MON=1, TUE, WED, THU, FRI, SAT, SUN};

The value of MON is one, TUE is two, etc instead of starting at zero. Another use of specific values is to create sets. Explicitly setting the values to powers of two represents each as separate bit. These values can then manipulated using the bit operations (&, |, ^ and ~).
enum Day {MON=1, TUE=2, WED=4, THU=8, FRI=16, SAT=32, SUN=64};

const int WEEKDAY = MON+TUE+WED+THU+FRI; . . . Day today; . . . if ((today & WEEKDAY) != 0) . . . // This will have one of the values in it.

Enum I/O
I/O of enums uses their integer values, not their names. This is not what is desired normally, so extra programming is required on input and output to use the names instead of integer values. The extra work for enum I/O means that they are often not used for simple programs.

Other languages
Java will have type-safe enums in version 1.5. Currently it requires programmers to explicitly declare each name as a constant ints. C# provides enums with additional facilities, eg to get names and check values.

C++ Notes: Summary - Expressions


Parentheses () have three uses: 1. Grouping to control order of evaluation, or for clarity. Eg, (a + b) * (c - d) After a function name to enclose parameters. Eg, x = sum(a, b); Around a type name to form a cast. Eg, i = (int)x; Operator Precedence

2. 3.

Order of evaluation 1. 2. Higher precedence are done before lower precedence. Left to right among equal precedence except: unary, assignment, conditional operators.

Abbreviations i, j - integer (char, short, int, long) values. m, n - numeric values (integers, floatingpoint). b, c - int expr: 0=true, non-zero=false; x, y any type. a - array; p - pointer; f - field.

:: Remember at a minimum . [] -> (args) post ++ -1. unary operators type_id 2. * / % dynamic_cast 3. + static_cast 4. comparisons 5. && || cont_cast 6. = assignments reinterpret_cast unary + - * & pre ++ Use () for all others ! ~ (type) new sizeof delete ->* .* * / % + << >> < <= > >= == != & ^ | && || ?: = += -= etc

obj - a class object; T - a class type. x - anything, t - a type.

throw ,

Arithmetic Operators
The result of arithmetic operators is double if either operand is double, else float if either operand is float, else long if either operand is long, else int. n+m n-m n*m n/m n%m Addition. Eg 7+5 is 12, 3 + 0.14 is 3.14 Subtraction Multiplication. Eg 3 * 6 is 18 Division. Eg 3.0 / 2 is 1.5 , 3 / 2 is 1 Remainder (Mod) after dividing n by m. Eg 7 % 3 is 1 Add 1 to i before using the value.

I/O Operators
There are two operators defined in <iostream>. cout << x cin >> x Output "insertion" operator. Input "extraction" operator.

Reference Operators
a[i] s.f Array element access. Member. The f field or method of struct/class s. Member. The f field or method of struct/class pointer p. Address of x. Dereference pointer p. Dereference pointer to class memeber. Dereference pointer to pointer to class memeber. Scope resolution operator.

p->f

++i --i i++ i--

&x As above for subtraction *p Add 1 to i after using the value. .* As above for subtraction ->*

Comparison Operators
The result of all comparisons is 0 (false) or non-zero (true). < <= == != > >=

std::cout

Misc Operators
sizeof x Memory size of x in bytes. Usually written as sizeof(x).

Logical Operators
Logical values are zero (false) and non-zero (true).

Casts
b && c Conditional "and". true if both operands are true, otherwise false. Short circuit evaluation. Eg (false && anything) is false. Use casts to make type conversion clear, and especially when "narrowing" range of a value. x is a value and T is a type. Casts x to type T. Eg us = (unsigned short)i; Most universal. Casts x to type T. Eg i = int(f); Use with simple type names.

b || c

Conditional "or". true if either operand is (T)x true, otherwise false. Short circuit evaluation. Eg (true || anything) is true. true if b is false, false if b is true. T(x)

!b

Conditional Operator

b?x:y

if b is true, the value is x, else y. x and y must be the same type.

dynamic_cast<T>(obj)

Downcasts obj to subclass T. May throw bad_cast. Used for traditional casts, eg int to double. To cast away const or volatile.

Assignment Operators
= += -= *= ... Left-hand-side must be an lvalue. All binary operators (except && and ||) can be combined with assignment. Eg a += 1 is the same as a = a + 1

static_cast<t>(x)

const_cast<t>(x)

reinterpret_cast<t>(x)

Reinterprets bit pattern. Evil, dangerous, non-portable.

Bitwise Operators
Bitwise operators operate on bits of ints. Result is int. i&j i|j i^j ~i i << j Bits are "anded" - 1 if both bits are 1 Bits are "ored" - 1 if either bit is 1

Dynamic memory allocation/deallocation


T is a type (simple, struct, or class) T* p; Pointer declaration. Eg, int* a; or Card c; Allocates memory for one T. Returns address. Allocates new object, constructor called to initialize fields. Allocates array of n elements. Returns pointer to first. Deallocates object p points to. Must not be an array. Deallocates array p points to.

p = new T; Bits are "xored" - 1 if bits are different Bits are complemented (0 -> 1, 1 -> 0) Bits in i are shifted j bits to the left, zeros inserted on right. p = new T[n]; i >> j Bits in i are shifted j bits to the right. On left 0 bits inserted for unsigned, sign bits for signed. p = new T(params,...);

delete p;

delete [] p;

C++: Example: Convert to binary by division


This program reads integers and prints them in binary, altho it prints the bits in reverse order. This isn't a useful program - it is simply used to illustrate a few concepts.
// to-binary-reversed.cpp - Convert decimal to binary by repeated mod, div. // Fred Swartz - 2001-09-04 // This algorithm prints the bits from low to high order. // Improvements on the order have to wait until // bit operations, arrays or strings, functions, etc // Doesn't work with negative numbers or zero. #include <iostream> using namespace std; int main() {

1 2 3 4

5 6 7 8 9 10 11 12 13

int n;

// number to convert to binary

while (cin >> n) { if (n > 0) { cout << n << " (decimal) = "; while (n > 0) { cout << n%2; n = n/2; } cout << " (binary) in reverse order" << endl; } else { cout << "Please enter a number greater than zero." << endl; } } return 0; }//end main

The text from the above example can be selected, copied, and pasted into an editor.

Programming Exercises
This isn't really a very good program. Here are some modifications you might want to try. 1. This doesn't print anything if the initial value is zero. Because you always want to go thru the loop once, it is a perfect application for the do...while statement. 2. This algorithm can not be easily modified to print negative numbers in their internal native form, but you could modify this to show a signed form of binary numbers by writing out a minus sign followed by the positive form of the number.

C++: sizeof operator


It's sometimes necessary to know how much memory is used for variables. Because the C standard was written after there were C compilers, the size of types was already different in different compilers and on different machine architectures. Therefore there is no single definition of how big an int is, for example. If it's necessary to find out how much memory a type uses, you can use the sizeof operator, which returns the amount of memory need in bytes. (8 bits per byte) Altho it is technically an operator, everyone writes it as if it was a function. For example,

cout << sizeof(int);

would print 4 on many current compilers because that is the most common (Dev C++, MS Visual Studio, Borland C++Builder, gcc, ...). In fact, many programs simply make some assumptions about the size of variables. It is common to assume they are 32, but if you want to write for maximum portability, it is better to use sizeof. The single operand (argument) to sizeof is either a type or a variable.

C++ Notes: Bitwise Operators


C++ provides operators to work with the individual bits in ints. For this to be useful, you must have some idea of how integers are represented in binary. For example the decimal number 3 is represented as 11 in binary and the decimal number 5 is represented as 101 in binary.

The bitwise operators


Operator Name a&b a|b a^b and or xor Description 1 if both bits are 1. 3 & 5 is 1. 1 if either bit is 1. 3 | 5 is 7. 1 if both bits are different. 3 ^ 5 is 6. This unary operator inverts the bits. If ints are stored as 32-bit integers, ~3 is 11111111111111111111111111111100. shifts the bits of n left p positions. Zero bits are shifted into the low-order positions. 3 << 2 is 12. shifts the bits of n right p positions. If n is a 2's complement signed number, the sign bit is shifted into the high-order positions. 5 >> 2 is 1.

~a

not

n<<p

left shift

n>>p

right shift

Packing and Unpacking

A common use of the bitwise operators (shifts with ands to extract values and ors to add values) is to pack multiple values in one int. [Bit-fields are another way to do this.] For example, let's say you have the following integer variables: age (range 0-127 requires 7 bits), gender (range 0-1 requires 1 bit), and height (range 0-127 requires 7 bits). These can be packed and unpacked into/from one int by using only the minimum number of bits to represent each variable. The 15 bits that these require could be stored like this (A for age, G for gender, and H for height).
1 int age, gender, height, packed_info; // Assign values

2. . .

3 // Pack as AAAAAAA G HHHHHHH using shifts and "or" 4 packed_info = (age << 8) | (gender << 7) | height; 5 // Unpack with shifts and masking using "and" // This constant is binary ...01111111 6 height = packed_info & 0x7F; gender = (packed_info >> 7) & 1; 7 age = (packed_info >> 8); 8 9 10 11

If you're using a CPU whose shift speed depends on the distance of the shift, you can use the following nested expression to pack the fields. However, I believe most newer CPUs don't depend on the shift distance.
packed_info = (((age << 1) | gender) << 7) | height;

Setting flag bits


Some library functions take an int that contains bits, each of which represents a true/false (bool) value. This saves a lot of space and can be fast to process. [needs example]

Don't confuse && and &


Don't confuse &&, which is the short-circuit logical and, with &, which is the uncommon bitwise and. They may not produce the same result in a logical expression.

Shift left multiplies by 2; shift right divides by 2


On some older computers is was faster to shift instead of multiply or divide by a power of two. For example, to multiply x by 8 and put it in y,

y = x << 3;

// Assigns 8*x to y.

Flipping between on and off with xor


Sometimes xor is used to flip between 1 and 0.
x = x ^ 1; // or the more cryptic x ^= 1;

In a loop this will change x alternately between 0 and 1.

Exchanging values with xor


Here's some weird code I ran across once. It uses xor to exchange two values (x and y). Never use it; this is just a curiosity from the museum of bizarre code.
x = x ^ y; y = x ^ y; x = x ^ y;

C++ Notes: Bit Ops - Convert to binary


This program reads integers and prints them in binary, using the shift and "and" operators to extract the relevant bits.
1 2 #include <iostream> 3 using namespace std; 4 int main() { 5 int n; while (cin >> n) { 6 cout << "decimal: " << n << endl; 7 // print binary with leading zeros 8 cout << "binary : "; 9 for (int i=31; i>=0; i--) { int bit = ((n >> i) & 1); 10 cout << bit; 11 } cout << endl; 12 }//end loop 13 return 0; 14 }//end main // to-binary-bitops.cpp Print binary representation of ints // Fred Swartz - 2001-09-04

Programming assignments

Here are some possible improvements. 1. It's difficult to read long sequences of bits. Put a space after every 4 digits (a common style). Hint: The idiom for doing this is to use the mod (%) operator to do something every nth time. 2. Suppress leading zeros. Hint: This is done most easily by defining a bool flag, setting it to false at the beginning of each conversion, setting it to true when a non-zero bit is encountered, and printing zeros only when this flag is set to true. Then there's the case of all zeros. Be sure that the number 0 prints correctly. 3. This program assumes ints are 32 bits. Altho this is now the most common size, the C++ specification allows other sizes. I've personally used machines where the int sizes were 16, 19, 32, 36, and 60 bits. Hint: use sizeof. 4. This is a simple demonstration program, but a conversion like this should be written as a utility function that returns a character string representation. This raises several tougher issues:
o o

Should it use the string class or a c-string (array of chars)? Where should the memory for the character string be allocated? By the caller and passed as a reference parameter? Dynamically by the function and retured to the caller (who then has responsibility for freeing it)?

C++ Notes: Bit Ops - Convert to hex


This program reads integers and prints them in hexadecimal, using the shift and "and" operators to extract the relevant digits. Note: This requires knowledge of character arrays. The problems require use of functions.
// Read ints and print their hex representations // Note sizeof(int) returns number of bytes in an int. #include <iostream> using namespace std;

void main() { int n; while (cin >> n) { cout << "decimal: " << n << endl;

//--- Print hex with leading zeros cout << "hex : ";

for (int i=2*sizeof(int) - 1; i>=0; i--) { cout << "0123456789ABCDEF"[((n >> i*4) & 0xF)]; } cout << endl << endl; } }

Exercises
Here are some modifications that could be made to this code. 1. This program writes the digits to cout. A more general solution is to write the conversions as functions which put their output into a character array as cstrings. Write these conversions as functions with the following prototypes:
2. 3. 4. 5. 6. 7. 8. 9. 10. 11. void intToBinary(char result[], int n); void intToHex(char result[], int n); // A call might look like this: char binary[50]; // big enough to hold an int with spaces. . . . while (cin >> n) { intToBinary(binary, n); // produce binary character string. cout << binary << " has " < strlen(binary) << " digits." << endl; }

There is a big difference between the hex conversion and the binary conversion. The hex conversion writes a char to cout while the binary conversion writes an int. It's necessary to put a char in the output array of the function of course.

C++ Notes: ++, =, ?: Expression Exercises


Assume the following:
int int int bool j = 6; k = 10; n; b = false;

Give the value that is assigned, or illegal.

Control Flow

Loops y Examples y Programming Problems - Reading from cin y Programming Problems - Writing from a loop y Programming Problems - Nested loops

Switch Statement Functions Exceptions

C++ Notes: Example - Loop - Line of stars - count up


This program reads an integer and prints a line with that number of stars (asterisks). A new variable, i, is used to count from 1 up to n. The outer loop reads these integers until an EOF or bad input.

Implemented as a while loop


1 2 3 4 5 6 7 8 9 // loops/line-of-stars-up.cpp - Read int and print that many stars on a line. // Fred Swartz = 1003-08-23 #include <iostream> using namespace std; int main() { int n; while (cin >> n) { //--- Loop counting int i = 1; while (i <= n) { cout << "*"; i++; } cout << endl; } return 0; }

i up from 1 to n. // Initialize counter. // Test counter. // Increment counter.

The text from the above example can be selected, copied, and pasted into an editor.

Implemented as a for loop


The most common way to write a loop that counts is the for loop because it combines the intitialization, testing, and increment in one statement. This makes

the program easier to read and maintain. Here is the inner while loop rewritten as a for loop.
for (int i=1; i<=n; i++) { cout << "*"; 2} 1 3

C++ Notes: Loop Example - Line of stars - count down


This program reads an integer and prints a line with that number of stars (asterisks). It counts down from the number it reads in. The outer loop reads these integers until an EOF or bad input.

Implemented with a while loop


1 2 3 4 5 6 7 8 9 // loops/line-of-stars-down.cpp - Read int and print that many stars on a line. // Fred Swartz = 1003-08-23 #include <iostream> using namespace std; int main() { int n; while (cin >> n) { //--- Loop counting n down to 0. while (n > 0) { cout << "*"; n--; } cout << endl; } return 0; }

The text from the above example can be selected, copied, and pasted into an editor.

Implemented as a for loop


Because the variable n already has a value when we start the inner loop, there is no need for an initialization clause. The for statement requires three clauses separated by semicolons; just leave any unneeded clause blank.

1 2 3 4 5 6

while (cin >> n) { for ( ; n > 0; n--) { cout << "*"; } cout << endl; }

The text from the above example can be selected, copied, and pasted into an editor.

C++: Programming Problem - cin Count Input Values


Write a program which reads a sequence of integers and counts how many there are. Print the count. For example, with input 55 25 1004 4 -6 55 0 55, the output should be 8 because there were eight numbers in the input stream. Your program should work for any input, not just this example..

C++: Programming Problem - cin Max and Min


(10 points) Write a program that reads daily temperatures, as floats. Read in a loop until an EOF. After the input has been read, print the maximum and minimum values. You can assume that there is at least one input data value. Sample input values
10.0 11.3 4.5 -2.0 3.6 -3.3 0.0

The output should look something like the following. Formatting may vary.
Maximum = 11.3 Minimum = -3.3

Hints Be careful in choosing the initial values for the maximum and minimum values. Will your program work correctly if all numbers are negative?

C++: Programming Problem - cin Max and Min


(10 points) Write a program that reads daily temperatures, as floats. Read in a loop until an EOF. After the input has been read, print the maximum and minimum values. You can assume that there is at least one input data value. Sample input values
10.0 11.3 4.5 -2.0 3.6 -3.3 0.0

The output should look something like the following. Formatting may vary.
Maximum = 11.3 Minimum = -3.3

Hints Be careful in choosing the initial values for the maximum and minimum values. Will your program work correctly if all numbers are negative?

C++: Programming Problem - Print Parallelogram


(10 points) Write a program which reads an int, then prints a parallelogram with edges that are that size. For example, when it reads the number 5, the output would be
***** ***** ***** ***** *****

Hints
y y

This is most easily solved using nested for loops (the outer loop for the row, and loops inside for the column. Each row will has one more blank at the beginning than the previous row. You can use a for loop to print the blanks, then a for loop to print the stars.

C++ Notes: Switch statement


The switch statement chooses statements to execute depending on an integer value. The same effect can be achieved with a series of cascading if statements, but in some cases the switch statement is easier to read, and some compilers will produce more efficient code. The break statement exits from the switch statement. If there is no break at the end of a case, execution continues in the next case, which is usually an error.
switch (expr) { case c1: statements break; // do these if expr == c1

case c2: statements break; // multiple values can share same statements // do these if expr == c2

case c2: case c3: case c4:

statements break;

// do these if expr == any of c2, c3, or c4

default: statements } // do these if expr != any above

Equivalent to if statement
The example switch statement above can be written as the following if:
if (expr==c1) { statements } else if (expr==c2) { statements } else if (expr==c2 || expr==c3 || expr==c4) { statements } else { statements

The unusual situation of one case falling into another without a break statement can not be as easily translated into an if statement; either extra tests or the forbidden goto must be used.

C++ Notes: Switch usage


When to use switch instead of if?
The if statement is used most frequently. However, when a single integral value (char, short, int, long) is used to select from many possibilities, it is better to use switch because the source code is simpler, and therefore the chance of writing an erroneous comparison is less and the code is easier to read and therefore maintain.

The break problem


The break statement at the end of the each switch clause is a major source of errors because it is easy to forget and C++ won't complain. Execution simply continues with the next case. Java was criticized for using adopting the C++ switch statement without fixing this problem. C# has fixed this by making the defualt action at the end of a case to break, and requiring the continue keyword for the unusual case of falling into the next case.

Indenting and spacing switch statements


A typical switch statement uses two levels of indentation: the case keyword is indented one level, and the statements in a case are indented an additional level. Single, short statements may be written on the same line as the case. Add blank lines after break statements if that makes the code more readable.

break after last case

Altho there is no need to put a break after the last case, it is good practice so that it isn't forgotten when addition cases are added. Exception: default, if used, should be the last case and there doesn't need to be a break because nothing will ever be added after it.

C++ Notes: Switch: Simple Calculator


Here is a very simple calculator.
// switch/infix-calc.cpp - Simple calculator. // Illustrates switch and continue statements. Not robust (eg, doesn't check for division by 0). 2 // // Fred Swartz 10 Aug 2003 3 1 4 #include <iostream> #include <iomanip> 5 using namespace std; 6 int main() { 7 int left, right; // Operands char oper; // Operator 8 int result; // Resulting value 9 while (cin >> left >> oper >> right) { 10 switch (oper) { 11 case '+': result = left + right; break; 12 case '-': result = left - right; 13 break; case '*': result = left * right; 14 break; 15 case '/': result = left / right; break; 16 default : cout << "Bad operator '" << oper << "'" << endl; continue; // Start next loop iteration. 17 } 18 cout << result << endl << endl; } 19 20 21 } 22 23 24 25 26 27 28 29 30 31 return 0;

32

C++ Notes: Function Concepts


Purpose of functions
The purpose of functions is to make the programmer's life easier.
y y y y

Write code once, and call it from many places. This insures consistency, and reduces the cost of writing and debugging. A function allows code to be parameterized by passing values to it. Functions can be collected in libraries and reused in other programs. Functions create new conceptual units that form the units that programmers use in thinking about a problem.

Naming
For a function to work well as a conceptual unit it has to have a name that is clear to the human reader.
y y

Choose a name that has a clear meaning. Eg, "f" is not a good name. Void functions are more readable if a verb name is used that describes what it does. Eg, "printAverage" would be a good name for a void function that prints the average of some numbers. "Average" would not be a good name for it. Value-returning functions A verb is a good choice for a value returning function that performs an operation that would naturally be understood to produce a value, eg, "add(x, y)". A noun which describes the the result can be used. For example, "remainder(a, b)" would be a good choice for a function that computes the remainder after division (ie, what the "%" operator does). The noun is a good choice if you're tempted to add a word like "compute" or "calculate" in front (eg, "computeRemainder(a, b)"). The "compute" or "calculate" is simple often omitted if the meaning is clear. o Getter and setter functions. Prefix functions which get a value from an object or data structure with "get" (eg, getTime()). Similarly use "set" for functions that set values (eg, setTime()). o boolean functions should usually start with "is" or another word that suggests a yes/no answer. Eg, "isOdd(n)" is a good name for a function that returns true if its parameter is odd, and it's better than simply "odd()". Case. There are several conventions, but whichever one you choose, be consistent to make programs readable. The most common convention is to start names with lowercase and capitalize the first letter of each additional
o

word in the name. Some programmers start functions with uppercase characters, but this is less common.

Cohesion
Each function should do only one thing. This should be done so that the conceptual unit can be understood easily. Sometimes it will be convenient to do two things in a function because you're working on the same data. Try to resist this temptation. Make two functions if you reasonably can. Separate I/O and computation. Greater cohesion is almost always achieved by separating I/O and computation. If possible, put the I/O in main or in separate functions. If I/O is properly separated, the same functions which make up a program can be used either in a GUI or command-line program.

Side-effects
Functions should avoid side-effects such as communicating by means of global variables. Use of global variables increases coupling, which makes programs harder to debug and maintain.

Functional Decomposition - top-down programming - successive refinement


The top-down programming style is to write a short main program that calls on additional functions to accomplish it task. These functions should similarly be kept short and call more functions if necessary to get their task done. Start with the main function, and write only function stubs which do almost nothing except maybe print a message that they were called. Get this running, then expand one of the stubs, get this running, ... . This very productive style of programming is related to iterative programming and is one of the cornerstones of Extreme Programming. Top-down programming is good for for small programming tasks. A better approach for larger programs is to divide them into objects.

Size - One page


It is a good idea to keep functions shorter than one page or one screen. This makes them easy to comprehend, If you want to make them larger, think about how you could break the processing into two or more functions (top-down programming).

Parameters

Value parameters copy the actual parameter value into the formal parameter. Changes to the formal parameter, which is a local variable, do not get passed back to the calling program. Reference parameters are implemented by passing the memory address of the actual parameter to the formal parameter. Reference parameters are used to pass values back from a function. This is necessary to return more than one value. It is helpful to have a clear idea of the direction of data flow of each parameter: in means the data only flows into the function (typically a value parameter), out means the data only flows out of the function (a reference parameter), or inout means that data flows both into and out of the function thru this (reference) parameter. There is no language support for these ideas in C++ altho these keywords are used in the Ada programming language.

Prototypes
The compiler must know about the return and parameter types before it can understand the call to a function. You should declare all of your function prototypes at the beginning of your program. This is one of the primary purposes of the system include files. If you've written a number of functions in many files, it is common to define your one include file which defines the prototypes.

C++ Notes: Function Structure


Prototypes
Every function (except main) should be declared near the beginning of a program with a prototype which gives the return type (or void), name, and the parameter names and types. Parameter names are not required in prototypes, but they are very useful to readers of your program.
void printChars(char c, int count);

Note that prototypes always end with a semicolon. If you are developing a program with many modules, its common to put the prototypes into an include file. Assuming you are using prototypes, the order of the later function definitions doesn't matter. Prototypes are needed by the compiler to know how many and type of the parameters, and the return value type.

Function Definition

The first line of a function definition (called the function header) is the same at a prototype, except that it is followed by the function body in braces. Neither the header nor the body is followed by a semicolon.
void printChars(char c, int count) { for (int i=0; i<count; i++) { cout << c; } }//end printChars

void functions
If a function doesn't return a value (perhaps it changes a reference parameter, changes a global, or produces a side-effect like I/O), it must be declared as void type. In some programming languages (Visual Basic, Pascal, Fortran, ...), void functions are called by a different name (subroutine, procedure, ...).

Local Variables
All variables that are declared in a function are local to the function. They can not be referenced from outside the function (local scope). Their lifetime is the same as the activation of the function: they are created when the function is called and are destroyed when the function returns. They have no initial values so will have whatever value happens to be in memory when they are allocated. Parameters are like local variables (local scope, function lifetime), but are assigned an initial value from the corresponding expression in the function call.

Return statement
The return statement stops execution and returns to the calling function. A void function doesn't need a return statement -- when the end is reached, it automatically returns. However, a void function may optionally have one or more return statements.

C++ Notes: Function return Statement


Return statement

The return statement stops execution and returns to the calling function. When a return statement is executed, the function is terminated immediately at that point, regardless of whether it's in the middle of a loop, etc.

Return optional in void functions


A void function doesn't have to have a return statement -- when the end is reached, it automatically returns. However, a void function may optionally contain one or more return statements.
void printChars(char c, int count) { for (int i=0; i<count; i++) { cout << c; }//end for

return;

// Optional because it's a void function

}//end printChars

Return required in non-void functions


If a function returns a value, it must have a return statement that specifies the value to return. It's possible to have more than one return, but the (human) complexity of a function generally increases with more return statements. It's generally considered better style to have one return at the end, unless that increases the complexity. The max function below requires one or more return statements because it returns an int value.
// Multiple return statements often increase complexity. int max(int a, int b) { if (a > b) { return a; } else { return b; } }//end max

Here is a version of the max function that uses only one return statement by saving the result in a local variable. Some authors insist on only one return statement at the end of a function. Readable code is much more important than following such a fixed rule. The use of a single return probably improves the clarity of the max function slightly.

// Single return at end often improves readability. int max(int a, int b) { int maxval; if (a > b) { maxval = a; } else { maxval = b; } return maxval; }//end max

Related pages
y y

Functions Function Parameters

C++ Notes: Function Parameters


Formal Parameters
Formal parameters are written in the function prototype and function header of the definition. Formal parameters are local variables which are assigned values from the arguments when the function is called.

Actual Parameters or Arguments


When a function is called, the values (expressions) that are passed in the call are called the arguments or actual parameters (both terms mean the same thing). At the time of the call each actual parameter is assigned to the corresponding formal parameter in the function definition. For value parameters (the default), the value of the actual parameter is assigned to the formal parameter variable. For reference parameters, the memory address of the actual parameter is assigned to the formal parameter.

Value Parameters
By default, argument values are simply copied to the formal parameter variables at the time of the call. This type of parameter passing is called pass-by-value. It is the

only kind of parameter passing in Java and C. C++ also has pass-by-reference (see below).

Reference Parameters
A reference parameter is indicated by following the formal parameter name in the function prototype/header by an ampersand (&). The compiler will then pass the memory address of the actual parameter, not the value. A formal reference parameter may be used as a normal variable, without explicit dereference - the compiler will generate the correct code for using an address. Reference parameters are useful in two cases:
y y

To change the value of actual parameter variables. To more efficiently pass large structures.

C++ Notes: Function Call Order


Here is an example that illustrates some of the issues with function calls.

1 2

// functions/call-order.cpp -- Illustrates order of calls. // Fred Swartz 2003-09-02

#include <iostream> using namespace std; 4 //--------------------------------- prototypes int even(int a); 5 int odd(int b); 6 int ulam(int c); 3 //--------------------------------- main 8 int main() { // What does each statement print? 9 cout << odd(4) << endl; 10 cout << (even(2) + odd(3)) << endl; // (Note 1) cout << ulam(ulam(ulam(5))) << endl; // (Note 2) 11 } 12 13 //---------------------------------- even 14 int even(int a) { cout << "even(" << a << ")" << endl; 15 return a/2; 16 } 17 //---------------------------------- odd 18 int odd(int b) { cout << "odd(" << b << ")" << endl; 7

19 20 }

return 3*b + 1;

21 //---------------------------------- ulam int ulam(int c) { 22 cout << "ulam(" << c << ")" << endl; 23 if (c%2 == 0) { return even(c); 24 }else{ 25 return odd(c); } 26 }

Notes
1. Order Ambiguity. The order of evaluation of operands is not defined in C++ (unlike Java where it's strictly left to right). It's possible for the rightmost call to be made before the leftmost call. Here are two possible orders of execution.

2. // Original: cout << (even(2) + odd(3)) << endl; 3. int t1 = even(2); // left before right operand eval. 4. int t2 = odd(3); cout << t1+t2 << endl;
or

// Original: cout << (even(2) + odd(3)) << endl; int t2 = odd(3); // right before left operand eval. int t1 = even(2); cout << t1+t2 << endl;
I've see it done both ways in various compilers, so this is a real portability issue. However, the order is only important if both functions have interacting side-effects (I/O, change globals, ...). 5. Use intermediate variables for clarity. If it's difficult to understand an expression, compute it in pieces. The compiler does this internally. One of the above examples can be rewritten.

6. //Original: cout << ulam(ulam(ulam(5))) << endl << endl; 7. int t1 = ulam(5); 8. int t2 = ulam(t1); 9. int t3 = ulam(t2); // the inner call must be made first. // that result is used to make the next call. // only now can we make the outer function call.

cout << t3 << endl << endl;

C++ Notes: Function Reference Parameters


Reference parameters are useful in two cases:
y

Change values. Use a reference parameter when you need to change the value of an actual parameter variable in the call. When a function computes only one value it is considered a better style to return the value with the return statement. However, if a function produces more than one value, it is common to use reference parameters to return values, or a combination of the return value and reference parameters. Efficiency. To pass large structures more efficiently. This is especially common for passing structs or class objects. If no changes are made to the parameter, it is should be declared const.

Reference parameters pass an address, not a value


When you declare a reference parameter, the function call will pass the memory address of where the actual parameter, instead of copying the parameter value into the formal parameter.

Declare reference parameters with a &


To indicate a reference parameter, an ampersand (&) is written in the function prototype and header after the parameter type name. For example,
void assign(int& to, int from) { to = from; } // Will change the actual parameter in the call.

has two parameters, to is a reference parameter as indicated by the ampersand, and from is a value parameter. This ampersand must be in both the prototype and the function header.

Example - Swap (bad solution)


Let's say you want to exchange the values in two arguments.
int a = 5; int b = 10; swap(a,b); // If we want a=10 and b=5 as the result, how do we write the function?

Here's an example that does NOT work correctly, altho there is no error message.
void swap(int x, int y) { x = y; y = temp; } // BAD BAD BAD BAD BAD BAD BAD

int temp = x; // temp is a local variable // changes only local copy // changes only local copy

Because x and y are value parameters, like local variables, changes to them have no effect on the calling functions arguments a and b.

Example - Swap (good solution)


If the parameters are marked as reference parameters, the memory address of each argument is passed to the function. The function uses this address to both get and set the value. Here is swap written correctly. The only change is the addition of the & to the parameter declaration following the type.
void swap(int& x, int& y) { int temp = x; x = y; y = temp; } // temp is a local variable // changes the actual parameter in the calling pgm. // changes the actual parameter in the calling pgm.

L-values required for actual reference parameters


An l-value is something that you can assign to. This name is short for left-value, referring to the kind of value that must be on the left side of an assignment statement. All actual reference parameters must have an l-value.
swap(1, 2); swap(x+5, c); swap(x, y); // BAD - literals don't have l-values. // BAD - numeric expressions don't have l-values. // OK - variables are l-values.

swap(a[i], a[i+1]); // OK - array references are l-values.

It's easy to figure this out if you just think about what you can write on the left of an assignment; '1', '2', and 'x+5' are obviously not legal.

C++ Notes: Exceptions


Exceptions and the throw and try..catch statements
Exceptions allow passing information about some exception condition (usually an error) by means of the throw statement to the catch clause that encloses it or any of the calls on the call stack. The beauty of this solution is that calls look identical, and the error processing code is in the function that wants to catch it.

throw something

The throw statement can throw a value of any type, but if your exceptional condition fits into one of the standard exceptions, use it. As a rule, use one of the logic_error exceptions if the error is a result of a programming logic error, eg, the programmer should have checked the bounds before referencing an element in a container. Use one of the runtime_errors if the error is the result of input that the programmer can not easily check for, eg, input values that result in ranges being exceeded, too much data, etc.

Standard exceptions
The C++ library defines a number of common exceptions. Use
#include <stdexcept> using namespace std; // Or prefix names with std::

to get the following classes defined. These are arranged in a class hierarchy shown by the indentation below.
y exception o logic_error  domain_error  invalid_argument  length_error  out_of_range o runtime_error  range_error  overflow_error  underflow_error

Standard exception constructor


To create an exception and throw it, call the constructor with a c-string parameter.
throw out_of_range("Subscript less than zero");

Catching a standard exception


You can catch an exception of a specific type, or any of its subclasses, by specifying the class name as the type of the parameter in the catch clause. Call the what method to get the error string from a standard exception.
vector<int> v; . . . try { . . . x = v.at(-2); // this throws out_of_range exception. . . . } catch (out_of_range e) { cerr << e.what() << endl; exit(1); } // stop program // print error // using standard vector class

Other pre-defined exceptions


Altho C++ popularized exceptions, they aren't used extensively within the STL or other libraries because exceptions were not available in all implementations for a long time. You can pursue these, but I don't think I'll write notes on them.
y y

Memory allocation errors. I/O errors.

C++ Notes: Exception Test


The following program illustrates exception handling.
y y y

Shows how many different types can be thrown. Shows how subclasses can be caught by superclasses, if not caught earlier. Shows how any exception can be caught with catch (...).

Source program
1 2 3 // exceptions/exception-test.cpp - Test exceptions. // 2004-02-10 - Fred Swartz //==================================== includes #include <iostream> #include <stdexcept> #include <string> using namespace std;

4 5 6 7 8 9 10 11 12 13 14

//================================================ prototypes void generateException(int whichException); //====================================================== main int main() { for (int i=1; ; i++) { // Loop forever try { cout << i; generateException(i); } catch (out_of_range orex) { cout << " Catching: out_of_range "<< orex.what() << endl; } catch (const char* mess) { cout << " Catching: const char* " << mess << endl; } catch (string smess) { cout << " Catching: string " << smess << endl; } catch (int iex) { cout << " Catching: int " << iex << endl; } catch (runtime_error rex) { cout << " Catching: runtime_error " << rex.what() << endl; } catch (exception eex) { cout << " Catching: " << eex.what() << endl; } catch (...) { cout << " ERROR: Nobody caught this!" << endl; } } system("PAUSE"); return 0; // Keep Dev-C++ window open

15 }

16 //========================================= generateException void generateException(int whichException) { switch (whichException) { 17 case 1: cout << " Throwing out_of_range()" << endl; 18 throw out_of_range("out_of_range meaningful comment"); break; 19 case 2: cout << " Throwing exception() // Can't specify comment" << 20 endl; throw exception(); // Doesn't take comment text. break; 21 case 3: cout << " Throwing underflow_error // caught by base class (runtime_error)" << endl; throw underflow_error("underflow_error"); 23 break; 22 24 25 case 4: cout << " Throwing runtime_error" << endl; throw runtime_error("a comment"); break;

26

case 5: cout << " Throwing length_error // caught be super-super-class (exception)" << endl; 27 throw length_error("length_error"); break; 28 case 6: cout << " Throwing int" << endl; 29 throw 26; break; 30 case 7: cout << " Throwing const char*" << endl; 31 throw "This is a const char*"; break; 32 case 8: cout << " Throwing string" << endl; 33 throw string("I'm a string"); break; 34 case 9: cout << " Throwing float" << endl; 35 throw 3.14159; break; 36 default: cout << " Throwing up" << endl; 37 system("PAUSE"); exit(0); } 38 return; 39 } 40 41 42 43 44 45 46 47

Output The following output is from Dev-C++ (using gcc version ?). MS Visual C++ v6 gives a similar result, differing only in what catching exception prints.
1 Throwing out_of_range() Catching: out_of_range out_of_range meaningful comment 2 Throwing exception() // Can't specify comment

Catching: St9exception 3 4 Throwing underflow_error Throwing runtime_error Catching: runtime_error a comment 5 6 7 Throwing length_error Catching: St9exception Throwing int Catching: int 26 Throwing const char* Catching: const char* This is a const char* 8 9 Throwing string Catching: string I'm a string Throwing float ERROR: Nobody caught this! 10 Throwing up // caught be super-super-class (exception) // caught by base class (runtime_error) Catching: runtime_error underflow_error

Input / Output
y y y y y y y y

I/O streams - overview Idiom: cin loop Anti-idiom - Using cin in three places, instead of one End-Of-File (EOF) Reading numbers Reading characters Reading lines Reading text files

y y y y y y

Example: File open dialog I/O operators - << and >> I/O manipulators String streams String stream example Idiom - Separate User Interface from Logic

GUI
y

Graphical User Interfaces

C++ Notes: I/O - Overview


cin and cout can be used for most I/O

One of the great strengths of C++ is that simple I/O is simple. It seems like it should be obviously true, but Java sadly makes simple I/O difficult. To input most values, just use cin (pronounced see-in) and the >> operator. Similarly, for output use cout (pronounced see-out) and the << operator. For example,
int x; int y; cin >> x >> y; cout << "Sum = " << (x+y) << endl;

The input is treated as a stream of characters without regard to spacing or lines. It's also easy to read in a loop.
while (cin >> x) { sum += x; } // Reads until EOF

See
y y y y y

The cin loop End-Of-File (EOF) Reading numbers I/O operators - << and >> Anti-idiom: Testing cin for EOF

Reading individual characters and lines


Because the default stream I/O skips whitespace, to read all characters or to read lines, you have to use some different functions. See
y y

Reading characters Reading lines

Reading text files


Reading text files is very easy. It's basically the same as reading from cin, but you have to define a new file input stream and "open" it with a file name. See
y

Reading Text Files

Example: File open dialog

Using strings as source and destination of I/O


You can easily use all of the stream I/O capabilities to "read" and "write" using strings. This isn't so commonly done, but it can be very useful. See
y y

String streams String stream example

Formatting output
The I/O manipulators can be used to format your output, eg, to choose field widths and number of decimals printed. See
y

I/O manipulators

C++ Notes: End-Of-File (EOF)


End-Of-File (EOF) when reading from cin
When a program is reading from a disk file, the system "knows" when it gets to the end. This condition is called End-Of-File (EOF). All systems also provide some way of indicating an EOF when reading from the keyboard. This varies from system to system. Dev-C++ Type: Enter Control-z Enter MS Visual C++ Type: Enter Control-z Enter Enter Reportedly there is a Microsoft patch that can be applied so that only one Enter is required after the Control-z. I wouldn't bother. Other systems Some may use other characters: control-D then Enter, or control-D followed by a control-Z, or ... . You can just provide bad data to make cin fail in many cases. A student once claimed that typing "EOF" was the way to indicate and end-of-file from the console.

Yes, it stops reading (because of an error) if you're reading numbers, but not when reading characters or strings!

Resetting after EOF


Altho it doesn't make sense to read after an EOF on a file, it is reasonable to read again from the console after an EOF has been entered. The clear function allows this.
while (cin >> x) { ... // loop reading until EOF (or bad input) }

cin.clear(); cin >> n; ...

// allows more reading

C++ Notes: Reading numbers


Idiom: Read from cin in a while loop
The standard C/C++ style for reading is to put the read operation in a while loop condition. If the read proceeds correctly, then the value is true. If there is an endof-file (EOF) meaning that the end of the data has been reached, or if there is an error in reading, then the value returned is false and execution continues at the end of the loop.

Example -- Adding numbers in the input


int sum = 0; int x; while (cin >> x) { sum += x; } cout << sum;

Anti-idiom: Checking for EOF instead of reading


The following code does almost the same thing as the code above, but it has disadvantages.
int sum = 0;

int x; cin >> x; while (cin) { sum += x; cin >> x; } cout << sum; y y y // BAD idiom for input. // Required by inadequate Pascal I/O. // Should not be used in C++.

Duplication. The input is read in two places, not one. Duplicate code creates an opportunity to update one and not the other when modifying the program. More code. The reading and testing is split between three lines instead of one. This makes it more difficult to read and maintain. Possible failure. The GCC I/O library, which is used by DevC++, does a good job with this. However, I've used systems that behaved differently using the while (cin) test, depending on whether there is whitespace after the last value. Think about it. The issue is rather subtle - how does the I/O system know that you aren't going to read the blanks after the last value? Well, it can't really know, so it makes some assumptions. Not all systems make the same assumptions about how to handle trailing whitespace after the last value.

C++ Notes: Reading Characters


These notes are written using cin as an example, but they apply to all input streams.

What about whitespace?


The standard input stream (cin) does just what you want when reading integers, floating point etc. It ignores all whitespace (blanks, tabs, etc) that appears in front of a number. But when you're reading characters, you will often want to read whitespace too. For example, here's a little loop to count the number of characters in the input stream.
char c; int count = 0; // DOESN'T READ WHITESPACE!!!!! count++; }

while (cin >> c) {

The fails to give an accurate count of characters because it ignores all the whitespace characters. There are two solutions.

Use the noskipws I/O manipulator to cause a further cin reads to not ignore whitespace (or until the skipws manipulator is used). Here's that same loop rewritten.
y y y y } char c; cin >> noskipws; while (cin >> c) { count++; // Stops all further whitespace skipping // Reads whitespace chars now.

This isn't a bad solution, but if you need to switch back and forth between skipping whitespace and not, I'd choose the next alternative.
y y y y }

Use the cin.get() function.


char c; while (cin.get(c)) { count++; // Always reads whitespace chars.

Read one line with cin.get(...) or cin.getline(...)


char ca[100]; . . . while (cin.get(ca)) { // ca has one input line without crlf and with terminating 0 . . . }

Ignoring input
When an error is encountered, it is often useful to skip all remaining characters on the line. You can do that with:
cin.ignore(n, c);

where n is the number of characters to skip, and c is the character to skip up to, whichever comes first. For example, to skip to the end of the line,
cin.ignore(1000, '\n');

C++ Notes: Reading Lines


These notes are written using cin as an example, but they apply to all input streams.

What kind of strings?


Strings (sequences of characters) are stored two different ways in C++. C-strings are arrays of characters with a terminating zero value at the end. There is also a string class in the C++ standared library. In general, it's better to use the string class, but many times that C-strings must be used because the string class was not available when many other classes were developed.

Reading a line into a string


string s; . . . while (getline(cin, s)) { // s contains the input line, without final newline char. . . . }

Reading a line into a C-string


char ca[100]; . . . while (cin.get(ca, 99)) { // ca has one input line without crlf and with terminating 0 . . . }

C++ Notes: Reading Text Files


Reading a text file is very easy using an ifstream (input file stream). 1. Include the necessary headers.
2. #include <fstream> using namespace std;

3. Declare an input file stream (ifstream) variable. For example,


ifstream inFile;

4. Open the file stream. Path names in MS Windows use backslashes (\). Because the backslash is also the string escape character, it must be doubled. If the full path is not given, most systems will look in the directory that contains the object program. For example,
inFile.open("C:\\temp\\datafile.txt");

5. Check that the file was opened. For example, the open fails if the file doesn't exist, or if it can't be read because another program is writing it. A failure can be detected with code like that below using the ! (logical not) operator:
6. if (!inFile) { 7. 8. } cerr << "Unable to open file datafile.txt"; exit(1); // call system to stop

9. Read from the stream in the same way as cin. For example,
10. 11. } while (inFile >> x) { sum = sum + x;

12.Close the input stream. Closing is essential for output streams to be sure all information has been written to the disk, but is also good practice for input streams to release system resources and make the file available for other programs that might need to write it.
inFile.close();

Example
The following program reads integers from a file and prints their sum.
// io/read-file-sum.cpp - Read integers from file and print sum. // Fred Swartz 2003-08-20 #include <iostream> #include <iomanip> #include <fstream> using namespace std; int main() { int sum = 0; int x; ifstream inFile; inFile.open("test.txt"); if (!inFile) { cout << "Unable to open file"; exit(1); // terminate with error } while (inFile >> x) { sum = sum + x; } inFile.close(); cout << "Sum = " << sum << endl; return 0; }

1 2 3 4 5 6 7 8 9 10

11 The text from the above example can be selected, copied, and pasted into an

editor.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

C++: Example - File open dialog


Example of file open dialog
When you aren't using a GUI, you can make a simple text-based file open dialog with something like the following code. A portability problem with this code is that the default directory depends on the settings of the I/O library that you're using. The system I'm using defaults to the directory where the object program is located, but results will vary.

#include <fstream> using namespace std; . . . //--- select and open file ifstream fin; string filename; do { cout << "Enter input data file name:"; cin >> filename; if (!fin) { } } while (!fin); while (fin >> . . . . . . // Continue until good file. // Get the file name. // Convert to C-string and open it. // Will fail if didn't exist. // The new file stream. // Where to read file name.

fin.open(filename.c_str());

cout << "Unable to open " << filename << endl;

fin.close();

Parameter to open must be a C-string


The call to open takes a file name parameter. It must be a C-string (type char *), not type string. The example above illustrates the use of the c-str() function which returns a C-string from a string.

C++ Notes: What values do the I/O operators (<< and >>) return?
What values do the I/O operators (<< and >>) return?
C++ allows you to define functions to associate with existing operators. The << and >> operators, whose orginal meanings were bitwise left and right shift, have additional meanings for C++ iostreams. So how do they work? The >> and << operators are evaluated left-to-right, so the following are equivalent statements, altho writing the parentheses would be rather weird.
cin >> a >> b >> c; (((cin >> a) >> b) >> c); // Same as above.

What value is produced, for example, by (cin >> a)? And I mean what value is produced in the expression, not what value is read into the variable. It calls an overloaded templated function (operator>>) in an istream class which reads input and stores it into the variable on the right. It then returns the left operand (ie, cin) so the result can be used if there is another >> operator. This chaining of the I/O operators is a rather clever solution. Here's an example program that shows this.
if ((cin >> a) == cin) { cout << "Equal" << endl; } else { cout << "Not Equal" << endl; } // Yes, it is true

Why cin can be used as a truth value


It's possible to make tests like
if (cin)

which will be true if cin is ok and false if at an end-of-file or has encountered an error. It's type is istream& (input stream reference), so how can that be used as a truth value. The trick is that when it's evaluated in the context of a condition, eg, in an if or while statement, a special function is called in the istream class. This function returns a value that can be interpreted as true or false.

Reading in the while condition


Because the >> operator returns the iostream (eg, cin), which can be tested for EOF or errors, the cin loop idiom can be used.
while (cin >> x) { . . . }

which effectively tests for EOF, and also that the input is a valid value.

C++ Notes: I/O Manipulators


Manipulators are the most common way to control output formating.

#include <iomanip>
I/O manipulators that take parameters are in the <iomanip> include file.

Default Floating-point Format

Unless you use I/O manipulators (or their equivalent), the default format for each floating-point number depends on its value.

y y y

No decimal point: 1.0000 prints as 1 No trailing zeros: 1.5000 prints as 1.5 Scientific notation for large/small numbers: 1234567890.0 prints as 1.23457e+09

I/O Manipulators
The following output manipulators control the format of the output stream. Include <iomanip> if you use any manipulators that have parameters. The Range column tells how long the manipulator will take effect: now inserts something at that point, next affects only the next data element, and all affects all subsequent data elements for the output stream. Manip.
General output endl

Rng

Description

now

Write a newline ('\n') and flush buffer.

setw(n)

next

Sets minimum field width on output. This sets the minimum size of the field - a larger number will use more columns. Applies only to the next element inserted in the output. Use left and right to justify the data appropriately in the field. Output is right justified by default. Equivalent to cout.width(n); To print a column of right justified numbers in a seven column field:

cout << setw(7) << n << endl;


width(n) next Same as setw(n).

left

next Left justifies output in field width. Only useful after setw(n).

right

next

Right justifies output in field width. Since this is the default, it is only used to override the effects of left. Only useful after setw(n).

Only useful after setw. If a value does not entirely fill a field, the character ch will setfill(ch) all be used to fill in the other characters. Default value is blank. Same effects as cout.fill(ch) For example, to print a number in a 4 character field with leading zeros (eg, 0007):

cout << setw(4) << setfill('0') << n << endl;


Floating point output

setprecision(n) all

Sets the number of digits printed to the right of the decimal point. This applies to all subsequent floating point numbers written to that output stream. However, this won't make floating-point "integers" print with a decimal point. It's necessary to use fixed for that effect. Equivalent to cout.precision(n);

fixed

all

Used fixed point notation for floating-point numbers. Opposite of scientific. If no precision has already been specified, it will set the precision to 6.

scientific

all

Formats floating-point numbers in scientific notation. Opposite of fixed.

bool output

boolalpha noboolalpha

all

Uses alphabetic representation (true and false) for bool values. Turned off with noboolalpha.

Input

skipws noskipws

all

For most input values (eg, integers and floating-point numbers), skipping initial whitespace (eg, blanks) is very useful. However, when reading characters, it is often desired to read the whitespace characters as well as the non-spacing characters. The these I/O manipulators can be used to turn whitespace skipping off and on. Eg, cin >> noskipws; turns whitespace skipping off for all subseqent cin input.

ws

now

Reads and ignores whitespace at the current position.

Other

showpoint, noshowpoint, uppercase, nouppercase, dec, oct, hex, setbase(8|10|16), showbase, noshowbase, ends, showpos, noshowpos, internal, flush, unitbuf, nounitbuf, setiosflags(f), resetiosflags(f)

Example
#include <iostream> #include <iomanip> using namespace std; int main() { const float tenth = 0.1; const float one = 1.0; const float big = 1234567890.0; cout << "A. << endl; cout << "B. << endl; cout << "C. << endl; cout << "D. << big << endl; cout << "E. cout << "F. " " << fixed " << scientific << tenth << ", " << one << ", " << big << tenth << ", " << one << ", " << big << tenth << ", " << one << ", " << big

" << fixed << setprecision(3) << tenth << ", " << one << ", " " << setprecision(20) << tenth << endl; " << setw(8) << setfill('*') << 34 << 45 << endl;

cout << "G. " << setw(8) << 34 << setw(8) << 45 << endl; return 0; } produces this output (using DevC++ 4.9.8.0): A. 0.1, 1, 1.23457e+009 B. 0.100000, 1.000000, 1234567936.000000 C. 1.000000e-001, 1.000000e+000, 1.234568e+009 D. 0.100, 1.000, 1234567936.000 E. 0.1000000014901161 F. ******3445 G. ******34******45 Lines F and G show the scope of setw() and setfill().

C++ Notes: String Streams


Like files, strings are a sequence of characters. C++ provides a way to associate an I/O stream with a string. Here are some common uses for string streams. 1. Localizing errors in input. It is common to process one line at a time. The standard input streams proceed to the next line if there are insufficient values on one line. Also, an error in the input may make error recovery difficult. The solution is to read the input one line at a time into a string. Then "read" that string using a string stream. 2. Reading one of two types. Suppose you don't know what type the next input element is - it's either an int or a name. Solution: read it into a string, then try reading from the string as an int. If that fails assume it's a name. Use this to read to a sentinal value. For example, let's say you are reading integers until a certain value. But what value can you use to stop the input? Why not use "STOP" or some other string. Well, you can't do this easily while reading ints from cin, but you can read from cin into a string, compare that string to "STOP", and if it doesn't match, "read" from the string with a string stream as an integer. 3. Converting internal values to their external representations. Sometimes you want a character representation of data in memory. The best facilities for formatting data (eg, numbers) are the output stream facilities. You can create an output stream that goes into a string to make use of the stream fomatting.

String stream input - istringstream


String streams support all the normal iostream capabilities. There are only two additional things you need. 1. Input string streams must be declared istringstream. 2. The string value to read is set with str().

Here is a simple example.


#include <iostream> #include <sstream> using namespace std; . . . int a, b; string s = "34 22"; istringstream ins; // Declare an input string stream. . . . ins.str(s); ins >> a >> b; // Specify string to read. // Reads the integers from the string.

String stream output - ostringstream


Simlarly, there are only a couple of things you need to know to use output string streams. 1. Output string streams must be declared ostringstream. 2. The string value is obtained from the stream with str(). Here is a simple example that puts the character value of the square root of 2 into a string.
#include <iostream> #include <sstream> using namespace std; . . . ostringstream outs; . . . outs << sqrt(2.0); s = outs.str(); // Convert value into a string. // Get the created string from the output stream. // Declare an output string stream.

C++ Notes: String stream example


This example reads lines with an integer, an operator, and another integer. For example,
25 * 3 4 / 2

It's simple to read each of these three elements, but what happens if something is wrong with the input. For example, errors in the input like
25 * 3 + 1 or

25 *

will cause the whole program to fail. The solution is to read each line into a string, then read from the string. This will isolate the errors to one line, which is something that isn't possible with normal stream I/O, which will simply ignore whitespace, including line breaks. The example below shows how to do this.
// sstream-line-input.cpp - Example of input string stream. // This accepts only lines with an int, a char, and an int. 2 // Fred Swartz 11 Aug 2003 1 3 #include <iostream> 4 #include <sstream> #include <string> 5 using namespace std; 6 //================================================================ main int main() { 7 string s; // Where to store each line. int a, b; // Somewhere to put the ints. 8 char op; // Where to save the char (an operator) 9 istringstream instream; // Declare an input string stream 10 while (getline(cin, s)) { // Reads line into s 11 instream.clear(); // Reset from possible previous errors. instream.str(s); // Use s as source of input. 12 if (instream >> a >> op >> b) { 13 instream >> ws; // Skip white space, if any. if (instream.eof()) { // true if we're at end of string. 14 cout << "OK." << endl; 15 } else { cout << "BAD. Too much on the line." << endl; 16 } } else { 17 cout << "BAD: Didn't find the three items." << endl; 18 } } 19 return 0; 20 } 21 22 23 24 25 26 27 28 29 30 31

Note that ws is an input manipulator that skips whitespace. The flow could be simplified somewhat by making the if more complicated. Here is the central part rewritten, adding some calculator code.
if ((instream >> a >> op >> b >> ws) && instream.eof()) { switch (op) { case '+': result = a + b; break; case '-': result = a - b; break; case '*': result = a * b; break; case '/': result = a / b; break; default : cout << "Bad operator '" << op << endl; continue; } cout << result << endl << endl; } else { cout << "BAD INPUT." << endl; } // next loop iteration

C++ Notes: Arrays


Many values, one name
An array stores many values in memory using one name, and individual values are identified by number. Unlike math, arrays must be declared and a fixed amount of memory must be allocated for them. Individual elements are identified by integer subscripts which are enclosed in square brackets [] following the array variable. xi in mathematics is written as x[i] in C++ and most other programming languages. Arrays are the basic underlying mechanism for storing multiple values, and many additional ways to store values are built on them, especially in the Standard Template Library (STL). A solid understanding of arrays is essential, even tho you will want to use some of STL library data structures. In general, you should use arrays only for storing a fixed number of elements, and vectors when storing a variable number of elements. This section is about arrays, so it uses them in all examples, even when there are better choices (esp, vector).

Declaring an array

Array size and element type must be declared. All elements must be the same type, eg, all int or all char. Write the element type name, the name of the array variable, then the size enclosed in square brackets ("[]").
int scores[100]; char name[40]; // 100 ints, scores[0] to scores[99] // 40 chars, name[0] to name[39]

Subscripts start at zero


Subscript ranges always start at zero. This is unfriendly because it isn't the way that humans normally count, but it's the way that C, C++, Java and many other languages do it.

Length of an array
There is no general way to find the length of an array. You must keep the size (both the maximum, and currently filled size) of the array in variables. For example,
const int MAXSIZE = 366; ... int temperature[MAXSIZE]; int numOfTemps = 0; // Number of entries in temperature.

Arrays are to data what loops are to control flow


Loops and arrays go together. This example sets all array elements to zero.
float height[1000]; . . . for (int i=0; i<1000; i++) { height[i] = 0.0; }

Operations on arrays
Very few operations are legal for arrays.
y y y

No assignment. Use a loop to copy elements from one array to another. No comparisons. Use a loop to compare elements of two arrays. No arithmetic operations. Use a loop to perform arithmetic operations between two arrays.

Subscription is one of the few operations that can be applied to arrays. Because arrays are like pointers, the pointer operations are legal, but those aren't covered here.

C++ Notes: Array Examples


Example -- adding all elements of an array
This program values into an array and sum them. Assume fewer than 1000 input values. Yes, we could have summed them in the input loop.
1 2 #include <iostream> 3 using namespace std; 4 int main() { 5 int a[1000]; int n = 0; 6 7 8 9 10 11 12 13 14 15 } 16 17 18 19 20 21 22 23 // arrays/readsum.cpp -- Read numbers into an array and sum them. // Fred Swartz - 2003-11-20

// Declare an array of 1000 ints // Number of values in a.

while (cin >> a[n]) { n++; } int sum = 0; // Start the total sum at 0. for (int i=0; i<n; i++) { sum = sum + a[i]; // Add the next element to the total } cout << sum << endl; return 0;

Why read numbers into memory

The previous example, which reads numbers into an array then sums the elements of the array, is not a convincing use of arrays. It would have been just as easy to add them while we were reading them. But usually the computation can not be performed while reading, for example, sorting them and printing them in order by their value. An even simpler example is given below - printing them in reverse order of input.

Example -- printing the input values last to first


Here is something than can't be done with a simple input loop.
// arrays/reverse-input.cpp - Reverses order of numbers in input. #include <iostream> 2 using namespace std; 1 3 int main() { //--- Declare array and number of elements in it. 4 float a[100000]; 5 int n; // Number of values currenlty in array. 6 //--- Read numbers into an array 7 n = 0; while (cin >> a[n]) { 8 n++; 9 } 10 //--- Print array in reverse order 11 for (int i=n-1; i>=0; i--) { cout << a[i] << endl; 12 } 13 return 0; 14 }//end main 15 16 17 18 19 20 21 22

C++ Notes: Array Memory Diagrams


Here is an array declaration and code to initialize it.
int a[5]; . . . // Allocates memory for 5 ints.

a[0] = 1; for (int i=1; i<5; i++) { a[i] = a[i-1] * 2; }

Arrays are often represented with diagrams that represent their memory use. The diagram below is one typical way to represent the memory used by an array. Each box represents the amount of memory needed to hold one array element. For ints this is usually 4 bytes. We can write the value of an element inside the box. Pointers hold the memory address of other data and are represented by a black disk with an arrow pointing to the data it references. The actual array variable, a in this example, is a pointer to the memory for all of its elements. A common shortcut is to omit writing the array variable, and write only the elements that hold the data because these are usually the focus of attention. Sometimes the cells are written horizontally, especially when showing C-strings (arrays of chararcters).

or simply

or just

C++ Notes: Array Initialization


An array can be initialized in the declaration by writing a comma-separated list of values enclosed in braces following an equal sign.
int days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

Altho this looks like an assignment, assignment statements with arrays are not allowed, and this syntax is legal only in a declaration. Size can be set from initial value list If the size is omitted, the compiler uses the number of values. For example,
// is the same as the statement below: int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};

No default value If an array has no initialization, the values are undefined.


float pressure[10]; // Initial values of pressure undefined.

Missing initialization values use zero If an explicit array size is specified, but an shorter initiliazation list is specified, the unspecified elements are set to zero.
float pressure[10] = {2.101, 2.32, 1.44};

This not only initializes the first three values, but all remaining elements are set to 0.0. To initialize an array to all zeros, initialize only the first value.
float p1[1000]; // No intitialization. float p2[1000] = {0.0}; // All 1000 values initialized to zero.

Initialization of character arrays Character arrays can be initialized on the right by writing a double-quoted string.
char greeting[100] = "Hello"; // Remaining characters zero. char goodbye[] = "Adios"; // Array size is 6 (final zero on strings).

C++ Notes: Passing Array Parameters


Array variables as parameters
When an array is passed as a parameter, only the memory address of the array is passed (not all the values). An array as a parameter is declared similarly to an array as a variable, but no bounds are specified. The function doesn't know how much space is allocated for an array. See the example below.

Example -- Function to add numbers in an array


This main program calls a function to add all the elements in an array and uses the returned value to compute the average.
1 2 #include <iostream> 3 using namespace std; 4 float sum(const float x[], const int size); // prototype 5 6 //====================================================== main int main() { 7 float a[1000]; // Declare an array of 1000 floats int n = 0; // number of values in a. 8 9 10 11 12 13 14 } 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //======================================================= sum // sum adds the values of the array it is passed. float sum(const float x[], const int size) { float total = 0.0; // the sum is accumulated here for (int i=0; i<size; i++) { total = total + x[i]; } return total; } while (cin >> a[n]) { n++; } cout << "Average = " << sum(a, n)/n << endl; return 0; // arrays/average.cpp - Averages numbers // Fred Swartz - 2003-11-20

Because this application reads an unbounded number of values, it is subject to buffer overflow, trying to read more values into an array than will fit. A more appropriate data structure to use is a vector, which will expand as necessary to accomodate data. See Passing Vectors as Parameters.

C++ Notes: Arrays - Problems and Solutions


Inherent Problems
Arrays can be difficult to use reliably for several reasons. Unknown maximum size of an array Because arrays are implemented as a pointer to the first element of the allocated storage, the length, or maximum allocated size, is not generally available. It's necessary to pass this maximum size to functions which need to add elements to an array. [Note: Vectors solve this problem.] Lack of current number of elements The number of elements currently in an array is another quantity that the programmer must keep track of. [Note: Vectors solve this problem.] Fixed size The programmer has to decide how much memory to allocate for an array, but sometimes there may be large variations in the requirements. For example, I was just writing a program to process daily mutual fund values. The data is downloaded from a service that provides this data, but I don't know how much data there will be for any fund. It can vary from a few entries for new funds to many years of data. How big should I declare the arrays? If I make all arrays large enuf for the largest possible number of data points (50 years?), then memory usage will be very wasteful and the program might not even fit into memory on some machines. If I make the arrays smaller, there might not be suffcient space for all data. [Note: Vectors solve this problem.] Lack of subscript checking C++ doesn't check subscription to see that the subscript is in a legal range. This lack of checking means that errors are often undetected for a long time. This is also the source of the most common hacker exploitation of programs, known as the buffer overflow exploit. [Note: Vectors can solve this problem. Also, Java and C# provide bounds checking.] Buffer Overflow Imagine that in the previous example, which involved reading numbers into an array, the input consisted of more than 1000 (the size of the array) numbers. What happens? The numbers are read into memory locations after the end of the array.

What is the consequence of this? Hmmmm, that's what hacker knowledge is all about. [Note: Vectors solve this problem.]

Solutions
Dynamic allocation - Solution to fixed size restriction Arrays can be dynamically allocated, which is a nice solution for some problems. But you have to be comfortable with pointers before using dynamic allocation (Dynamic Allocation of Arrays). [Note: This is how vectors are implemented.] Vectors - Solution to buffer overflow, fixed size, unknown maximum and current size, ... Object-oriented programming libraries, such as the STL (Standard Template Library) which comes with modern versions of C++, provide data structures which are nice replacements for arrays. The simplest alternative is vector, which is essentially a kind of expandable array.

C++ Notes: Multiple Source Files


Small programs are typically written in a single .cpp file. However, as programs get larger, it's necessary to split the source into several files in order to make the project manageable.

Use .h files to for shared declarations


Because C++ needs to know the declarations of functions before they are called, function prototypes (declarations) are typically written at the top of a single-file, multiple-function program. This ensures that the call and the definition are both consistent. Similarly, when using muliple source files, you want to use the same declaration where the function is called as where it's defined. The solution is to create a header file (.h) which contains the function declarations. By #including this in all source files (.cpp) that use or define this function, consistency is maintained.

Project?
If you user an IDE (eg, instead of a make file), you often have to create a project and add all source files to it, so that it knows which files belong together. A project may also include other, non-source, resource files.

Example with two source files and a shared header

Here's a example, with the main program in one file, a function in another, and a header file containing the function prototype. This is a common pattern, alto the header file may contain definitions without a corresponding .cpp file. Header file - average.h
1 3 // multiple-files/average.h - Function prototype. 2 float average(const vector<float>& x);

Main program - averageMain.cpp


// muliple-files/averageMain.cpp -- Print above average numbers. // Illustrates multiple source file compilations. 2 // Fred Swartz -- 2004-11-17 1 3 //... Standard includes 4 #include <iostream> #include <stdlib.h> 5 #include <vector> 6 using namespace std; 7 //... Private include for average function. 8 #include "average.h" 9 //=========================================================== main 10 int main(){ 11 12 13 14 15 16 17 18 19 20 21 22 23 24 } 25 26 27 28 vector<float> fv; float temp; // Store the input floats here. // Temp for input.

//... Read floats into vector while (cin >> temp) { fv.push_back(temp); } //... Compute average. float avg = average(fv); //... Print greater than average numbers. cout << "Average = " << avg << endl; for (int i = 0; i < fv.size(); i++) { if (fv[i] > avg) { cout << fv[i] << endl; } } return 0;

29 30 31 32 33 34 35 36 37

Function definition - average.cpp


// multiple-files/average.cpp -- Compute average in vector. // It would be nicer to make this a template function. 2 // Fred Swartz -- 2004-11-17 1 3 //... Standard includes 4 #include <vector> using namespace std; 5 6 //... Private header file with prototype for average. #include "average.h" 7 8 //======================================================== average float average(const vector<float>& x) { 9 float sum = 0.0; for (int i=0; i<x.size(); i++) { 10 sum += x[i]; 11 } 12 13 } 14 15 16 17 18 19 20 return sum / x.size();

C++: Addresses, Pointers, References


Memory addresses

Every byte in memory has an integer memory address. Addresses start at zero and go to the maximum amount of memory that the computer has. For example, in a computer that has 256 MB of RAM the bytes would be numbered 0, 1, 2, 3, 4, 5, etc (up to the power of 2 closest to 256,000,000). Instructions and data are stored in RAM when a program is running, so each instruction or data element is identified by its memory address, or the address of its first byte because many types are longer than one byte. The CPU uses these memory addresses for all operations. One of the great advantages of higher-level language is that all these addressing details are taken care of automatically by the programming language and the programmer doesn't have to worry about them. However, it can be very useful for the programmer to be able to explicitly use memory addresses as data. This data type is called a pointer.

Pointers
C and C++ have variables that hold memory addresses. These are called pointers. Pointers are an extremely powerful programming feature, long regarded as essential to any full-strength programming language. But the power of pointers also comes at a price, and safe use of pointers turns out to be rather difficult in large programs and the source of many bugs. The most common uses of pointers are:
y y y

Working with arrays, especially char arrays. For dynamic memory allocation of data structures. To link data structures together.

Pointers are so powerful they are dangerous. They are dangerous because they can access any memory location and a small error in their use can have mysteriously bizarre results, often showing up only later in execution or when the program is run in a different environment. It is estimated that about 50% of the bugs in production ("shrink-wrapped") software are due to pointer misuse. Consequently, some languages do not have pointers, and only have a more restricted use of memory addresses called references. See Pointers

References - Pointers with restrictions


A much safer and simpler use of memory addresses are references. References are pointers that can't be modified with addition and subtraction. Removing this capability makes references much safer to use than pointers. In addition, they are automatically dereferenced so the programming notation is simpler and less error prone. Newer programming languages, such as Java, have completely eliminated pointers and only use references for greater reliability and security.

The most common use of references is in function parameters. Passing a reference to a value has two advantages.
y y

It is more efficient than passing a large structure. With OOP this is the main use of references. It allows the parameter to be changed.

C++ Notes: Pointers


Pointers in C/C++
Pointers are variables that contain memory addresses (see Addresses, Pointers, and References). They are an essential data type in C and C++, and are used for the following:
y y y y

Array variables are pointers. Dynamic memory allocation/deallocation uses pointers. To pass the address of a struct or object instead of the entire object to a function. For function parameters that will be changed by the function (out or inout parameters).

Declaring a pointer
Pointers are declared to point to a particular datatype. A "*" is written after the type to indicate that this is a pointer to that type. For example, a pointer to an int would be declared like this.
int* ip; // declares ip to be a pointer to an int.

You will also see variations in the spacing such as


int *ip; int * ip;

NULL
NULL is the pointer to nothing, and should be used as the initial value for pointers because using NULL will cause an error in most systems.

Pointer operators: * (dereference) and & (address of)

Two operators are used when dealing with memory addresses:


y y

Unary & gives the address of (pointer to) something. Unary * takes the contents of a pointer (dereferences a pointer).

You may also do some addition and subtraction operations.

Example
char* cp; char c, d; // declare pointer to char // declare char variables

cp c d cp

= &c; = 'x'; = *cp; = &d;

// puts address of c into cp // assigns 'x' to c // also assigns 'x' to c // copies c's value to d // puts address of d into cp // puts value of c into d

*cp = 'x';

*cp = c;

C++ Notes: The NULL Pointer


Problem: Initial value for pointers
Variables are not automatically given an intial value by the system, and start with whatever garbage is left in memory when they are allocated. Pointers have the potential to cause substantial damage when they are used without a valid address. For this reason it's important to initialize them. NULL. The standard initialization is to the predefined constant NULL. Using the NULL value as a pointer will cause an error on almost all systems, making the debugging process somewhat easier. NULL is defined in <cstdlib>.
#include <cstdlib> . . . Node* head = NULL; // Initialized pointer to NULL.

Is NULL zero?
In theory, NULL can be defined to be non-zero. In practice this doesn't happen on any mainstream system. So much code is written making the assumption that NULL is zero, it is hard to image it using anything except 0.

C++ Notes: Object-Oriented Programming


History: The Rise and Decline of Structured Programming
For many years (roughly 1970 to 1990), structured programming was the most common way to organize a program. This is characterized by a functionaldecomposition style - breaking the algorithms in to every smaller functions. This technique was a great improvement over the ad hoc programming which preceded it. However, as programs became larger, structured programming was not able control the exponential increase in complexity. The Problem - Complexity Complexity measurements grow exponentially as the size of programs grow. One measurement is coupling, or much different elements (modules, data structures) interact with each other. The fewer the connections, the less complex a program is. Low coupling is highly desirable. There have been several post-structured programming attempts to control complexity. One of these is to use software components - preconstructed software "parts" to avoid programming. And when you have to program, use object-oriented programming (OOP).

Object-Oriented Programming (OOP)


Object-Oriented Programming groups related data and functions together in a class, generally making data private and only some functions public. Restricting access decreases coupling and increases cohesion. While it is not a panacea, it has proven to be very effective in reducing the complexity increase with large programs. For small programs may be difficult to see the advantage of OOP over, eg, structured programming because there is little complexity regardless of how it's written. Many of the mechanics of OPP are easy to demonstrate; it is somewhat harder to create small, convincing examples. OOP is often said to incorporate three techniques: inheritance, encapsulation, and polymorphism. Of these, you should first devote yourself to choosing the right classes (possibly difficult) and getting the encapsulation right (fairly easy). Inheritance and polymorphism are not even present in many programs, so you can ignore them at that start. Encapsulation Encapsulation is grouping data and functions together and keeping their implementation details private. Greatly restricting access to functions and data reduces coupling, which increases the ability to create large programs. Classes also encourage coherence, which means that a given class does one thing. By increasing coherence, a program becomes easier to understand, more simply

organized, and this better organization is reflected in a further reduction in coupling. Inheritance Inheritance means that a new class can be defined in terms of an existing class. There are three common terminologies for the new class: the derived class, the child class, or the subclass. The original class is the base class, the parent class, or the superclass. The new child class inherits all capabilities of the parent class and adds its own fields and methods. Altho inheritance is very important, especially in many libraries, is often not used in an application. Polymorphism Polymorphism is the ability of different functions to be invoked with the same name. There are two forms. Static polymorphism is the common case of overriding a function by providing additional definitions with different numbers or types of parameters. The compiler matches the parameter list to the appropriate function. Dynamic polymorphism is much different and relies on parent classes to define virtual functions which child classes may redefine. When this virtual member function is called for an object of the parent class, the execution dynamically chooses the appropriate function to call - the parent function if the object really is the parent type, or the child function if the object really is the child type. This explanation is too brief to be usesful without an example, but that will have to be written latter.

C++ Notes: OOP Terminology


Along with each programming revolution comes a new set of terminology. There are some new OOP concepts, but many have a simple analog in pre-OOP practice. OOP Term method Definition Same as function, but the typical OO notation is used for the call, ie, f(x,y) is written x.f(y) where x is an object of class that contains this f method.

send a message instantiate class

Call a function (method)

Allocate a class/struct object (ie, instance) with new A struct with both data and functions

object member constructor

Memory allocated to a class/struct. Often allocated with new. A field or function is a member of a class if it's defined in that class Function-like code that initializes new objects (structs) when they instantiated (allocated with new). Function-like code that is called when an object is deleted to free any resources (eg, memory) that is has pointers to. Defining a class (child) in terms of another class (parent). All of the public members of the public class are available in the child class. Defining functions with the same name, but different parameters. A function is overloaded if there is more than one definition. See polymorphism. Redefine a function from a parent class in a child class. Same as child, derived, or inherited class. Same as parent or base class. Same as data member or member field.

destructor

inheritance

polymorphism overload

override subclass superclass attribute

C++ Notes: Classes


History of C++
Bjarne Stroustrup of Bell Labs extended the C language to be capable of ObjectOriented Programming (OOP), and it became popular in the 1990's as C++. There were several enhancements, but the central change was extending struct to allow it to contain functions and use inheritance. These extended structs were later renamed classes. A C++ standard was established in 1999, so there are variations in the exact dialect that is accepted by pre-standard compilers.

Public and Private parts


All member fields in a class are private by default (no code outside the class can reference them), whereas fields of a struct are public, meaning that anyone can use them. For example,
struct Product { char mfg_id[4]; char prod_id[8]; int int }; price; qty_on_hand; // 4 char code for the manufacturer. // 8-char code for the product // price of the product in dollars. // quantity on hand in inventory

could be rewritten as a class like this


class Product { public: char mfg_id[4]; char prod_id[8]; int int }; price; qty_on_hand; // 4 char code for the manufacturer. // 8-char code for the product // price of the product in dollars. // quantity on hand in inventory

C++: OOP: this


When you are writing code in a member function, the predefined variable this contains a pointer to the current object. Any member element, for example m_x, can be referenced in two ways:
m_x = 10; this->m_x = 10; // same as above.

References to members are usually written without this because there is no need to use it, however, there are situations where this is needed.

Naming clarity
If a naming convention isn't used that makes member/instance variables clear (eg, prefixing such variables with "m_"), the use of this can be used to document the nature of the variable. Naming rules are to be preferred however.

Returning or passing the current object

The primary use of this is to return a pointer to the current object, or to pass the object to a function.

C++ Notes: OOP: Classes: Example: floatVector - Version 1


Fixed array sizes and Vectors
The fixed size of arrays is often a difficult programming constraint and frequently the source of bugs. The STL (Standard Template Library) vector class is a dynamically expandable array. The floatVector example here shows how vector is implemented, but without the distraction of templates and iterators of the STL vector class. The code is divided into three files, one for testing, one containing the header file, and one which has the implementation of the methods. This first version is a very small subset of the features of the actual vector class.
y y y

floatVectorTest.cpp - test program floatVector.h - header floatVector.cpp - implementation

Enhancements
Many improvements can be made to this class. 1. Additional functions. Add the following functions that are defined in the vector class, but not yet in floatVector:
2. bool empty() const; //True if no elements, size()==0. //Returns reference to first element //Remove last element.

3. float& front() const; void pop_back();

They should all do the same thing as the corresponding STL: vector<T> template class functions. Modify the test program to test them. 4. Exceptions. The at function in the vector class throws the out_of_range exception if the subscript is out of range. Change at to do that, but also do the same for subscription and throw an exception in the constructor if the size isn't greater than zero. See Exceptions. 5. Operator overloading - Write the subscription operator and equality comparison operators. Subscription should do the same thing as the at() function. The equality operator (==) should first compare the sizes. If the sizes are unequal, return false. If the sizes are equal, it's necessary to loop thru the data. If any corresponding elements are unequal, return false.
6. float& operator[](int position) const;

bool

operator==(const floatVector& v2) const;

7. Assignment, copy constructor, destructor. Because this class dynamically allocates memory, it's necessary to define assignment (operator=), a copy constructor, and a destructor. See Overloading assignment, Copy constructor, and Destructors.
8. floatVector& operator=(const floatVector& fv); 9. floatVector(const floatVector& fv); // Copy constructor. ~floatVector(); // Destructor.

10.Friend function. Define the output operator << by overloading operator<< as a friend function. See Overloading << and >> Output format: vector output should produce a left brace, a list of values separated by comma followed by space, and terminated by a right brace. This is like the notation for array initialization. For example
{1.1, 7.3345, 3.14} friend ostream& operator<<(ostream& os, const floatVector& fv); o o

floatVectorTest.cpp - Problem 5 cumulative test program floatVector.h - Problem 5 cumulative header file

11.Template class. Make this into a general template class that will work with any type. Call it xvector to distinguish it from the standard vector class. Use the test program Test Program for xvector.

Header file and test program for first five problems


You may find the following useful for testing solutions to the first 4 problems too, but you will have to comment out the tests and prototypes that you haven't yet implemented..
y y

floatVectorTest.cpp - Problem 5 cumulative test program floatVector.h - Problem 5 cumulative header file

C++ Notes: Classes - Visibility - public and private


Public and Private parts

All members (fields and methods) of a class are private by default (no code outside the class can reference them), whereas fields of a struct are public, meaning that anyone can use them. For example,
struct Product { char mfg_id[4]; char prod_id[8]; int int }; price; qty_on_hand; // 4 char code for the manufacturer. // 8-char code for the product // price of the product in dollars. // quantity on hand in inventory

could be rewritten as a class like this


class Product { public: char mfg_id[4]; char prod_id[8]; int int }; price; qty_on_hand; // 4 char code for the manufacturer. // 8-char code for the product // price of the product in dollars. // quantity on hand in inventory

The public and private keywords may be written before any declarations, but it is common to group all public members, then all private members. For example,
class Point { public: int getX(); int getY(); int setX(); int setY();

private: int x; int y; };

C++: OOP: Data member initializers


Initializing data members

There are two ways to initialize data members.


y y

Explicit assignments in a constructor. In a member initializer in the constructor header.

Member initializer syntax


The definition (not the prototype) header is followed by a colon, the name of the field, and a parenthesized initial value. Separate multiple initializations with commas.
Insert example here

Member initializer required in the following cases


y y y y

To initialize const data members. To initialize references. To initialize member objects. If a member initializer is not provided, the default constructor for that class will be called. To initialize base class members.

C++: Constructors
When an object of a class is created, C++ calls the constructor for that class. If no constructor is defined, C++ invokes a default constructor, which allocates memory for the object, but doesn't initialize it.

Why you should define a constructor


Uninitialized member fields have garbage in them. This creates the possibility of a serious bug (eg, an uninitialized pointer, illegal values, inconsistent values, ...).

Declaring a constructor
A constructor is similar to a function, but with the following differences.
y y

No return type. No return statement.

Example [extracts from three different files].

//=== point/point.h ================================================= #ifndef POINT_H #define POINT_H class Point { public: Point(); // parameterless default constructor constructor with parameters

Point(int new_x, int new_y); // int getX(); int getY(); private: int x; int y; }; #endif

Here is part of the implementation file.


//=== point/point.cpp ============================================== . . . Point::Point() { x = 0; y = 0; } Point::Point(int new_x, int new_y) { x = new_x; y = new_y; } . . . // constructor // default constructor

And here is part of a file that uses the Point class.


//=== point/main.cpp ============================================== . . . Point p; Point q(10,20); // calls our default constructor // calls constructor with parameters // calls default constructor

Point* r = new Point(); Point s = p; . . .

// our default constructor not called.

C++ Notes: Shallow vs Deep Copies


A shallow copy of an object copies all of the member field values. This works well if the fields are values, but may not be what you want for fields that point to dynamically allocated memory. The pointer will be copied. but the memory it points to will not be copied -- the field in both the original object and the copy will then point to the same dynamically allocated memory, which is not usually what you want. The default copy constructor and assignment operator make shallow copies. A deep copy copies all fields, and makes copies of dynamically allocated memory pointed to by the fields. To make a deep copy, you must write a copy constructor and overload the assignment operator, otherwise the copy will point to the original, with disasterous consequences.

Deep copies need ...


If an object has pointers to dynamically allocated memory, and the dynamically allocated memory needs to be copied when the original object is copied, then a deep copy is required. A class that requires deep copies generally needs:
y y y y

A constructor to either make an initial allocation or set the pointer to NULL. A destructor to delete the dynamically allocated memory. A copy constructor to make a copy of the dynamically allocated memory. An overloaded assignment operator to make a copy of the dynamically allocated memory.

C++ Notes: OOP: Copy Constructors


When copies of objects are made
A copy constructor is called whenever a new variable is created from an object. This happens in the following cases (but not in assignment).
y y y y

A variable is declared which is initialized from another object, eg,


Person q("Mickey"); // constructor is used to build q. Person r(p); Person p = q; declaration. p = q; constructor. // copy constructor is used to build r. // copy constructor is used to initialize in // Assignment operator, no constructor or copy

A value parameter is initialized from its corresponding argument.

f(p); parameter. y

// copy constructor initializes formal value

An object is returned by a function.

C++ calls a copy constructor to make a copy of an object in each of the above cases. If there is no copy constructor defined for the class, C++ uses the default copy constructor which copies each field, ie, makes a shallow copy.

Don't write a copy constructor if shallow copies are ok


If the object has no pointers to dynamically allocated memory, a shallow copy is probably sufficient. Therefore the default copy constructor, default assignment operator, and default destructor are ok and you don't need to write your own.

If you need a copy constructor, you also need a destructor and


operator=

If you need a copy constructor, it's because you need something like a deep copy, or some other management of resources. Thus is is almost certain that you will need a destructor and override the assignment operator.

Copy constructor syntax


The copy constructor takes a reference to a const parameter. It is const to guarantee that the copy constructor doesn't change it, and it is a reference because a value parameter would require making a copy, which would invoke the copy constructor, which would make a copy of its parameter, which would invoke the copy constructor, which ... Here is an example of a copy constructor for the Point class, which doesn't really need one because the default copy constructor's action of copying fields would work fine, but it shows how it works.
//=== file Point.h ============================================= class Point { public: . . . Point(const Point& p); . . . //=== file Point.cpp ========================================== . . . Point::Point(const Point& p) { x = p.x; y = p.y; // copy constructor

} . . . //=== file my_program.cpp ==================================== . . . Point p; Point s = p; p = s; // calls default constructor // calls copy constructor. // assignment, not copy constructor.

Difference between copy constructor and assignment


A copy constructor is used to initialize a newly declared variable from an existing variable. This makes a deep copy like assignment, but it is somewhat simpler: 1. There is no need to test to see if it is being initialized from itself. 2. There is no need to clean up (eg, delete) an existing value (there is none). 3. A reference to itself is not returned.

Unaddressed issue
[Question: When is the base (parent) class copy constructor called? Is it like Java and the parent constructor is called at the beginning of each constructor? Does it have to be explicit?]

C++: Destructors
When are destructors called?
A destructor for an object is called whenever
y y

The scope of a local variable or parameter is exited, the destructor is called. By explicitly calling the destructor.

Default constructor vs custom constructor


If you don't have anything to clean up, like memory that you allocated for some of the members, then the default constructor is probably ok.

C++: Key Points - Constructors


Purpose

Constructors have one purpose - to allow you to guarantee that objects are correctly initialized. The compiler will guarantee that your constructors are called, inserting constructor calls as necessary. This is a great leap in reliability over C programming where, if the programmer forgot to initialize something explicitly, it contained whatever garbage was in memory. Three types - No-parameter (default), copy constructors, and all others. Only the first two have unexpected aspects.

1. No-parameter (default) constructors


y

Default: If you don't declare any constructors, the compiler creates a default, parameterless constructor. Unfortunately, it does nothing good; it only satisfies the compiler's need for a parameterless constructor. No default: If you declare constructors with parameters, no default parameterless constructor will be created. Forbid creation. If you want control over creation of objects from you class (eg, to enforce use of a factory method, declare a private parameterless constructor. Automatically called: The parameterless constructor for class A would be called in these circumstances:
y A x; // Calls default constructor A xa = new A[99]; // Calls default constructor for each array element.

y y

2. Copy constructors
y

Purpose: To initialize one object from another. This is different than assignment because there is no pre-existing value that may have to be destroyed. Syntax:
y y } A(const A& a2) { . . .

Note that the parameter must be a const reference.


y

Default creation. If you don't define a copy constructor, the compiler creates one which simply does a shallow memberwise copy. If you dynamically allocate memory for your class, you can only get a deep copy by writing your own copy constructor. Automatically called: A copy constructor is called in the following circumstances.

y y

A x(y); f(x);

// Where y is of type A. // A copy constructor is called for value parameters.

x = g(); // A copy constructor is called for value returns. y

Preventing copying. To prevent copies, eg, to prevent an object from being passed by value, declare the copy constructor as private.

Questions
Assume A is a class. Function definitions
A f(A x) { return x;} A g(A& x) { return x;} A& h(A& x) { return x;}

Questions For each of the following function calls, how many times is the copy constructor called?
A a; A b; a = f(b); a = g(b); a = h(b);

C++ Notes: Operator Overloading


Why overload operators
Clarity. It's common to define a meaning for an existing operator for objects of a new class. Operators are defined as either member functions or friend functions. Don't use operator overloading just because it can be done and is a clever trick. The purpose of operator overloading is to make programs clearer by using conventional meanings for ==, [], +, etc. For example, overloading the [] operator for a data structure allows x = v[25] in place of a function call. This is purely a conveniece to the user of a class. Operator overloading isn't strictly necessary unless other classes or functions expect operators to be defined (as is sometimes the case). Whether it improves program readability or causes confusion depends on how well you use it. In any case, C++ programmers are expected to be able to use it -- it's the C++ way.

Example - Defining + for Point

Using the Point class, adding (does this make sense?) can be defined like this:
//=== Point.h file ============================= Point operator+(Point p) const; //=== Point.cpp file =========================== Point Point::operator+(Point p) const { return Point(x+p.x, } //=== myprogram.cpp ============================ Point a(10, 20); Point b(1, 2); Point c = a + b; y+p.y);

Define a function which begins with the "operator" keyword


Define a function with the keyword "operator" preceding the operator. There can be whitespace between operator and the operator, but usually they are written together.

Restrictions
Most operators can be redefined, there are restrictions.
y y y y y y y

It's not possible to change an operator's precedence. It's not possible to create new operators, eg ** which is used in some languages for exponentiation. You may not redefine ::, sizeof, ?:, or . (dot). Overloading + doesn't overload +=, and similarly for the other extended assignment operators. =, [], and -> must be member functions if they are overloaded. ++ and -- need special treatment because they are prefix and postfix operators. There are special issues with overloading assignment (=) (see Overloading Assignment). Assignment should always be overloaded if an object dynamically allocates memory.

C++ Notes: OOP:Overloading Assignment


When to define assignment (and a copy constructor and destructor)

If your object has a pointer to dynamically allocated memory, eg allocated in the constructor, you will want to make a deep copy of the object. Deep copies require overloading assignment, as well as defining a copy constructor and a destructor).

Pattern
The following steps are a typical for the assignment operator. 1. No self-assignment. Test to make sure you aren't assigning an object to itself, eg x = x. Of course no one would write that statement, but in can happen when one side of the assignment is not so obviously the same object. Self assignment fails because the memory associated with the current value of the left-hand-side is deallocated before the assignment, which would invalidate using it from the right-hand-side. 2. Delete the associated memory. Same as copy constructor. 3. Copy all the members. Same as copy constructor. 4. Return *this. This is necessary to allow multiple assignment, eg x = y = z;

Example
//--- file Person.h . . . class Person { private: char* _name; int public: Person& Person::operator=(const Person& p); . . . } //--- file Person.cpp . . . //=================================================== operator= Person& Person::operator=(const Person& p) { if (this != &p) { // make sure not same object // Delete old name's memory. // Copy new name // Copy id _id;

delete [] _name; strcpy(_name, p._name); _id = p._id;

_name = new char[strlen(p._name)+1]; // Get new space

} return *this; }//end operator= // Return ref for multiple assignment

More
See Overloading Derived Class Assignment for the secret to getting a parent's members assigned.

C++: Overloading Derived Class Assignment


Copying parent class members
If there is a parent (base) class, those fields must also be copied. You can accomplish this with the following cryptic statement,
this->Parent::operator=(source);

where Parent is the name of the base class.


//--- file Parent.h class Parent {...}; //--- file Child.h #include "Parent.h" class Child : public Parent { // declaration of derived class public: Child& Child::operator=(const Child& source); };//end class Child //--- file Child.cpp #include "Child.h" Child& Child::operator=(const Child& source) { if (this != &source) { this->Parent::operator=(source); . . . // copy all our own fields here. } return *this; }//end operator= // declaration of base class

C++: OOP: const member functions


const member functions can use const objects

Member functions should be declared with the const keyword after them if they can operate on a const (this) object. If the function is not declared const, in can not be applied to a const object, and the compiler will give an error message. A const function can be applied to a non-const object A function can only be declared const if it doesn't modify any of its fields.

More efficient code


I remember something about compilers being able to generate more efficient code for const member functions.

Syntax
The const keyword is placed after the function header and before the left brace in both the prototype and the definition.

Example
In this example from a header file for a vector-like class, the capacity and empty functions don't change the object, and therefore are declared const. clear may change the object and therefore can not be declared const.
int capacity() const; // max size before reallocation // delete all items // true if contains no elements (size()==0)

void clear(); bool empty() const;

const can't be used for constructors and destructors

The purpose of a constructor is to initialize field values, so it must change the object. Similarly for destructors.

Can overload with non-const functions


There can be both const and non-const functions. The compiler will choose the appropriate one to call depending on the object they are being applied to. [Note: this seems like more of a theoretical possibility rather than something that would ever be used. Is there a common use for defining more than the const version?]

C++: OOP: Friend Functions


Friend functions are functions defined outside a class (ie, not member functions), but which the class declares to be friends so that they can use the class's private members. This is commonly used in operator overloading.

Overloading input/output operators


Perhaps the most common use of friend functions is overloading << and >> for I/O. See Overloading << and >>.

Commutative operators
Another use of friend functions is to permit operators to be commutative. An operator is commutative if the result is the same regardless of the order of its operands. Some typical examples are addition and multiplication. Subtraction and division are not commutative. Assuming x is an object of a class you've written and i is an integer, x+i should have the same meaning as i+x. The + in x+i is ok if the + operator between an object and an int is defined as a member function in x's class. However the + in the second case (i+x) can only be written as a friend function. That's because overloading (redefining) operators can only be done for classes, not the primitive types, and the operator definition that is used is based on the class of the left operand.

Declare friends before public and private


A class doesn't control the scope of friend functions so friend function declarations are usually written at the beginning of a .h file. Public and private don't apply to them.

Efficiency
Another possible use of friend functions is efficiency since they can access the private members directly they can avoid the cost of using getter functions. However, never do this unless you really need to because it makes code harder to read and introduces more possible sources of errors thru increased dependencies.

Cosmetic use
Another use is to make some function calls more attractive. For example,

Time t1, t2; . . . if (t1.compareTo(t2) == 0) . . .

Might be more attractively written as


if (compareTo(t1, t2) == 0) . . .

Because compareTo needs to examine the private values in t1 and t2, this second form is only possible if the class declares that compareTo is a friend.

C++: OOP: Overloading << and >>


Perhaps the most common use of friend functions is overloading << for I/O. This example overloads << (ie, defines a operator<< function) so that Point objects can use cout and <<.
// example usage Point p; . . . cout << p; // not legal without << friend function.

Declare before public and private in header file


Declare friend functions outside the public and private sections of the header file. Often they are placed first as in the example below.
//=== Point.h file ============================= class Point { friend ostream& operator<<(ostream& output, const Point& p); public: . . . private: . . .

Definition
The definition of the operator<< function can be in any file. It is not a member function, so it is defined with two explicit operands. The operator<< function must return the value of the left operand (the ostream) so that multiple << operators may be used in the same statement. Note that operator<< for your type can be defined in terms of << on other types, specifically the types of the data members of your class (eg, ints x and y in the Point class).
//=== Point.cpp file ===========================

. . . ostream& operator<<(ostream& output, const Point& p) { output << "(" << return output; } p.x << ", " << p.y <<")";

// for multiple << operators.

Notational practice
The following are identical. First using traditional operator notation.
Point p, q; cout << p << q;

This can be written with parentheses to show the order of evaluation. This makes it clearer that the first operation has a result that is used by the second output operation.
((cout << p) << q);

An equivalent way is to write this using function for << is


operator<<(operator<<(cout, p), q);

Friend functions are not always needed


If there is no need to reference private data members, operator<< doesn't need to be a friend function; it can be a function that isn't associated with the class in any way.

Defining >> for input


>> can be defined in a similar way.

C++ Notes: Templates


The Problem: To define containers for any class
It is relatively easy to define a class, like vector, that will work with one specific type. to use the class with another type, you must replace all type names in the original file with the new type, an error-prone process. C++ provides a way to write a generic definition that works with any type -- templates. Use templates only to define classes that you want to work with many data types.

Standard Template Library


Common containers (data structures) and algorithms have already been written and are available in what is usually called the Standard Template Library (STL). This library includes definitions for vector, set, multiset, map, multimap, queue, stack, and more.

Templates can be used with classes or functions


Precede the class or function with
template <class sometype>

The header file (.h) includes all method definitions


Because the compiler needs to know which for types to compile the implementation of a template, it is necessary to include the implementations of all methods in the .h file. There will be no separate .cpp file for the template methods, constructors, and destructors, unlike the typical C++ pattern which requires the separation. There are two styles for doing this: 1. Putting the implementation directly in the class definition. Advantage: The header information doesn't have to be repeated twice. It's the way Java defines all classes. Disadvantage: It's harder to read a short summary of the class. Template Example 1 uses this style. 2. Making a class definition with only prototypes and immediately following it with the definitions. Advantage: the class definition is short and easily readable. Disadvantage: the header information is in two places.

C++ Notes: STL Overview


STL features: containers, iterators, and algorithms
C++'s Standard Template Library (STL), standardized in 1999, solves many standard data structure and algorithm problems. The STL is (or should be) the first choice in all programming. Altho you many need to write certain specialized data structures, or build on top of the STL structures, but you should never be writing duplicate code. The STL provides functionality in several areas: containers, iterators, and algorithms.

1. Containers STL containers implement many or the standard ways to storing data to achieve performance goals. These are all template types. They store values, and may copy them. If you don't want values copied, use addresses of the values.
vector

Like an expandable array. Double-Ended Queue. Like an array expandable at both front and back. Doubly linked-list. Associates key with single value. Balanced tree. Associates key with multiple values. Balanced tree. Records whether value belongs to it. Balanced tree. Like set but allows more than one entry with the same value. Adapter to permit only LIFO (Last In, First Out) access. Adapter to permit only FIFO (First In, First Out) access.

deque

list map multimap set multiset stack queue

priority_queue Adapter to return element with the highest priority. string bitset

Character strings. Bit strings, including set operations.

2. Iterators Iterators provide uniform ways to go over the elements of the STL containers, as well as arrays. There are iterators for going sequentially forward and/or backward as well as random access iterators. Iterators are used by the STL algorithms. Iterator syntax uses the ++, --, and * operators which are familiar to users of pointers. 3. Algorithms Many common algorithms are implemented for containers and arrays. Sorting, searching, inserting, deleting, shuffling, permuting, rotating, reversing, moving, copying, counting, minimum and maximum, for each, ... Other The STL contains other classes useful for numerics (eg, complex and valarray), I/O (eg, stream I/O classes), internationalization (eg, wide characters and locales), etc.

Other sources of STL information


y

Standard Template Library Online Reference.

Limited built-in C++ data structures


There are two kinds of built-in data structures in C++: 1. Arrays - Fixed size collection of elements accessed by index/pointer. 2. Structs/Classes - Record structure with fields accessed by name. These are useful, but have numerous serious deficiencies (eg, performance or fixed size) or construct their own data structures using these basic features (most coding, slower development, less reusable, ...).

C++ Notes: STL: Containers: Introduction


The STL (Standard Template Library) provides a number of predefined containers (data structures), most of which are generalize as templates to hold any type element. By far the most commonly used are string and vector. Sequence containers These containers hold sequences of data elements.
y vector provides a dynamic array structure with fast random access to any

element. Inserting and deleting elements at the end is fast. Can do subscript bounds checking.
y deque also provides a dynamic array structure with random access and adds

fast insertion and deletion of elements at front as well as from the back. Very slightly slower than vector because of an extra level of indirection.
y list is usually implemented as a doubly linked list. There is no random access to the elements. Insertion and deletion anywhere is fast.

Associative containers Associative containers contain key/value pairs, providing access to each value using a key. The elements are sorted by the key. Usually implemented as a balanced binary tree.
y map provides access to elements using any type of key. This is a

generalization of the idea of accessing a vector with an integer subscript.


y multimap is the same as map, but allows a key to map into more than one

element. Ordered sets

The set containers keep the elements in them in order, and are usually implemented as balanced binary trees. They do not implement standard set operations (union, intersection, ...) as you might expect from the name.
y set orders the elements that are added to it. A set contains only one copy of

any value added to it.


y multiset is like set but allows more than one entry with the same value.

Container adapters These are based on other containers, and are used only to enforce access rules. Because there are special access restrictions, they have no iterators.
y y y stack allows only LIFO (Last In, First Out) access. queue allows only FIFO (First In, First Out) access. priority_queue always returns the element with the highest priority.

Specialized containers The following containers are specialized in some ways: specific data type, special utility routines, limited, but fast, implementations.
y string holds character strings - similar to vector<char>, but with many useful utility functions. bitset is a storage-efficient data structure for bits. By defining the bit

operations, it effectively implements set operations.


y valarray is an especially efficient implementation of arrays, but it doesn't

have all the standard container behavior.

C++ Notes: STL Vector, list, and deque operations


The sequence container template classes (vector, list, deque) have many common operations which makes it easy to learn and use them. Assume that T is some type (eg, int) and the following declarations:, where seq is one of the sequence containers (vector, list, or deque).

T e; seq<T> c, c1; seq<T>::iterator iter, iter2, beg, end; seq<T>::reverse_iterator riter; // beg, end could also be here int i, n; bool b;

Common sequence container constructors, functions, operators, etc


Result Method V LD Description

Constructors and destructors

seq<T> c; seq<T> c(n); seq<T> c(n, e); seq<T> c(beg, end); c.~seq<T>();
Size

V LD Creates an empty seq of T's. V - D Creates seq of n default values. V LD Creates seq of n copies of e. V LD Creates seq with elements copied from range beg..end. V LD Destroys all elems and frees memory.

i = c.size(); b = c.empty();
Altering

V LD Number of elements. V LD True if empty. Same as c.size()==0

c = c1; c[i] = e; c.at(i) = e; c.front() = e; c.back() = e; c.push_back(e); c.pop_back(); c.push_front(e); c.pop_front(); c.clear(); iter = c.assign(n, e); iter = c.assign(beg, end); iter2 = c.insert(iter, e); c.insert(iter, n, e);

V LD Assigns c1 to c. V - D Sets ith element. Subscripts from zero. V - D As subscription, but may throw out_of_range. V LD Same as c[0] = e. V LD Same as c[c.size()-1] = e. V LD Adds e to end of c. Expands c if necessary. V LD Removes last element of c. - LD Adds e to front of c. Expands c if necessary. - LD Removes first element of c. V LD Removes all elements. V LD Replaces existing elements with n copies of e. V LD Replaces existing elems with copies from range beg..end. V LD Inserts a copy of e at iter position and returns its position. V LD Inserts n copies of e starting at iter position.

c.insert(iter, beg, end);VLD Inserts all elements in range beg..end, starting at


iter position.

iter2 = c.erase(iter);

V LD Removes elem at iter and returns position of next elem.

iter = c.erase(beg, end);

V LD Removes range beg..end and returns position of next element.

Access

e = c.front(); e = c.back(); e = c[i]; e = c.at(i);

V LD First element. No range checking. V LD Last element. No range checking. V - D ith element. No range checking. V - D As subscription, but may throw out_of_range.

Iterators (operators apply to both forward and reverse iterators)

e = *iter; iter = c.begin(); iter = c.end(); riter = c.rbegin(); riter = c.rend(); ++iter; --iter; iter2 = iter + i; iter2 = iter - i;

V LD Dereference the iterator to get the value. V LD Returns iterator to first element. V LD Returns iterator to after last element. V LD Returns iterator to first (in reverse order) element. V LD Returns iterator to after last (in reverse order) element. V LD Preincrement iter to next elem. Prefer to postincrement. V LD Predecrement iter. Can also postincrement. Returns value. V - D Iterator i elements after iter. += assigment also defined. V - D Iterator i elements before iter -= assignment also defined.

Value-based functions ... Algorithms - implemented as member function. (a indicates non-member in <algorithm>) ...

C++ Notes: <string> class


Assume the following declarations:
string s, s1, s2; char c; int i, start, len, start1, len1, start2, len2, newSize; istringstream istr; // requires <sstream>

char* cs;

ostringstream ostr; // requires <sstream>

Methods and operators


Type Method Description

Constructors and destructors


string s; string s(s1); string s(cs);

Creates a string variable. Creates s; initial value from s1. Creates s; initial value from cs.

Altering
s1 = s2; s1 = cs; s1 = c; s[i] = c; s.at(i) = c;

Assigns s2 to s1. Assigns C-string cs to s1. Assigns char c to s1. Sets ith character. Subscripts from zero. As subscription, but throws out_of_range if i isn't in string. Concatenates s2 on end of s. Same as s += s2; Concatenates cs on end of s. Same as s += cs; Assigns s2[start..start+len-1] to s. Removes all characters from s

s.append(s2); s.append(cs); s.assign(s2, start, len); s.clear();

Access
cs = s.c_str(); s1 = s.substr(start, len); c = s[i]; c = s.at(i);

Returns the equivalent c-string. s[start..start+len-1]. ith character. Subscripts start at zero. As subscription, but throws out_of_range if i isn't in string.

Size

i = s.length(); i = s.size(); i = s.capacity();

Returns the length of the string. Same as s.length() Number of characters s can contain without reallocation. True if empty, else false.

b = s.empty();

i = s.resize(newSize, padChar);Changes size to newSize, padding with padChar

if necessary. Searching All searches return string::npos on failure.


i = s.find(c); i = s.find(s1); i = s.rfind(s1); i = s.find_first_of(s1);

Position of leftmost occurrence of char c. Position of leftmost occurrence of s1. As find, but right to left. Position of first char in s which is in s1 set of chars. Position of first char of s not in s1 set of chars. Position of last char of s in s1 set of chars. Position of last char of s not in s1 set of chars.

i = s.find_first_not_of(s1); i = s.find_last_of(s1); i = s.find_last_not_of(s1);

Comparison
i = s.compare(s1);

<0 if s<s1, 0 if s==s1, or >0 if s>s1.

i = s.compare(start1, len1, s1,Compares s[start1..start1+len1-1] to start2, len2);

s1[start2..start2+len2-1]. Returns value as above. The comparison operators work as expected.

b = s1 == s2 also > < >= <= !=

Input / Output
cin >> s; getline(cin, s); cout << s; istr.str(s);

>> overloaded for string input. Next line (without newline) into s. << overloaded for string output. Sets input string stream to s.

s = ostr.str();

Returns string generated in output string stream.

Concatenation
The + and += operators are overloaded to concatentate/append two strings.
s = s1 + s2; s += s2; s += cs; s += c;

And much more


And lots more: replace, insert, iterators, string streams, different parameter types, ...

C++ Notes: STL: <vector> class


The vector template class provides a form of dynamic array that expands at the end as necessary to accommodate additional elements. Assume that T is some type (eg, int) and the following declarations:
T e; vector<T> v, v1; vector<T>::iterator iter, iter2, beg, end; vector<T>::reverse_iterator riter; /* beg, end could also be here */ int i, n; bool b;

Common vector constructors, functions, operators, etc Result Method Description Constructors and destructors
vector<T> v; vector<T> v(n); vector<T> v(n, e); vector<T> v(beg, end);

L Creates an empty vector of T's. Creates vector of n default values. L Creates vector of n copies of e. L Creates vector with elements copied from range beg..end. L Destroys all elems and frees memory.

v.~vector<T>();

Size
i = v.size(); i = v.capacity();

L Number of elements. Max number of elements before reallocation. Implementation max number of elements. L True if empty. Same as v.size()==0 Sets capacity to n before reallocation

i = v.max_size();

b = v.empty(); v.reserve(n);

Altering
v = v1; v[i] = e; v.at(i) = e;

L Assigns v1 to v. Sets ith element. Subscripts from zero. As subscription, but may throw out_of_range. L Same as v[0] = e. L Same as v[v.size()-1] = e. L Adds e to end of v. Expands v if necessary. L Removes last element of v. L Removes all elements. L Replaces existing elements with n copies of e. L Replaces existing elements with copies from range beg..end. L Inserts a copy of e at iter position and returns its position. L Inserts n copies of e starting at iter position.

v.front() = e; v.back() = e; v.push_back(e);

v.pop_back(); v.clear(); iter = v.assign(n, e);

iter = v.assign(beg, end);

iter2 = v.insert(iter, e);

v.insert(iter, n, e);

v.insert(iter, beg, end);L Inserts all elements in range beg..end,

starting at iter position.


iter2 = v.erase(iter);

L Removes element at iter position and returns position of next element. L Removes range beg..end and returns position of next element.

iter = v.erase(beg, end);

Access
e = v[i]; e = v.at(i);

ith element. No range checking. As subscription, but may throw


out_of_range.

e = v.front(); e = v.back();

L First element. No range checking. L Last element. No range checking.

Iterators (operators apply to both forward and reverse iterators)


iter = v.begin(); iter = v.end(); riter = v.rbegin();

L Returns iterator to first element. L Returns iterator to after last element. L Returns iterator to first (in reverse order) element. L Returns iterator to after last (in reverse order) element. L Preincrement iter to next element. Use in both forward and reverse iterators normally. Can also postincrement. Returns value. L Predecrement iter. Can also postincrement. Returns value. Iterator i elements after iter. += assigment also defined. Iterator i elements before iter -= assignment also defined. L Dereference the iterator to get the value.

riter = v.rend();

++iter;

--iter;

iter2 = iter + i;

iter2 = iter - i;

e = *iter;

C++: STL: Iterators for vector


An iterator is used to move thru the elements an STL container (vector, list, set, map, ...) in a similar way to array indexes or pointers. The * operator dereferences an iterator (ie, is used to access the element an iterator points to) , and ++ (and -for most iterators) increments to the next element.

Iterating over a vector with subscripts


Passing over all the elements of a vector is simple, and is usually done using subscripts instead of an iterator. The main advantage of an iterator is that it can be used by many of the functions in <algorithms>. Subscripts provide an easy, familiar way to access vector elements in much the way that array elements would be accessed.
//--- Iterating over vector with subscript. vector<int> v; . . . for (int i=0; i<v.size(); i++) { cout << v[i] << endl; }

Iterators are similar to pointers


In fact, vectors iterators are usually implemented as pointers. If you feel comfortable using pointers to iterate over an array, you will feel comfortable using an iterator. For review, here is an example of one way to use a pointer to iterate over an array. Note that &a[n] is the address of the element after the last value currently in the array.
//--- Iterating over array with pointer. int a[100]; int n = ...; // current number of elements

We could loop over this array like this.


for (int* p = &a[0]; p != &a[n]; p++) { cout << *p << endl; }

or
for (int* p = a; p != a+n; p++) { cout << *p << endl; }

Iterating over a vector with an iterator


//--- Iterating over vector with iterator. vector<int> v; . . . for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { cout << *it << endl; }

Why use iterators when subscripts work so well


There are several reasons to use iterators.
y

Not always possible. Subscripts can not be used on most of the containers (eg, list and map), so you must use iterators in many cases. Flexible. It is easily to change underlying container types. For example, you might decide later that the number of insertions and deletions is so high that a list would be more efficient than a vector. Member functiuons. Many of the member functions for vector use iterators, for example, assign, insert, or erase. Algorithms. The <algorithm> functions use iterators.

C++ Notes: STL Iterators - Introduction


An iterator is used to move thru the elements an STL container (vector, list, set, map, ...). Each type of container has an underlying implementation (eg, vector or linked list) for which there is a "natural" way to get to the next element (incrementing a pointer for vectors, following the next or prev pointer for lists, ...). Iterators are defined within each of the container classes to work specifically with that type of container. An iterator may be as simple as a pointer, or may be defined as a class. Regardless of the implementation, the same operators are defined.

Why iterators are so useful


y

Uniform access. Iterators can be used with all containers, and also with arrays. If you start with a vector, but later decide a list would be more efficient, iterator code will not have to change. Algorithms for all containers. The algorithms in the STL <algorithm> library work on iterators. This means that they operator on the standard containers. New containers can use existing algorithms. Not only do the library algorithms work with the STL containers, but they will work with any new

data structures (containers) you define, if iterators are defined for the new container. New algorithms can use existing containers. When a new algorithm is written based on iterators, it applies to all existing containers for which there are iterators. Iterators for arrays. In addition to the STL containers, pointers to arrays act as iterators.

Kinds of iterators
There are several types of iterators, but most often you will use either bidirectional (list) or random iterators (vector, deque, array), which have all the operations of bidirectional iterators and more. Because so many algorithms only require bidirectional access, they can be applied to all containers. Some of the other types are as follows. Forward iterators move only forward over the elements of a container. Input iterators that move only in a forward direction, at most one time, and can only access values. Output iterators that move only in a forward direction, at most one time, and can only write values. Constant iterators To preserve constantness there are both non-constant and constant versions of iterators

C++ Notes: STL: Iterator Operators


Assume C is a container class, containing elements of type T.
bool b; int i; T value; C::iterator it, it1, it2;

Result

Operator

Description

Operators for most iterators. Vectors, lists, arrays, ....


value = *it; ++it; it++; it1 = it2;

Use dereference (*) op to get/set value. Points to next element. Value after update. Points to next element. Value before update. Assignment

b = b =

it1 == it2; Equality comparison. it1 != it2; Inequality.

Additional operators for bidirectional iterators. Vectors, lists, arrays, ...


--it; it--;

Predecrement. Postdecrement. May be less efficient than predecrement.

Additional operators for random-access iterators. Vectors and arrays, but not lists.
it += it -= it1 = it1 = i; i; it2 + i; it2 - i;

Increments it by i positions. Decrements it by i positions. Increments it by i positions. Decrements it by i positions. Returns reference to ith element after it. Comparison.

value = it[i]; b = b = b = b = it1 < it2;

it1 <= it2; Comparison. it1 > it2;

Comparison.

it1 <= it2; Comparison.

C++: STL: Iterators for vector


An iterator is used to move thru the elements an STL container (vector, list, set, map, ...) in a similar way to array indexes or pointers. The * operator dereferences an iterator (ie, is used to access the element an iterator points to) , and ++ (and -for most iterators) increments to the next element.

Iterating over a vector with subscripts


Passing over all the elements of a vector is simple, and is usually done using subscripts instead of an iterator. The main advantage of an iterator is that it can be used by many of the functions in <algorithms>. Subscripts provide an easy, familiar way to access vector elements in much the way that array elements would be accessed.
//--- Iterating over vector with subscript.

vector<int> v; . . . for (int i=0; i<v.size(); i++) { cout << v[i] << endl; }

Iterators are similar to pointers


In fact, vectors iterators are usually implemented as pointers. If you feel comfortable using pointers to iterate over an array, you will feel comfortable using an iterator. For review, here is an example of one way to use a pointer to iterate over an array. Note that &a[n] is the address of the element after the last value currently in the array.
//--- Iterating over array with pointer. int a[100]; int n = ...; // current number of elements

We could loop over this array like this.


for (int* p = &a[0]; p != &a[n]; p++) { cout << *p << endl; }

or
for (int* p = a; p != a+n; p++) { cout << *p << endl; }

Iterating over a vector with an iterator


//--- Iterating over vector with iterator. vector<int> v; . . . for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { cout << *it << endl; }

Why use iterators when subscripts work so well


There are several reasons to use iterators.

Not always possible. Subscripts can not be used on most of the containers (eg, list and map), so you must use iterators in many cases. Flexible. It is easily to change underlying container types. For example, you might decide later that the number of insertions and deletions is so high that a list would be more efficient than a vector. Member functiuons. Many of the member functions for vector use iterators, for example, assign, insert, or erase. Algorithms. The <algorithm> functions use iterators.

Random Access
Vector iterators are random-access.

C++: Containers: Example 1 Using Array


One of three contrasting examples of solving the problem with increasingly powerful tools: using arrays, then vectors, then stacks.
// Read words and print them in reverse order. // Variation 1: Fixed array sizes, use new to copy word.

// Fred Swartz 2001-11-08, 2001-12-04 #include <iostream> #include <cstring> using namespace std; int main() { char *allwords[1000]; // array of POINTERS to char strings char word[500]; int n = 0; // input buffer for longest possible word. // count of number of words. // for cin, cout // for strlen, strcpy

// read words/tokens from input stream while (cin >> word) { allwords[n] = new char[strlen(word)+1]; // allocate space strcpy(allwords[n], word); n++; } // copy word to new space

cout << "Number of words = " << n << endl; // write out all the words in reverse order. // // // // The dynamically allocated space is freed after the word is printed, and the pointer is set to NULL. but it's a good, safe practice. This isn't necessary here because the program immediately terminates,

for (int i=n-1; i>=0; i--) { cout << allwords[i] << endl; // print the word delete [] allwords[i]; allwords[i] = NULL; } return 0; }//end main // free space // remove pointer

The big problem with this program is that is is subject to buffer overflow bugs -words larger than 499 characters or more than 1000 words will simply overflow the arrays until something so serious happens that the program can't continue running. Let's hope it didn't overwrite one of you open output file buffers, for example.

C++: Containers: Example 1 Using a Vector


One of three contrasting examples of solving the problem with increasingly powerful tools: using arrays, then vectors, then stacks. The great advantage of using vectors and strings instead of arrays is that there are no size limits - strings and vectors expand as needed.
// Read words and print them in reverse order. // Variation 3: Using vector and string // Fred Swartz 2001-11-08, 2001-12-04 #include <iostream> #include <vector> #include <string> using namespace std;

int main() {

vector<string> allwords; // vector of strings to hold words string word; // input buffer for each word.

//--- read words/tokens from input stream while (cin >> word) { allwords.push_back(word); }

int n = allwords.size(); cout << "Number of words = " << n << endl; //--- write out all the words in reverse order. for (int i=n-1; i>=0; i--) { cout << allwords[i] << endl; } return 0; }//end main

C++ Notes: Random Numbers


Random positive integers - rand()
The rand function returns a "random" positive integer from 0 to a large value (at least 32,767) every time it is called. To scale the value into the range you want, use the mod (%) operator and addition. For example to generate a random number in the range 1 to 10 and assign it to r:
#include <ctime> #include <cstdlib> . . . srand(time(0)); . . . r = (rand() % 10) + 1; // Initialize random number generator. // For time() // For srand() and rand()

Pseudo-random numbers and seeds - srand(...)


The sequence of numbers returned by rand() are called random because they satisfy statistical tests for randomness, eg, uniform distribution, coorelation between sequential numbers is zero, no apparent patterns). But of course they

really can't be truly random (whatever that means) because computers are deterministic. Therefore they are more properly called pseudorandom numbers. For a given seed (starting value), the sequence of numbers that rand() returns will always be the same. Because the starting point for the pseudorandom sequence can easily be varied (see below) and because the sequence is very long (perhaps billions before the sequence repeats), these pseudorandom numbers are as good as random. Having the same sequence generated each time can be useful for debugging, but it isn't very useful when you're actually using the program. To generate a different random sequence, it's necessary to set the seed that starts the sequence. The srand() function takes a positive integer parameter which tells where to start the sequence.
srand(2345);

The above call sets the initial seed to 2345. However, this still isn't very useful. If you want to have what appears to be a truly random sequence each time you run the program, you need to start with a different seed each time.

Using the time as a seed - srand(time(0))


The standard way to start with a different initial value, the seed, is to use the current time as a seed. Use the time() function as follows:
srand(time(0)); // Initialize random number generator.

at the beginning of the program to initialize the random seed. time(0) returns the integer number of seconds from the system clock. This will almost always be a different value.

Include files
Use the following include files.
#include <ctime> #include <cstdlib> // For time() // For srand() and rand()

C++ Notes: Example - Shuffle Array


Suppose you want to randomize an array of 52 values, from 0 to 51 with no repeats, such as you might want for a deck of cards. First fill the array with the values in order, then go thru the array and exchange each element with a randomly chosen element in the range from itself to the end. It's possible that an element will be exchanged with itself, but there is no problem with that.
1 // File : misc/random/deal.cpp - Randomly shuffle deck of cards. // Illustrates : Shuffle algorithm, srand, rand.

2 // Improvements: Use classes for Card and Deck. // Author : Fred Swartz 2003-08-24, shuffle correction 2007-01-18 3 // Placed in the public domain. 4 #include <iostream> 5 #include <cstdlib> // for srand and rand // for time 6 #include <ctime> using namespace std; 7 8 int main() { int card[52]; 9 int n; srand(time(0)); 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 } } return 0; // array of cards; // number of cards to deal // initialize seed "randomly"

for (int i=0; i<52; i++) { card[i] = i; // fill the array in order } while (cin >> n) { //--- Shuffle elements by randomly exchanging each with one other. for (int i=0; i<(52-1); i++) { int r = i + (rand() % (52-i)); // Random remaining position. int temp = card[i]; card[i] = card[r]; card[r] = temp; } //--- Print first n cards as ints. for (int c=0; c<n; c++) { cout << card[c] << " "; // Just print number } cout << endl;

C++ Notes: Example - Deal Cards


This simple example shows how to use srand() and rand(), as well as showing some simple classes. main.cpp
1 2 // Summary: Reads a number and then "deals" that many cards. 3 // Illustrates: Random methods (strand and rand). 4 #include <iostream> 5 #include <cstdlib> 6 #include <ctime> #include <string> 7 using namespace std; 8 #include "card.h" 9 #include "deck.h" 10 //================================================== main 11 int main() { int numOfCards; // Input number for how many cards to deal. 12 srand(time(0)); // Initializes random "seed". 13 Deck deck; 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } return 0; }//end main while (cin >> numOfCards) { deck.shuffle(); cout << "Your hand is: "; for (int cardNum=0; cardNum<numOfCards; cardNum++) { Card c = deck.dealOneCard(); string suit = c.getSuit(); string face = c.getFace(); cout << face << suit << " "; } cout << endl; // cardplay-1/main.cpp -- Very simple program to deal cards. // Fred Swartz 2004-11-22

33 34 35 36

card.h
// cardplay-1/card.h // Fred Swartz 2004-11-22 2 #ifndef CARD_H #define CARD_H 3 1 4 class Card { public: 5 Card(); Card(int card); 6 string getSuit() const; 7 string getFace() const; 8 private: 9 int _card; // range 0..51 10 static const string CARD_FACES[]; 11 static const string CARD_SUITS[]; }; 12 13 #endif 14 15 16 17 18 19 20

card.cpp
1 2 #include <string> 3 using namespace std; 4 #include "card.h" 5 6 //================================================= static constants const string Card::CARD_FACES[] = {"A", "2", "3", "4", "5", "6", "7" 7 , "8", "9", "10", "J", "Q", "K"}; const string Card::CARD_SUITS[] = {"S", "H", "C", "D"}; 8 9 // cardplay-1/card.cpp // Fred Swartz 2004-11-22

10 //================================================= Constructor Card::Card() { _card = 0; // TODO: Should initialize to Joker. 12 } 13 11 14 //================================================= Constructor 15 Card::Card(int card) { _card = card; 16 } 17 18 //================================================== getFace 19 // Action : Returns face value of card. : a string representing card face: "A", "2", ... 20 // Returns 21 string Card::getFace() const { return CARD_FACES[_card%13]; 22 }//end getFace 23 24 //================================================== getSuit 25 // Action : Returns suit of a card value. // Returns : a string "S" (Spades), "H", (Hearts), 26 // "C" (Clubs), or "D" (Diamonds). 27 string Card::getSuit() const { 28 return CARD_SUITS[_card/13]; 29 }//end getSuit 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

deck.h

// cardplay-1/deck.h // Fred Swartz 2004-11-22 2 #ifndef DECK_H #define DECK_H 3 1 4 class Deck { public: 5 Deck(); Card dealOneCard(); 6 void shuffle(); 7 private: 8 Card _cards[52]; 9 int _nextCardIndex; }; 10 11 #endif 12 13 14 15 16 17

deck.cpp
1 2 #include <cassert> 3 #include <cstdlib> 4 #include <string> #include <algorithm> 5 using namespace std; 6 #include "card.h" 7 #include "deck.h" 8 //=========================================== Constructor 9 Deck::Deck() { // Initialize the array to the ints 0-51 10 for (int i=0; i<52; i++) { 11 _cards[i] = Card(i); } 12 shuffle(); 13 _nextCardIndex = 0; // index of next available card } 14 15 16 17 Card Deck::dealOneCard() { //================================================== deal // Action : Returns random Card. // cardplay-1/deck.cpp // Fred Swartz 2004-11-22, shuffle modified 2007-01-18

18 19 20 }

assert(_nextCardIndex >= 0 && _nextCardIndex<52); return _cards[_nextCardIndex++];

21 //================================================ shuffle : Shuffles Deck. 22 // Action // Returns : none. 23 void Deck::shuffle() { // Shuffle by exchanging each element randomly 24 for (int i=0; i<(52-1); i++) { 25 int randnum = i + (rand() % (52-i)); swap(_cards[i], _cards[randnum]); 26 } 27 _nextCardIndex = 0; 28 } 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

S-ar putea să vă placă și