C++ CLASS PDF

110
CSCI 240 Lecture Notes -- Part 1 A program = data + instructions Data: there are several data types (numbers, characters, etc.) each individual data item must be declared and named each individual data item must have a value before use initial values come from o program instructions o user input o disk files program instructions can alter these values original or newly computed values can go to o screen o printer o disk Instructions: for data input (from keyboard, disk) for data output (to screen, printer, disk) computation of new values program control (decisions, repetition) modularization (putting a sequence of instructions into a package called a function) The C++ Language - Introduction C++ is a superset of the C programming language. Any C code is also C++ code, but some C++ code is not C. Much of what you learn in this course could also be used in a C program. There are lots of new features and capabilities in C++. We will learn some of them (but far from all of them) in this course. The C++ Language is made up of keywords/reserved words (if, while, int, etc.) symbols: { } = | <= ! [ ] * & (and more) programmer-defined names for variables and functions

Transcript of C++ CLASS PDF

Page 1: C++ CLASS PDF

CSCI 240 Lecture Notes -- Part 1

A program = data + instructions

Data:

there are several data types (numbers, characters, etc.)

each individual data item must be declared and named

each individual data item must have a value before use

initial values come from

o program instructions

o user input

o disk files

program instructions can alter these values

original or newly computed values can go to

o screen

o printer

o disk

Instructions:

for data input (from keyboard, disk)

for data output (to screen, printer, disk)

computation of new values

program control (decisions, repetition)

modularization (putting a sequence of instructions into a package called a function)

The C++ Language - Introduction

C++ is a superset of the C programming language. Any C code is also C++ code, but some C++

code is not C. Much of what you learn in this course could also be used in a C program. There

are lots of new features and capabilities in C++. We will learn some of them (but far from all of

them) in this course.

The C++ Language is made up of

keywords/reserved words (if, while, int, etc.)

symbols: { } = | <= ! [ ] * & (and more)

programmer-defined names for variables and functions

Page 2: C++ CLASS PDF

These programmer-defined names:

1 - 31 chars long; use letters, digits, _ (underscore)

start with a letter or _

are case-sensitive: Num is different than num

should be meaningful: studentCount is better than s or sc

C++ Data Types

Each data item has a type and a name chosen by the programmer. The type determines the

range of possible values it can hold as well as the operations that can be used on it. For

example, you can add a number to a numeric data type, but you cannot add a number to

someone's name. (What would "Joe" + 1 mean?)

Commonly-used data types

int (integer numbers)

no decimal point, comma, or $

leading + - allowed

range (on our system): about plus or minus 2 billion

int constants are written as: 1234 -3 43

To make a variable that your program can use, you must declare it.

Declaration Examples:

int x; //gives type and name; no value yet

int x, y; //declares 2 ints; no values yet

int population = 16000; //declares & sets the initial value

So you could picture the results of the last two lines as:

x

??

y

??

population

16000

Note: It is critically important that variables have values before they are used in a program. In

the examples above, x and y have no values even though they have been declared. More

accurately, they do have values but those values could be anything between -2 billion and +2

billion. Before you use x or y you would have to give them values.

float and double (floating point or real numbers)

Page 3: C++ CLASS PDF

These data types are also commonly referred to as real variables (following mathematics usage)

when it is not important which one we mean and we don't want to always have to say "float or

double".

has decimal point

leading + - allowed

no comma, $

range (float) plus or minus 10 to 38th (limited to ~ 6 significant digits)

range (double) plus or minus 10 to 308th (limited to ~12 significant digits)

floating point constants are written as 3.08 -32.0 0.15 9.3e7 (9.3 times 10 to the 7th

power)

Declaration Examples:

float pi = 3.1416; //declares, names, sets init value

double x = 3.5, //note comma here

y = 1245.6543; //can use > 6 digits

OR

double x = 3.5;

double y = 1245.6543;

float big = 5.28e3; // exponential notation: 5280

char (single characters)

can hold just one character

char constants are written with single quotes: 'a'

Declaration examples:

char ch;

char choice = 'q';

string (character "strings" or sequences)

can hold 0 to many characters

string constants are written with double quotes: "Hello, world"

Declaration Examples:

string s;

string MyName = "Amy";

Arithmetic Operations and Examples

Page 4: C++ CLASS PDF

The arithmetic operators are:

+ addition

- subtraction or unary negation (-5)

* multiplication

/ division (see special notes on division below)

% modulus division -- integer remainder of integer division

Note: there is no exponentiation operator.

Special Note on Division:

Recall 3rd grade division:

3 R1

_______ 4 is divisor

4| 13 13 is dividend

12 3 is quotient

1 1 is remainder

In C++, a division with 2 int operands has an int resulting value:

13/4 --> 3 // int quotient

13%4 --> 1 // int remainder

But with 1 or 2 float/double operands, the resulting value is a float or double:

So: 13.0 / 4 --> 3.25

13 / 4.0 --> 3.25

Be aware. Forgetting this can easily cause an error.

Arithmetic Expressions are formed by operands and operators. Operands are the values used,

operators are the things done to the numbers. Parts of arithmetic expressions are often grouped

by () for clarity or to affect the meaning of the expression:

//declare and initialize some variables

int x = 11,

y = 2;

double realnum = 2.0;

expression value notes

x + y 13

x * y 22

x * y + x 33

Page 5: C++ CLASS PDF

x - y 9

-x + y -9 unary negation:

"minus x"

x / y 5 int since both ops are

int

x % y 1 rem when x divided

by y

x / realnum 5.5 one op is real so

result is real

Note: The expressions above by themselves are not valid C++ statements - they would be part of

a statement. But each expression (if it is in a valid C++ statement) has a value.

Also - the spaces written between operands and operators are not required by C++, but do

improve legibility.

More complex expressions are evaluated by C++ using Rules of Precedence (like algebra)

1. sub-expressions in ()

2. unary -

3. * and / - left to right

4. + and - - left to right

In the examples below, the red numbers show what operation is done as C++ evaluates and

expression:

3 + 5 * 4 / 2 =

3 + 20 / 2 =

3 + 10 =

13

But () can be used to change this (or to make expression more readable)

(3 + 5) * 4 / 2 =

8 * 4 / 2 =

32 / 2 =

16

Assignment Statement/Operation

The symbol "=" is a command. It means: "evaluate the expression on the right and store this

value in the variable on the left"

In general:

Page 6: C++ CLASS PDF

someVariable = some expression;

Examples:

x = y * 3;

x = x + y;

Notice that there is always exactly one variable on the left to receive the value of the expression

on the right.

This is NOT like algebra; it is NOT an equation.

Algebra: x = 1 (T or F for a given x)

C++: x = 1; // store 1 into x

Algebra: x = x + 1 (false for all x)

C++: x = x + 1; //find x+1; result in x

More Assignment Examples

//declare variables

double dAns, x, y;

int iAns, i, j;

//assign values via numeric literals

i = 5;

j = 8;

x = 4.0;

y = 1.5;

//assign values from complex expressions

i = i + 2; // 7

dAns = x / y; // 2.666..

iAns = j/2*2; // 8/2*2 = 4*2 = 8

iAns = 8/5; // 1

iAns = 8 % 5; // 3

iAns = 5 / 8; // 0

iAns = 5 % 8; // 5

Page 7: C++ CLASS PDF

i = x/y; // 2

The last needs an explanation. the value of the expression x/y is 2.666.. but i is an int variable

and so cannot hold the .666 part. C++ simply drops it (truncates it) and so only the whole

number part of the value is stored into i.

Notes:

1. All variables in expressions on the right must have defined values or you get random results.

This is an example of what I meant before when I said variables must have values before they are

used.

int x, y; // declared; no init values

x = y + 1; // ??? in x

int x; // declared; no init value

int y = 2; // declared; given a value

x = y + 1; // x has value 3.

// It is Ok if x has no value before this

Inadvertent use of un-initialized variables is one of the most common causes of program

errors. Learn to be very careful about this.

2. You can do multiple assignments on one line:

x = y = z = 3; //all get value 3

3. Naming Variables: use meaningful names that explain themselves to reader of the program.

(You, me, the maintainer of the program after you go to your next job...)

No Yes

s score

cs class_size ClassSize classSize

cav class_average ClassAvg classAvg

num student_count NumStudents numStudents

stnum student_id StudentID studentID

Sample C++ program

#include <iostream> // a.

#include <iomanip>

using namespace std; // b.

int main() // c.

{

int num1, num2, sum; // d.

Page 8: C++ CLASS PDF

num1 = 10; // e.

num2 = 15;

sum = num1 + num2; // f.

return 0; // g.

}

Notes:

a. C++ standard libraries that must be included at the beginning of C++ programs

b. So the standard (std) objects can be used from the included libraries

c. program name is always main with int (which stands for integer) before it

d. num1, num2 and sum are the data items (which we often call variables)

e. num1 and num2 get initial values from instructions via assignment (the = is the

assignment command)

f. sum gets its value from an arithmetic expression

g. return an integer: main "promised" to provide an integer "answer". In this program (and

usually) the "answer" doesn't matter. But we have to keep the promise, so we just return

0. We will discuss this further later.

h. on any line, typing // turns the rest of the line into a comment. A comment is not "seen"

by the computer - it is not part of the program. It is used by human readers of the program

to explain something in the program.

i. there are no program control or modularization statements

j. this program has no input or output

k. notice the semicolons - where they are and aren't

Output

We will use cout for output. cout's task is to display information on the screen.

The simplest form of cout is:

cout << "Hello";

You can output (for display) several values, including the values calculated and stored into

variables, by stringing them together connected by "<<":

cout << "The average is: " << avg; // avg is a variable

cout << "The temperature is: "

<< temp

<< " and the rainfall today was: "

<< rainAmt;

Page 9: C++ CLASS PDF

Note that I have put each item to display on a separate line for clarity. This is both legal and is

usually desirable if you have more than 2 values.

There are a couple of special techniques to position cursor before or after you display characters,

using an escape sequence. (A \ followed by a char. For example, \n is the newline escape

sequence.) Note that this is a backslash.

cout << "\nhello"; //moves to next line, shows hello

cout << "hello\n"; //shows hello, then goes to new line

cout << "hel\nlo"; //shows hel, then lo on next line

Note: to display a single \, you must code two: \\

cout << "the \\ char is special";

This shows: "the \ char is special"

Also, there is an alternate way to move the cursor to the beginning of a new line: the special

value "endl":

cout << endl << "Some stuff" << endl;

cout << "\nSome stuff\n";

Both will first move the cursor to a new line, then display "Some stuff" and then move the cursor

to the next line.

Note that if you don't include endl or \n, all your output will be on one line:

cout << "The average is: " << avg; // avg is a variable

cout << "The temperature is: "

<< temp

<< " and the rainfall today was: "

<< rainAmt;

This will display something like this:

The average is 77.5The temperature is: 78 and the rainfall today was: 1.32362

Input

We will use cin for input. cin's task is to accept user input and store it into variables.

Note that

Page 10: C++ CLASS PDF

the C++ language itself has no defined I/O functions or commands; just a standard

library that can accomplish I/O. We could use other libraries, including those from the C

language or those developed by others.

most often when we want user input, we will need to do two things:

1. tell the user what to enter and then

2. get the value entered.

So typically we will do something like this:

int i; // a.

...

cout << "Enter the temperature: "; // b.

cin >> i; // c.

Notes:

a. we declare an int to hold the value to be typed in

b. we tell (prompt) the user to enter something

c. we get the value that is entered by the user

d. when a cout occurs in a program, the characters are displayed immediately on the screen

e. when cin occurs in a program, the program waits for user to type in a value and press

<Enter>.

f. the << and >> are meant to suggest the direction the data is going:

o for cout, the prompt is going from the string constant through cout to the screen

o for cin the data entered is coming in from the keyboard, through cin, to the

variable i

g. for numeric input with cin, if the user types a non-number like "w" or "23e" then results

are unpredictable. (For a double or float, "2e5" is ok - it's the same as 200000)

h. the variable receiving the value from cin must be declared (of course) but need not have

an initial value. Any previous value in that variable is lost.

i. details about formatting numeric output (number of decimal places, etc.) will be covered

later.

j. cin "understands" the different data types we have covered. So you can do cin with int,

float, double, char, and string. But you must ensure that the data item getting the value is

compatible with what the user is asked to enter. You can't store "jim" into an int, for

example. You can store the value 4 into a double, however.

Program Error Types

1. Compile error - invalid C++ statements; so your code cannot be translated to machine

language. Examples:

1. integer x, y;

2. num1 = num1 + num2

Page 11: C++ CLASS PDF

In example 1 you must use int, not integer. In example 2 the ; at the end of the line is missing

2. Link error: one of the functions used in the program is not found when it's time to run the

program. In the program above, there are no such functions, but we could encounter this problem

later.

3. Run-time error - program does not run to completion. Example:

divide by zero: set x = 10 and y = 0 and then try to compute x/y

4. Logic error - program runs to completion, but the results are wrong. Example:

you really wanted x - y but coded x + y

in calculating an average, you use the wrong number to divide

Fixing 3 and 4 is called debugging.

You must learn to understand that the computer did what you said, not necessarily what you

meant.

CSCI 240 Lecture Notes - Part 2

Typecasting

Suppose you accumulate 5 integer test scores in sum. And suppose that sum is 104.

Then to write an expression that is the average, you could write:

sum/5

and the value of that expression is 20 (a bit wrong - we lost the decimal fraction .8)

But if you write:

sum/5.0

Then the value of the expression is 20.8 (correct)

Page 12: C++ CLASS PDF

Suppose you don't know how many numbers there are when you write the program. So you can't

use 5 or 5.0 because there might be 21 or 3 or 97 numbers. You could use a variable to hold the

count of numbers: declare int count and write some instructions that will give it the proper value

before you want to calculate the average. We'll see how to do this soon.

You can't write "count.0" to force it to be real (like we wrote 5.0 above). C++ simply does not

allow this.

You must use a typecast - to tell C++ that it should temporarily use the variable as if it were

some other data type. To do this, put the name of the data type you want in parenthesis before the

variable name. It will not change the variable type or value permanently, but it will force C++ to

temporarily treat the variable as a different type:

(double) sum/count // or

sum /(double)count

(It is true that we could avoid the need to type cast here by declaring count to be double instead

of int. But there are other situations where you will either want or need to do this.)

You cannot do just any typecast: (char) sum would not make sense and would not compile.

More on Assignment Statements

Common shortcuts for altering the value of a variable:

1. Add a value to a variable:

var += value_to_add; //var = var + value_to_add

x += 4; //add 4 to x

2. Multiply a variable by a value:

var *= val_to_mult_by //var = var * val_to_mult_by

x *= 4; //multiply x by 4

3. Same for / and - and %

Examples:

int n = 5;

double x = 3.5,

y = 2.0;

n += 2; // now n is 7

Page 13: C++ CLASS PDF

x *= y; // now x is 7.0

x *= y + 2; // mult x (7.0) by (y+2) so x is 28.0

Note that the last line is equivalent to:

x = x * (y + 2);

4. Incrementing and decrementing by 1 is quite common; so there is a special even shorter

notation:

i++; means "increment i by 1"

n--; means "decrement n by 1"

So:

i++; i += 1; i = i + 1; // all the same

j--; j -= 1; j = j - 1; // all the same

Example: use the new notations to accumulate (i.e. add up a series of values) and also to count

the number of values :

int sum = 0; //initializing the sum to 0 is vital

int count = 0; //ditto for count

int val; //no need to initialize this

double average; //or this

cout << "\nEnter 1st number: ";

cin >> val;

sum += val;

count++;

cout << "\nEnter 2nd number: ";

cin >> val;

sum += val;

count++;

cout << "\nEnter 3rd number: ";

cin >> val;

sum += val;

count++;

//Now calculate an average...

Page 14: C++ CLASS PDF

average = (double) sum/count;

Note the reuse of val - there is no need for a new variable for each new value, since we only need

the sum.

Also - in some books or code you will see the notation ++i; This is also legal but is slightly

different in some contexts. We will not cover this variation or use it in this course.

Example: Suppose we want to print squares of first few integers:

int i = 1;

cout << i*i; //note use of expression

i++;

cout << i*i; //now i = 2; displays 4

i++;

cout << i*i; //now i = 3; displays 9

...

Example: Suppose we want to print squares of first few odd numbers:

int i = 1;

cout << i*i; //i is 1; displays 1

i += 2;

cout << i*i; //i is 3; displays 9

i += 2;

cout << i*i; //i is 5; displays 25

...

Formatted Numeric Output Using cout

All the examples so far that have displayed numeric output have used the simplest possible

method:

cout << num;

This will not always display the number in the way you would like. Often you want to control

the field width (the number of display columns that the number takes up) or the number of

significant figures, or the number of decimal places. cout has a number of moderately awkward

ways to control these things.

Page 15: C++ CLASS PDF

First, in order to use them you must #include <iomanip>

Then, in the particular cout operation, you include one or more of several special modifiers to the

command.

setw( size )

sets a MINIMUM width

size - must be an integer value

must #include <iomanip>

the width is only valid for the variable that is printed

int num1 = 1234;

int num2 = 56789;

cout << setw(6) << num1;

_ _ 1 2 3 4

cout << setw(6) << num1 << setw(6) << num2;

_ _ 1 2 3 4 _ 5 6 7 8 9

setprecision( num )

does 1 of 2 things:

1. If setprecision is used by itself, the value num specifies the total number of digits

to display.

2. If setprecision is used in conjunction with the fixed flag, the value num specifies

the number of digits to display after the decimal point

num - must be an integer value

the value is rounded up when it is displayed

the precision stays set until it is changed

must #include <iomanip>

double val = 123.456;

cout << setprecision(5) << val;

123.46

Stream Format Flags

There are various flags that can be set to specify the kind of formatting to be performed.

To set a flag:

setiosflags( ios:: flag ) -or- setiosflags( ios::flag1 | ios:: flag2 )

Page 16: C++ CLASS PDF

-or- cout << flag ...

Floating Point Format Flag

There are two ways to format floating point numbers:

1. fixed -- print with a fixed number of digits after the decimal point

2. scientific -- print in scientific notation

These will stay set until changed.

double y = 50.0512;

cout << setiosflags( ios:: fixed ) << setprecision(2) << y;

50.05

cout << setiosflags( ios:: fixed ) << setprecision(2);

cout << y;

50.05

cout << fixed << setprecision(2) << y;

50.05

cout << fixed << setprecision(2);

cout << y;

50.05

Justification

There are three ways to justify numbers:

1. left -- left justify the number

2. right -- right justify the number (default)

3. internal -- for a signed number, the sign is left justified and the number is right justified

These will stay set until changed.

The output of the following program is shown below, assuming the user enters 3.14159, with

notes about each output line.

#include <iostream>

Page 17: C++ CLASS PDF

#include <iomanip>

using namespace std;

int main()

{

double d;

double quo = 100.0/3.0;

cout << "100.0/3.0 as double is: " << quo; // line A

cout << "\nEnter a floating point number: ";

cin >> d;

cout << "Num in native format: " << d; // line B

cout << "\nW= 5 p = 3: |" << setw(5)

<< setprecision(3)

<< d << "|"; // line C

cout << "\nW= 5 fixed p = 3: |" << setw(5)

<< fixed

<< setprecision(3)

<< d << "|"; // line D

return 0;

}

Line A: "100.0/3.0 as a double is: 33.3333" Note that with no special formatting commands, 6

figures are displayed, although more could be: 33.3333333... If the division had been 100.0/2.0

the result would have been 50 (not 50.0000).

Line B: "Num in native format: 3.14159" Again, 6 figures display. If you had entered more

digits, still only 6 would display. If you had entered fewer, fewer would display.

Line C: "W= 5 p = 3: | 3.14|" Here we first use the I/O format manipulators. Before we send the

value (num) to display, we tell cout how to display it.

setw() tells cout how many display positions to use for the value. If the value needs more space,

it will just take it. If the number requires less space, it will be padded with blanks. You can

control left- or right-justification with left or right. See the below. In this example, notice that

there is a space before the 3.

Note that you must use setw() for each numeric value you output, even if the width is the same:

cout << setw(5) << num1 << setw(5) << num2;

setprecision() tells cout how many significant figures to display. Here we see 3.14 which is just 3

significant figures. If we had specified setprecision(4) we would see 3.142. (The value is

rounded.)

Page 18: C++ CLASS PDF

Line D: "W= 5 fixed p = 3: |3.142|" Here, the fixed tells cout that the precision applies to the

number of decimal places, so we see 3 decimal places. Note that all 5 width spaces are used up,

so there is no space before the 3.

Unlike setw(), once setprecision() and fixed have been set, they retain their values (or settings)

until you change them.

Field Justification and blank padding: left and right

If the field width (setw()) is larger than the number of characters needed, the data will display

either all the way to the left or all the way to the right within the field, padded or filled with

leading or trailing blanks. Numeric values will display to the right (right-justified) and strings to

the left (left-justified). You can alter this behavior using the left and right manipulators:

cout << "xx" << right << setw(20) << "Jim Henry" << "xx";

cout << "\nyy" << setw(10) << left << quo << "yy";

will display:

xx Jim Henryxx

yy3.333 yy

Control Structures

So far all programs execute from first to last instruction (in sequence top to bottom). So we have

one pattern of execution so far:

1. Sequence

To write complex programs, we need to alter the flow of execution.

We will need only 3 new patterns:

2. decide which block of instructions to execute

3. repeat a set of instructions (also called looping)

4. branch to a separate group of instructions and return (or function calling - which we will

study in detail later).

Any program - no matter how complex - can be written with just the first three patterns:

sequence, repetition, decision.

Writing programs using these three patterns together is called structured programming.

Page 19: C++ CLASS PDF

Branch and return is nice and has real advantages in complex programs, but the first three are

sufficient for any program.

Conditional Expressions

Both looping and decision statements depend on a conditional expression, which is a logical

expression that is either true or false.

The condition is enclosed in parenthesis (there is one exception - the for construct).

It usually uses a relational operator.

== equality

!= not equal

> greater than

< less than

>= greater than or equal

<= less than or equal

!> not greater than

!< not less than

(By the way, => =< >! will not compile.)

Numeric conditions are common:

( sum == 0 ) or (i < 10)

Other kinds of conditional expressions are possible. For example, we will study more about

functions that "become" true or false later. In C++, integer values by themselves have a true or

false meaning. Specifically, 0 is false and anything else is true. So it is legal to write:

( n )

Here, if n is 0 then the condition is false; if n is non-zero, the condition is true

Using one = is an assignment statement which will compile and execute, but will not mean what

you think it means.

In short: to compare for two things being equal, you must use two = signs:

(x == 10)

If you code:

(x = 0)

then you are assigning 0 to x and the truth of the condition will depend on the value stored into x.

Page 20: C++ CLASS PDF

In C and C++ the value 0 is considered to be false and any other number is true.

We will revisit this idea later. For now just remember to use

two = when you want to compare and

one = when you want to assign.

Decisions

The decision structure allows the program to execute one or another set of instructions - to

decide which set to execute. Or it can decide simply not to do a set of instructions.

So it determines whether or not sections of code are executed.

Example: imagine you need to calculate an average, but there is a possibility that actually no

values have been accumulated. That is, the count of numbers whose average is to be found is 0.

We'd like to be able to code something that expresses:

"if count is 0 then display a message, otherwise calculate and display the average"

We should do this just when we are ready to try to calculate an average. And we can do it in C++

like this:

if (count == 0) //Note: two = signs for comparison

{

cout << "\nNo data entered";

}

else

{

cout << "\nAverage: " << (double) tempSum/count;

}

There are two basic forms of the if statement

if (condition)

{

block of statements to execute if condition is true;

}

if (condition)

{

block of statements to execute if condition is true;

}

else

{

block of statements to execute if condition is false;

Page 21: C++ CLASS PDF

}

If the block is only 1 statement long, the {} aren't required:

if (cond)

stmnt;

if (cond)

stmnt;

else

stmnt;

Note the use of ;

We will study a couple of further variations on if statements later as well as a couple of

alternative instructions that implement decisions.

Repetition or Looping

In C++ there are 3 instructions that build loops:

for - executes a block of instructions a certain number of times

while - repeats a block of instructions 0 to many times, as long as a given condition is

true

do..while - executes a block of instructions once and then 0 to many more times, as long

as a given condition is true. That is, the block is executed at least once.

Definitions:

1. "a block of instructions" are those that should be executed repeatedly. They are

called the body of the loop.

2. "a given condition" is a logical condition that is true or false (i.e. is i > 10?). This

condition is called the exit condition of the loop.

The for Statement.

int i;

for (i = 0; i < 10; i++)

{

cout << i;

}

... the pgm continues...

Page 22: C++ CLASS PDF

This says:

1. set i equal to 0

2. if i is less than 10, execute the body of the loop (the cout); otherwise go to next

instruction after the for loop and continue

3. increment i

4. go to step 2

This loop would produce the following output:

0123456789

Note that "running the loop" from 0 to 9 executes the body 10 times.

Recall the code to print the squares of the first few odd numbers? Now we can use a for loop to

compress it:

int i;

int num = 1;

for (i = 1; i <= 5; i++)

{

cout << " The square of " << num << " is " << num*num << endl;

num += 2;

}

This would produce:

The square of 1 is 1

The square of 3 is 9

The square of 5 is 25

The square of 7 is 49

The square of 9 is 81

Notes:

we could print the first 25 squares-of-odd numbers just by changing 5 to 25 in the for

statement

the loop runs from 1 to (and including 5). We could have run from 0 to < 5, but then the

numbers would have been 0 to 4. We could have fixed that by coding i+1 as the first data

sent to cout

the body of the loop is traditionally indented (you must do this in this course)

notice that the body of loop is enclosed in {}. This is required if loop body is more

than 1 statement.

If you don't code the {}, only the first statement following the for is considered to be part of

the loop body.

Page 23: C++ CLASS PDF

and in the code above (without the {}), you would get:

The square of 1 is 1

The square of 1 is 1

The square of 1 is 1

The square of 1 is 1

The square of 1 is 1

because although i is incremented, num += 2; was not executed until after the loop finished.

The three expressions in the for loop's () can be thought of as:

the initialization part: this is done only once, before the body is executed the first time

the exit condition: this is tested before each execution of the loop body. When it is false,

the loop body is not executed any more and whatever instruction is after the for construct

is then executed.

the "final" part: this is executed after each execution of the loop body. It's as if it were

added on to the end of the loop's body.

The initialization and the "final" part can have more than one instruction - they are separated by

commas. For example, if you wanted to initialize two variables, i and j to 0 and to increment

both after the body of the loop is executed, it would look like this:

for (i = 0, j = 0; i < 10; i++, j++)

{

loop body

}

Finally, because the exit condition is tested before the loop body is executed, the for loop is

classified as a top-driven loop. If the exit condition is true the first time it is tested, the loop

body will not execute at all.

The while Statement

Often we don't know in advance how many times loop should execute, so we can't use for in its

role as a counting loop as shown above.

Example: ask the user to enter some numbers and then find their average.

We could:

1. ask user in advance how many numbers will be supplied (if so we could use a for loop;

but this is bad for user).

2. tell user to enter a special value when done (e.g. negative for test scores). Sometimes no

special value can be found.

3. Ask user after each value if there's another number.

Page 24: C++ CLASS PDF

We'll use the while loop to code the second way.

Format of while loop:

while (condition)

{

body of loop

}

This is a top-driven loop. The "condition" is tested before the execution of the code in the body

of the loop. It is tested before the body is executed the very first time - and if it is false, the body

of the loop will not be executed at all. So the loop could execute 0 times.

There are a couple of patterns we could use for the averages program.

Pattern 1:

get aTemp from the user

while (aTemp > -999) //using special value for exit

{

process aTemp

get next aTemp from user

}

calculate average

Pattern 2:

while (1)

{

get aTemp from user

if (aTemp <= -999)

break; //break jumps out of loop

process aTemp

}

calculate average

Notes:

the value of 1 is evaluated in a condition by C++ as "true" (0 is false)

some consider breaking out of loop to be bad

others consider Pattern 2 to be closer to the way we think

break is a new instruction - in this course it should only be used to implement this

pattern.

Page 25: C++ CLASS PDF

Complete averages program, Pattern 1:

//Program to calculate average temperatures from user input.

//User enters -999 or less to quit

#include <iostream>

#include <iomanip>

using namespace std;

int main()

{

int aTemp;

int tempSum = 0;

int tempCount = 0;

cout << "\nEnter a temperature (-999 to quit): ";

cin >> aTemp;

while (aTemp > -999)

{

tempCount++;

tempSum += aTemp;

cout << "\nEnter next temperature (-999 to quit): ";

cin >> aTemp;

}

cout << "\nAverage is " << (double)tempSum/tempCount;

return 0;

}

As an exercise: convert this to Pattern 2 but notice that there is one flaw. Hint: what if user enters

-999 as first number? You should know how to fix this by now.)

Another version of the average program

Some applications will not have any natural "special value" - e.g. a speed can be any + or - value.

So we need another way to tell when to stop the looping - that is, a different exit condition.

One way is to ask user each time, and accept an answer (as a string).

//Program to calculate average speeds from user input

//User is asked after each value of there is more data.

#include <iostream>

#include <iomanip>

Page 26: C++ CLASS PDF

using namespace std;

int main()

{

int aSpeed;

int speedSum = 0;

int speedCount = 0;

string choice = "y";

while (choice == "y") // see note below

{

cout << "\nEnter a speed: ";

cin >> aSpeed;

speedCount++;

speedSum += aSpeed;

cout << "\nAnother? (y/n) ";

cin >> choice;

}

cout << "\nAverage is " << (double) speedSum / speedCount;

return 0;

}

Remember, though, In this case, as well as other comparisons for equality (e.g. for testing

numbers) you must be careful to code two = rather than one. Using one is an assignment

statement which will compile and execute, but will not mean what you think it means. In short,

again:

to compare for two things being equal, you must use two = signs:

if (x == 10)

if (s == "jim")

The do...while Statement

This version of a loop instruction ensures the body of the loop always executes at least once.

After each repetition, the exit condition is tested. So this is classified as a bottom-driven loop.

Format:

do // note: NO ; here

{

.. loop body

}

while (condition); // note: ; required here!!

Page 27: C++ CLASS PDF

You could re-write the average program using a do..while without having to initialize the

variable choice.

#include <iostream>

#include <iomanip>

using namespace std;

int main()

{

int aSpeed;

int speedSum = 0;

int speedCount = 0;

string choice;

do

{

cout << "\nEnter a speed: ";

cin >> aSpeed;

speedCount++;

speedSum += aSpeed;

cout << "\nAnother? (y/n) ";

cin >> choice;

}

while (choice == "y");

cout << "\nAverage is " << (double) speedSum / speedCount;

return 0;

}

Loop Exit Conditions

Be very careful. You must ensure that

loop exit condition has correct value first time

loop exit condition eventually becomes false

The following will loop forever:

i = 1;

while (i < 10)

{

n = i * 4 - j / 16;

cout << n;

}

Page 28: C++ CLASS PDF

Why?

Likewise:

i = 1;

while (i < 10)

n = i * 4 - j / 16;

cout << n;

i++;

Why?

You need to be careful to code loop exit conditions correctly for all three C++ loop constructs.

Symbolic Constants - C Version

Symbolic Constants allow us to associate a value with a symbolic and meaningful name. You use

the name wherever you want the value.

In this course we will restrict use of symbolic constants to numeric values.

By convention, symbolic constants are always CAPITALIZED as a signal to the (human) reader

of the program that the value is a constant.

Use these whenever you have a fixed value used in several places in a program. This value will

not change while the program is running, but the value may change at some later time (example:

postal rates - cents per ounce). You would then change the value and recompile the program.

Using a symbolic constant allows a change to this value to be made in just one place rather than

finding and changing it in many places in the program. Finding and changing in multiple places

is error-prone. Then just re-compile the program.

Also, use symbolic constants for "magic" numbers whose values do not explain their meaning:

Which is clearer?

Total = amt + amt * 0.0625;

Total = amt + amt * TAX_RATE; //TAX_RATE is a symbolic constant

You create C-style symbolic constants with #define statements at beginning of program (before

main). The symbolic names are always capitalized by C and C++ programmers. In this course

you should also capitalize them.

Page 29: C++ CLASS PDF

Note there are no commas or semi-colons in a #define:

#include <iostream>

#include <iomanip>

using namespace std;

#define TAX_RATE 0.0625 //CAPS by convention

#define INDENT_PRCNT 0.0625 //diff meaning; same value

int main()

{

...

//0.0625 replaces TAX_RATE

total = amt + amt*TAX_RATE;

//0.0625 replaces INDENT_PRCNT

x = 2 * INDENT_PRCNT;

//then next is illegal: equivalent to 0.0625 = 0.07 and generates an error

TAX_RATE = 0.07;

...

So:

Changing TAX_RATE in the #define changes it everywhere it occurs in the program (potentially

many times), but does not change INDENT_PRCNT anywhere.

Meaning is clearer.

Symbolic Constants - C++ Version

C++ has a alternate mechanism to create a symbolic constant. It is typically declared as a data

item, but in the same place as a #define. The declaration is prefaced with the keyword const. The

compiler will flag as an error any attempt by program instructions to alter the value of this data

item.

#include <iostream>

#include <iomanip>

using namespace std;

const double TAX_RATE = 6.025;

const double PI = 3.14159;

int main()

Page 30: C++ CLASS PDF

{

...

total = amt + amt*TAX_RATE; // ok - as above

...

TAX_RATE = .07; // illegal - produces compiler error message

...

CSCI 240 Lecture Notes - Part 3

Cascading if's

Sometimes the two alternatives provided by if aren't enough.

Suppose you need to decide if a student is a Freshman, Sophomore, etc. based on the number of

credit hours. There are more than two alternatives.

You can use this pattern - which is an extension of the simple if:

if (cond1)

{

action1;

}

else if (cond2)

{

action2;

}

else if (cond3)

{

action3;

}

If none of cond1, cond2, and cond3 are true, no action is taken.

If you want some action to be taken when none of the conds are true, use this pattern:

if (cond1)

{

action1;

}

else if (cond2)

{

action2;

}

else if (cond3)

Page 31: C++ CLASS PDF

{

action3;

}

else // note no (cond) here

{

action4;

}

action4 is done if and only if none of the conds is true.

In the first pattern, zero or one action is done. Never more than one.

In the second pattern, one and only one action is done.

So for the credit-hour - class standing problem, we could code the following:

int credits;

string classStanding;

cout<< "Enter number of credit hours: ";

cin >> credits;

if (credits >= 90)

{

classStanding = "Senior";

}

else if (credits >= 60)

{

classStanding = "Junior";

}

else if (credits >= 30)

{

classStanding = "Sophomore";

}

else if (credits >= 0)

{

classStanding = "Freshman";

}

else

{

classStanding = "Error: ....";

}

cout << "\nClass is: " << classStanding;

Alternative Decision Statements

We have studied and used the if statement to implement the idea of a decision.

There are a couple more ways in C++ to implement decisions that you should know about.

Page 32: C++ CLASS PDF

switch Statement

The switch statement can cause 0 to many alternative blocks of code to execute based on the

value of a scalar variable. Normally, it is used to execute one and only one block of code.

Scalar Data Types

Any data type whose variables can take on a set of values whose number is no greater than the

number of integers representable on the computer is called a scalar data type.

int and char are scalars. Strings and floats and doubles are not.

Example:

int n;

n = some value;

switch (n) //go to case that matches n's value

{

case 1: //if n == 1, these execute

stmt1;

stmt2;

break; //go to stmnt after closing }

case 3:

stmnt3;

break;

case 5:

stmnt4;

stmnt5;

break;

default: //n not 1, 3, 5: this executes

stmnt6;

stmnt7;

}

The default block is optional.

Sometimes you want to do the same thing for several values. You can do this:

char ch;

string s;

ch = some value;

switch (ch)

{

case 'a':

case 'b':

case 'c':

case 'd':

s = "pass";

break;

Page 33: C++ CLASS PDF

case 'f':

s = "fail";

break;

case 'i':

case 'n':

case 'w':

s = "other";

break;

default:

s = "invalid";

}

If ch is 'b', for example, it will match case 'b' and go there - then just "fall through" the case 'c',

case 'd', assign "pass" to s, and then break out of the switch structure.

Note: forgetting a break is a common error. If you forget a break, execution will "fall

through" and execute all code after it until it finally hits a break or finishes all the code in the

switch.

Use of the ? and : operators - a "ternary" instruction

A short and somewhat cryptic decision structure is allowed by C++. The general format is:

(condition) ? stmnt_to_do_if_true : stmnt_to_do_if_false;

Some compilers do not require the () around the condition.

As an example:

(x < y) ? x++ : y++;

This is the same as writing:

if (x < y)

x++;

else

y++;

But there is one difference. The expression as a whole becomes the value computed in one of

the two "branches". Here are a couple of examples. Note the () around the whole ternary

expression:

char ch = 'y';

int x = 3, y = 2;

cout << ((ch == 'y') ? "Yes" : "No");

Page 34: C++ CLASS PDF

cout << "The value of the smaller of x and y is " << ((x < y) ? x : y);

and this would display "YesThe value of the smaller of x and y is 2" This somewhat terse syntax

can be handy sometimes.

Data type bool and flags

C++ defines a data type called bool. (Short for "boolean", from the name of a 19th century Irish

logician, George Boole)

bool variables can have one of two possible values: true and false

This data type comes in handy in writing programs that are easier to understand.

For example, consider the following partial example:

bool found = false;

while ( !found )

{

//some complex code to search for something...

//somewhere in it is the following:

if (some condition is true)

found = true;

}

These kinds of variable that can be true or false are often called flags. They typically indicate if

some event has happened yet or not, or as a category:

bool okToWithdraw;

double bal, request;

//get user's bank balance and store into bal

//get user's withdrawal request and store into request

if (bal - request > 0)

okToWithdraw = true;

else

okToWithdraw = false;

...

if (okToWithdraw)

// give out money

Page 35: C++ CLASS PDF

else

// print error message

Scope

The scope of a variable is the location within a program where a variable can be used.

With control structures, all of the statements that are contained within a block of code belong to

the control structure. Therefore, any variable that is declared within such a block of code only

has meaning between the declaration for the variable and the closing curly brace for the block of

code.

For example:

for( int i = 0; i < 10; i++ )

{

cout << endl << i; //this line of code will compile

}

cout << i; //this line of code will NOT compile

CSCI 240 Lecture Notes - Part 3 continued

Compound Conditions

Sometimes one condition is not enough.

Suppose we want to test if a point is inside a rectangle, and we know the x and y coordinates of

the point; also we know the left, right, top, and bottom coordinates of the rectangle. So we

declare:

int ptX, ptY;

int rectLeft, rectRight, rectTop, rectBot;

Now assume all of these variables have valid values and Cartesian coordinates. If we wanted to

test if a point is inside the rectangle, then:

if ptX is greater than rectLeft AND if ptX is less than rectRight AND

if ptY is greater than rectBot AND if ptY is less than rectTop

THEN (meaning, if ALL of these are true...), the point is in the rectangle.

Page 36: C++ CLASS PDF

We can code this in C++, using && for the AND's (newer C++ compilers also allow the word

and):

if (ptX > rectLeft && ptX < rectRight && ptY > rectBot && ptY < rectTop)

{

cout << "pt is in rect";

}

else

{

cout << "pt is not in rect";

}

Note that we required that the point be inside the rectangle because we used > and <.

We could also allow the point to be on or inside the rectangle by using <= and >=.

How could we check to see if the point lies ON the top edge of the rectangle?

(Try it)

We can also code OR's of two conditions, using 2 vertical bars: || (again, newer compilers allow

or):

Example: Suppose you want to ask if the user wants to continue:

cout << "Another? (y/n)";

cin >> choice;

But - you want to accept either "y" or "Y" to indicate "continue". (The user may have hit caps

lock, or just carelessly hit shift).

do

{

... some processing of one thing...

cout << "Another? (y/n)";

cin >> choice;

}

while (choice == "y" || choice == "Y");

Another example: suppose your program is accepting a number representing a shoe size, and

you know that shoe sizes cannot be smaller than 5 or greater than 18. Validate this number - that

is, make the user type in a "valid" shoe size. To do this use a compound condition:

cout << "Enter a shoe size";

cin >> size;

if (size < 5 || size > 18)

{

cout << "invalid size....Try again: ";

cin >> size;

}

Page 37: C++ CLASS PDF

This is good, but there is a better way to do error checking:

Version 1:

cout << "Enter shoe size (5 to 18): "; //initial prompt

cin >> size;

while (size < 5 || size > 18) // OR not AND!!

{

cout << "Shoe size must be in range 5 to 18; try again: ";

cin >> size;

}

cout << "You entered " << size;

This version needs two input lines, and uses two different messages. (The second only if a data

entry error has been made.)

Sample script:

Enter the shoe size (5 to 18): 3

Shoe size must be in range 5 to 18; try again: 19

Shoe size must be in range 5 to 18; try again: 5

You entered 5

Note clearly the loop exit condition: you want to do the loop only if the user has entered a bad

value. So you code "while the size is bad - i.e. less than 5 or greater than 15"

If the initial value entered by the user is ok - say 10 - then the loop body won't execute at all

because neither of the conditions is true. 10 is not less than 5; 10 is not greater than 18. So both

subconditions are false, so the whole thing is false, so the loop is not executed.

Don't code "while (size < 5 && size > 18)" because an invalid size would pass as valid: Suppose

the user types in 20. 20 < 5 is false. 20 > 15 is true. Since both are not true the whole thing is

false and so the loop body would not execute and so the invalid size would pass as valid.

Another way to understand it is simply: how could ANY size be both less than 5 and at the same

time greater than 18? It's impossible. So ANY shoe size would create one false condition and so

the loop body would never be executed for ANY shoe size, valid or invalid.

Clearly, compound conditions require careful thought.

Version 2:

do

{

cout << "Enter the shoe size (5 - 18): ";

cin >> size;

// Note changes to cond!!

Page 38: C++ CLASS PDF

if (size >= 5 && size <= 18)

break;

}

while (1);

cout << "You entered " << size;

Note the new condition. Whereas before we wanted to execute the loop for an invalid value, now

we want to break out of the loop when we have a valid value.

This version will work. But there's only one message. So user sees:

Enter the shoe size (5 - 18): 3

Enter the shoe size (5 - 18): 19

Enter the shoe size (5 - 18): 5

You entered 5

It's not 100% clear that the first two tries were wrong. We could put an else to the if and patch it

to print out an error message. But Version 1 is probably better.

Combining && and ||

Truth tables:

(true && true) => true

(true && false) => false

(false && true) => false

(false && false) => false

So for (c1 && c2) to be true, both must be true.

(true || true) => true

(true || false) => true

(false || true) => true

(false || false) => false

So for (c1 || c2) to be true, one or the other or both must be true.

There are rules of evaluation for these operators:

1. The evaluation looks at one condition at a time (i.e. cond1 op cond2)

2. sub expressions inside () are done first

3. && binds tighter than || (like * and / are done before + and -)

4. otherwise, conditions are evaluated left to right

5. as soon as the truth of the whole is known, evaluation stops

6. but you can provide your own () to change the default rules (or to make the evaluation

order clearer to the reader).

Suppose you have:

Page 39: C++ CLASS PDF

if (cond1 || cond2 && cond3)

The whole thing has to be true or false.

The parts are considered in pairs.

Now will C++ look at this as (cond1 || cond2) && cond3

or as cond1 || (cond2 && cond3)

Because && precedes ||, the latter. So

1. Evaluate cond1 (left-to-right rule).

2. If true, done.

3. Else (cond1 is false, so) evaluate (cond2 && cond3). If both are true, the whole condition is

true; otherwise it is false.

Example:

We want to judge num as valid if it is between 0 and 100 or if it is 2000.

if (num == 2000 || num >= 0 && num <= 100)

cout << "valid";

else

cout << "not valid";

1. Suppose num is 30. Since num is NOT = 2000, we have to evaluate the second half of the

condition. Since 30 is >=0 AND is <= 100, the && is satisfied and we have

if (false || true)

Since this is an OR and for an OR, the whole thing is true if either is true - the whole

thing is true.

2. Suppose num is 2000. Since the first half is true, we don't have to evaluate the second

half. We have:

if (true || anything)

Since this is an OR, it can be true if just one part is true, so the whole thing is true.

3. Suppose num is 150. Since num is NOT = 2000, we have to evaluate the second half of

the condition. 150 >= 0 BUT 150 is NOT <= 100. So the && is not satisfied and we

have:

if (false || false)

Page 40: C++ CLASS PDF

Since both halves are false, the whole thing is false.

CSCI 240 Lecture Notes - Part 4

Three important ideas so far:

program = data + instructions

data is characterized by its data type which defines what operations can be performed

with it and what values it can hold

instructions are written as structures: sequence, repetition, decision

Next important idea:

programs can be broken up into functions, each of which does part of the job.

Functions

As a first introduction, understand that the main() function will become the "boss" function

which calls on "worker" sub-functions to do pieces of the work.

We will learn to use functions written by others and also how to write and use our own. Sets, or

"libraries" of such functions are the main way in which the C++ programming language can be

extended or expanded. For example, there are libraries of mathematical functions, libraries of

graphical functions, libraries of I/O functions, and many more.

These worker sub-functions are relatively small and simple (compared to the whole program,

which does a larger, more complicated task than any one of its sub-functions).

We can make an analogy of a building contractor: main()) who hires subcontractors (plumber,

roofer, electrician - the functions) to do parts of the whole job. (The plumber may hire sub-sub-

contractors to do part of the plumbing job (diggers to get to the sewer, etc.). We will see that in

C++ sub-functions can themselves call on other functions to do part of their work.

We decompose a program into functions for several reasons:

to hide irrelevant detail at the main() level, so the the program's primary purpose is

clearer

to divide a complex problem into a series of simple problems

to make subsequent modification of the program easier

Page 41: C++ CLASS PDF

to reduce the errors that inevitably come with a single large complex program

Note on terminology:

When one function uses another, we say that it calls the other. For example, if main() uses the

sqrt() function to calculate a square root, we say that main() calls sqrt() - and that sqrt() is called

by main(). So the boss is the caller function and the worker is the called function.

Boss's (caller's) view of a function

The called function is a black box that does a job:

1. It may need to be given certain information to do its job (called arguments). For example,

sqrt() needs to be given the number whose square root is to be found

2. It then does its job (from caller's point of view, we don't care how)

3. It may "return" a result or answer to the caller.

Arg1 => => a result

Arg2 =>

Example: a square root function. Takes (i.e. must be given) a real number as an argument and

returns its square root:

num => [sqrt fn] => sqrt_of_num

In main() (or the calling program), we code:

sqrt_of_num = sqrt(num);

num is the (single) argument - the info that sqrt() needs to get an answer. It is a numeric

expression.

sqrt is the name of the function

sqrt_of_num is the name of a variable (float or double) into which the result of the

function call is stored.

The result is a value returned by the function. In a sense, we could also say that the

function call sqrt(num) becomes a value, and that (in this example) the value is then

assigned to sqrt_of_num.

In this case, the result was stored in a variable. However, we can use a function result

anywhere we can code an expression: e.g.:

Page 42: C++ CLASS PDF

cout << sqrt(num); or y = (b + sqrt(b*b - 4*a*c))/(2*a);

Notice that from the boss's or caller's point of view, we don't know or care how the function does

its work. This leaves the client free to concentrate on overall program logic and structure.

The sqrt() function is in a standard library. In some systems, you must

#include <math.h>

to make this library accessible to your program.

Function's view of its boss or caller

NONE. Except the arguments passed.

That is, the function has no idea what the boss is doing.

It cannot see or use or access the boss's plan, logic, variables, or anything.

The function just does its job with the information (arguments) given to it and it may (depending

on how it is written) return a single result (an answer).

Advantages:

1. divide and conquer strategy.

2. a function can be called from multiple places in a program. This avoids code duplication.

3. a function can be used in other programs easily (in a library. Ex: sqrt())

4. makes maintenance easier, since any change (fix a bug, improve the speed) can be done

in one place, not everywhere the code occurs

5. makes code more reliable, less prone to bugs - more later

Disadvantages:

1. a little more up-front planning work

2. sometimes a few more lines of code

How to Write a Function

Decide

Page 43: C++ CLASS PDF

1. name

2. what the function needs to return (if anything) including the data type of the returned

result.

3. what information it needs to do its job (its arguments)

4. what local or temporary variables it needs to do its job (see below)

5. the logic of the function

Example 1: we want a function to cube an integer. No error checking.

1. Name: cube

2. Returns: an integer - the cube of a number.

3. Arugments: one argument - the number to be cubed

4. Local variables: an integer to hold the calculated result

5. Logic: trivial

int cube( int x ) //See Note 1

{

int result;

result = x * x * x;

return result; // See Note 2

}

Note 1:

int - tells the data type of the value to be returned

cube - the name of the function

int x - tells the data type of the arg, and gives it a name to be used inside the function. This name

may or may not be the name of a variable in the caller. It doesn't matter.

Note 2:

return value; - here you supply the value to be returned. In this case it must be an integer

expression since the function has promised to return an integer.

In boss or caller you could use the function cube() as follows:

int num, ans;

num = 2;

ans = cube(num);

cout << ans; // 8

ans = cube(ans);

cout << ans; // 512

ans = cube(3);

cout << ans; // 27

Page 44: C++ CLASS PDF

cout << cube(ans/3); // 729

Note that 3 different things were passed to cube() in the first three different calls: num, ans, and

3.

Inside cube(), each passed-in value - was called x.

cube() does not know the names of the variables in the caller (num, and ans; and 3 is not even

the name of a variable).

In fact, cube() does not even get access to num, ans, or 3 or 9. It gets copies of their values. It

uses the copies to do its work. Since it gets copies, it cannot change the original data passed.

(Think about this - what would it mean to alter the value of 3???)

Example 2: similar to 1, but we want to return the cube of half the number passed as the

argument.

int cube_half( int x )

{

x = x / 2;

return x * x * x;

}

Here x (the "alias" for the argument passed from the caller) has been changed in the function, but

understand that only the copy is changed. The original (back in the calling program) is

unchanged:

in caller:

num = 4;

ans = cube_half(num);

cout << ans << num; // displays 8 4

num is still 4. It was not altered by the division within cube_half(). Only the copy of num was

halved in cube_half().

Example 3: Sometimes functions don't need to return a value. Most often that's when they just

print something.

Then we say they return void.

Page 45: C++ CLASS PDF

Here's a function to display a page header with page number at the top of the page. (Assume

cout's are sending output to the printer here; note \t = tab and \f = newpage for the printer)

void pageHeader(int pagenum)

{

cout << "\f\tInventory Control System\t\tpage " << pagenum;

cout << "\nDescription\t\tQuantity\t\tPrice";

}

Notes:

1. \f is formfeed = new page

2. \t is a tab

3. nothing is returned; so the return statement is not required, but it can be added with no

arguments. In a void function, if there is no return statement, control returns to the calling

program after the last line in the function has executed.

In caller, you'd code:

pageHeader(1);

or, better, keep the current page number in an int variable, initialized to 0 and incremented before

printing each page:

int pnum = 0;

//these two would probably be in a loop that prints 1 page per repetition

pnum++;

pageHeader(pnum);

Example 4a: write a function to display two dashed lines on the screen. Like this:

-------------------------------

-------------------------------

The number of dashes is determined by the caller by supplying that number as an argument to

the function.

void lines(int n)

{

int i; //NOTE: use of a local variable

cout << "\n";

for (i = 1; i <= n; i++)

cout << "-";

Page 46: C++ CLASS PDF

cout << "\n";

for (i = 1; i <= n; i++)

cout << "-";

}

Notes on i:

i here is a local variable.

It is used only in the function

It is invisible outside the function

It does not retain any value between function calls. In fact, it does not exist between calls

to lines(). It only exists when lines() is executing.

Other functions (including the calling function) can have their own variable named i

typical calling statement would be: lines(40);

Example 4b: write a function to display n dashed lines each of which has m dashes in it. So a

calling statement would be, for example,

dashes(3, 40); //make 3 lines of 40 dashes

We'll write this as a function dashes() which calls a sub-function (in a loop) oneLine() to do one

line:

//display numLines lines of dashes

void dashes(int numLines, int numDashes)

{

int i;

for (i = 0; i < numLines; i++)

oneLine(numDashes);

}

//display one line of num dashes on a new line

void oneLine(int num)

{

int i;

cout << "\n";

for (i = 0; i < num; i++)

cout << "-";

}

Study this example and be sure you understand how it works.

Page 47: C++ CLASS PDF

Notice that each function is quite simple.

Notice that the local loop counter variable is called i in each function, but since functions

know nothing about each other, this is not a problem.

Notice that the loop that does dashes goes from 0 to < num instead of 1 to <= num as in

Example 4a - but that the same number of dashes are produced, given the same argument.

Notice that the name of the argument to oneLine() is not the same as its alias inside

oneLine()

etc. etc.

Example 5: Write a function to raise an integer number to an integer power. Assume both

arguments are > 0; also assume that the result will fit in a int. Since n-to-the-i could generate a

really big value, you would have to be careful that you don't exceed the maximum integer, or

perhaps use a special data type with a larger range.

int n_tothe_i(int n, int i) //note 2 args passed

{

int ndx;

int temp;

//temp = n to the first power

temp = n;

//now mult temp by n i-1 times

for (ndx = 1; ndx < i; ndx++)

temp *= n;

return (temp);

}

("Play computer" with this to satisfy yourself that it works correctly.)

Sample calling statements:

x = n_tothe_i(3, 2); //3 to the 2 power

y = n_tothe_i(x, 4);

z = n_tothe_i(r, (i*j)); //note use of expr as arg

Example 6: Write a function that returns the (double) average of a sum and count (passed as a

double and an int) if the value in count is greater than 0. Have it return 0 if count is 0 or negative.

(Yes, I know that n/0 is not defined and shouldn't be set to 0. And that 0/n is 0. This is just for

illustration.)

Page 48: C++ CLASS PDF

double avg(double sum, int count)

{

if (count <= 0)

{

return 0;

}

else

{

return sum/count;

}

}

Note that there are 2 return statements here. The first one executed is the last statement executed

in the function. It is an immediate bailout. Some people discourage the use of multiple returns in

a function. They argue that in a large function, you might not see all the return statements and so

you might misunderstand how the function works. In small simple functions, this is usually not a

big problem. However, if you want to have just one return, you could re-write the function as

follows. You'll need one local variable:

double avg(double sum, int count)

{

double temp;

if (count <= 0)

{

temp = 0;

}

else

{

temp = sum/count;

}

return temp;

}

One further requirement for using functions:

You must tell the C++ compiler a bit about each function you use in your program before you

use it.

After the #includes (and any #defines), but before int main(), add one line for each function that

you are writing and using in this source code file.

These lines are called the function prototypes.

Each line is of the following form:

return-type function-name(arg1-type, arg2-type, ...);

Page 49: C++ CLASS PDF

The arguments need only be listed by type - you don't have to write a name here. (But you can

include a name if you'd like to remind yourself of what the argument stands for.)

If a function takes no arguments, you can just type a set of empty ().

Note the semi-colon at the end of each line. It is required for function prototypes.

Examples - prototypes for all the previous functions

int cube(int);

int cube_half(int);

void pageHeader(int);

void lines(int);

void dashes(int, int);

void oneLine(int);

int n_tothe_i(int, int);

double avg(double, int);

Finally, how do all these pieces go together? Here's a skeleton program that uses cube()

#includes...

using statement

#defines...

int cube(int);

//****************************************************

int main()

{

int ans;

ans = cube(5);

cout << ans;

return 0;

}

//****************************************************

int cube(int c)

{

int result;

result = c * c * c;

return result;

}

Page 50: C++ CLASS PDF

Notes:

Precede every function with a //***********... to make it easier to find them, and to

remind you that each function is a separate entity and cannot see anything in any other

function.

Code main() first - all of it, including its opening "{" and its closing "}" - and then any

other functions you write, in any order you feel is useful.

Make the order of the prototype statements match the order of the actual functions in the

listing - just to make them easier to find..

CSCI 240 Lecture Notes - Part 4B -- Function

Summary Sheet

Functions are chunks of code designed to do a particular task.

They are called from other functions (like main()) to perform the task.

The calling function often passes information used by the function to accomplish its task (via

arguments).

You may need to define additional variables (local variables) inside the function to accomplish

its task. Local variables are invisible outside the function. They do not retain their values

between calls to the function (actually, there is a way to make this happen, but we do not cover it

here).

The function often returns an answer to the caller. In a sense, the function "becomes" the value

returned.

When you design a function, from the caller's point of view, decide:

the task of the function

the name of the function

the return data type of the function

the information needed by the function (arguments)

From the point of view of writing the function, decide:

how to accomplish the task, given the arguments provided

what local variables may be needed

You must make the following true of the function you write and use:

Page 51: C++ CLASS PDF

the data type of the value returned matches the function's declared return type

the arguments supplied in a function call match those specified in the function's header in

terms of number, order, and data type

the arguments supplied in a function call have defined values before the call is made

Consider the following function:

int cube(int x)

{

return x*x*x;

}

It returns an int

Its name is cube

It takes one int argument, the number to be cubed

The name of the argument inside the function is x. You can use it as if it were a declared and

initialized variable in the body of the function

The value returned is a int

Calling statements for this function might be:

n = cube(3); //pass a literal

n = cube(y); //pass a variable

n = cube(i*j); //pass an arith. expr.

cout << cube(k);

When a function is called, copies are made of the supplied arguments, and the flow of control is

transferred to the function, which then can use the arguments as if they were local variables. Any

changes to an argument are made to the copy, not to the original in the calling program.

The code in the function executes until a return statement is encountered or control passes out of

the end of the function.

If a function is declared to return a value, it must execute a return statement with a value of the

declared return type.

The value supplied after the return statement is the value returned. Control then returns to the

calling program.

A function may have more than 1 return statement, but only one is executed per function

execution (the first one executed).

A function may have no return statement if it does not return a value (i.e. declared as void).

Remember, too:

Page 52: C++ CLASS PDF

main() is a function. It's always the first one called when the program starts.

No function can see anything about anything (including variables) in any other function. All it

knows from the "outside world" is the values of the arguments passed to it.

CSCI 240 Lecture Notes - Part 5 - Notes on

Allegro C++ Graphics

Graphics functionality is not a part of the C++ language. However, graphics functionality can be

provided by writing a library of functions which access the computer's graphics hardware. These

libraries of functions can be provided by a compiler vendor such as Borland or Microsoft and

included in a commercial product, or can be written by knowledgeable individuals and sold or

provided free as software that can be added to your development system.

In this course, we will use a (free) software graphics package called Allegro. It has been installed

on the CSCI lab systems, and is provided as part of the 240 Course CD so you can install it on

your personal computer. Since the graphics functions in Allegro are not part of C++, programs

written using it will not run on computers that do not have certain Allegro files installed.

Other graphics libraries will likely have similar functions, but their names and arguments will be

different in some or most details.

GRAPHICS VS. TEXT

Text

Text resolution is 80 x 25 (usually): each cell can contain one of 255 possible characters

Usually output to screen is sequential by column and row; top/left to bottom/right. You

can skip to next line by writing \n.

Graphics

graphics commands only work in "graphics mode" (see below)

graphics resolution can vary according to the computer type and graphics card. A

standard resolution is 640 by 480 (usually; this is known as standard VGA); resolutions

of up to 1280 x 800 are common. Each cell is a single dot or pixel and can be one of a

number of colors (16 colors up to millions of colors are possible in most systems).

graphics output can be done at any one of the dot locations.

there are also special graphics functions for text output in graphics mode.

Graphics Mode Programs using Quincy

Page 53: C++ CLASS PDF

See the document entitled Creating a Graphics Program in Quincy for details on how to create a

graphics mode program in Quincy. The remainder of this document will cover concepts of

graphics programming and a set of graphics functions you can use in your programs. Note that

one or two functions presented in the Creating a Graphics... document are used in the examples

below.

SCREEN COORDINATE SYSTEM (standard VGA)

Upper left corner is (0, 0)

Upper right corner is (639, 0)

Lower left corner is (0, 479)

Lower right corner is (639, 479)

VGA can support 16, 256, or up to 16 million colors per pixel, depending on the hardware.

Other screen resolutions are possible (800 x 600, etc.)

In Allegro, you can set the screen resolution to one of several allowed values. The grInit()

function presented in the Creating a Graphics... document will set the screen to 640 x 480. (See

the set_gfx_mode() function call in grInit().)

After calling grInit(), you can find the current screen resolution (width and height are ints) by

using the Allegro values SCREEN_W and SCREEN_H which act like symbolic constants in

your program.

Even though you may know the screen resolution, it is good practice to use these constants in

positioning graphics output whenever possible. This way, if you change the program to use a

different resolution, you can keep the relative position of various graphics elements (for

example, we will use these to center certain graphics elements).

For example, suppose you want to position a dot in the middle of the screen, for any resolution.

The function to use is putpixel (screen, x, y, color);

where

"screen" is the Allegro predefined variable representing the visible graphics screen

x is the x-coordinate of the dot (int)

y is the y-coordinate of the dot (int)

color is an int representing a color. By default, there will be 256 colors (8 bits per pixel).

Page 54: C++ CLASS PDF

So, after grInit() call:

putpixel( screen, SCREEN_W/2, SCREEN_H/2, 50 );

You can use other numbers 0 .. 255 for the color.

SKELETON GRAPHICS PROGRAM

#include "allegro.h"

int grInit(void);

void wait_for_keypress();

int main()

{

int rc;

//Initialize graphics mode - see Creating a Graphics... document

//If grInit fails, it returns 1. So we just quit the program. no point in

continuing.

rc = grInit();

if (rc == 1)

{

return 0;

}

//Allegro draw-a-dot function

putpixel( screen, SCREEN_W/2, SCREEN_H/2, 50 );

//Freeze the display until user hits a key. This also releases the graphics

//screen. See Creating a Graphics... document.

wait_for_keypress();

return 0;

}

END_OF_MAIN(); // <-- ABSOLUTELY REQUIRED EXACTLY HERE

//You must include the code for grInit() and wait_for_keypress() here.

GRAPHICS TEXT DISPLAY

Page 55: C++ CLASS PDF

There are facilities for text output in graphics mode. We will use one simple predefined font

provided by Allegro. Text can be output at any screen location.

textout( screen, font, msg, x, y, color ); //displays msg at x, y in

color

where

"screen" is the Allegro predefined variable representing the visible graphics screen

"font" is the Allegro predefined variable representing the default font

msg is your string variable (or a string constant) representing the text to display

x and y are the coordinates of the upper left corner of the bounding rectangle for the text.

color is the color of the text

For example:

textout(screen, font, "Hello", 10, 20, 50);

Will display the word "Hello" in light green at screen coordinates 10, 20.

To find the width of a string in pixels (not characters), use:

w = text_length( font, msg );

Where

"font" is the Allegro predefined variable representing the default font

msg is your string variable (or a string constant) representing the text

This function returns the width of msg in pixels

To find the height of a string in pixels, use:

h = text_height( font );

Where

"font" is the Allegro predefined variable representing the default font

no msg is required: the height is the height of the font as a whole.

This function returns the font height in pixels

So to display centered text:

textout( screen, font, msg, SCREEN_W/2 - text_length(font, msg)/2, 100, 50);

Where

Page 56: C++ CLASS PDF

"screen" is the Allegro predefined variable representing the visible graphics screen

"font" is the Allegro predefined variable representing the default font

SCREEN_W/2 = midscreen

text_length(font, msg)/2 = half the width of the text

100 is the y-coordinate for the text

50 represents the (light-green) color

But actually, Allegro provides a self-centering function:

textout_centre( screen, font, msg, x, y, color );

Where x is the center of the string, so the center goes at x, y. So you could just code:

textout_centre( screen, font, msg, SCREEN_W/2, 100, 50 );

Note the British spelling of centre.

Allegro allows the creation and use of different fonts, but these topics will not be covered here.

GEOMETRIC OBJECTS

Lines:

line( screen, x1, y1, x2, y2, color );

where

"screen" is the Allegro predefined variable representing the visible graphics screen

x1 = upper left endpoint x coordinate

y1 = upper left endpoint y coordinate

x2 = lower right endpoint x coordinate

y2 =lower right endpoint y coordinate

color is the integer controlling the line color

To do a big "X" on the screen:

line(screen, 0, 0, SCREEN_W, SCREEN_H, 50);

line(screen, 0, SCREEN_H, SCREEN_W, 0, 50);

Circles:

circle( screen, x, y, radius, color );

Where x and y are the coordinates of the center of the circle; the other arguments should be clear

by now.

Page 57: C++ CLASS PDF

To center a circle on the screen:

circle( screen, SCREEN_W/2, SCREEN_H/2, SCREEN_H/2, 50 );

Rectangles:

rect( screen, x1, y1, x2, y2, color );

Where x1, y1 and x2, y2 are the coordinates of the upper left and lower right corners

To draw a series of nested rectangles:

for( i = 10; i < SCREEN_H/2; i += 10 )

{

rect(screen, SCREEN_W/2 - i, SCREEN_H/2 - i, SCREEN_W/2 + i, SCREEN_H/2 +

i, 100);

}

FILLING A REGION

The floodfill() function will fill a bounded region of the screen with a solid color.

To do the floodfill(), you need to supply the coordinates of any point inside the region and the

color you want to use in the fill.

floodfill() will change all the background color pixels to the color you specify until it comes to

pixels of a different color. Note that if your region's boundary has even a tiny "hole" in it - where

the hole is the background color - the flooding will spill out of the region and fill a large part - or

all - of the screen.

floodfill( screen, x, y, color );

Where

"screen" is as usual

x and y are the place to start filling from

color is the fill color

Problem: to draw three rectangles of width = Rwd and height = Rht across the middle of the

screen, equally spaced, margins = 1/2 of the spacing, and flood fill each of them.

w = SCREEN_W;

h = SCREEN_H;

//y coord of rect tops and y coord of rect bottoms

Page 58: C++ CLASS PDF

top = h/2 - Rht/2;

bot = h/2 + Rht/2;

//size of space between rects

space = (w - 3*Rwd)/3;

//draw the three rectangles in green

rect( screen, space/2, top, space/2 + Rwd, bot, 50 );

rect( screen, 1.5*space + Rwd, top, 1.5*space + 2*Rwd, bot, 50 );

rect( screen, 2.5*space + 2*Rwd, top, 2.5*space + 3*Rwd, bot, 50);

//calc pt at 1,1 offset from top left of each rect;

floodfill( screen, space/2 + 1, top + 1, 100 );

floodfill( screen, 1.5*space + Rwd +1, top + 1, 150 );

floodfill( screen, 2.5*space + 2*Rwd + 1, top + 1, 200 );

CSCI 240 Lecture Notes - Part 6

More on the char data type

We have seen that C++ has a built-in data type to represent single characters. It is one of the

fundamental data types of the language.

Here's a surprise - internally, a character is represented by a numeric code in the range of 0 to

255. (32 to 127 are standard; some of 0 to 31 and all of 128 to 255 depend on the computer you

use). This is why char is a scalar - there are only 256 possible values.

Since each char is associated with an integer value, in C++ chars can be treated as integers. This

is unusual since usually C++ does not permit us to mix data types. We will see some examples of

this below.

To review:

The data type name is char. To declare a char variable:

char ch;

A char literal is written with single quotes around it: 'a'

cout knows how to display char data:

Page 59: C++ CLASS PDF

char ch = 'a';

cout << ch;

Some special cases:

A backslash char is written: '\\' - single quote, backslash, backslash, single quote

A single quote char is written as: '\'' - single quote, backslash, single quote, single quote

A double quote char is written as: '\"' - single quote, backslash, double quote, single quote

The common "escape sequence" characters (newline, tab, etc.) are also written with single

quotes, but are still typed with the leading \: '\n'

ch = '\n'; //stores newline char in ch

Other chars with no keyboard representation are written in base 8 using three digits: '\007'.

This is not covered further in this course.

The ASCII Character Set

The American Standard Code for Information Interchange (ASCII) is a standard mapping of

integer values to characters, used by most computer systems (exception - IBM mainframes).

Some useful ones to know (i.e. memorize these for this course)

blank = 32

digit '0' = 48

digit '9' = 57

'A' = 65

'Z' = 90

'a' = 97

'z' = 122

Note: '1' is 49, '2' is 50, etc. so if you know '0' you know all the digits. 'B' is 66, 'C' is 67, 'b' is 98,

'c' is 99, etc. so you can figure out any letter, too.

Since chars are really small ints (internally) we can do some useful tricks with them by treating

them as integers.

char ch = 'A';

int i;

//print Ascii value of ch (65). Note the typecast

cout << (int) ch;

Page 60: C++ CLASS PDF

// print ASCII values of A..Z

for (i = 'A'; i <= 'Z'; i++)

{

cout << "\nThe ASCII value of " << (char) i << " is " << i;

}

will print:

The ASCII value of A is 65

The ASCII value of B is 66

The ASCII value of C is 67

...

Character Functions

There is a large set of standard C++ functions that deal exclusively with characters. They can be

found in the <ctype.h>, which is automatically included in most IDEs, but if it's not, just add a

#include <ctype.h> at the top of the source code file.

One subset of the standard C++ character functions test whether a character *IS* of a certain

type: capital, lower case, digit, punctuation, whitespace, etc.

For example, if you want to test if a character entered by the user is a digit, you can call

if ( isdigit(ch) ) //ch is a char variable

{

cout << ch << " is a digit";

}

else

{

cout << ch << " is NOT a digit";

}

Some of the other "is" functions:

Function Purpose

isdigit This function tests if the passed in character is a digit. It returns true if the

character is a digit and false otherwise.

isalpha This function tests if the passed in character is alphabetic. It returns true if the

character is a alphabetic and false otherwise.

isupper This function tests if the passed in character is an uppercase alphabetic character.

It returns true if the character is an uppercase character and false otherwise.

islower This function tests if the passed in character is a lowercase alphabetic character. It

returns true if the character is a lowercase character and false otherwise.

Page 61: C++ CLASS PDF

isalnum This function tests if the passed in character is an alphanumeric character. It

returns true if the character is alphanumeric and false otherwise.

isspace

This function tests if the passed in character is a whitespace character (space, tab,

newline, etc...). It returns true if the character is a whitespace character and false

otherwise.

ispunct This function tests if the passed in character is a punctuation character. It returns

true if the character is punctuation and false otherwise.

Another subset of the character functions changes a character. Two of those functions change an

alphabetic character to the opposite case. They are:

toupper tolower

The toupper function takes a character as its argument and returns a character. If the passed-in

character is a lowercase alphabetic character, then the uppercase version will be returned. If the

passed-in character is not a lowercase alphabetic character, it is returned unchanged.

The tolower function takes a character as its argument and returns a character. If the passed-in

character is an uppercase alphabetic character, then the lowercase version will be returned. If the

passed-in character is not an uppercase alphabetic character, it is returned unchanged.

Suppose you have a single char (not a string) from the user. If it is 'q' or 'Q', that signals quit.

Before we had to code something like:

while (ch != 'q' && ch != 'Q')

{

// do something

}

Now we could do this:

while (toupper(ch) != 'Q')

{

// do something

}

In other words, ch is converted to uppercase and the result of that is compared to 'Q'.

Notice, too, that we can compare chars with !=, = =, <=, etc. just like numbers (since they are

small ints).

CSCI 240 Lecture Notes - Part 7

Page 62: C++ CLASS PDF

Arrays - Part 1

Up to now, we have used simple data types (int, char, etc.) except for strings. Now we look at the

first data structure. A data structure is a compound data type; that is, it is a single entity made

up of numerous parts.

It's kind of like a numbered list of "things" where every "thing" must be of the same data type -

all ints or all chars, etc...

Arrays can help solve problems that would otherwise be quite difficult and can sometimes make

solutions clearer.

As an example of a problem that is difficult without arrays, consider the following:

Problem: read n values and calculate an average. For each number, print whether it is above,

below, or equal to the average.

The first part of the problem is easy to solve. Values are read from the user and then the average

is calculated. The second part isn't so easy because in the process of reading the values from the

user we "lose" the previous values and therefore don't have anything to compare with the average

except for the last value entered.

Solution 1

If we know there are exactly n numbers, we can declare nvariables to hold all of the values. So if

there are 3 numbers:

int n1, n2, n3;

double avg;

cout << "Enter the first number: ";

cin >> n1;

cout << "Enter the second number: ";

cin >> n2;

cout << "Enter the third number: ";

cin >> n3;

avg = (n1 + n2 + n3) / 3.0;

if (n1 > avg)

{

cout << "n1 is above";

}

else if (n1 < avg)

{

cout << "n1 is below";

}

else

Page 63: C++ CLASS PDF

{

cout << "n1 is equal to avg";

}

//same pattern of code repeated for n2 and n3

Problem: what if there are 50 or 500 or 5000 numbers? This solution, awkward when n = 3, is

terrible when n get big.

Solution 2

Read the numbers twice. That is: read in all the numbers and calculate average, read in all the

numbers again, this time checking each as it is read against the previously calculated average.

If input is from keyboard, then the user has to enter each number twice. Accurately, with no

mistakes.

If input is from an explicitly opened file (which we will be able to do later), it will work, but is

inefficient because the file has to be read twice.

Note: this will not work with I/O redirection - we can't re-open the file and read from the

beginning again.

Solution 3:

Use arrays to store all the values as they are read and thus "remember" them.

First some information on how to create and manipulate arrays.

An array is a data structure consisting of an ordered set of data values of the same type. Think of

it as a numbered list, but all items in the list must be the same kind of thing (the same data type).

When defining an array in a program, we have to specify three things:

what kind of data it can hold (ints, chars, doubles, strings, etc.)

a name

how many values it can hold (i.e. the maximum number it can hold)

The generic format for declaring an array:

data_type array_name[ # of items to hold ];

So, to declare an array that can hold up to 10 integers:

int numArray[10];

This reserves space for 10 integers. It does NOT place any values into the array.

Page 64: C++ CLASS PDF

Each position in the array is called an array element.

The elements in this array are numbered from 0 to 9. (Not 1 to 10.)

Notice that when you declare an array, you specify how many items it can hold. And that these

elements are numbered starting at 0 and ending at one less than the number of elements. This

array holds 10 integers, which are numbered from 0 to 9.

You must use a literal number or a previously declared const int or #defined constant in an array

declaration. No regular variables.

In our code when we need to refer to a particular element in an array, we use subscript notation:

[n]

n can be:

an integer literal: [3]

an integer variable: [n]

an integer expression: [n+1]

any scalar expression: ['A'] (= to [65] //ASCII value of 'A')

any function that returns an integer value

NOTE: that in this example, the ['A'] example refers to a non-existent array element - since 'A' is

65 and the array only has room for elements up to [9]. This would almost certainly result in a

program bug. If we had declared numArray to hold 100 values, it would be perfectly fine).

Note that [n] is never used by itself. It is used to specify which array element to refer to, but we

also need to specify which array:

So to refer to the value of a particular array element, we write:

array_name[ arrayelement # ]

So, for example, to assign values to (i.e. store values into) an array:

numArray[0] = 1;

numArray[1] = 3;

numArray[2] = 5;

...

numArray[9] = 19;

Or:

for (i = 0; i < 10; i++)

{

numArray[i] = (i*2) + 1;

}

Page 65: C++ CLASS PDF

Notice how the variable i, used as a subscript, runs from 0 to "less than" 10, i.e. from 0 to 9.

To get values from an array, the same notation is used:

cout << numArray[2];

thirdOdd = numArray[2];

nthOddSquared = numArray[n-1] * numArray[n-1];

Example: To add up the 1st ten elements in an array:

sum = 0;

for (i = 0; i < 10; i++)

{

sum += numArray[i];

}

Remember:

[n] specifies which position you are referring to

numArray[n] evaluates to the value stored in the array at position n

elements are numbered starting at 0

when you declare an array to hold n elements, its subscripts go from 0 to n-1

Now we can solve the problem posed earlier. Assume that a negative number is the end-of-data

signal.

Solution 3 code

#include <iostream>

#include <iomanip>

using namespace std;

const int ARSIZE = 100;

int main()

{

int numArray[ARSIZE]; //Note use of constant

int numberElements = 0, //number of elements in the array

i, //subscript for processing the array

num, //number from the user

sum = 0; //sum of the numbers

double avg;

//read numbers from the user and put them into the array

cout << "Enter an integer number (negative to quit): ";

Page 66: C++ CLASS PDF

cin >> num;

while ( num >= 0 )

{

numArray[numberElements] = num;

numberElements++;

cout << "Enter an integer number (negative to quit): ";

cin >> num;

}

//Add up elements. Note that numberElements is the total number of values

that

//were entered by the user before the negative number

for ( i = 0; i < numberElements; i++ )

{

sum += numArray[i];

}

//Calculate the average of the numbers

avg = (double) sum / numberElements;

//Now go through array and print whether the number is < > = the average

for ( i = 0; i < numberElements; i++ )

{

if ( numArray[i] < avg )

{

cout << numArray[i] << " is less than the average of " << avg << endl;

}

else if ( numArray[i] > avg )

{

cout << numArray[i] << " is greater than the average of " << avg << endl;

}

else

{

cout << numArray[i] << " is equal to the average of " << avg << endl;

}

}

return 0;

}

More Notes on arrays:

Arrays can be initialized when they are declared:

Page 67: C++ CLASS PDF

int ar[5] = { 1, 3, 5, 7, 9 }; //exactly enough values

int ar2[5] = {2, 4, 6}; //the rest are 0

int ar3[] = {1, 4, 9, 16}; //allocates 4 elements at

positions 0..3

char vowels[] = {'a', 'e', 'i', 'o', 'u'}; //5 elements

double sqrts[4] = {1.0, 1.414, 1.732, 2.0};

Executable program statements, such as ones that assign computed values, or data read from the

keyboard or a disk file can also be used to initialize an array.

Just because an array was declared to hold a specific number of values, the entire array does not

have to be used. It's typical to declare an array to hold more values than are expected and have

the program keep track of how many array elements are actually used as values are put into the

array. This is what happened in Solution 3.

Sometimes it is more natural to start at 1, so you could just not use ar[0]:

int monthlenAr[13] = {0, 31, 28, 31, ...};

Then for January, use monthlenAr[1], for February use [2], etc.

Other Misc. Operations

To increment the ith element:

ar[i]++;

ar[i] += 1;

ar[i] = ar[i] + 1;

To add n to the ith element:

ar[i] += n;

ar[i] = ar[i] + n;

To copy the contents of the ith element to the kth element:

ar[k] = ar[i];

To exchange the values in ar[i] and ar[j]:

First, understand that you must declare a "temporary" variable to hold one value, and that it

should be the same data type as the array elements being swapped:

Page 68: C++ CLASS PDF

int temp;

temp = ar[i]; //save a copy of value in i

ar[i] = ar[j]; //copy value from j to i

ar[j] = temp; //copy saved value from i to j

Sample Exercises:

1. Given a character in variable ch, determine if it is a vowel, i.e. if ch matches one of the

values in:

char vowels[] = {'a', 'e', 'i' 'o', 'u'};

2. An array of 10 elements called AR exists. Find and print the smallest element in it.

3. A Fibonnacci sequence consists of a series of integers in which each is equal to the sum

of the of the previous two numbers. The first two values are 1 and 1. So the series is: 1, 1,

2, 3, 5, 8, 13 ...

Write a program to store the first 50 Fibonnaci numbers in an array using executable

statements (ie. don't initialize the array when it's declared). Then fill a second array with

the squares of these numbers.

4. Write a program to read numbers from the keyboard until the user enters a negative

number. Once all of the numbers have been entered, print them out in reverse order.

CSCI 240 Lecture Notes - Part 8

Memory Organization

All variables are stored in memory (often called RAM - random access memory).

Memory is measured using different units. The most common are:

bits - can hold a 0 or a 1 - nothing more. A 2 is too big.

bytes - can hold 8 bits, or a decimal value from 0 to 255

words - depends on the system. Usually 2 or 4 bytes. 2 bytes can hold a value form 0 up

to about 65,000. 4 bytes can hold a value up to about 4 billion.

Note: in the C++ compiler that Quincy uses, the data type int occupies 4 bytes. But since its

values can be positive or negative, its values range from about -2 billion to about +2 billion

because the leading bit is not part of the number, but rather the sign.

Page 69: C++ CLASS PDF

An "unsigned int" can also be declared. It has a range from 0 to about 4 billion (no sign bit -

always positive).

There are also a "long int" or an "unsigned long int" which may have still different sizes

depending on systems. These are official C++ data types.

sizeof (see below) can be used to get the sizes of the different data types, which means that it is

then possible to calculate the ranges of values that can be held.

The uncertainty of the size of various data types in C and C++ is a problem. It was never defined

as part of the language and it's too late to change now. More modern languages such as Java

define the size and therefore the range of numeric data types so there is no uncertainty.

Usually memory is "addressed" in terms of bytes. Memory addresses range from 0 to the

maximum amount on the computer.

We rarely have to know the actual address of a variable, but we do need to understand the idea of

addresses in memory and the fact that variables take up a certain amount of space in memory.

Size of different data types (Quincy C++):

char = 1 byte

int = 4 bytes

float = 4 bytes

double = 8 bytes

string = depends on number of chars

To find the size of a data type or a variable if you don't know it, use the sizeof operator:

sizeof (int); //an expression that evaluates to 4

sizeof x; //evaluates to amount of memory x takes up

Note: sizeof looks a bit like a function, but it's not, really.

So to summarize: each variable has an address (which we usually don't know or care) and a size.

Arrays and Functions

First, understand that for any array, the array starts at some given address (say 1000). Each

element in the array comes after the preceding one in memory, one after another.

A Surprise!

Page 70: C++ CLASS PDF

When an array is passed to a function, the function can change values in the array.

This is unlike anything we have done before when passing arguments to a function. Before,

values were passed as a copy.

An array is passed to a function by passing its name.

//prototype: note the notation for an array of int: int[]

void incrAr(int[]);

//*****************************************

int main()

{

int i;

int ar[4] = {1, 2, 3, 4};

incrAr(ar);

for (i = 0; i < 4; i++)

{

cout << setw(3) << ar[i]; // 2, 3, 4, 5

}

return 0;

}

//*****************************************

void incrAr(int A[])

{

int i;

for (i = 0; i < 4; i++)

{

A[i]++; // this alters values in ar in main()

}

}

So - why does passing an array to a function allow the function to alter the array? What is

different about passing an array and passing a simple variable such as an int or double or float?

The unsubscripted name of an array is the address of the array.

So passing the name of the array to the function is passing the location in memory where the

array was created and therefore the function "knows" where to get and place values.

Page 71: C++ CLASS PDF

In the example above, when we called incrAr(ar); we were giving the address of the array ar to

the function. So any change the function makes to the array is to the (original) array in the

calling function, not to a copy of the array. Since the function has the address of the array, it can

operate directly on it - it "knows where it is".

So, to summarize, the rules for passing arrays to a function are different than the rules for

passing simple data types.

Rules for passing arrays:

1. The function prototype. Declare an array argument as "data-type []"

int doSomething( double [] );

Note: a number can be included in the [], but usually the effective length of the array will

be passed as a 2nd

argument. (Discussed later under #4.)

2. In the function header itself, same as above, but also include a name for the array

argument

int doSomething( double anArray[] )

3. Inside the function, use the subscripted local name as given in the argument list:

int doSomething( double anArray[] )

{

double j;

j = anArray[3];

anArray[5] = 22.5;

return j*2;

}

Note: as explained above, any assignment to an array element will change the actual

value in the array passed by the calling program.

4. As mentioned above, it is typical (there are exceptions) to pass a second argument to a

function that processes an array, to tell it how many elements there are to be processed.

This is sometimes referred to as the "effective size" of the array.

For example, suppose you want to call a function to load values into an array. When you

call this function, you don't know how many values there will be. So you design the

function to pass the array and return the number of values loaded.

int loadAr( int[] ); // note the return type

Page 72: C++ CLASS PDF

Then later, let's say you want to print the array. You design a function to pass the array

and the number of array elements you want to print (i.e. the value returned from

loadAr()).

void printAr( int[], int )

returns void - since there's no "answer" to report back.

1st arg is the array

2nd

arg is the number of elements to process. This was returned by loadAr() and

(presumably) stored into a variable so it could be passed to printAr().

int numArray[100];

int arSize;

arSize = loadAr( numArray );

printAr( numArray, arSize );

....

void printAr( int ar[], int size )

{

int i;

for( i = 0; i < size; i++ )

{

cout << ar[i] << endl;

}

}

It's important to recognize that if a single array element is passed to a function, it is passed by

value (i.e. a copy is passed) since an element of an array is a simple data item.

So if a function is called:

myFn( ar[i] );

Then myFn() cannot alter ar[i] no matter what it does, since ar[i] is a simple integer (or whatever

type ar was declared as).

And, since ar[i] is an integer, the argument in the function prototype for myFn() would need to

be int (not int[]):

void myFn(int);

To reiterate: It is good to recall that unlike arrays, when a simple argument is passed, a copy of

the value of the argument is passed. Code in a function can't directly alter the original value of its

Page 73: C++ CLASS PDF

argument (in the calling function) since it has no idea where it is. It has a copy. It does not have

the address.

Examples:

Function cannot alter original value of the argument passed:

void fn( int );

int main()

{

int num = 5;

cout << num; // 5

fn(num);

cout << num; // still 5

return 0;

}

void fn(int i)

{

cout << i; // 5

i = i + 1;

cout << i; // copy is now 6

}

Notice that a function can be used to indirectly alter a value. If a function is written so it returns a

value:

int fn(int i)

{

cout << i; // 5

i = i + 1;

cout << i; // now 6

return (i);

}

and (for example) main() calls it like this and assigns the return value to a variable:

cout << num; //5

num = fn(num); //num altered by assignment

cout << num; //now 6

Page 74: C++ CLASS PDF

The variable num is altered. But understand clearly that the function didn't alter it - main()

altered it by assigning a value to it.

Sample Exercises:

1. Suppose you have declared two arrays, A and B in a program. Array A already has values

in it, and the number of values is stored in N. Write a function to copy the N values in A

to B. Write the calling statement also.

2. Do the same, but store the values from A into B in reverse order.

3. Write a function to add up and return the sum of all the numbers in an array of int up to

but not including the first 0 value. (Assume there is a 0 value in the array.)

Sorting an Array of Values: Selection Sort

Given an array of variables and a way of comparing them (which we can do with char, int,

double, and float, and later with strings), it is often useful to be able to sort them in the array.

That is, no matter what order they were entered, we can sort them so they end up in numeric (or

alphabetical) order.

There are many ways (algorithms) to do this. Some are simple and some are complex. Some are

slow and some are fast. We will look at one way that is fairly simple and fairly fast, called

selection sort.

Imagine you have an list of numbers:

8 4 2 5 3 7 6 5 9

Look through the whole list and find the smallest. (the 2)

Then exchange the smallest with the first item on the list and put a mark after the first number:

2 | 4 8 5 3 7 6 5 9

Note the | which denotes that everything to the left is (a) already sorted and (b) is smaller than

any value on the right.

Now look through the right-hand part of the list (to the right of the |) and find the smallest in that

part. (the 3)

Exchange it with the first number to the right of the | and move the mark to after the 2nd number

Page 75: C++ CLASS PDF

2 3 | 8 5 4 7 6 5 9

Repeat this:

2 3 4 | 5 8 7 6 5 9

2 3 4 5 | 8 7 6 5 9 // either 5; doesn't

matter

2 3 4 5 5 | 7 6 8 9

2 3 4 5 5 6 | 7 8 9

Now you can see you're done, but if you blindly continue, the results will be the same.

We will now turn this idea into a flowchart to represent computer code, with certain sub-

functions to help control the complexity. (In lecture)

Two-Dimensional Arrays

So far we have used one-dimensional arrays. In C++, you can create multi-dimensional arrays. A

2-dimensional array can be imagined as a grid of cells or elements. To access or specify one of

the elements, you need two subscripts - a row number and a column number, if you like.

In memory, the array elements are laid out in a linear manner (since memory is addressed

linearly). So everything in the first "row" will come before everything in the second "row" and so

on.

Declaration: data_type array_name[ # of rows ][ # of columns ];

int twoDArray[10][5];

this reserves memory for 50 elements.

Let's initialize it to contain some multiplication facts.

int row, col;

for (row = 0; row < 10; row++)

{

for (col = 0; col < 5; col++)

{

twoDArray[row][col] = row*col;

}

}

We would then have:

Page 76: C++ CLASS PDF

0 0 0 0 0 <- row 0

0 1 2 3 4 ...

0 2 4 6 8 ...

0 3 6 9 12 ...

etc.

Understand that these are arranged in memory as just a linear string of values from a beginning

address to an ending address. All of each row is stored before the next. So if you were to look in

memory, you would see:

0 0 0 0 0 0 1 2 3 4 0 2 4 6 8 0 3 6 9 12

2D arrays are passed to functions much like single-dimensional arrays are passed. The main

difference is that you have to declare the number of elements in a row explicitly. That is, you

have to tell C++ how many columns are in a row. Otherwise C++ would not know where one

row ends and the next begins - remember all you pass is the address of the beginning of the

array. So where would array[2][2] be? It would depend on how many elements (columns) are in

a row.

So you'd declare the above array - passed as an argument - as follows:

void aFn(int ar[][5])

{

code to process the array goes here

}

Note that the first dimension can be left empty. Alternately, you can supply it as well: (int

ar[10][5]) in the function header above.

You will study more about multidimensional arrays in later courses.

CSCI 240 Lecture Notes - Part 9

We have seen that when passing simple variables to a function, the function cannot change those

variables.

We have also seen that when passing arrays to a function, the function can change the values in

the array - in part because the thing passed is the address of the array.

It turns out that in C++ a function can change the values of simple variables passed to it. We

have not told you the whole truth - yet. The whole truth is that in C++ there are two slightly

different ways to arrange this.

Page 77: C++ CLASS PDF

When you (the programmer) design a new function, you are in control of when and how you will

do this and when you won't. When you use an existing function, someone else has already

decided. So you must understand both ways to properly supply the arguments if you want to use

other people's functions.

Why would you want to have a function change the value of an argument passed to it?

Suppose the purpose of the function was to calculate two or more answers (results). With the

pass-by-value mechanism we have used up to now, you can't change even one simple variable,

and you can return at most one value.

What if you want two or three answers?

As a simple example, suppose you want to write a function that calculates both the quotient and

the remainder of an integer division. We would like to be able to call it something like this:

divide( dividend, divisor, quotient, remainder );

and have the answers stored into quotient and remainder by the function.

We can do that! One way is by using call-by-reference, the other is call-by-address.

We will cover only call-by-reference in this course. It is new in C++ and is a bit easier. Call-by-

address is used in C and is also supported in C++. You should know both (sorry!) because both

are used in C++ programming.

Call-by-Reference

To pass an argument to a function by reference so that the function can change it, you must

declare a variable of the proper type in the calling program (to hold the value value that

will be passed and changed)

use an & in the function prototype and header after the data type of the argument

alter the argument (using its local name in the function) directly

Here is an example:

Caller:

//prototype

void fn( int&);

//variable declaration

int num;

Page 78: C++ CLASS PDF

//function call

fn(num);

Function:

void fn( int &i )

{

//actually stores 10 into num back in the caller

i = 10;

}

Notice that if you look at the function call itself, you can't tell if it is pass-by-value or pass-by-

reference. Only by looking at the function prototype or header can you tell that the argument is

passed by reference so it can be altered. Likewise, if you look in the function body you can't tell.

The only way to tell is to look at the function prototype or header. That's what the C++ compiler

looks at. When it sees that & it will (quietly) arrange to pass the address of the variable to the

function, so the function can access the original. So pass-by-reference is really passing an

address, but it's done automatically when you use the &.

Here is the divide() function and calling code mentioned above:

Caller:

//prototype

void divide( int, int, int&, int& );

//declarations

int num1 = 20, num2 = 6, quo, rem;

//function call

divide( num1, num2, quo, rem);

//now quo has a value of 3 and rem has a value of 2

Function:

void divide( int dividend, int divisor, int& q, int& r )

{

q = dividend / divisor;

r = dividend % divisor;

}

Page 79: C++ CLASS PDF

Notice that we passed the divisor and dividend by value since we just wanted to use their values

and not alter them. We passed the quotient and remainder by reference since the purpose of the

function is to alter both of these values.

Notice, too, that quotient and remainder were not initialized by the caller since their values are

never actually used in the function, they are just assigned values. But the divisor and dividend

were initialized since their values were used to compute the answers.

For example, remember swap() as explained in lecture? It could be written as a function as

follows:

void swap( double a[], int top, int small )

{

double temp;

temp = a[top];

a[top] = a[small];

a[small] = temp;

}

In this case we had to pass the array and two integers to serve as the subscripts of the elements

we wanted to swap, because the only way to use a function to change a part of the array was to

pass the whole thing.

We would call it as follows:

swap( ar, top, s );

Now, however, we could write it using reference arguments:

void rSwap( double & val1, double & val2)

{

double temp;

temp = val1;

val1 = val2;

val2 = temp;

}

And it would be called by code like this:

rSwap( ar[top], ar[s] );

In fact, any two double in memory could be swapped using this function now, not just doubles in

a particular array.

double first = 4.7;

double second = 6.8;

Page 80: C++ CLASS PDF

rSwap(first, second);

Now first has 6.8 and second has 4.7

More Examples of Call By Reference

Example 1: suppose we have a program that prints (on paper) a multi-page report. We want a

function to print a title or header at the top of each new page, complete with page number.

We will declare a page number variable in main(), but would like the function to increment it

automatically each time it is called.

void pgHeader(int&);

int main()

{

int pageNum = 1;

while (...)

{

// more code ...

if (time to print a pg hdr)

pgHeader(pageNum);

}

return 0;

}

void pgHeader( int& pnum )

{

cout << "The Title " << pnum;

//increment pageNum in main()

pnum++;

}

Note: we could alternately have designed pgHeader() to return an incremented page number (the

old pass-by-value way). For comparison, here it is:

int pgHeader(int);

int main()

{

int pageNum = 1;

Page 81: C++ CLASS PDF

while (…)

{

// code to do something useful…

if (it's time to print a page header)

pageNum = pgHeader(pageNum);

}

return 0;

}

int pgHeader(int pnum)

{

cout << "The Title " << pnum;

pnum++; // increment copy

return (pnum); // new value of page number

}

Example 2: suppose we have a sum and a count, and want to write a function to calculate an

average. However, sometimes the count will be zero, so we also want the function to tell us if it

was able to successfully accomplish its task.

We could design the function to return a value indicating success or failure, and to pass back the

calculated average (when count != 0).

We will return 0 to indicate failure and 1 to indicate success.

int calcAvg( int sum, int count, double &avg )

{

if (count == 0)

return (0);

else

{

avg = (double)sum / count;

return (1);

}

}

And the calling function:

int total, count, retCode;

double avg;

//get values into total and count

retCode = calcAvg(total, count, avg);

if ( retCode == 0 )

cout << "no average calc'd";

else

Page 82: C++ CLASS PDF

cout << "Average is " << avg;

Here, the function is returning one value (the return code) and passing back one value - the

calculated average, when the calculation is performed, i.e. when count is not 0. If the count is 0,

the avg argument is not altered.

Study these examples until they all seem clear to you. Notice the patterns that are common to

each function call. This may take some time. You will probably find it helpful to come back to

them several times. Each time, you will be able to understand them more quickly. After you have

done this, try writing your own similar functions.

Sample Exercises

Here are a few sample exercises to try.

1. Write a function that takes an array of integers and passes back the highest and lowest

value in the array.

2. Write a function that takes an array of doubles and passes back the sum and the average

of the numbers in the array.

3. Write a function that takes an integer number and passes back the sum of the integers up

to that number. For example, passing in 5 would pass back 1 + 2 + 3 + 4 + 5 = 15

Local Reference Variables

We will not have much use for this topic in this course, but you should be aware of it: it is

possible to declare a special variable type that can hold a reference to another simple variable:

int a = 4;

int& b = a;

b = 5;

What does this mean?

Line 1: declare a simple int variable a, and give it a value of 4.

Line 2: declare a reference variable, b, and make it "refer to" an int variable, a. The int & denotes

a new data type, one that can hold a reference to an int. In a sense, b is an alias for a.

Line 3: when we assign 5 to b, we really are assigning 5 to what b refers to, which is a. a now

has the value 5.

We can make reference variables of any simple data type. You may revisit this topic in future

courses.

Page 83: C++ CLASS PDF

CSCI 240 Lecture Notes - Part 10

The string data type

The data type called string which we have used a little is really an example of a C++ class. We

will study more about classes at the end of the course. The point is, it is not really a data type in

the same sense as int, double, char, etc...

Using the string class, we did not know how strings were represented (stored) internally. (How

much memory do they take up? Do they grow and shrink? Is there a maximum size? etc...)

Thus we did not have to specify and control certain internal details, and this simplified things

somewhat, as compared to...

Standard C/C++ strings

The C++ language does not have a string data type. But there is a way to represent and

manipulate sequences of characters (which are usually called strings, just as a short way to refer

to them).

1. There is no such thing as a string data type in standard C++.

2. strings are created as arrays of char.

3. A null character (ASCII 0 or '\0') in the array signals the effective end of the array of

char; that is, it marks the end of the string. Characters in the array beyond the null are

ignored by the functions designed to work on these strings.

4. A set of standard string manipulation functions are provided in the interface defined in

<string.h>. (This contains a set of function prototypes to functions in the C++ standard

libraries.)

Since a string is an array of char, you can manipulate it with array notation (subscripts).

However, it is often easier to use the string functions - which mostly use an address-based

notation rather than array notation. They do not use a reference notation.

We will look at both approaches.

How to you create a string?

1. Define an array of char.

Page 84: C++ CLASS PDF

char s[80];

Note that we hereby reserve 80 bytes of memory for this string. We are not obligated to use it all,

but we better not overfill it or we will be in trouble (because we would overwrite memory after

it).

2. Put some chars in it and end it with a null. For now, let's look at the string as an array. So we

could do this:

s[0] = 'H'; //H as a char

s[1] = 'e';

s[2] = 'l';

s[3] = 'l';

s[4] = 'o';

s[5] = '\0';

Note that because chars and ints are interchangeable in C++, so we could also assign the integer

0 to s[5] since that is the ASCII value for the null character.

However, we cannot do this to insert the null character:

s[5] = '0'; //Char '0' is ASCII 48, not ASCII 0

So we have an 80-byte array, and we use only the first 6 bytes.

However, we can now print it using cout:

cout << s; //prints: Hello

If we had forgotten to put in the terminating null, this cout could print something like:

Hello. %x6^^* #8, ,d @ b,,.....

That is, it would print bytes in memory starting at s, interpreted as chars, until it found (by

chance) a null (zero-valued byte).

There are simpler ways to initialize a string:

1. Based on the fact that a string is an array of char, we can do the following:

char s[80] = {'H', 'e', 'l', 'l', 'o', '\0'}; //here we must use '\0'

in this case, the rest of the array would contain all 0's.

2. There is a shortcut that is allowed by C++:

char s[80] = "Hello";

Page 85: C++ CLASS PDF

in this case also, the rest of the array would contain all 0's.

3. Looking ahead, there is a string function that will copy one string value into another:

char s[80];

strcpy(s, "Hello");

in this case, the rest of the array (after the terminating null) would contain whatever was there

before the strcpy().

Since a string is an array of chars, we can look at individual chars in the string using array

notation.

cout << s[2]; //displays: l

Suppose we want a function that will return the Ith character of a string. We could write it as

follows:

char IthChar(char s[], int i)

{

return s[i];

}

And we could call it as:

cout << IthChar(s, 2); // displays: l

Note how we designed the function:

it returns a char

it takes 2 arguments:

o an array of char (the string)

o the subscript of the char desired

Note also what would happen if we used:

char ch;

ch = IthChar(s, 10); //or

ch = IthChar(s, j); //if j has a value > 5

It would return some random byte (char) that happened to be in memory at 10 bytes (or j bytes)

beyond s - at s[10] or s[j]. But the actual effective string is just "Hello". So we don't know what

is in s[10] or s[j].

We could find out, of course, if we cout-ed the character returned, but it would be meaningless.

It was never assigned a value, so its value is random.

Page 86: C++ CLASS PDF

One way to fix the function so we couldn't make this mistake is to re-design the function to

return a code (0 = fail, 1 = ok, for example), and to pass back the char (if found). We need to

know if the subscript i is beyond the end of the string (the null).

int IthChar(char s[], int i, char &ch)

{

int p;

for( p = 0; p <= i; p++ )

{

// if null is before ith pos in s, return(fail)

if( s[p] == '\0' )

return 0;

}

//if we're here, we got to ith char without encountering '\0'

ch = s[i];

return 1;

}

This considers s simply as an array of char, recognizes that the 0 terminates the effective string,

and makes use of array notation to do all this.

We could (again, looking ahead) use one of the standard C string functions to help. In particular,

there's a function called strlen() that can help:

int IthChar(char s[], int i, int &ch)

{

if (i > strlen(s)-1) //?? check this carefully

return 0; //fail

ch = s[i]; //pass back ith char

return 1; //success

}

Let's check the if-condition carefully. Suppose s has the string "dog"

subscript: 0 1 2 3 4

string: d o g \0 ? etc.

strlen(s) is 3 so we want this condition to be true if i is 3 or more (and then return 0). Another

way to say it is that we want the condition to be false if i is 0, or 1, or 2, but not if it's more than

that (so that we can go on and pass back one of the chars in the string).

So if (i > 2) - for this example - we want the condition to be true so that we will execute the

return(0).. Since strlen(s) is 3, we can code strlen(s)- 1 to represent 2.

Alternately (verify this yourself) we could use >= strlen(s).

Page 87: C++ CLASS PDF

Suppose we want to write a function to change a string to all uppercase.

void strUpper( char s[] )

{

int i; //used as a subscript here

for ( i = 0; s[i] != '\0'; i++ )

s[i] = toupper(s[i]);

}

The ANSI C and C++ String Library functions.

C++ has standard library functions to manipulate strings which are stored as null-terminated

arrays of char.

Every vendor who markets a C++ compiler implements these functions, which are then callable

from any C++ program.

With C++ standard string functions you have to:

create memory for each string by declaring a char array

be sure (one way or the other) that the array of chars has a null terminator

be sure that you don't overflow the array

Here is a summary of a small subset of the string functions:

The arguments called s1 and s2 are names of arrays of char.

A "valid string" is one with a null terminator.

strcpy(s1, s2) This function will copy s2 into s1. s1 may or may not have a valid string in it. s2 must be a valid

string (either a character array with a null terminator OR a string literal). s2 must not be bigger

than s1 or array overflow will result.

strcat(s1, s2) This function concatenates s2 to the end of s1. Both s1 and s2 must be valid strings, but s2 could

be a string literal. There must be room in s1 for the entire result.

int strlen(s1) This function returns the integer length of s1. This is the number of chars in s1 but DOES NOT

include the null terminator.

int strcmp(s1,s2) This function compares the two string arguments alphabetically. It returns:

Page 88: C++ CLASS PDF

a positive value if s1 is alphabetically greater than s2

a negative value if s1 is alphabetically less than s2

0 if s1 is alphabetically equal to s2

For example, suppose you have:

char s1[9], s2[9];

and both have valid strings in them. Suppose you want to concatenate s2 to the end of s1. You

could do a test:

if (strlen(s1) + strlen(s2) < 9)

{

strcat(s1, s2);

}

else

{

cout << "not enough room";

}

More examples will be given in lecture.

CSCI 240 Lecture Notes - Part 11

A Last Clarification on Arrays

We have noted that arrays are passed differently than simple variables. In fact, that's a lie.

In C++, everything is passed by copy. For example:

0. When we code

fn(i);

where i is a simple variable, we are passing a copy of the value of i.

1. When we code

fnA(ar);

where ar is the name of an array, we are really passing a copy of the address of the array.

2. When we code

fnB(&i);

Page 89: C++ CLASS PDF

where i is the name of a simple variable, we are again passing a copy of the addressof i.

3. When we code

fnC(x);

where fnC() defines its argument as a reference argument, C++ is actually supplying a copy of

the address of x when the function is called.

But in all cases, copies are passed.

Passing reference variables is a little different. While it is true that a copy of the address is

passed, the compiler generates code so that if you assign something to that reference argument,

the value-referred-to gets changed (not the address in the reference argument).

Again: any assignment to the passed-in reference argument alters the data at the address passed.

So although the function gets a copy of the address of the data, the function cannot change the

value of that copy (of the address).

Structures

A structure is a custom data type defined by the programmer.

It can hold a collection of variables of different data types (unlike arrays, in which all the data

elements are the same type).

The data items in a structure are usually related (different kinds of information about a person, or

about a part, or about an account, etc...).

Each data item in a structure is called a member. Sometimes these members are also called

fields.

There are several minor variations in structure declarations. We will use the following:

struct new-type-name

{

data-type data-name1;

data-type data-name2;

...

};

Write this before your main() function and before any function prototypes that use this new type.

Then it will be "known" to the compiler for the rest of the compilation. If you declare a struct

inside main(), for example, it will only be "known" inside main() - and thus unusable in any

other function.

Nick
Sticky Note
Nick
Highlight
Page 90: C++ CLASS PDF

Then to declare a variable of this new type, declare in any function that needs one:

new-type-name aVar;

A specific example:

struct Acct

{

char acctNum[10];

char acctName[40];

char acctType;

double acctBal;

};

int main()

{

Acct myAcct; //declare a single variable of this new type.

...

}

Note that the struct definition does not create any variable at all. It is just a template or a pattern.

It takes up no memory and has no address. You still must declare one or more variables of this

new type before you can use it.

Note also that after the above declarations, although we have a variable, it has no defined values

in any of its members.

Again, the struct definition can be placed before the main() function so it is visible to all

functions in the program. However, instances of variables of this type should normally be

declared in the appropriate function or passed as arguments when necessary.

To reference a member of a structure, there is a "dot notation"

myAcct.acctBal = 1000.00;

sets the acctBal member in the structure myAcct to the value 1000.00.

strcpy(myAcct.acctName, "Jane Doe");

sets the acctName member of myAcct to "Jane Doe".

Note that we have an array of char (i.e. a string) as a structure member. Because it is a string, we

have to use appropriate means to manipulate it.

We could not do this:

myAcct.acctName = "Jane Doe"; // NO NO

Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 91: C++ CLASS PDF

We could do this (but why bother?):

myAcct.acctName[0] = 'J';

myAcct.acctName[1] = 'a';

myAcct.acctName[2] = 'n';

...

myAcct.acctName[8] = '\0';

It is allowed to do compile time initialization (similar to other variables) as follows:

Acct anAcct = {"123456789", "Jane Doe", 'S', 1000.00};

Note that the data type of each member is clear.

The order must match the structure definition.

Omitted members (only at the end, if any) are set to 0's (for strings, null

However, this kind of initialization is rarely very useful, except for small test programs.

We can use sizeof on whole structures:

sizeof( Acct ); //the type

sizeof myAcct; //a variable of that type

Both evaluate to the number of bytes occupied by the structure.

Arrays of structs

It is perfectly acceptable and sensible to create an array of structs:

Acct ar[20] //an array that can hold 20 Acct structs

Refer to them as:

//for the whole struct

ar[i]

//for the acctBal member of the struct in ar[i]

ar[i].acctBal

To pass an array of structs to a function, you can use the array notation.

void fn( Acct empAr[] )

Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 92: C++ CLASS PDF

{

empAr[3].acctBal = 300.00; //for example

}

The calling statement would be:

fn( array_of_structs_name );

Returning Structures from Functions

It is allowed to create a structure as a local variable in a function and then return it to the calling

program:

Declare a function:

Acct fn();

then in main:

Acct rec;

rec = fn(); // rec has valid values created by fn()

and the function:

Acct fn()

{

Acct r = {"123", "John Doe", 'C', 200.01};

return r;

}

Passing Structures to Functions

We can pass structures by copy (value) or by reference.

We usually pass by reference because:

structures tend to be large, and making the copy wastes time and memory

usually we will want to change some member or members of the structure

But if a structure is small and/or need not be modified, we can pass by value.

Suppose we want to write a function, incrBal(), to increment the account balance by some

percentage (monthly interest or some such).

Nick
Highlight
Nick
Highlight
Page 93: C++ CLASS PDF

Call-by-Reference

void incrBal( Acct &anAcct, double percentage )

{

anAcct.acctBal = anAcct.AcctBal * (1.0 + percent):

}

The call would look like this:

incrBal(oneAcct, 0.06);

In the case of this particular task, since there is just one thing in the structure being altered, we

could take another approach: just pass a reference to the acctBal member and write a new

version of the function as:

void incrBal( double &bal, double percent )

{

bal = bal * (1.0 + percent);

}

and call it like:

incrBal(oneAcct.acctBal, 0.06);

CSCI 240 Notes - Input and Output

This section of the Notes contains several topics on Input and Output used in various parts of the

course.

cin

cin allows the user to enter values into variables from standard input (the keyboard or via I/O

redirection).

It can accept one or more values, depending on how many << are used.

Its behavior is slightly different depending on the data type being read.

When you code

int i;

cin >> i;

Page 94: C++ CLASS PDF

cin attempts to read an integer from standard input (let's assume the keyboard). It waits until the

user presses <Enter> and then attempts to convert the keystrokes entered into an int, and then

store it in the variable whose name is supplied.

So if the user enters

123<Enter>

the characters '1', '2', and '3' are converted by cin to the integer 123 and this value is stored into

the memory occupied by i.

You can read several values with one cin. Suppose you want the user to enter integer values:

cin >> int1 >> int2 >> int3;

You would separate the numbers with any whitespace character when entering the values.

cin and Input Errors

If the data supplied by the user does not match what is expected by the program, errors can

occur. Sometimes you will get no indication of them, except that the program will not work as

expected.

For example, if the user is supposed to enter a number, but enters:

2w3<Enter>

cin stops at the first non-digit, and converts just what has been entered up to there; in this case

the '2'. So the value 2 would be stored. No error would be signaled. In this case of 2w3 - the w,

the 3, and the \n are still "waiting" to be processed by subsequent input instructions - which are

probably expecting something else - maybe a string representing someone's name. So the "w3"

would be read as the name.

The Keyboard Buffer

Keystrokes (chars) entered by user are stored in a special area in memory called the keyboard

buffer. cin gets chars from there.

Suppose user types _123<Enter> //the _ represents a space

So in kbd buffer is: '_' '1' '2' '3' '\n' //as chars

Or 32 49 50 51 10 //as ASCII values

Page 95: C++ CLASS PDF

cin takes chars from the start (the left), removing them as it processes them. Typing adds chars

to the end (the right).

cin for numeric values (float, double, int, long int, etc.) works like this for keyboard input:

starts its work when user presses <Enter>

scans past leading "white space" (blanks, tabs, newlines)

stops on first non-valid char

converts digits (and +,-,.) into specified internal numeric format, and stores result at

address given by argument

does not check for input errors; just stops on non-valid character

cin for a character will scan past leading whitespace and then take the next char from the

keyboard buffer if there is one. If it is empty, it lets the user type chars until <Enter> is pressed.

Then it gets the 1st char in the buffer.

Reading Multi-Word Strings

We have used cin to read strings (both char arrays and the string class). However, it is flawed for

most purposes because it stops reading when it hits any whitespace character. So if the string you

want to read consists of words separated by spaces, you'll only get the first word. You'd have to

code additional cins or << to get additional words in the string.

For example, to read a first, middle, and last name, you could code:

cin >> fName >> mName >> lName;

But if you don't know how many words are in a line, you are in trouble.

You can use a "method of cin" to read a line of text (up to the \n character) in one operation. This

is object-oriented terminology which will be covered late in the course. For now, just know how

to do this:

cin.getline( strVar, maxlen );

Where

strVar is a string class variable or char array

maxlen is a number specifying the maximum number of characters to store (if you are

storing into a char array, there must be room for a terminating null. So if you specify 80

as the max, only 79 chars from input will be stored plus the null).

If you want to stop inputting characters when you hit some other character, that character can be

a third argument. For example, to stop on a '.', code:

Nick
Highlight
Page 96: C++ CLASS PDF

cin.getline( str, 80, '.' );

Incidentally, there is another method that will read the next character in the keyboard buffer

whether it is a whitespace character or not:

ch = cin.get();

Where ch is a char variable. Note that cin.get() returns the value value of the char it reads.

File Input and Output

You may know that you can run a normal program to write output to a file or read input from a

file using I/O redirection. When you do this, the user of the program must type the name(s) of

these files on the command line. Also, you are limited to one input and one output file.

However, you can also have a program itself connect to and read or write from one or more input

and/or output files. The techniques for doing this are based on object oriented principles which

are not covered until late in this course, but the techniques are nonetheless simple to use.

Files are objects. You can think of an object as a special data type. The data type has a set of

special functions associated with it that only work on variables of the particular type. The

functions are called methods (just a different terminology). We will discuss the methods open(),

fail(), getline(), and close() below.

So - how do we do this?

1. First, you need to

#include <fstream>

2. Then, for each file you want to use, you must declare a variable. The variable type (or the

object type) is either:

ifstream (for input file stream)

ofstream (for output file stream)

The word stream is used because files are treated as a stream or sequence of values, one after

another.

ifstream inFile;

ofstream outFile;

3. Then you must "open" or connect the program to the file.

Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 97: C++ CLASS PDF

inFile.open( "test.txt" );

or

outFile.open( "output.txt" );

Because inFile is of type ifstream, the open method prepares the file to be read.

Because outFile is of type ofstream, the open method prepares the file to be written. (If there is

already a file called output.txt it will be overwritten.)

There are some optional arguments to open() that are covered more fully in the textbook, Chapter

14. For example, you can arrange to append written data to an existing file.

4. Check that the open() succeeded. To do this, code:

if ( inFile.fail() ) // or outFile.fail()

{

cout << "open for test.dat failed";

// exit the program

exit(1);

}

Note: on some systems, the exit() method may require <stdlib.h>. Alternately, if the open is in

main() you could just return.

5. Now you can read and write information from/to the file. For example:

inFile >> num;

outFile << "Hello";

5a. Reading strings with embedded spaces presents some problems, since an ifstream will stop at

the first space. You can use the getline() method (function) to read such strings - see the example

below. Alternately, you can use multiple input operations if you know how many "words" you

want to read:

Example: if your input looks like this:

Chicago IL

inFile >> str1;

inFile >> str2;

then str1 will have "Chicago" and str2 will have "IL". This will be true even if there are extra

spaces or even newline chars between the two words.

Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 98: C++ CLASS PDF

6. Close the files. On some systems (especially for input files) this may not be necessary, but it is

considered good programming practice:

inFile.close();

outFile.close();

Example: the program below copies the contents of one file to another. It reads and writes

strings. When a string is read from a file, the newline character is not put into the string data item

in the program, so when we write each string out to the second file, we have to add it back in.

Also, we use the regular console cout to echo the data to the screen (in addition to writing it to

the file).

Notice that there are two versions of the read-write loop. The first uses a sentinel value to signal

end-of-input (a line with the single character 'x'); the second tests a special return value from

getline:

#include <fstream>

#include <iostream>

using namespace std;

int main()

{

ifstream in;

ofstream out;

char s[80];

//Note the two \\ needed to specify one \ in the string.

//Otherwise, the \t would be taken as a tab and the \o would be an undefined

//escape sequence

in.open( "c:\\temp\\original.dat" );

if (in.fail())

{

cout << "open for original.dat failed";

// exit the program

exit(1);

}

out.open("c:\\temp\\newCopy.dat");

//read-write loop version 1:

//assumes that the last line in the file is "x" This code will NOT

//copy the "x" line

in >> s;

while ( strcmp(s, "x") != 0 )

{

cout << s << "\n";

Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 99: C++ CLASS PDF

out << s << "\n";

in >> s;

}

//read-write loop version 2:

//uses the function getline(). See note below. This version will copy

//the last line (the "x") since it reads data until all of the

//records have been read from the file.

while ( in.getline(s, 80) )

{

cout << s << "\n";

out << s << "\n";

}

in.close();

out.close();

return 0;

}

Note: version 1 above stops reading chars at any whitespace. So a line consisting of several

words separated by spaces would require several reads (in >> s). getline() reads a whole line up

to a newline character (or other delimiter which can be specified as an optional third argument)

so you can easily read such a line.

So getline() reads one line from the file in and stores it into s. The function returns:

true when it can read a line and

false when it can't read a line (because there are no more lines)

So the return value is used to exit the loop upon end-of-file.

I/O of Structures - Block I/O

To read and write structures to disk, you could read and write the individual data members in

order. However, this is inefficient and awkward. It is much neater and more efficient to input and

output a whole structure at a time, with one operation.

Assume you have a structure defined and created in your program. Let's say the structure name is

Person and the structure variable is person:

struct Person

{

char name[40];

Nick
Highlight
Page 100: C++ CLASS PDF

int id;

};

Person person;

Supply a

#include <fstream> // Note: just fstream (not if- or of- stream)

Writing a structure

1. Declare the file and open it for output:

ofstream outfile;

//Note the "ios::binary"

outfile.open( "people.txt", ios::binary );

Don't neglect to check for failure. Use the fail() method of ofstream:

if( outfile.fail() )

{

...

}

2. Populate person with data:

strcpy(person.name, "Joe");

person.id = 1234;

3. Write the structure to disk:

outfile.write( (char *)&person, sizeof(Person) );

Note the typecast of &person to a char *. &person is just an address, but the write method needs

a char * argument. &person doesn't say what kind of data the address is pointing to, but the

function insists that it be a pointer-to-char. So the typecast does that.

So the first argument tells write where to find the data to write, but notice that it doesn't say "this

is a Person thing". It just says where the Person begins.

The second argument tells write how many bytes to write out from that beginning address. In this

way, write will output the correct number of bytes for just one Person.

4. Do steps 3 and 4 as often as needed to create a file of Persons.

5. Close the file:

Nick
Highlight
Nick
Highlight
Nick
Highlight
Page 101: C++ CLASS PDF

outfile.close();

Reading a structure

Assuming you have written a file of Person structures with a program based on the information

in the previous section, you can write a program to read these structures back from disk in a

similar way.

1. Declare the file and open it for input:

ifstream infile;

// Note the ios: stuff

infile.open("people.txt", ios::binary);

Don't neglect to check for failure. Use the fail() method of ifstream:

if (infile.fail())

{

...

}

2. Read the data until end of file:

infile.read((char *)&person, sizeof(Person));

while (infile)

{

// do whatever with this person: print it or ...

infile.read((char *)&person, sizeof(Person));

}

Note that the value of infile is tested as the loop exit condition. infile will be true after a

successful read and false when end-of-file is reached.

3. Close the file:

infile.close();

Building an Array of Structures from input

You can declare an array of structs and then fill it with structs read from disk.

1. Declare the array:

Person people[80];

2. Open the file as usual, but the read commend will look like this:

Page 102: C++ CLASS PDF

infile.read((char *)&people[i], sizeof(Person));

Of course, the subscript i will need to be initialized to 0 and will need to be incremented after

each read so that the next read puts the next struct into the next array element.

CSCI 240 Lecture Notes - Part 12

Object-Oriented Programming

Object-Oriented Programming (OOP) is the next step in modern programming practice after

functional programming (60's) and structured programming (70's).

OOP is complex. The first of three main principles of OOP is that of the class, which is the basis

of OOP.

A class is a kind of a package that consists of functions and the data they work on. However, in

OOP, functions are called methods, and the individual variables defined in a class are called

data members.

An object is an instance of a class that contains data and methods.

So, a class is a template for the creation of multiple instances of that class, each of which is

called an object.

The generic format for a class:

class class_name

{

method_prototype_1;

method_prototype_2;

...

data_type data_member_1;

data_type data_member_2;

...

};

A class for a bank account might resemble:

class Acct

{

//The class variables

char acctNum[10];

char acctName[40];

Page 103: C++ CLASS PDF

char acctType;

double acctBal;

//The class methods

Acct( char [], char [], char, double ); //the "constructor"

void printAcct(); //display the account information

};

The constructor is a special method that is used to supply initial values for the data members.

Before the class can be used, code must be written for the "constructor" and the printAcct()

method:

Acct::Acct( char newAcctNum[], char newAcctName[], char newAcctType, double

newAcctBal )

{

strcpy( acctNum, newAcctNum );

strcpy( acctName, newAcctName );

acctType = newAcctType;

acctBal = newAcctBal;

}

void Acct::printAcct()

{

cout << "Account Number: " << acctNum << " Account Name: " << acctName

<< endl;

if( acctType == 'C' )

{

cout << "Account Type: Checking";

}

else

{

cout << "Account Type: Savings";

}

cout << " Account Number: " << acctBal << endl;

}

Now before we get to a program that uses these, a few notes:

The "constructor" is the method called when you create an instance of a class. Here, its

arguments are stored into the data members of the object. So it is a kind of special initialization

method.

The name of the constructor is always the same as the name of the class.

The notation of Acct:: in front of the constructor and the method are used to specify what class

the methods belong to, since the code for the methods are not defined inside the class definition.

Page 104: C++ CLASS PDF

It would be possible to have some other class with a printAcct() method, for example. It would

be defined as OtherClass::printAcct(). This way the compiler can tell which class a given method

definition belongs to.

Now that the Acct class is defined, we can write a program that uses it. Just type in the class

definition before int main() (the actual methods can go before or after main).

int main()

{

Acct account = Acct( "123456", "Byrnes, Amy", 'C', 1000000.00 );

account.printAcct();

return 0;

}

That's it. Clearly, there's some work up front to design and define the Acct class, but once that's

done, the program becomes simpler.

Note that to add another Acct, we need 1 more variable, 1 initialization statement, and 1 (simple)

method call to print it.

Notice the way the method is called - it's just like the structure dot notation - in this case, the

objectname.methodname(args if any).

Also, notice that the call to printInfo() does not need any arguments! The Acct account "knows"

its account number, account name, etc... (they are data members of account). So since the code in

printAcct() (which is inside account) can "see" the data members (which are also inside account),

no arguments are needed.

It's like main is telling the Acct account to print itself. In fact, in OOP, calling a method of an

object is called "sending a message to the object". In this case, it's the "print yourself" message.

Another thing: each object of the Acct class has its own set of variables (data members). So

calling the printAcct() method for different Acct objects will cause different information to be

printed:

int main()

{

Acct account1 = Acct( "987654", "Amonte, Tony", 'S', 5000000.00);

Acct account2 = Acct( "321098", "Belfour, Ed", 'C', 6000000.25);

account1.printAcct(); //prints the account information for Amonte, Tony

account2.printAcct(); //prints the account information for Belfour, Ed

return 0;

}

Page 105: C++ CLASS PDF

Additional Methods for class Acct

Now that we have the Acct class, we can add functionality to it by adding some useful methods.

All that needs to be done is to add a prototype to the class definition and then code the new

method.

Guarding the Data

One important advantage of OOP is that since the data for an object is kept inside that object, we

can make sure that the data is always valid and consistent by making checks in the object's code.

For example, suppose we wanted to be sure that all of our Accts don't have negative balances.

We could write the constructor so that we would initialize the salary values no matter what the

passed-in values were:

Acct::Acct( char newAcctNum[], char newAcctName[], char newAcctType, double

newAcctBal )

{

strcpy( acctNum, newAcctNum );

strcpy( acctName, newAcctName );

acctType = newAcctType;

if( newAcctBal < 0 )

{

acctBal = 0;

}

else

{

acctBal = newAcctBal;

}

}

Now it's impossible for an Acct to have a negative balance.

Access Methods and Private/Public

There is also another action that can be taken to guard the data. Typically, we do not want

outside methods or functions to be able to access the individual data members directly. To guard

against the access, we will make some class items public and others private.

Public data and methods are "visible" and "accessible" to code outside the object - that is, to

code that is not in the object's methods.

Private data and methods are "invisible" and "inaccessible" to code outside the object. This is

the default access.

The new definition for the class might resemble:

Page 106: C++ CLASS PDF

class Acct

{

private:

char acctNum[10];

char acctName[40];

char acctType;

double acctBal;

public:

Acct( char [], char [], char, double ); //the "constructor"

void printAcct(); //display the account information

};

However, it is possible that an outside method or function will need access to the private data

members or methods. To grant the access...

We will write some public methods to allow other code access to the account number, account

name, etc..., but we write the code to guard against invalid values.

A new version, with a couple of new public access methods follows:

class Acct

{

private:

char acctNum[10];

char acctName[40];

char acctType;

double acctBal;

public:

Acct( char [], char [], char, double ); //the "constructor"

void printAcct(); //display the account information

void set_acctNum( char [] );

void set_acctName( char [] );

void set_acctType( char );

void set_acctBal( double );

};

void Acct::set_acctNum( char newAcctNum[] )

{

if( strcmp( newAcctNum, "" ) != 0 )

{

strcpy( acctNum, newAcctNum );

}

}

void Acct::set_acctName( char newAcctName[] )

{

if( strcmp( newAcctName, "" ) != 0 )

{

Page 107: C++ CLASS PDF

strcpy( acctName, newAcctName );

}

}

void Acct::set_acctType( char newAcctType )

{

if( newAcctType == 'C' || newAcctType == 'S' )

{

acctType = newAcctType;

}

else

{

acctType = 'C';

}

}

void Acct::set_acctBal( double newAcctBal )

{

if( newAcctBal < 0 )

{

acctBal = 0;

}

else

{

acctBal = newAcctBal;

}

}

Now main() (or any other function with access to an Acct object) can reset one or more of the

data members - but never to an invalid value:

account1.set_acctType( 'C' );

account2.set_acctBal( 100.01 );

It's also possible that an outside method or function just wants to retrieve the value in one of the

data members. This can be easily achieved by writing a set of getXXXX access methods that

simply return the value in the specific data member:

double Acct::get_acctBal()

{

return acctBal;

}

char Acct::get_acctType()

{

return acctType;

}

You get the idea (hopefully). Now, to use them:

Page 108: C++ CLASS PDF

cout << account1.get_acctBal();

char type;

type = account1.get_acctType();

Most classes will have a set of getXXX and setXXX access methods.

The getXXX methods usually have no arguments and simply return the value of a particular data

member.

The setXXX methods usually take one or more arguments and do some kind of validity or

consistency checking of their argument(s) before assigning the argument's value to a data

member. They may or may not print an error message, return a success/error code, or "throw an

exception" (another error handling system in C++).

Method Overloading

Sometimes it's convenient to have different versions of a method that do the same thing. That is,

several methods with the same name, but which take different arguments.

One of the most common types of overloading is to overload the constructor.

As an example, take the constructor for the Acct class. It takes 4 arguments. But let's suppose

you have made the following declaration:

struct minimalAcct

{

char number[10];

double balance;

};

and in your program you have created and initialized a couple of these:

minimalAcct min1 = { "24680", 50.00 },

min2 = { "97531", 2670.34 };

Now you could do the following:

Acct account3 = Acct( min1.number, "Flintstone, Fred", 'S', min1.balance );

But it would be nice if you could just do this:

Acct account4 = Acct( min1, "Flintstone, Fred", 'S' );

And - guess what? - You can. Just write a new constructor:

Acct::Acct( minimalAcct acctInfo, char newAcctName[], char newAcctType )

Page 109: C++ CLASS PDF

{

strcpy( acctNum, acctInfo.number );

strcpy( acctName, newAcctName );

acctType = newAcctType;

if( acctInfo.balance < 0 )

{

acctBal = 0;

}

else

{

acctBal = acctInfo.balance;

}

}

Of course, anytime you write any new method, you must write the prototype of the method in the

class definition.

So in a program that uses Accts, you are now free to create Accts using either of the

constructors:

Acct account5 = Acct( "147852", "Olczyk, Eddie", 'C', 65412.90 );

Acct account6 = Acct( min2, "Rubble, Barney", 'S' );

The rule for creating different methods with the same name is that the number, order, or data

type of the arguments must be different in each case (so that the compiler can tell which one to

call). The return value has nothing to do with it. So you could have the following:

void dog(int, int)

int dog(int, double);

void dog(double, int);

double dog(double);

etc.

Here's another example. Suppose you have a private int data member in an object called num,

and you want to write a setNum() method. That's easy:

void ClassType::setNum( int i )

{

num = i; // you might have some "guarding" code also

}

Now you also realize that sometimes the calling program will have the numeric value they want

to use stored in a double. So, for the convenience of the calling program, you can write:

void ClassType::setNum( double i )

{

num = (int) i;

}

Page 110: C++ CLASS PDF

The informal rule is that functions with the same name should do the same thing. You can

sometimes see the following:

int length();

void length(int);

The first is used to return (i.e. get) the length of something:

int len = obj.length();

The second is used to set the length of that thing.

obj.length( 10 );

Although they both have something to do with length, they do very different things, so this is not

a good use of this feature.