Functions in C


Functions in C++

The title of this part of the chapter is not "Functions and Procedures in C++" because C++ doesn't have procedures like Delphi does. In C++, you can only create functions, but these can work like Delphi procedures if you make them return nothing. To make the function return nothing, all you have to do is tell it to return void. The void data type is a special data type that is used to indicate that no value exists.

Here is how you declare a function in C++:

return_type function_name(parameter list) {   function body }

As you can see, there are some differences between a function declaration in Delphi and C++:

  • The return type is specified before the function name, not after the function name and parameter list as it is in Delphi.

  • There is no semicolon after the function name, or after the parameter list, if there is one.

The example in Listing 5-18 shows how to create and use a simple procedure in C++.

Listing 5-18: A simple procedure

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop void hello() {    cout << "Hello from a C++ procedure." << endl; } #pragma argsused int main(int argc, char* argv[]) {    hello(); // call the hello() procedure    getch();    return 0; }
image from book

When declaring and calling a function in C++, you have to write the parentheses even when the function has no parameters. To show that a function accepts no parameters, you can also include the reserved word void inside the parentheses:

void hello(void) {    cout << "Hello from a C++ procedure." << endl; }

Parameters

The parameter list in C++ is a comma-separated list:

return_type function_name(data_type first_param, data_type last_param) {   function body }

Listing 5-19 features a simple function named add, which accepts two integer values and returns their sum.

Listing 5-19: A simple function

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop int add(int first, int second) {    return first + second; } #pragma argsused int main(int argc, char* argv[]) {    int i, j, sum;    cout << "First number: ";    cin >> i;    cout << "Second number: ";    cin >> j;    sum = add(i, j); // call the add(int, int) function    cout << "Sum = " << sum << endl;    getch();    return 0; }
image from book

To return a value from a function in C++, you have to use the reserved word return. However, you have to be careful how you use it, since it greatly differs from the Result variable in Delphi. Since Result is a variable, it can be used as such without disrupting the normal flow of execution. But the reserved word return breaks execution and immediately exits the function, returning the expression that follows it to the caller function, so you can't write anything after it.

For instance, Listing 5-20 shows the Max3 function implemented in Delphi. As you can see in the listing, the Result variable is freely used throughout the function.

Listing 5-20: Delphi version of the Max3 function

image from book
program Project1; {$APPTYPE CONSOLE} uses   SysUtils; function Max3(num1, num2, num3: Integer): Integer; begin   Result := num1;         // num1 is the largest?   if num2 > Result then     Result := num2;       // num2 is the largest?   if num3 > Result then     Result := num3;      // num3 is the largest end; begin   WriteLn('Max = ', Max3(1, 2, 3));   ReadLn; end.
image from book

The C++ version of the function is different — not in logic, but in the way things are done. There are two differences: You have to declare a variable in the function to temporarily store the max value and use the reserved word return after all other statements to return the result to the calling function, as shown in Listing 5-21.

Listing 5-21: The C++ version of the Max3 function

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop int max3(int num1, int num2, int num3) {    int max_val = num1; // num1 is the largest?    if(num2 > max_val)       max_val = num2;    // num2 is the largest?    if(num3 > max_val)       max_val = num3;    // num3 is the largest    return max_val; } #pragma argsused int main(int argc, char* argv[]) {    cout << "Max = " << max3(1, 2, 3) << endl;    getch();    return 0; }
image from book

There's also another way of implementing the Max3 function — by using the conditional operator — but if you've got a weak stomach, feel free to skip the following listing. Listing 5-22 contains two versions of the Max3 function, called Max3 and Max3_2. The Max3 function is easier to read since it uses a temporary variable to store the max value. The Max3_2 function is something you'd want to do to your worst enemy, but it is nice from a geek's point of view since it returns the max value without using a temporary variable.

Listing 5-22: Two more versions of the Max3 function using the conditional

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop int max3(int num1, int num2, int num3) {    int max_val = num1 > num2 ? num1 : num2;    return num3 > max_val ? num3 : max_val; } int max3_2(int num1, int num2, int num3) {    return num3 > (num1>num2 ? num1:num2) ? num3: (num1>num2 ? num1:num2); } #pragma argsused int main(int argc, char* argv[]) {    cout << "Max = " << max3(10, 20, 30) << endl;    cout << "Max(again) = " <<  max3_2(500, 400, 300) << endl;    getch();    return 0; }
image from book

Before we move on to the topic of parameter passing, you should remember one last thing: You cannot define more parameters of the same type in a comma-separated list, as you can in Delphi:

procedure ParamSample(I, J, K: Integer; A, B: Double); begin end;

In C++, all parameters must be fully defined, with the data type first and then the parameter name:

void param_sample(int i, int j, int k, double a, double b) { }

Passing Parameters By Value and By Reference

When you declare a parameter list as we did earlier (data type first and then the name of the parameter), you declare parameters that receive a copy of the argument's value. (An argument is anything passed to a function parameter, like a constant value or a variable.) Value parameters in C++ work just like Delphi value parameters — the parameter receives only a copy of the value. The value of the parameter can be freely modified in the function's body, but no matter what you do with it, the changes aren't reflected to the world outside the function.

To change the value of a variable passed to the function, you need to declare the function's parameters a bit differently so that they accept the address of the variable instead of a copy of its value. By accepting the address of the original variable, the code in the function's body is able to modify the value of the variable. This is known as passing by reference.

To declare a reference parameter (known as a var parameter in Delphi), you have to use the reference operator (&) after the data type, between the data type and the parameter name, or before the parameter name — it doesn't matter. Here's how to declare a reference parameter (the following declarations differ only in the position of the reference operator):

return_type function_name(data_type& param_name); return_type function_name(data_type & param_name); return_type function_name(data_type &param_name);

Listing 5-23 shows two versions of the my_abs function, which is actually the my_abs procedure, since it doesn't return the absolute value of the argument as the result but changes the argument itself.

Listing 5-23: Passing by value and by reference

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop void my_abs(int i)       // passing by value, doesn't work {    if (i < 0)       i = -i; } void my_abs_ref(int& i) // passing by reference, works OK {    if (i < 0)       i = -i; } // pass normally, by value, since we aren't changing the value of num void show_int(int num) {    cout << num << endl; } #pragma argsused int main(int argc, char* argv[]) {    int test = -5;    my_abs(test);    show_int(test);    // -5, not good    my_abs_ref(test);    show_int(test);    // 5, OK, since test was passed by reference    getch();    return 0; }
image from book

Default Parameters

Default parameters in C++ are declared the same way as they are in Delphi:

void function_name(data_type param_name = default_value);

Default parameters in C++ have the same limitation as they do in Delphi — you cannot declare a non-default parameter after a default one:

int default_OK(int i = 5, int j = 20) {    return i * j; } int default_ERROR(int i = 5, int j) {    return i * j; }

Function Prototypes

In C++ and Delphi, all functions (and procedures) must be declared before they are used. So far, we've tackled this problem by declaring all functions above the main function, which made them automatically usable in the program. However, this is not the preferred way of function declaration in C++. The preferred way of declaring functions, which gives the compiler the ability to perform stronger type checking, is to declare them below the main function. But when you declare a function below the main function, it cannot be used in the main function without a function prototype.

A function prototype is similar to a forward declaration in Delphi. In C++, a function prototype is a function header followed by a semicolon:

return_type function_name(parameter_list);

To make the function visible in the entire unit, function prototypes are declared at the top of the unit. The following example shows how to declare functions below the main function and how to write a function prototype.

Listing 5-24: Function prototypes

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop void prototyped_hello();    // function prototype, no body #pragma argsused int main(int argc, char* argv[]) {    prototyped_hello();    getch();    return 0; } void prototyped_hello()      // function's body after the main function {    cout << "function prototype" << endl; }
image from book

When you have a function with a parameter list, you can omit parameter names in the function prototype. The following example illustrates how to create a function prototype for a function with a parameter list:

// void prototyped_func(int i, char c, double d);

or

void prototyped_func(int, char, double); #pragma argsused int main(int argc, char* argv[]) {    prototyped_func(0, 'a', 1.0);    return 0; } #pragma argsused void prototyped_func(int i, char c, double d) { }

Local, Global, and Static Local Variables

As in Delphi, local variables are variables declared in a function, allocated on the stack, and can only be used inside the function in which they are declared. In C++, you can also declare a "really" local variable inside a block. A variable declared inside a block only exists in the block in which it is declared:

void local_variables() {    int local_i = 10;    if(local_i == 10)    {       int block_local_i = 20;       cout << local_i << endl;       cout << block_local_i << endl;    }    /*       local_i can be used anywhere in the function       block_local_i can only be used in the if statement's block    */ }

Global Variables

Global variables are variables declared outside of any function, can be used in any function, and unlike local variables, they exist as long as the application is running. The following example shows how to declare and use a global variable in C++:

#include <iostream.h> #include <conio.h> #pragma hdrstop // function prototype void display_global(); // global variable int global_i = 101; #pragma argsused int main(int argc, char* argv[]) {    // use the global variable in the main() function    cout << "global_i in main() = " <<  global_i << endl;    display_global();    getch();    return 0; } void display_global() {    // use the global variable in the display_global() func    cout << "global_i in display_global() = " << global_i << endl; }

Static Local Variables

Both global and local variables can be marked as static, but since there are differences between static local and static global variables, static local variables are described first, and static global variables are described later in this chapter.

To declare a static local variable, use the static storage specifier before the data type:

static data_type identifier; 

Here are the characteristics of static local variables:

  • They are declared inside a function block, like local variables.

  • Because they are declared inside a function, they can only be used inside the function in which they are declared.

  • Unlike ordinary local variables, static local variables aren't destroyed when the function exits. Because of this, static local variables retain their value through multiple function calls.

The code in Listing 5-25 and Figure 5-11 illustrate the difference between local and static local variables.

image from book
Figure 5-11: Static local variables preserve their value between multiple function calls.

Listing 5-25: Using static local variables

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop void local_var(); void static_local_var(); #pragma argsused int main(int argc, char* argv[]) {    int i;    for(i = 1; i <= 5; i++)       local_var();    for(i = 1; i <= 5; i++)       static_local_var();    getch();    return 0; } void local_var() {    int cnt = 0;    cout << "local_var() function has been called " <<         ++cnt << " times." << endl; } void static_local_var() {    static cnt = 0; // initialized to 0 only the first time the func is called    cout << "static_local_var() function has been called " <<         ++cnt << " times." << endl; }
image from book

Function Overloading and Inlining

To overload a function in C++ you don't have to mark it with a special directive like you do in Delphi. To create overloaded functions in C++, you only have to declare several functions that have the same name but differ in the number or type of parameters.

The example in Listing 5-26 shows how to create an overloaded function in C++ and how to use data type suffixes to make sure the appropriate overload is called. If you omit the "f" suffix for the float data type, the example will fail to compile because the compiler won't be able to decide if it should call max(int, int) or max(float, float).

Listing 5-26: Overloaded functions

image from book
int max(int, int); int max(int, int, int); int max(float, float); #pragma argsused int main(int argc, char* argv[]) {    max(1, 2);    max(1, 2, 3);    /* to make sure that the max(float, float) overload       is called, append the floating-point type suffix ("f")       to the 1.0 and 2.0 constant values */    max(1.0f, 2.0f);    return 0; } int max(int a, int b) {    return 0; } int max(int a, int b, int c) {    return 0; } int max(float a, float b) {    return 0; }
image from book

To inline a function in C++, use the reserved word inline at the beginning of function declaration:

inline return_type function_name(parameter_list);

You can read more about inlined functions earlier in this chapter in the section titled "The Inline Directive."

Units

In Delphi, a unit is a single file with interface and implementation sections. Function and procedure declarations, constants, types, and variables that are to be used from outside the unit are written in the interface part of the unit. Function and procedure implementations, as well as constants, types, and variables that are used only in the unit, are written in the implementation part.

In C++, the interface section is stored in a header file (.h or .hpp) and the implementation section is stored in the standard source file (.cpp).

For instance, let's see how to move the my_abs() function from the following example to a my_math unit:

#include <iostream.h> #include <conio.h> #pragma hdrstop int my_abs(int num); #pragma argsused int main(int argc, char* argv[]) {    int n;    cout << "Enter a number: ";    cin >> n;    cout << "The absolute value of " << n <<         " is " << my_abs(n) << "." << endl;    getch();    return 0; } int my_abs(int num) {    return num < 0 ?  -num : num; }

First, you need to create a new .cpp file and save it to the project directory under image from book my_math.cpp. When you've saved the file, move the implementation of the my_abs() function to the image from book my_math.cpp file (see Listing 5-27A).

Listing 5-27A: The my_math.cpp file

image from book
int my_abs(int num) {    return num < 0 ?  -num : num; }
image from book

The second thing that you have to do is to create a new header file (both the CPP File and Header File items can be found in the C++Builder Files category on the Tool Palette). When you've created a new header file, move the my_abs() function's prototype to it and save the header file to the project directory under image from book my_math.h (see Listing 5-27B).

Listing 5-27B: The my_math.h file

image from book
int my_abs(int num);
image from book

Now that you've moved the my_abs() function to the my_math unit, you won't be able to compile the application until you include the my_math header file using the #include directive. However, to include a custom header, you have to do two things differently:

  • Include the header by using the #include "header_name" syntax, not by using #include <header_name>

  • Include the header below the #pragma hdrstop directive

The difference between #include "header_name" and #include <header_name> is in how the compiler searches for header files. The <header_name> syntax is meant to be used with standard headers that are stored in default directories. If you use the <header_name> syntax with your own units, like <my_math.h>, the application will fail to compile. But when you include the header using the "header_name" syntax, the compiler searches in the current directory, the directory of the file that contains the #include directive, or other user-supplied directories.

The following listing shows how to properly include the my_math header file.

Listing 5-27C: Including a custom header file

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop #include "my_math.h" #pragma argsused int main(int argc, char* argv[]) {    int n;    cout << "Enter a number: ";    cin >> n;    cout << "The absolute value of " << n <<         " is " << my_abs(n) << "." << endl;    getch();    return 0; }
image from book

Static Global Variables and Static Functions

When you declare a variable in the interface section of a Delphi unit, the variable is automatically usable in all other units and the main project file. When you declare a variable in a C++ unit, it is also global and likewise usable in other units, but not automatically. To use a variable declared in another unit, you have to redeclare the variable using the extern modifier in the unit in which you want to use it. When you use the extern modifier on a variable, you are simply telling the compiler that the variable is stored in some other unit.

For instance, the following listing shows an updated version of the image from book my_math.cpp file that now contains an int variable.

Listing 5-28A: An updated version of the my_math.cpp file

image from book
int unit_integer = 2005; int my_abs(int num) {    return num < 0 ?  -num : num; }
image from book

To use the unit_integer variable in the main unit, you have to include the my_math header file and you have to redeclare the variable using the extern modifier. When you redeclare a variable using the extern modifier, you can omit the initialization part since you're only telling the compiler to look for the variable elsewhere (see Listing 5-28B).

Listing 5-28B: Using an external variable

image from book
#include <iostream.h> #include <conio.h> #pragma hdrstop #include "my_math.h" extern int unit_integer; #pragma argsused int main(int argc, char* argv[]) {    cout << unit_integer << endl; // correctly displays 2005    getch();    return 0; }
image from book

If you need a global variable that is only visible in the unit in which it is declared, like variables declared in the implementation section of a Delphi unit, you need to mark the variable as static to create a static global variable:

static int unit_integer = 2005;

If you mark the unit_integer variable in the image from book my_math.cpp file as static, you won't be able to run the application anymore because the extern modifier in the main unit is no longer able to see the unit_integer variable in the image from book my_math.cpp file.

The static directive has the same effect on functions. By default, all functions are globally visible, but when marked as static, they can only be used inside the unit in which they are declared (see Figure 5-12).

image from book
Figure 5-12: Static global variables and static functions can't be used outside of the unit in which they're declared.



Inside Delphi 2006
Inside Delphi 2006 (Wordware Delphi Developers Library)
ISBN: 1598220039
EAN: 2147483647
Year: 2004
Pages: 212
Authors: Ivan Hladni

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net