
Beginning Visual C++ 2005 (2006) [eng]-1
.pdf
More about Program Structure
A function template has one or more type parameters, and you generate a particular function by supplying a concrete type argument for each of the template’s parameters. Thus the functions generated by a function template all have the same basic code, but customized by the type arguments that you supply. You can see how this works in practice by defining a function template for the function max() in the previous example.
Using a Function Template
You can define a template for the function max() as follows:
template<class T> T max(T x[], int len)
{
T max = x[0];
for(int i = 1; i < len; i++)
if(max < x[i]) max = x[i];
return max;
}
The template keyword identifies this as a template definition. The angled brackets following the template keyword enclose the type parameters that are used to create a particular instance of the function separated by commas; in this instance you have just one type parameter, T. The keyword class before the T indicates that the T is the type parameter for this template, class being the generic term for type. Later in the book you will see that defining a class is essentially defining your own data type. Consequently, you have fundamental types in C++, such as type int and type char, and you also have
the types that you define yourself. Note that you can use the keyword typename instead of class to identify the parameters in a function template, in which case the template definition would look like this:
template<typename T> T max(T x[], int len)
{
T max = x[0];
for(int i = 1; i < len; i++)
if(max < x[i]) max = x[i];
return max;
}
Some programmers prefer to use the typename keyword as the class keyword tends to connote a userdefined type, whereas typename is more neutral and therefore is more readily understood to imply fundamental types as well as user-defined types. In practice you’ll see both keywords used widely.
Wherever T appears in the definition of a function template, it is replaced by the specific type argument, such as long, that you supply when you create an instance of the template. If you try this out manually by plugging in long in place of T in the template, you’ll see that this generates a perfectly satisfactory function for calculating the maximum value from an array of type long:
long max(long x[], int len)
{
long max = x[0];
for(int i = 1; i < len; i++) if(max < x[i])
289

Chapter 6
max = x[i]; return max;
}
The creation of a particular function instance is referred to as instantiation.
Each time you use the function max() in your program, the compiler checks to see if a function corresponding to the type of arguments that you have used in the function call already exists. If the function required does not exist, the compiler creates one by substituting the argument type that you have used in your function call in place of the parameter T throughout the source code in the corresponding template definition. You could exercise the template for max() function with the same main() function that you used in the previous example.
Try It Out |
Using a Function Template |
Here’s a version of the previous example modified to use a template for the max() function:
//Ex6_08.cpp
//Using function templates
#include <iostream> using std::cout; using std::endl;
// Template for function to compute the maximum element of an array template<typename T> T max(T x[], int len)
{
T max = x[0];
for(int i = 1; i < len; i++) if(max < x[i])
max = x[i]; return max;
}
int main(void)
{
int small[] = { 1, 24, 34, 22};
long medium[] = { 23, 245, 123, 1, 234, 2345};
double large[] = { 23.0, 1.4, 2.456, 345.5, 12.0, 21.0};
int lensmall = sizeof small/sizeof small[0]; int lenmedium = sizeof medium/sizeof medium[0]; int lenlarge = sizeof large/sizeof large[0];
cout << endl << max(small, lensmall); cout << endl << max(medium, lenmedium); cout << endl << max(large, lenlarge);
cout << endl; return 0;
}
If you run this program, it produces exactly the same output as the previous example.
290

More about Program Structure
How It Works
For each of the statements outputting the maximum value in an array, a new version of max() is instantiated using the template. Of course, if you add another statement calling the function max() with one of the types used previously, no new version of the code is generated.
Note that using a template doesn’t reduce the size of your compiled program in any way. The compiler generates a version of the source code for each function that you require. In fact, using templates can generally increase the size of your program, as functions can be created automatically even though an existing version might satisfactorily be used by casting the argument accordingly. You can force the creation of particular instances of a template by explicitly including a declaration for it. For example, if you wanted to ensure that an instance of the template for the function max() was created corresponding to the type float, you could place the following declaration after the definition of the template:
float max(float, int);
This forces the creation of this version of the function template. It does not have much value in the case of our program example, but it can be useful when you know that several versions of a template function might be generated, but you want to force the generation of a subset that you plan to use with arguments cast to the appropriate type where necessary.
An Example Using Functions
You have covered a lot of ground in C++ up to now and a lot on functions in this chapter alone. After wading through a varied menu of language capabilities, it’s not always easy to see how they relate to one another. Now would be a good point to see how some of this goes together to produce something with more meat than a simple demonstration program.
Let’s work through a more realistic example to see how a problem can be broken down into functions. The process involves defining the problem to be solved, analyzing the problem to see how it can be implemented in C++, and finally writing the code. The approach here is aimed at illustrating how various functions go together to make up the final result, rather than providing a tutorial on how to develop a program.
Implementing a Calculator
Suppose you need a program that acts as a calculator; not one of these fancy devices with lots of buttons and gizmos designed for those who are easily pleased, but one for people who know where they are going, arithmetically speaking. You can really go for it and enter a calculation from the keyboard as a single arithmetic expression, and have the answer displayed immediately. An example of the sort of thing that you might enter is:
2*3.14159*12.6*12.6 / 2 + 25.2*25.2
To avoid unnecessary complications for the moment, you won’t allow parentheses in the expression and the whole computation must be entered in a single line; however, to allow the user to make the input look attractive, you will allow spaces to be placed anywhere. The expression entered may contain the
291

Chapter 6
operators multiply, divide, add, and subtract represented by *, /, + and — respectively, and the expression entered will be evaluated with normal arithmetic rules, so that multiplication and division take precedence over addition and subtraction.
The program should allow as many successive calculations to be performed as required, and should terminate if an empty line is entered. It should also have helpful and friendly error messages.
Analyzing the Problem
A good place to start is with the input. The program reads in an arithmetic expression of any length on a single line, which can be any construction within the terms given. Because nothing is fixed about the elements making up the expression, you have to read it as a string of characters and then work out within the program how it’s made up. You can decide arbitrarily that you will handle a string of up to 80 characters, so you could store it in an array declared within these statements:
const int MAX = 80; |
// |
Maximum expression length |
including ‘\0’ |
char buffer[MAX]; |
// |
Input area for expression |
to be evaluated |
To change the maximum length of the string processed by the program, you will only need to alter the initial value of MAX.
You need to understand the basic structure of the information that appears in the input string, so let’s break it down step-by-step.
Make sure that the input is as uncluttered as possible when you are processing it, so before you start analyzing the input string, you will get rid of any spaces in it. You can call the function that will do this eatspaces(). This function can work by stepping through the input buffer — which is the array buffer[] — and shuffling characters up to overwrite any spaces. This process uses two indexes to buffer array, i and j, which start out at the beginning of the buffer; in general, you’ll store element j at position i. As you progress through the array elements, each time you find a space you increment j but not i, so the space at position i gets overwritten by the next character you find at index position j that is not a space. Figure 6-2 illustrates the logic of this.
The buffer array before copying its contents to itself
index j 0 1 2 3 4 5 6 7
Index i is not incremented at these positions because they contain a space.
These spaces are overwritten by the next non-space character that is found in buffer.
2 |
+ |
5 * 3 \0 |
..... |
|
2 |
+ |
5 |
* |
3 |
\0 |
|
|
|
|
..... |
|
index i 0 |
1 |
2 |
3 |
4 |
5 |
|
|
|
|
|
|
The buffer array after copying its contents to itself
Figure 6-2
292

More about Program Structure
This process is one of copying the contents of the array buffer[] to itself, excluding any spaces. Figure 6-2 shows the buffer array before and after the copying process and the arrows indicate which characters are copied and the position to which each character is copied.
When you have removed spaces from the input you are ready to evaluate the expression. You define the function expr() which returns the value that results from evaluating the whole expression in the input buffer. To decide what goes on inside the expr() function, you need to look into the structure of the input in more detail. The add and subtract operators have the lowest precedence and so are evaluated last. You can think of the input string as comprising one or more terms connected by operators, which can be either the operator + or the operator -. You can refer to either operator as an addop. With this terminology, you can represent the general form of the input expression like this:
expression: term addop term ... addop term
The expression contains at least one term and can have an arbitrary number of following addop term combinations. In fact, assuming that you have removed all the blanks, there are only three legal possibilities for the character that follows each term:
The next character is ‘\0’, so you are at the end of the string
The next character is ‘-’, in which case you should subtract the next term from the value accrued for the expression up to this point
The next character is ‘+’, in which case you should add the value of the next term to the value of the expression accumulated so far
If anything else follows a term, the string is not what you expect, so you’ll display an error message and exit from the program. Figure 6-3 illustrates the structure of a sample expression.
2 + 5 * 3 - 7 / 2 \0 |
..... |
End of input
term |
term |
term |
addop |
|
addop |
Figure 6-3
Next, you need a more detailed and precise definition of a term. A term is simply a series of numbers connected by either the operator * or the operator /. Therefore, a term (in general) looks like this:
term: number multop number ... multop number
293

Chapter 6
multop represents either a multiply or a divide operator. You could define a function term() to return the value of a term. This needs to scan the string to first a number and then to look for a multop followed by another number. If a character is found that isn’t a multop, the term() function assumes that it is an addop and return the value that has been found up to that point.
The last thing you need to figure out before writing the program is how you recognize a number. To minimize the complexity of the code, you’ll only recognize unsigned numbers; therefore, a number consists of a series of digits that may be optionally followed by a decimal point and some more digits. To determine the value of a number you step through the buffer looking for digits. If you find anything that isn’t a digit, you check whether it’s a decimal point. If it’s not a decimal point it has nothing to do with a number, so you return what you have got. If you find a decimal point, you look for more digits. As soon as you find anything that’s not a digit, you have the complete number and you return that. Imaginatively, you’ll call the function to recognize a number and return its value number(). Figure 6-4 shows an example of how an expression breaks down into terms and numbers.
|
expression |
|
|
term addop |
term |
|
addop term |
number |
number multop number |
number |
|
digit digit |
digit point digit |
digit digit |
digit digit |
The value of the expression is returned by the expr() function
The value of each term is returned by the term() function
The value of each number is returned by the number() function
2 |
3 |
+ |
3 |
. |
5 |
* |
1 |
7 |
- |
1 |
2 |
\0 |
..... |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
End of input |
|
Figure 6-4 |
|
|
You now have enough understanding of the problem to write some code. You can work through the functions you need and then write a main() function to tie them all together. The first and perhaps easiest function to write is eatspaces(), which is going to eliminate the blanks from the input string.
Eliminating Blanks from a String
You can write the prototype for the eatspaces() function as follows:
void eatspaces(char* str); |
// Function to eliminate blanks |
294

More about Program Structure
The function doesn’t need to return any value because the blanks can be eliminated from the string in situ, modifying the original string directly through the pointer that is passed as the argument. The process for eliminating blanks is a very simple one. You copy the string to itself, overwriting any spaces as you saw earlier in this chapter.
You can define the function to do this as follows:
// Function to eliminate spaces from a string |
|
void eatspaces(char* str) |
|
{ |
|
int i = 0; |
// ‘Copy to’ index to string |
int j = 0; |
// ‘Copy from’ index to string |
while((*(str + i) = *(str + j++)) != ‘\0’) |
// Loop while character is not \0 |
if(*(str + i) != ‘ ‘) |
// Increment i as long as |
i++; |
// character is not a space |
return; |
|
} |
|
|
|
How the Function Functions
All the action is in the while loop. The loop condition copies the string by moving the character at position j to the character at position i and then increments j to the next character. If the character copied was ‘\0’, you have reached the end of the string and you’re done.
The only action in the loop statement is to increment i to the next character if the last character copied was not a blank. If it is a blank, i is not be incremented and the blank can therefore be overwritten by the character copied on the next iteration.
That wasn’t hard, was it? Next, you can try writing the function that returns the result of evaluating the expression.
Evaluating an Expression
The expr()function returns the value of the expression specified in the string that is supplied as an argument, so you can write its prototype as follows:
double expr(char* str); |
// Function evaluating an expression |
The function declared here accepts a string as an argument and returns the result as type double. Based on the structure for an expression that you worked out earlier, you can draw a logic diagram for the process of evaluating an expression as shown in Figure 6-5.
295

Chapter 6
Get value of first term
Set expression value to value of first term
Next character |
Return expression |
|
is '/0'? |
value |
|
Next character |
Subtract value of next |
|
term from expression |
||
is '-'? |
||
value |
||
|
||
Next character |
Add value of next |
|
term from expression |
||
is '+'? |
||
value |
||
|
||
ERROR |
|
|
Figure 6-5 |
|
Using this basic definition of the logic, you can now write the function:
// Function to evaluate an arithmetic expression
double expr(char* str) |
|
{ |
|
double value = 0.0; |
// Store result here |
int index = 0; |
// Keeps track of current character position |
value = term(str, index); |
// Get first term |
for(;;) |
// Indefinite loop, all exits inside |
296

|
More about Program Structure |
|
|
{ |
|
switch(*(str + index++)) |
// Choose action based on current character |
{ |
|
case ‘\0’: |
// We’re at the end of the string |
return value; |
// so return what we have got |
case ‘+’: |
// + found so add in the |
value += term(str, index); |
// next term |
break; |
|
case ‘-’: |
// - found so subtract |
value -= term(str, index); |
// the next term |
break; |
|
default: |
// If we reach here the string |
cout << endl |
// is junk |
<<“Arrrgh!*#!! There’s an error”
<<endl;
exit(1);
}
}
}
How the Function Functions
Considering this function is analyzing any arithmetic expression that you care to throw at it (as long as it uses our operator subset), it’s not a lot of code. You define a variable index of type int, which keeps track of the current position in the string where you are working, and you initialize it to 0, which corresponds to the index position of the first character in the string. You also define a variable value of type double in which you’ll accumulate the value of the expression that is passed to the function in the char array str.
Because an expression must have at least one term, the first action in the function is to get the value of the first term by calling the function term(), which you have yet to write. This actually places three requirements on the function term():
1.It should accept a char* pointer and an int variable as parameters, the second parameter being an index to the first character of the term in the string supplied.
2.It should update the index value passed to position it at the character following the last character of the term found.
3.It should return the value of the term as type double.
The rest of the program is an indefinite for loop. Within the loop, the action is determined by a switch statement, which is controlled by the current character in the string. If it is a ‘+’, you call the term()function to get the value of the next term in the expression and add it to the variable value. If it is a ‘-’, we subtract the value returned by term() from the variable value. If it is a ‘\0’, you are at the end of the string, so you return the current contents of the variable value to the calling program. If it is any other character, it shouldn’t be there, so after remonstrating with the user you end the program!
As long as either a ‘+’ or a ‘-’ is found, the loop continues. Each call to term() moves the value of the index variable to the character following the term that was evaluated, and this should be should be either another ‘+’ or ‘-’, or the end of string character ‘\0’. Thus, the function either terminates
297

Chapter 6
normally when ‘\0’ is reached, or abnormally by calling exit(). You need to remember the #include directive for <cstdlib> header file that provides the definition of the function exit() when you come to put the whole program together.
You also could also analyze an arithmetic expression using a recursive function. If you think about the definition of an expression slightly differently, you could specify it as being either a term, or a term followed by an expression. The definition here is recursive (i.e. the definition involves the item being defined), and this approach is very common in defining programming language structures. This definition provides just as much flexibility as the first, but using it as the base concept, you could arrive at a recursive version of expr() instead of using a loop as you did in the implementation above. You might want to try this alternative approach as an exercise after you have completed the first version.
Getting the Value of a Term
The term() function returns a value for a term as type double and receive two arguments: the string being analyzed and an index to the current position in the string. There are other ways of doing this, but this arrangement is quite straightforward. You can, therefore, write the prototype of the function term() as follows:
double term(char* str, int& index); |
// Function analyzing a term |
You have specified the second parameter as a reference. This is because you want the function to be able to modify the value of the variable index in the calling program to position it at the character following the last character of the term found in the input string. You could return index as a value, but then you would need to return the value of the term in some other way, so this arrangement seems quite natural.
The logic for analyzing a term is going to be similar in structure to that for an expression. A term is a number, potentially followed by one or more combinations of a multiply or a divide operator and another number. You can write the definition of the term() function as follows:
// Function to get the value of a term |
|
double term(char* str, int& index) |
|
{ |
|
double value = 0.0; |
// Somewhere to accumulate |
|
// the result |
value = number(str, index); |
// Get the first number in the term |
// Loop as long as we have a good operator
while((*(str + index) == ‘*’) || (*(str + index) == ‘/’))
{
if(*(str + |
index) == ‘*’) |
value *= |
number(str, ++index); |
if(*(str + |
index) == ‘/’) |
value /= |
number(str, ++index); |
} |
|
return value; |
|
}
//If it’s multiply,
//multiply by next number
//If it’s divide,
//divide by next number
//We’ve finished, so return what
//we’ve got
298