Topics to Cover

void return type

Sometimes we're interested in functions that return nothing at all. These functions are called for their side effects. This is an issue for our prototype, however, since we need to specify something for return type. In this case, we use void in place of a return type.

For example, look at the code on the right. The function printgreetings is only used for the side effect of writing something on the screen, not for any value it would return.


// void return type: a function that returns nothing
void printgreetings();  

int main()
{
  printgreetings();

  return 0;
}

void printgreetings()
{
  cout << "G R E E T I N G S !" << endl
       << endl
       << "This is a little demo program." << endl
       << "I hope you enjoy it." << endl;   
}

Predicates

What are predicates?
Functions which return the type bool are traditionally referred to as predicates.

Imagine that you want to know whether an integer is a prime number or not. We could write a function isprime with the following prototype:


bool isprime(int n);
With this predicate, you can use the function as follows:

if( isprime(n) )    // isprime returns true or false
  cout << n << " is a prime number!" << endl;

Really there's no need to bring predicates up as a special subject, since functions that return bools are not any more special than functions that return, say, an int. However, you might not have thought much about the use of having such functions.

Multi-Parameter Functions

Functions get infinitely more interesting when they have more than one argument or parameter. Specifying multiple parameters for a function is just like specifying several single parameters in a comma-separated list.

For example, suppose you wanted to define a function max that looked at two ints and returned the larger of the two. It's prototype would be


int max(int, int);
The definition would look like this:

int max(int a, int b)
{
  if (a < b)
    return b;
  else
    return a;
}
Note that this example shows you that you can return from anywhere within a function (e.g., it returns b in the middle of the function definition, if a is less than b), just like you can return from anywhere within main.

Order of the Arguments and Types of Arguments

Order of arguments

Consider code below. The function name rep stands for repetition. That is, it prints the char argument to the screen the number of times given by the int.

void rep(int n, char c)
{
  for(int i=0; i<n; i++)
    cout << c;
  cout << endl;
}

It's important to note that the order of arguments matters.

It is because the function expects the int object first.

Types of Arguments

When implicit conversions aren't possible, the compiler gives you an error message.

Composing Functions

We have defined a max function above. How about having three ints, x, y and z, amongst which I need the largest. Were I to write
max(x,y,z)
the compiler would complain ... the only max function it knows about only takes two arguments! However, I could say the following:
max(max(x,y),z)
This is our first example of composition of functions. When the function max gets called, its two argument expressions are evaluated.
  1. The first argument is max(x,y), which evaluates to the larger of the two values.
  2. The second is simply z.
So, what we get out of this is the maximum of all three values.

Overloading a Function Name

C++ allows overloading of a function name, i.e., having two or more function definitions with the same name, but different number or types of arguments.

Example: multiple functions that behave similarly

Using the function overloading, you can implement have two max functions taking a different number of arguments.

int max(int a, int b)
{
  if (a >= b)
    return a;
  else
    return b;
}

int max(int a, int b, int c)
{
  return max(max(a,b),c);
}

int main()
{
  cout << max(2, 3) << endl;
  cout << max(2, 4, 3) << endl;
  return 0;
}

Two functions where only the return type is different?

Q: Will the following code compile?

double f(int);
char f(int);
Answer:
No. The return type of a function is not considered in function
overloading. Thus, it is illegal to have two functions whose prototype differs
only in the return type. The above won't compile. 

Caution: do not write confusing functions

While overloading a function name is a convenient feature, we must be careful to avoid defining functions that are confusing to use. For example, suppose we have two functions named rep as follows:
Prototypes Definitions main output? (drag your mouse)

void rep(char,int);
void rep(int,int);

void rep(char c, int k)
{
  for(int i = 0; i < k; i++)
    cout << c;
  cout << endl;
}
void rep(int n, int k)
{
  for(int i = 0; i < k; i++)
    cout << n;
  cout << endl;
}

int main()
{
  int a = 55, b = 6;
  char c = 'X';

  rep(c,b); 

  rep(a,b); 

  return 0;
}
XXXXXX
555555555555
What happens here? Which function gets used? These two rep functions are quite confusing to use. Don't write code this way.

Caution: Implicit conversions and overloading sometimes create issues.

When you make a function call with arguments whose types and number do not match any prototype, the compiler will try to use implicit type conversion to match a prototype. This is pretty straightforward (remember the example at the top of the page?) when function name overloading is not used. With oveloading, however, things can get complicated. For example:
Definitions Function Call Discussion

void rep(char c, int k)
{
  for(int i = 0; i < k; i++)
    cout << c;
}

void rep(int n, int k)
{
  for(int i = 0; i < k; i++)
    cout << n;
}
rep(42.23,10);
Error! This function call is ambiguous! The compiler has no way of knowing:
  • whether the double 42.23 is supposed to be implicitly converted to the char '*', so we can use void rep(char,int)
  • or whether it should be implicitly converted to the int 42 so we can use void rep(int,int).
This results in a compiler error!
How is the compiler supposed to decide which implicit conversion is best?

Mandatory Practice Problem

  1. What percentage of numbers are prime?

    Sample run:

    Enter n: 10
    40% of the first 10 integers are prime! 
    solution

Practice Problems

  1. Surveying Problem This gives you another simple example of top-down design.
  2. Drawing a triangle with *'s This is a simple multi-parameter function problem.
  3. Distance between points This is another simple multi-parameter function problem.