Chapter 1Introduction
In this course, we will be looking for generalprinciples underlying the notion of problem solv-ing with computers. Particularly, we are inter-ested in answering the following questions:
1. What are the different ways to solve a prob-lem? How are the solutions related?2. How do we verify the solution indeed solvesthat problem?3. What solution is best for a particular prob-lem? What does that mean by “best”?4. What problems can and can’t be solved byusing a computer?
The goal is to study techniques that will enableyou to develop correct, and efficient, programs.
Before we will do that, we will preview someessential aspects of C++, the programminglanguage we use in this course.
1
Functions and parameters
The following function Abc computes the ex-pression a + b + b ∗ c + (a + b − c)/(a + b) + 4when a, b, and c are integers, and leads to aninteger type result.
#include<iostream.h>
int Abc(int a, int b, int c){
return a+b+b*c+(a+b-c)/(a+b)+4;
}
void main(void){
int x = 2, y = 3;
cout << Abc(2,x,y) << endl;
}
In the above, a, b, and c are the formal param-eters, while 2, x, and y are actual parameters.When the invocation Abc(2, x, y) is executed,a, b, and c are assigned the values of 2, x, and y,respectively. Hence, a, b, and c are called valueparameters.
2
More specifically, at run time, the value of an
actual parameter is copied into the correspond-
ing formal one via a copy constructor for the
data type of the formal parameter. In case the
actual parameter and the formal one are of
different types, a type conversion will be done
first, if it is defined. For example, in the pre-
cious case, if x has a type of float, and has a
value of 3.8, then b will be assigned a value of
3.
When a function terminates, the destructor for
the data type of formal parameters will destroy
the values of the formal parameters. Hence,
the most important side effect of value pa-
rameter is when a function terminates, formal
parameter values are not copied back into the
actual ones. Therefore, you can not bring any
values back.
3
Template functions
Assume that we want to calculate the same
expression as computed by the previous pro-
gram. But, this time, the types of the formal
parameters are float, instead of int. A much
more general approach is to write a template
function to deal with any type of formal pa-
rameters.
template<class T>
T Abc(T a, T b, T c){
return a+b+b*c+(a+b-c)/(a+b)+4;
}
void main(void){
cout << Abc(2,3,4) << endl;
}
This mechanism eliminates the need to know
the actual type when the code is written.
4
Reference parameters
When using value parameters, both copy con-
structors and destructors have to be called im-
plicitly. This can become quite time consum-
ing if some “large” data type, e.g., an array of
1000 pieces, is involved.
In the following code, a, b, and c are reference
parameters, with which the actual parameters
will be bound. In other words, they refer to the
same memory locations. Hence, there will be
no need to copy over the values, and destroy
them afterwards.
template<class T>
T Abc(T& a, T& b, T& c){
return a+b+b*c+(a+b-c)/(a+b)+4;
}
5
Reference in review
A variable declared type & is a reference of the
given type type and must be initialized when
declared. For example,
int a;
int& ra=a;
The reference ra refers to the actual variable
a, and they can be used interchangeably.
In the above, the variable a is the initializer of
the reference ra. In general, such an initializer
must be an lvalue, i.e., something that can be
used on the lefthand side of an assignment.
6
Common lvalues could be variables(a), array
cells(a[i]), and dereferenced pointers(*pa). More
specifically, constants and arithmetic expres-
sions can not initialize references (?). Hence,
the following are wrong.
int& a=256;
Once initialized, a reference can’t be switched.
So, the following is wrong:
int& a=b;
...
int& a=c;
Question: What does the following do?
int& ra=a;
int c=9;
ra=c;
7
Reference continued
Because reference parameter refers to the same
location as actual parameters do, potentially,
the code involved can change the values of the
actual ones. For example,
template<class T>
void Abc(T& a, T& b, T& c){
a=a+b+b*c+(a+b-c)/(a+b)+4;
}
void main(void){
int x = 2, y=3, z=4;
Abc(x,y, z);
cout << x;
}
8
Const references
There is yet another mode of parameter pass-
ing, const reference. If a parameter is so desig-
nated, by prefixing const in the front, the code
will no longer be able to change the values of
such parameters.
For primitive parameters, we use const param-
eters to tell the calls that the function will not
change the values of such parameters. For
parameters of user-constructed types, we use
const references. (?)
9
Reference returned
All the functions we have seen so far make
value returns, when a value to be returned is
copied into a temporary location. If we don’t
return them, their values will be lost, since
when a function terminates, the space for the
local variables will all be freed.
We can also use reference return by suffixing &
to the return type, which will return a reference
to a variable.
For example, T& A Sample(int i, T& z) declares
a function that will return a reference to a vari-
able of type T.
10
An example
#include <iostream.h>
int& maxi(int& x, int& y){
return (x > y ? x:y);
}
void main (){
int a=9, b=9;
cout << "a=" << a << "b=" << b << "\n\n" ;
maxi(a, b)=16;
cout << "a=" << a << "b=" << b << "\n\n" ;
maxi(a, b)-=10;
cout << "a=" << a << "b=" << b << "\n\n" ;
maxi(a, b)++;
cout << "a=" << a << "b=" << b << "\n\n" ;
}
11
In the previous example, a reference to an lvalue
not local to the function is returned.
Question: Can we return a reference to a lo-cal variable?
For example, will the following code work?
#include <iostream.h>
int& doubtful(){
int a=1;
return a;
}
void main(){
cout << doubtful();
}
Answer: No.12
Homework
1.1. Write a template function Input, to bring
in a nonnegative value and verify that this value
is not negative. If it is negative, tell the user
to bring in another. Give the user three oppor-
tunities before she quits. If she does it, return
the valid value via a reference parameter.
1.2. Write a template function to determine
whether the elements in an array a are sorted.
Return true if it is; otherwise, return false.
13
Recursive functions
A recursive function includes at least one call
to itself, either directly or indirectly, through,
possibly, a sequence of function calls. For ex-
ample, given the following factorial function.
f(n) = 1, n ≤ 1
f(n) = 1 × 2 × · · · × n, n > 1.
We have the following iterative algorithm.
int Factorial(int n){
if(n<=1) return 1;
else {
int product=1;
for(int i=1;i<=n;i++)
product*=i;
return product;
}
}
14
Based on the definition of the function, we
can also rewrite it into the following recursive
version.
f(n) = 1, n ≤ 1
f(n) = n × f(n − 1), n > 1,
which leads to the following recursive function.
int RFactorial(int n){
if(n<=1) return 1;
else return(n * RFactorial(n-1);
}
This latter version is more elegant, and cap-
tures the essence of the factorial function.
15
Well defined recursion
A well defined recursive function must meet
the following two requirements:
1. The definition must include a base com-
ponent, in which f(n) is defined directly. For
example, f(n) = 1, n ≤ 1.
2. The parameters in the recursive component
must be smaller and/or simpler. This guaran-
tees that the more complicated recursive ex-
pressions will be eventually transformed into
the base case. For example, f(5) = 5×f(4) =
5 × 4 × f(3) = · · · = 5 × 4 × 3 × 2 × 1 = 120.
Sometimes, it is not easy to see if a function
meets the requirement. For example,
bad(0) = 0
bad(n) = bad(n/3 + 1) + n − 1
16
To be or not to be?
Even though a recursive algorithm is usually
shorter in terms of lines then its non-recursive
cousin, sometimes, it can take much more time
to run, or use more space. For example, if we
compare the recursive and non-recursive algo-
rithm of the Factorial function, we will find
that the former, with n as its actual parame-
ter, uses roughly 2n units of spaces, while the
former uses only 3 units of space.
Another well-known recursive function is the
Fabinocci function, defined as the following:
f(0) = 1,
f(1) = 1,
f(n) = f(n − 1) + f(n − 2), n > 1.
17
If we use the direct definition of Fabinocci
function to write up a recursive function, its
run-time will be exponential, while that of its
iterative cousin is only linear.
long Fib(int m){
if(n<=1) return 1;
else return (Fib(n-1)+Fib(n-1));
}
The reason for this behavior is lots of dupli-
cate values are calculated, which is really not
necessary. We will see later how to improve.
18
Homework
1.3. Write a non-recursive function to calcu-
late n!, and test its correctness.
1.4. (i) Write a recursive function to calculate
the Fibonacci function F(n). (ii) Show that
the function you just wrote calculate the same
F(i) more than once, when n ≥ 2. (iii) Write a
non-recursive function to calculate F(n), which
calculates each F(i) no more than once.
19
More recursion
The following code sums up the values in an
array.
T Sum(T a[], int n){
T tsum = 0;
for (int i = 0; i < n; i++)
tsum += a[i];
return tsum;
}
It can also be put into the following recursive
function.
T Rsum(T a[], int n){
if (n > 0)
return Rsum(a, n-1) + a[n-1];
return 0;
}
20
Permutation
In many cases, recursion provides a powerful
problem solving tool. The generation of per-
mutation provides a demonstrating example.
The permutations of 3 items, e.g., a, b, and c
are abc, acb, bac, bca, cab, and cba.
Given n distinguishable objects: e1, e2, . . . , en,
we want to know 1) how many ways are there
to line them up, i.e., the number of permuta-
tions, and 2) how to construct all these per-
mutations.
Question: If there are 3 burgers, 4 soft drinks,
and 2 side orders, how many valued meals can
you make out of them?
21
Number of permutation
There are n! permutations for n distinguishable
objects.
Nonrecursively, there are n choices for the first
position, n−1 for the second, ..., and 1 for the
last. Hence, n × (n − 1) · × · 1 = n! ways.
Recursively, let M(n) be the number of permu-
tations for n distinguishable objects, we have
that
M(1) = 1
M(n) = n ∗ M(n − 1), n > 1.
Solving the above, we have that M(n) = n!.
22
Constructing permutations
Let E = {e1, . . . , en} denote the n objects, let
Ei be the set obtained by removing ei from E,
let perm(X) be the collection of all the permu-
tations based on X, and let eiperm(X) be all
the permutations of X ∪ {ei} obtained by pre-
fixing each and every permutation in X with
ei. For example, if E = {a, b, c}, then E1 =
{b, c}, perm(E1) = {bc, cb} and e1perm(E1) =
{abc, acb}.
When n = 1, i.e., E = {e}, the only permu-
tation is just e. Otherwise, perm(E) equals to
the union of e1perm(E1), e2perm(E2), . . . , and
enperm(En). For example, if E = {a, b, c}, then
perm(E) is aperm(E1) plus bperm(E2), and plus
perm(E3). Now the code.
23
The function
If we store {e1, . . . , en} in a[n], we will call thefollowing with
Perm(a, 0, n-1);
void Perm(T list[], int k, int m){
int i;
if (k == m) {
for (i = 0; i <= m; i++)
cout << list[i];
cout << endl;
}
else for (i = k; i <= m; i++) {
Swap(list[k], list[i]);
Perm(list, k+1, m);
Swap(list[k], list[i]);
}
}
Question: What is going on?
24
Homework
1.5. Write a recursive function to output all
subsets of n elements.
1.6. Write a recursive function to determine
whether element x is included in the array a[0:n-1].
25
This Swap or that Swap?
In the previous function, we use a Swap functionto exchange the values of two variables. Willthe following version work?
void Swap_Value(T a, T b){
T temp = a;
a = b;
b = temp;
}
Question: What will the following programprint out?
void main(void){
int x = 1, y = 2;
Swap_Value(x, y);
cout << x << y << endl;
}
Question: How could we fix it?Answer: Use reference parameter(s).
26
Proof by induction
When a statement is involved with a natural
number argument, n, we have infinite number
of instances to prove. Proof by mathematical
induction turns out to be an effective way to
deal with the proof of this sort of statements.
The general approach is as follows:
1.Show that some statement, with argument
n, is true for a base case, e.g., n = 1..
2. Assume that the statement is true for n,
prove that it is true for n + 1.
27
An example
Given the following Fibonacci series:
F0 = 1,
F1 = 1,
Fi+1 = Fi + Fi−1, i ≥ 1.
Then, for all i ≥ 1, Fi < (53)
i.
Proof: F1(= 1) < 53. Assume that Fn < (5
3)n,
then we have
Fn+1 = Fn + Fn−1 < (5
3)n + (
5
3)n−1
= (5
3)n+1 · {
3
5+
9
25}
=24
25(5
3)n+1 < (
5
3)n+1.
28
Another example
∀i ≥ 1,n∑
i=1
i2 =n(n + 1)(2n + 1)
6.
Proof by induction: When n = 1, we havethat
LHS =1∑
i=1
i2 = 1 =1 · 2 · 3
6= RHS.
Now, we assume it holds for n, and try to provethat it holds for n + 1, as well:
LHS =n+1∑
i=1
i2 =n∑
i=1
i2 + (n + 1)2
=n(n + 1)(2n + 1)
6+ (n + 1)2
=(n + 1)
6[n(2n + 1) + 6(n + 1)]
=(n + 1)
6(2n2 + 7n + 6)
=(n + 1)
6(n + 2)(2n + 3) = RHS.
29
Watch out!
Claim: The colors of all flowers are the same.
Proof: By induction on n, the number of flow-
ers. The base case is trivial.
Assume the statement is true for n. Observe a
bouquet of n + 1 flowers. Take one, f1, out.
As there are only n flowers left, by inductive
assumption, they must be of the same color, c.
Now, take another one, f2, out of the bouquet
and put f1 back. As the bouquet still has n
flowers, those n flowers must also be of the
same color, particularly, the color of f1 must
be the same as that of those n−1 flowers, i.e.,
c. Thus, the color of all of the n + 1 flowers is
the same. 2
30
Question: What’s wrong?
Answer: When n = 1, n − 1 = 0. Thus, our
claim that “the color of f1 must be the same
as that of those n − 1 flowers” does not hold
up. Thus, F(1) 6⇒ F(2). Thus, the inductive
part, “ for all n ≥ 1, F(n) ⇒ F(n+1)”, doesn’t
work.
If we treat n = 2 as the base case, then, al-
though we do have the inductive statement,
for all n ≥ 2, F(n) ⇒ F(n + 1), we can’t prove
the base case, i.e., the colors of any two flow-
ers are the same. Thus, we still can’t finish
the whole proof.
Lesson: To prove something inductively, both
parts have to be checked out.
31
Homework
1.7. Prove by induction that the following is
true.
n∑
i=0
i2 =n(n + 1)(2n + 1)
6.
1.8. Prove by induction that
∀n ≥ 1, n! ≥ 2n−1.
1.9. Prove by induction that, if one has unlim-
ited access to five-cent and seven-cent stamps,
show that any postage value greater than or
equal to 24 cents can be exactly met.
32
Proof by contradiction
• General approach: To prove some state-
ment is true, assume it is false first. If
this latter assumption leads to a contradic-
tion to what has been known, or common
sense, then that assumption must be false,
i.e., the original statement must be true.
• Example: Either I am tired or sick. If I am
sick, I go home. I am not going home. So,
I am tired.
33
An example
Theorem:√
2 is not a rational number.
Proof: Just suppose that√
2 is rational. Then,
there are integers m′ and n′ such that√
2 =
m′/n′. By dividing both m′ and n′ by all the fac-
tors common to both , we obtain√
2 = m/n
for some integers m and n having no com-
mon factors. Since√
2 = m/n, m = n ×√
2,
i.e.,m2 = 2n2. Therefore, m2 is even. Thus,
m must be even, (?) i.e., for some k, m = 2k.
Replace m with 2k, we have that 4k2 = 2n2.,
i.e., n2 = 2k2. By the same token, for some
j, n = 2j. Hence, both m and n has a common
factor of 2. This is a contradiction.
Hence,√
2 must not be rational. 2
34
Dynamic memory allocation
To dynamically assign space for an (integertype of) variable, we must declare another vari-able as a pointer first.
int * y;
When the program needs to actually use thevariable, we have to use new to ask for thespace:
y = new int;
Then, we can manipulate *y, the integer typevariable pointed by y, e.g.,
*y = 10;
These three steps can also be combined, e.g.,
int * y = new int (10);
35
An example
Below shows an example on the relationship
between pointers and arrays: one of the mostimportant application of the pointer type.
#include <iostream.h>
typedef int* intPointer;
void main(){
int Array[5]={0,1,2,3,4};
intPointer p;
p=Array;
p=p+3;
cout << *p << endl;
}
Question: What will be the output?
Answer: 3.36
Yet another Swap?
Using the ideas of pointers, we can come up
with another example of a Swap function as fol-
lows:
void Swap_via_pointer(T* pa, T* pb){
T temp = *pa;
*pa = *pb;
*pb = temp;
}
Question: What will the following program
print out?
void main(void){
int x = 1, y = 2;
Swap_Value(&x, &y);
cout << x << y << endl;
}
37
Exception handling
We can also dynamically set up an array.
float * a = new float[100];
But, what happens if the computer does not
have this much space? An exception will oc-
cur. More specifically, The exception xalloc
will be thrown by new, when the latter is un-
able to assign the space. This exception can
then be caught. For example,
float *a;
try {float * a = new float[100];}
catch (xalloc){
ceer << "Out of Memory" << endl;
exit(1);
}
38
Set up an array
The following should be pretty familiar:
int a[2][4];
a[1][2]=12;
When dynamically declaring a 2-d array, if both
dimensions are known at the coding time, it is
piece of cake.
int* a= new char[2][4];
a[2][3]=23;
Question: What do the following do?
int *p=&a[0][0];
cout << *(p+i*4+j);
39
What is going on?
The model of the memory is a linear one. Thus,
when we want to get a piece of 2-d array,
once we have secured a piece of space from
the memory, there has to be a way to map
each and every element in the 2-d array to a
location in the memory. There are basically
two ways to do that: row major and column
major.
In most of the modern languages, the row ma-
jor mechanism is used, namely, the first row is
put in first, then the second, ..., until the last
row. For each row, the first element of that
row is put in, then the second, ..., until the
last one.
40
Questions
Question: If the first element of a[m,n] is
mapped to the starting address start, where
is a[i,j]?
Answer: start +(i ∗ n+j).
Homework 1.10. If the first element of
a[n_1,n_2,n_3] is mapped to the starting ad-
dress start, where is a[i,j,k]? What about
the general case when a has m dimensions?
41
A couple of notes
1. Dynamically assigned space should be freed,when it is no longer needed, by using delete
y, for a single variable; or delete [] a, for anarray.
2. When both dimensions of an array are known
when we write it up, it is easy to set up, e.g.,c[7][5].
3. If the number of rows is unknown, but thenumber of columns is known, it can still be
created during the compilation time as follow:
char (*c)[5];
try {
c=new char [n][5];
}
catch (xalloc) {
ceer << "out of memory" << endl;
}
42
4. If we don’t know the number of columns,
we have to regard it as a collection of several
dynamically created rows, whose pointers can
then be saved in another array.
bool Make2DArray(T ** &x, int rows, int cols){
try {
x = new T * [rows];
for (int i=0; i < rows; i++)
x[i]=new int [cols];
return true;
}
catch(xalloc)
{return false;}
}
43
5. The above definition reports any possi-
ble exception to the invoking function with a
Boolean value of false. Another approach is
to do nothing when there is an exception, and
let the invoking function handles it. For exam-
ple,
void Make2DArray(T ** &x, int rows, int cols){
x = new T * [rows];
for (int i=0; i < rows; i++)
x[i]=new int [cols];
}
void main(void){
try {Make2DArray(x, 4, 5);}
catch (xalloc){
ceer << "Could not create x" << endl;
exit(1);
}
}
44
6. The memory assigned this way can be freed
by freeing the rows first, then freeing the space
allocated for the row pointers.
void Delete2DArray(T ** &x, int rows){
for (int i=0; i < rows; i++)
delete [] x[i];
delete [] x;
x = 0;
}
In the above code, x is assigned a NULL pointer
to prevent it being used.
45
Homework
1.11. Assume that a 1-d array a[0:size-1]
stores a collection of elements. If we have n
elements, and if n exceeds size, they will not
fit in a. We need to enlarge a in this case.
On the other hand, if n is less than size, we
need to reduce size to make it more efficient.
Write a function ChangeSize1D to change the
size of array a from Size to ToSize. Your func-
tion should allocate space for a new 1-d array
of size ToSize, copy the elements over, then
free the space allocated to a[0:size-1].
1.12. Write a function ChangeSize2D to change
the size of a 2-d array.
46
Classes
C++ only supports very few atomic data types.The most flexible way to define our own datatypes is to use the class construct. As an ex-ample, assume we need to deal with objectsof Currency type. Every instance of such atype has a sign component, a dollar compo-nent, and a cents component. For example,$4.25 and -$17.18.
We also need to provide such operations assetting values, determining values, arithmeti-cally evaluating expressions involved with cur-rencies, incrementing values, outputting val-ues, etc..
Assume that we choose to represent the sign
type as the following:
enum sign {plus, minus};
we can define the Currency type as follows:
47
Class Currency{
public:
Currency(sign s=plus, unsigned long d=0,
unsigned long c=0);
~Currency(){};
bool Set(sign s, unsigned long d, unsigned c);
bool Set(float a);
sign Sign()const {return sgn;}
unsigned long Dollars() const{return dollars;}
unsigned long Cents() const{return cents;}
Currency Add(const Currency& x) const;
Currency& Increment(const Currency& x);
void Output() const;
private:
sign sgn;
unsigned long dollars;
unsigned int cents;
};
48
Everything declared in the public section is vis-
ible to the users, while those declared in the
private section are not. Hence, we can hide
information, particularly, those implementation
details, from the users.
The function that has the same name as that
of the class is called the constructor of that
class. It will be automatically called to create
objects for that class. For example, the follow-
ing code will construct several objects of type
Currency with various initial values.
Currency f, g(plus, 3, 45), h(minus, 10);
Currency * m = new Currency(plus, 8, 12);
Similarly, ~Currency is called the destructor of
the Currency type.
The rest of the functions are used to carry out
other operations.
49
Class definition
All the functions have to be defined. Below
show some examples.
Currency::Currency(sign s, unsigned long d,
unsigned int c){
if (c > 99){
cerr << "Cents should be < 100" << endl;
exit(1);
}
sgn = s; dollars = d; cents = c;
}
bool Currency::Set(float a){
if (a < 0) {sgn = minus; a = -a;}
else sgn = plus;
dollars = a;
cents = (a - dollars) * 100;
return true;
}
50
Currency Currency::Add(const Currency& x) const{
long a1, a2, a3;
Currency ans;
a1 = dollars * 100 + cents;
if (sgn == minus) a1 = -a1;
a2 = x.dollars * 100 + x.cents;
if (x.sgn == minus) a2 = -a2;
a3 = a1 + a2;
if (a3 < 0) {ans.sgn = minus; a3 = -a3;}
else ans.sgn = plus;
ans.dollars = a3 / 100;
ans.cents = a3 - ans.dollars * 100;
return ans;
}
Since ans is a local variable, when the function
terminates, its space is destroyed. Thus, we
have to copy it back to the invoking environ-
ment via a value return.51
Currency& Currency::Increment(const Currency& x){
*this = Add(x);
return *this;
}
When we call the above using
g.Increment(h);
the currency object g invokes the public func-
tion Increment to get a copy of an object what
is the sum of g and h, which is assigned to
*this, namely, g itself, which is then return.
Notice that since a reference to this global vari-
able is returned, there is no need to make an
explicit copy, thus some time is saved.
52
A complete program
Assume the Currency class is declared and de-
fined in currency.h file, then the following pro-
gram can be used as a driver.
void main(void)
{
Currency g, h(plus, 3, 50), i, j;
g.Set(minus, 2, 25);
i.Set(-6.45);
j = h.Add(g);
j.Output(); cout << endl;
i.Increment(h);
i.Output(); cout << endl;
j = i.Add(g).Add(h);
j.Output(); cout << endl;
j = i.Increment(g).Add(h);
j.Output(); cout << endl;
i.Output(); cout << endl;
}
53
Homework
1.13. (i) What are the maximum and minimumcurrency values permissible in the Currency class,assuming that an object of type long is repre-sented with 4 bytes, and that of type int withtwo bytes. (ii) Answer the same questions asin (i) if the data types of dollar and cents arechanged to int. (iii) If function Add is usedto add two currency amounts, what are theirlargest possible values so that no error wouldoccur when converting type Currency to long
int as done to variables a1 and a2?
1.14. Extend the Currency class by adding inan input function that input a currency valuefrom the keyboard and assign it to the invokingobject; a subtract function that does the op-posite as Add does; a percent(X) function thatreturns X percent of the value of the invok-ing object; and multiply(X), which returns theproduct of X and the value of the invoking ob-ject.
54
Operator overloading
In the definition of the Currency type, it ismore natural to use such standard operatorsas + and +=, instead of Add and Increment.
We can overload these standard operators.
class Currency {
public:
...
Currency operator+(const Currency& x) const;
...
private:
long amount;
};
Currency Currency::operator+
(const Currency& x) const
Currency y;
y.amount = amount + x.amount;
return y;
}
55
A more friendly program
#include <iostream.h>
#include "currency.h"
void main(void)
{
Currency g, h(plus, 3, 50), i, j;
g.Set(minus, 2, 25);
i.Set(-6.45);
j = h + g;
cout << j << endl;
i += h;
cout << i << endl;
j = i + g + h;
cout << j << endl;
j = (i+=g) + h;
cout << j << endl;
cout << i << endl;
}
56
Friends and protected members
In some cases, we must give access to some
private components to other classes and func-
tions. This may be done by declaring these
classes and/or functions friends. For example,
for our Currency class, we may want to over-
load the standard output operator <<. As the
latter cannot access the private data compo-
nents, we have to declare it as a friend func-
tion. For example,
class Currency{
friend ostream& operator<<
(ostream&, const Currency&);
public:
...
}
57
Later we will see that a class can be derived
from another class. The former is called the
derived class, and the latter is called the base
class. In order to grant access to the derived
class to the private components of the base
class, we have to declare those components
protected. Those component will then behave
like private members, except they are accessi-
ble to the derived classes.
To reiterate, class components that are to be
accessible to user application and all the other
classes should be declared as public. Data
components should never be included in this
category. As a result, we may change the data
component later on, without touching the ap-
plication codes, since the former is transparent
from the latter’s point of view.
Homework 1.15. Redo Exercise 10 following
the more friendly approach.
58
Testing and debugging
By testing, we mean executing the completed
code using input data, and compare the out-
put with the expected behavior. If they don’t
match, we have a problem. On the other hand,
even if they match, we still can not rule out
the possibility that the program is incorrect,
because we can’t test the program on all the
(infinite) number of cases. However, by using
many sets of test data and verify that what
we observe do match with what we expect, we
can increase our confidence on the correctness
of the program.
59
How to do it?
There are two general techniques in dealing
with designing test data, black box and white
box.
In a black box method, we consider the func-
tion, but not the actual code; while in the
white box case, we go through the code to
design test data that will cover a good por-
tion of the statements and logical paths in the
program.
Thus, we either ensure that all the statements
will be executed once, or all the conditions will
be tested once for both the true and false val-
ues.
60
An example
Consider the following piece of code
template<class T>
int Max(T a[], int n){
int pos=0; \\1.
for(int i=1; i<n; i++) \\2.
if(a[pos]<a[i])\\3.
pos=i;\\4.
return pos;\\5.
}
the data set of a[0:4]=[2, 4, 6, 8, 10] pro-vides a statement coverage, but not decisioncoverage, since the only condition never be-comes false.
If we sequence the statements in their order ofexecution by a certain set of data, we get anexecution path. Execution path coverage re-quires the use of a set of test data that causesall the paths be executed, which is usually themost demanding requirement.
61
Homework
1.16. Consider the Max function. When n = 1,
there is only one execution path: (1, 2, 5);
when n = 2, there are two: (1,2,3,2,5) and
(1,2,3,4,2,5), depending on if the input is al-
ready ordered. Design a test set that provide
execution path coverage when n = 4.
1.17. How many execution paths exist in the
Sum function? Design a test set that provide
execution path coverage when n = 4.
1.18. How many execution paths exist in the
RSum function? Design a test set that provide
execution path coverage when n = 4.
62