C++
Chapter 3 - Functions
Function Prototype:
C++ is a strictly typed language. As such the compiler requires that every function be fully prototyped. A function prototype tells the compiler what type the function will return and the expected types in the functions parameter list. The following is an example of a C/C++ prototype:
void Vertex::Rotate ( float dx, float dy, float dz );
void is used to specify NO object
will be passed or returned. Although C++ will allow the absence of void, most
programming standards require that void be used so as to create explicit and
understandable code.
Vertex::Rotate indicates the the class the function Rotate is
scope resolved to the class Vertex.
( float dx, float dy, float dz ) indicates that three floats
are required by the function.
Although it is perfectly legal to leave out the identifiers for each passed parameter
(dx,dy,dz), these identifiers provide an easily understandable interface. The
programmer should always strive to use identifier which provide the semantic of the use of
the identifier. The passed identifier should always vary in name to any attribute of
the class
For Example:
// The passed parameter x_val is different than the
// class attribute x
class foo
{
public:
void set_x ( int x_val ) // accessor function
{ // to a private parameter
x = x_val;
}
private:
int x;
};
Function Definition:
The function prototype belongs in the class specification.
The function definition belong in the class implementation. The above example
illustrates a special case called automatic inlining of a function within a class
specification. This type of design is desirable for accessor functions as the
function call is a short jump within the same code segment as the class definition.
Most all class methods will be prototyped in the *.h file for the class specification and
the function definition will be in the *.cpp or implementation file. It is a syntax
error when the function prototype does not match the function definition
Prototype:
class Vertex
{
...
void
Move ( float dx, float dy, float dz );
}
Definition:
void
Vertex::Move ( float dx, float dy, float dz )
{
x += dx;
y += dy;
z += dz;
}
Functions as a Black Box:
A function ideally should have one incoming path and one out going
path. A function which allows multiple exit points may be very difficult to maintain
and debug. The following is poor design. One should use "break" to
exit logic structures like "while", "for" and "switch".
Switch ( choice )
{
case 'a':
return; //
The function return at this point not
case 'b':
// at
the normal end of the function!
.....
}
Scope Rules
Scope is the region in a program where an identifier is defined. When an stack based variable drops out of scope, the destructor of the object is fired.
These are six different types of scope used within a program.
Program scope: This scope is associated with global
variables. Any identifier outside a function scope and placed before the
"main" function retain global scope. A goal in Object Oriented design is
to eliminate all global variables.
int version
= 2;
int main ( void ) { ... }
File scope: Identifier placed outside functions and
classes have file scope. If you wish to bind the a function or an identifier to a
file then use the key word static on the identifier or function. The function or
identifier then becomes shared among all instances of the file, like #include would form.
Modern C++ uses anonymous namespaces to achieve the File Scope binding.
// file.cpp
namespace
{
const double PI = 3.14159; // file scoped variable
}
// eof file.cpp
However, you will sill see the old style form...
// file.cpp
static char foo ( void )
{
std::std::cout << "File scoped function" << std::endl;
}
static const double PI = 3.14159; // file scoped variable
// eof file.cpp
Function Scope: Object or identifiers declared within a
function block are known only within that function block. When an object drops out
of function or block scope the destructor will be fired.
void foo ( int x, float y, char z )
{
}
Function Prototype Scope: Identifiers listed in
a function parameters list in a function prototype are ignored by the compiler but should
exist for understandability of the function interface. Identifiers used within
prototypes can be reused in a program without ambiguity. However, identifiers used
within a function definition retain function block scope.
void foo ( int x, float y, char z );
Block Scope: Use of braces {} any
where within a function, class or block scope create local scope for identifiers at that
particular point in the program.
...
{
int i = 0;
}
Class Scope: The is the scope bound to the class specification. Identifiers bound by class scope exist as long as an instance of the class is active.
class foo
{
}
Static used within any scope makes that identifier or object shared by all instances of that scope. Static identifiers of classes must be initialized in file or program scope.
Inline Functions
Inline functions can significantly increase the performance of a program. Functions can be automatically inlined by including them into the class specification or the programmer can place the key word inline prior to a function definition. The compiler will try to inline the function. This means the compiler will try to place an instance of the function in the same code segment as the function call. This locality of reference then requires only a short jump to the function rather than the standard long jump which most functions require. Functions with repetition or recursion cannot be inlined. The compiler will warn the programmer when a request for an inline function cannot be fulfilled by the compiler.
Examples of Macros, Inlines and Normal Functions.
The source code maccomp.cpp was compiled with varying level of
optimization.
Compiled with no optimization: output_none.txt
Compiled with Max Speed Optimization: output_maxspeed.txt
References and Reference Parameters
References are handle to objects in memory. A reference to an object in memory is the size of the memory address to that object, so passing objects by reference is an efficient mechanism. References are not traditional pointers. They serve as a handle to an object in memory. When an object is passed by reference, the receiving function operates directly on the object, but any operations effect the actual object passed NOT a copy as would normally occur in a pass by value situation
pass.cpp // Example of passing by reference #include <iostream> using namespace std; void Sqr ( int x ) // pass by value, bitwise copy made here { x = x*x; //operate on the copy } // copy is destroyed when is drops from scope
void
Sqr_r ( int & x ) // pass by reference to memory
{
x = x*x; //operate directly in the object
}
void
Sqr_p ( int * x ) // pass by pointer to memory
{
*x = *x * *x; //operate directly in the object
}
int
main ( void )
{
int x = 9;
int y = 9;
int z = 9;
std::std::cout << "x = " << x << std::endl;
std::std::cout << "Squaring By Value x" << std::endl;
Sqr ( x );
std::std::cout << "x = " << x << std::endl << std::endl;
std::std::cout << "y = " << y << std::endl;
std::cout << "Squaring By Reference y" << std::endl;
Sqr_r ( y );
std::cout << "y = " << y << std::endl << std::endl;
std::cout << "z = " << z << std::endl;
std::cout << "Squaring By pointer z..." << std::endl;
Sqr_p ( &z );
std::cout << "z = " << z << std::endl;
return(0);
}
Figure 03_20
Default Arguments
Section 3.18 - Default arguments insure that parameters in
function-prototype scope have a known default state. This feature also allows the
function to be called in a variety of ways. This appears as if there are several
different versions of the function. Default arguments can take the place of function
overloading and is a very efficient way to program. Class constructors are perfect
for using default arguments.
Default argument are place in the function prototype.
The following is an example of a constructor with default arguments.
class Vertex
{
public:
Vertex ( float _x = 0, float _y = 0, float _z = 0 );
}
Vertex::Vertex ( float _x, float _y, float _z )
{
x = _x;
y = _y;
z = _z;
}
A Vertex can be created in the following ways from one constructor:
Vertex v1; Vertex v2( 112.2 ) Vertex v3( 112.2, 200.3 ); Vertex v4( 112.2, 200.3, 11.4 );
The rule for default arguments:
Once you start defaulting arguments in a function definition, you must keep defaulting
arguments.
void foo0 ( int x, int y = 0, int z = 0 );
The following examples break the rule.
void foo ( int x = 0, int y );
void foo1 ( int x, int y = 0, int z );
Unary Scope Operator
The unary scope resolution operator resolves a variable to global
space.
::PI
// Using the unary scope resolution operator
#include <iostream.h>
#include <iomanip.h>
const double PI = 3.14159265358979;
int main()
{
const float PI = static_cast< float >( ::PI );
std::cout << setprecision( 20 )
<< " Local float value of PI = " << PI
<< "\nGlobal double value of PI = " << ::PI << std::endl;
return 0;
}
Function Overloading
Function overloading allows two functions with the same names but
with different parameters to be defined in the same program. This allow for a
uniform interface for different type as the parameters.
For Example
void Show ( Vertex & v );
void Show ( Line & l );
Rule for function overloading:
Overloaded functions are functions with the same name but vary by
more than the return type.
The following example is an invalid overload function as it breaks the rule.
int Show ( Vertex & v ); void Show ( Vertex & v);
// Using overloaded functions
#include <iostream.h>
int square( int x ) { return x * x; }
double square( double y ) { return y * y; }
int main()
{
std::cout << "The square of integer 7 is " << square( 7 )
<< "\nThe square of double 7.5 is " << square( 7.5 )
<< std::endl;
return 0;
}
Function Templates
A function template is the ability to specify a type independent function. When the compiler identifies the use of this function where an instance is not already in place, the compiler will create the code to fulfill the specified types. So older compiler may require you to place template into header files for guaranteed inclusion local to the function call and/or you will have to prototype the functions prior to the main function so the compiler creates the template function before they are referenced.
Template Swap Function
template <class mytype>
mytype
Swap ( mytype & a, mytype & b )
{
mytype temp;
temp = b;
b = a;
a = temp;
}
// Fig. 3.27: fig03_27.cpp
// Using a function template
#include <iostream>
template < class T >
T maximum( T value1, T value2, T value3 )
{
T max = value1;
if ( value2 > max )
max = value2;
if ( value3 > max )
max = value3;
return max;
}
int main()
{
int int1, int2, int3;
std::cout << "Input three integer values: ";
cin >> int1 >> int2 >> int3;
std::cout << "The maximum integer value is: "
<< maximum( int1, int2, int3 ); // int version
double double1, double2, double3;
std::cout << "\nInput three double values: ";
cin >> double1 >> double2 >> double3;
std::cout << "The maximum double value is: "
<< maximum( double1, double2, double3 ); // double version
char char1, char2, char3;
std::cout << "\nInput three characters: ";
cin >> char1 >> char2 >> char3;
std::cout << "The maximum character value is: "
<< maximum( char1, char2, char3 ) // char version
<< std::endl;
return 0;
}
04/12/00 03:51 PM