Chapter 3: Functions and Classes in C++
In this chapter, we will be taking another important step towards exploring the realm of C++ programming. This chapter builds upon some of the fundamentals that were briefly discussed or displayed in the first chapter’s C++ program. Functions and classes are integral parts of a program, and knowing how to use them ensures that the programmer’s skills have a strong foundation.
In this chapter, we will learn how to declare functions in a program and call a function in a program. In addition, readers will also be instructed on how to properly use standard classes in C++ along with header files and string objects that belong to the string
class.
Before we go into theoretical details and discussion describing the basics of declaring functions in a program, let’s first see a demonstration and build the concept from that. The prototype shown below depicts the structure of a typical function that has been declared:
If we analyze this function, we can see that the statement provides the following information to the C++ compiler:
-
The name of the function to be used is func
-
We are calling upon this specific function through two arguments. Both of these arguments are type
arguments with the first one being int
and the second one being double
-
The value that the function will return should be a long
type.
The following table highlights some of the most common and standard mathematical functions available for use in C++.
double sin (double);
|
//Sine
|
double cos (double);
|
//Cosine
|
double tan (double);
|
//Tangent
|
double atan (double);
|
//Arc Tangent
|
double cosh (double);
|
//Hyperbolic Cosine
|
double sqrt (double);
|
//Square Root
|
double pow (double, double);
|
//Power
|
double exp (double);
|
//Exponential Function
|
double log (double);
|
//Natural Logarithm
|
double log10 (double);
|
//Base-ten Logarithm
|
Declaring Functions
When writing a program, the compiler is like the main player who will execute the instruction set you are writing down. Following this analogy, it only makes sense that if we tell the compiler about a function that it does not know, it will not be able to execute it. Hence, a programmer needs to declare the functions and names (apart from keywords) for variables and objects; otherwise, the compiler will simply return an error message. This is why declaring functions is very important before we can even use them in our program. Usually, the point where you define a function, or a variable is also the point where they are declared as well. However, not all functions need to be defined for them to be declared. For example, if the function you wish to use has already been defined in a library, then you only need to declare that function instead of defining it again in your program.
Like a variable, a function features a name along with a type. The type of value defines the type of function it is supposed to return. In other words, if a function is supposed to return a long
type value, then the type of the function will also be ‘long.’
Similarly, the argument’s type also needs to be properly specified for the function to work properly. In other words, to declare a function, we need to provide the compiler with the following information:
Such a structure makes up a function prototype. An example of a function being declared has been shown below
int toupper(int);
double pow(double, double);
The following statement tells the compiler that the name of the function is toupper(),
and its type is int
. Since it is an int
type function, the value it will return will also be an int
type. The argument’s type is also an int,
so the function will expect an input argument that will be of the int
type.
Similarly, the second function’s name is pow(),
and its type is double
. It has two arguments, and both are of the type double,
meaning that when this function is called, then it needs two arguments of the type mentioned above to be passed to it. We can follow up such types of function prototypes with names as well, but the names will be considered as comments by the compiler.
Now, let’s discuss another case where a function has already been declared in the header file included in our program. For instance the function prototype:
int toupper(int c);
double pow(double base, double exponent);
It is the same for the compiler as the function prototype that has been demonstrated previously. If this function has already been declared in the header file (which has been added to the program initially using the directive #include), then we can use the function immediately without declaring it. For example, if we include the C++ math library ‘cmath,’ then we can use the standard mathematical functions immediately. The following program demonstrates the inclusion of this library to use the mathematical functions immediately.
// Calculating powers with
// the standard function pow()
#include <iostream> // Declaration of cout
#include <cmath> // Prototype of pow(), thus:
// double pow( double, double);
using namespace std;
int main()
{
double x = 2.5, y;
// By means of a prototype, the compiler generates
// the correct call or an error message!
// Computes x raised to the power 3:
y = pow("x", 3.0); // Error! String is not a number
y = pow(x + 3.0); // Error! Just one argument
y = pow(x, 3.0); // ok!
y = pow(x, 3); // ok! The compiler converts the
// int value 3 to double.
cout << "2.5 raised to 3 yields: "
<< y << endl;
// Calculating with pow() is possible:
cout << "2 + (5 raised to the power 2.5) yields: "
<< 2.0 + pow(5.0, x) << endl;
return 0;
}
The output of this program will be
2.5 raised to the power 3 yields: 15.625
2 + (5 raised to the power 2.5) yields: 57.9017
Function Calls
A function call is leveraging the properties and results of a function and passing it on to the variable calling the function mentioned above. For example,
y = pow( x, 2.0);
In this example, the variable ‘y’ is calling upon the pow()
function. The result of the function is x2,
and this exponential power is then assigned to the variable ‘y’ calling the pow()
function. In simpler terms, a function call represents a value. Since we are simply representing a value, we can also proceed to apply some other mathematical operations in a function call as well, like addition, subtraction, and multiplication, etc. For instance, we can perform an addition calculation on a function call for double
values as shown below:
cout << 2.0 + pow( 5.0, x);
In this statement, the value of ‘2.0’ is added to the value returned by the pow()
function. Afterward, the cout
argument displays this result as the final output of the statement.
It is important to remember that, even though any type of expression, be it a constant or mathematical expression, can be passed to a function as the argument, we must make sure that the argument is passed on is of a type that the function is expecting. In other words, the type of argument being passed should be in accordance with the type that was specified when the function was initially defined.
To ensure that the argument’s type is indeed correct, the compiler refers to the prototype function to double-check the type that has been specified in the input argument. In the scenario where the compiler identifies that the type that has been initially defined in the function prototype does correspond to the argument’s type being inputted, the compiler tries to convert it to the desired type. But this is not always possible, so the type conversion can only take place if it is feasible. This has been demonstrated in the code of the program shown previously as:
y = pow( x, 3); // also ok!
Note that the compiler expects a double
type value instead of an int
type value (3). Since the argument’s type does not correspond to the prototype function, the compiler performs a type conversion of int
to double
. However, if the number of arguments being input is more or less than specified by the function prototype or the compiler cannot perform type conversion. It will simply return an error message. At this point, you can easily point out the origin of the error and fix it in the program’s developmental stages, saving you from dealing with runtime errors.
In the following example:
float x = pow(3.0 + 4.7); // Error!
The compiler identifies that the number of arguments being used does not comply with the structure specified in the function prototype. Moreover, the return value of the function is a double
type, and it cannot be assigned to a variable of a float
type, hence making the type conversion impossible for the compiler. In this case, the compiler will simply generate an error.
Functions Without Return Values or Arguments
In most cases, the purpose of a function call would be to use it returns for the variable calling the function. However, we can also create a function that executes a task but does not necessarily return any particular value to the variable calling upon this function. For such purposes, we use the void
type with these functions. In other programming languages, this is commonly referred to as a procedure.
To demonstrate the usability of a function call without a return value, let’s first consult the statement shown below:
void srand( unsigned int seed );
In this example, we see a function srand()
being used to generate a random number. The function does this by initializing an algorithm that produces the random number for the output of the function. Since this function does not return a tangible value, we assign it the void
type. The arguments of this function show that we are using an unsigned int
value, which is to be passed to the function for use. This value acts as a seed for the random numbers that the function will produce, enabling it to generate a series of random numbers.
Now that we have discussed a function that can perform an action without returning a value let’s discuss a function that does not expect an argument. If we have defined a function without specifying any arguments, then the programmer must declare this prototype function as void
. Alternatively, we can also leave the argument space empty in the parenthesis. This has been demonstrated below:
int rand( void ); // or int rand();
The program will call the function rand()
without providing it with any arguments. Since no arguments have been specified, the function simply generates a random number between 0 and 32767 and returns it as the output value. In this way, we can generate a series of random numbers by repeatedly calling this function.
Header Files
, header files are those text file components in a program module that feature declared functions and macros. For a program to make use of the data contained within header files, they need to be added into the program’s source code by using the #include
directive. A basic chart elaborating on how to use header files has been shown below:
By understanding this diagram, we can extract the following points of important information regarding how to use header files:
-
Header files containing the declared functions and macros being used in the program should always be included at the beginning of the program’s source code. If not, the compiler will not be able to refer to the function prototypes when executing the functions in the program itself.
-
Multiple header files cannot be included with a single #include
directive. If we want to add more than one header file into the program, then we will have to use separate #include
directives to do so, as shown in the diagram.
-
The name of the header file should be properly written (taking note of the upper and lower case along with any punctuation) when being declared by the #include
directive. The header file’s name can be either enclosed in angled brackets “<>” or in double-quotes, as shown in the diagram.
Searching for Header Files
Usually, whenever you include a header file, it gets automatically stored in a separate folder in your files directory. The directory where the header file that has been added to the program is stored is known as ‘include
.’ If you want to make changes to the header file or want to access it manually to use it with another project, then you first need to refer to the method you used to include it into the current project. If you have used angle brackets (<>) to include the header file, then it will be in the include
directory. However, since most of the programmers using C++ create their own header files, they usually store it in the same folder where the project is stored. In such cases where the header file is not in the include
directory, then you will need to add it to the source code file using double quotes (“”). Similarly, if a header file has been included in the program’s source code using double quotes, then you need to search the project folder itself to find the header file.
Standard Class Definitions in Header Files
By now, we know that a header file contains important data such as function prototypes that help the programmer to code functions with ease efficiently. However, a header file is not that one-dimensional. It can store other important programming data as well such, as one which we will discuss in this section, standard classes that have already been defined. Programmers can include objects and define classes inside the header file and use them in their program just as how they would use function prototypes. So, any class and object that has been defined in the header file, the program can access and use this data to execute tasks.
#include <iostream>
using namespace std;
In this example, you can see two statements. Our concern is with the first statement only, i.e., the inclusion of the istream
and ostream
classes in the program. By adding the header file containing these classes, the program becomes capable of using the cin
and cout
streams. This is because cin
is an object that belongs to the istream
class, and similarly, cout
is an object that belongs to the ostream
class.
Using Standard Header Files
The table shown below highlights some of the header files that include standard C++ libraries.
algorithm
|
ios
|
map
|
stack
|
bitset
|
iosfwd
|
memory
|
stdexcept
|
iomanip
|
locale
|
ssstream
|
vector
|
complex
|
iostream
|
new
|
streambuf
|
functional
|
list
|
set
|
valarray
|
dequeue
|
istream
|
numeric
|
string
|
fstream
|
limits
|
queue
|
utility
|
exception
|
iterator
|
ostream
|
typeinfo
|
Since C++ can use most of the libraries used by standard C, here’s a list of header files that include C standard libraries.
assert.h
|
limits.h
|
stdarg.h
|
time.h
|
ctype.h
|
locale.h
|
stddef.h
|
wchar.h
|
errno.h
|
math.h
|
stdio.h
|
wctype.h
|
float.h
|
setjmp.h
|
stdlib.h
|
ios646.h
|
signal.h
|
string.h
|
|
|
|
|
|
|
At first glance, we can see that the header files shown in the two tables have one key difference, and that is the inclusion of the .h
extension. This is because, in C programming language, header files are indicated by the extension ‘.h
.’ However, this is not the case for C++ header files as their declarations are all contained within their own namespace (std)
. However, it is important to specify to the program that you will be using the std
namespace globally to refer to identifiers. Otherwise, the compiler will not be able to recognize the cin
and cout
streams without the using
directive.
In this case, the compiler cannot identify the cin
and cout
streams.
#include <iostream>
#include <string>
using namespace std;
By adding the using namespace std
directive, the program can now easily identify and use the cin
and cout
streams without the need for any syntax. Note that this demonstration also added the string
header file as well. This will allow the program to perform string manipulation tasks in C++ easily.
C Programming Language Header Files
The C++ programming language has adopted and incorporated all of the standard libraries of the C programming language, making them available for use by C++ programs. As such, header files are also no exception. The header files which were originally standardized for the C programming language have been adopted for use by the C++ programming language as well. For example, we can use the C programming language’s standard math library in C++ as well by using the following statement as shown below:
Although we can use standard C libraries, there is one complication that can cause quite a problem. When using a C header file, the identifiers declared in this file become globally visible. This can cause complications in big programming projects. To take care of this issue, we simply use another header file in addition to the C header file in C++. This additional header file declares these identifiers in the std
namespace, thus solving the issue of identifier conflicts. For example, if we are using math.h
library in C++, then we will accompany it with another header file named cmath
. This cmath
library features all of the data declared in the math.h
library, but the difference is that the identifiers in cmath
have been declared in the std
namespace. This has been demonstrated below:
#include <cmath>
using namespace std;
An important topic to clarify while we are talking about header files is that the string
header file and string.h
or cstring
do not offer the same functionality. The string
header file only defines the string
class while the string.h
or cstring
header files declare those functions and variables that are used to manipulate string data in C programming language. In simpler terms, the string.h
and cstring
header files allow the user to access the functionality of the C string library, while the string
header file’s only purpose is to define the string
class.
Here’s an example showing the use of two header files, i.e., iostream
and string
in a C++ program.
// To use strings.
#include <iostream> // Declaration of cin, cout
#include <string> // Declaration of class string
using namespace std;
int main()
{
// Defines four strings:
string prompt("What is your name: "),
name, // An empty
line( 40, '-'), // string with 40 '-'
total = "Hello "; // is possible!
cout << prompt; // Request for input.
getline( cin, name); // Inputs a name in one line
total = total + name; // Concatenates and
// assigns strings.
cout << line << endl // Outputs line and name
<< total << endl;
cout << " Your name is " // Outputs length
<< name.length() << " characters long!" << endl;
cout << line << endl;
return 0;
}
This example prints out the string using cout
and <<
statements. This program asks the user to input their name and then proceeds to display their input name along with the total number of characters contained within their name.
What is your name: Zoldyck Killua
---------------------------------------
Hello Zoldyck Killua
Your name is 13 characters long!
---------------------------------------
Using Classes in C++
The program shown in the above example can be seen to incorporate the string
class into its core functionality. In the standard library of C++, multiple classes have been defined before-hand. These classes are very important for the foundations of a C++ program. They include the stream classes (iostream
) and classes that effectively help represent strings for the program and conditions through which an error arises.
Even though there are many classes available for use, each class is unique with regards to their respective type that represents their properties and capacities. Although each class is unique, whether they are predefined classes or classes that have custom designed by the programmer, their properties and capacities are still governed by two main aspects.
-
The data members of a class are responsible for defining the properties of their corresponding class.
-
The class’s methods
(functions belonging to a class. These functions coordinate with the members of the class to carry out an operation) define the capacity of a class. The method of a class is also commonly known as its member function.
Creating Objects of a Class
Objects are variables that are assigned the class type itself. For instance, if we create an object for a string class, then the variable will have the string
type accompanying it. This has been demonstrated in the example shown below:
string s("I am an object of the string class");
Whenever an object is created, it is allocated memory for its data members and then initialized with its corresponding values. In the example shown above, you can see that the variable s
is of the string
class type. An Object is also termed as an instance
of the corresponding class it represents. Take the statement shown above as an example. The object s
can also be referred to as an instance of the string
class. The object is followed by a string constant (I am an object of the string class) and is ultimately defined and initialized in this way.
Calling Methods
In a class, all the methods that have been defined as ‘public’
can be called by an object of this particular class. We must not confuse calling a public method with calling a global function. Although both share some basic similarities, however, there is one aspect that acts as a major differentiating factor between methods and functions. This aspect is that unlike global
functions that can be used by multiple statements of the program in which it is defined, a public
method can only be called for one particular object at a time only. A typical set up of a method and an object is shown below:
s.length(); // object.method();
In this statement, the length()
is the method for the object s
. The main purpose of this method is to provide information regarding the total number of characters in the string. This has already been demonstrated in the program shown in the previous section. Instead of the s
object, we can see that the length()
method is being used with the name
object.
Global Functions and Classes
A function that has been defined globally
can be used by some classes as well. A global function being used by a class mainly executes certain operations for the class’s objects, which have been passed as arguments to the function. For example, consider the following statement, which features a global function getline().
This has been used with an object s
(which has been passed to the function as an argument):
The global function executes an operation to store a line of keyboard input as a string. In this way, by pressing the return key, a new line character will be created by the ‘\n’
escape character, but this line will not be stored in a string.