Programming in C++

Background of Computer Programming

Since you will be learning programming skills, it is a good idea to get familiar with the concepts of computer science (denoted CS), but if you want to skip to the C++ straight away, go to the next page. CS is not computer literacy. CS is a combination of math and logic. When programming, you will be using problem solving stategies to find the best solution to a given problem. CS is a science because you will be extremely experimental. CS is engineering because you will usually look for a solution to a "real world" problem. CS is also interdisciplinary, which means it relates to many different fields.

Now you know the aspects of computer science, but you might wonder how programmers actually find solutions to the problems they are given. After a programmer is given a problem to solve, he begins a process called program development, which is discussed in length in the next section. Before writing any code, programmers develop an algorithm, which is a step-by-step plan for solving a particular problem in a finite amount of time. Finite refers to a certain amount of time to solve the problem.

Definition

Algorithm - a step-by-step plan for solving a particular problem in a finite amount of time.

Algorithms are developed by simply thinking about how to come up with a solution for the problem. From my experiences, I normally sit down with pen and paper and start jotting down everything that comes to mind until I eventually end up with a semi-organized outline that I can convert into an algorithm. There are three standard ways to "display" an algorithm on paper: a structure chart, pseudocode, or flowchart. A structure chart, also know as a hierarchy chart, shows the functional flow through a program. It is a way of breaking down a program into logical steps. Pseudocode is a verbal description of the solution, involving a mixture of English and a programming language. Pseudo is a prefix meaning almost, relating to (pseudo - code : almost, relating to code). A flowchart is essentially the same as pseudocode except it is a visual tool that uses different chart symbols to direct flow of the program. I prefer using pseudocode, but you should use whatever method works for you.

The following is an example of an algorithm written in pseudocode form:

Problem

Write an algorithm that will, given the current date, find the date of the next day.

1) Get the current date
      - Get the current month, day, and year in numerical form

2) If month is 2,
       If day = 29 or day = 28 and it's not a leap year
           increment month
       else
           increment day
   else if month = 1,3,5,7,8,10,12
       If day = 31
           increment month
       else
           increment day
   else if month = 4,6,9,11
       If day = 30
           increment month
       else
           increment day

3) Report "new" date [month, day, year]

Increment Day
     add 1 to current day

Increment Month
     if month = 12
           increment year
     else
           add 1 to current month
     day = 1

Increment Year
     add 1 to current year
     month = 1

Leap Year
     divisible by 400 or divisible by 4 but not 100

It is important to note that programmers develop an algorithm before writing any type of code. They don't just sit down and immediately start writing code when given a problem to solve. This is a common mistake for many beginning programmers. Several of my classmates approach me about problems they have when trying to complete class projects. The main problem I find is that they don't fully understand what they need to do because they haven't taken the time to actually think about what the problem requires of them. Move on to the next section for more...

Steps of Program Development

In the world of computing, the job of a computer programmer is to create programs that solve specific problems. The problems that programmers encounter may be as simple as printing a billing summary for customers at a store, or they can be as complex as performing financial calculations for a bank. Whatever the problem may be, programmers follow a multi-step process called program development in order to create successful programs. Program development is a five-step process requiring understanding of the problem at hand, developing a solution, writing a program, testing the program, and maintaining it.

The first step in the program development process is to understand the problem. This step is critical because a programmer cannot solve a problem until he fully understands it. During this step, the programmer carefully analyzes the problem in order to form a precise specification that includes the input required and the type of output needed. Input refers to the specific data that is put into a problem in order for it to be solved. Output refers to the exact answer that must be produced from the problem. Before the programmer can do anything else, it is vital that he fully understands the problem.

After he fully understands the problem, his next step is to develop a solution. It is important that the solution is developed before writing any type of computer code. When developing the solution, the programmer must devise an algorithm, which is a step-by-step plan for solving a particular problem. An algorithm can be displayed on paper in one of three ways: a flowchart, a structure chart, or pseudocode. Most programmers choose to use pseudocode, which is a verbal description of the solution involving a mixture of English and a programming language. During this step, the programmer must also make sure he is solving the problem correctly, and he is solving the correct problem (verification and validation).

After a solution has been developed, the next step of the process is to write the program code. Writing the code essentially means taking the algorithm and converting it into a computer programming language. The programmer must first pick an appropriate programming language to use, such as BASIC, Pascal, Ada, and C. When writing the code, the programmer starts at the beginning of the algorithm and works his way down to the end. He must make sure his program code is well-structured and includes adequate documentation. Documentation is statements written in the program code that does not affect the code itself, but lets the programmer know what specific parts of the code is supposed to do.

The next step in the process is to test the code. Testing can be done by running the program and manually checking the results. Two types of testing take place during this step: white box testing and black box testing. White box testing, commonly called glass box testing, refers to testing done by the person who wrote the program code. In other words, the person doing the testing knows everything about the program code. Black box testing, on the other hand, refers to testing done by someone who has no idea of how the program code is written. Documentation is especially important so other programmers (including yourself) can analyze your code easily in the future and may even help you find and correct mistakes.

After the code has been thoroughly tested, the fifth and final step is maintaining the completed program. The programmer maintains it by updating the code and editing the code in order to make it more efficient. During this maintenance phase, programmers also may have to correct “bugs”, which are errors in code that were not recognized during testing.

Every time a programmer is given a problem to solve, he calls upon the program development process for help. Every step in the process must be completed in order for the programmer to create a successful solution. If the problem is not understood, a solution will not be developed, and a program will not be written. If a program is successfully written but not maintained, the program will eventually become obsolete. Every step is critical towards the overall success of the program. Although the problems programmers encounter may change, the process of developing a solution will remain the same.

Here is an outline of each step in the program development process. Note what each step requires the programmer to do.

  1. Understand the Problem
         - You can't solve a particular problem unless you understand it.
         - Form a precise specification, including the input required for the program and the output needed.
  2. Develop an Algorithm
         - You should develop a plan before writing any type of program code.
         - Check verification : Are you solving the problem correctly?
         - Check validation: Are you solving the correct problem.
         - Ways to display algorithms: structure chart, pseudocode, flowchart [see also Introduction to CS]
  3. Write the Program Code
         - Essentially converting an algorithm into a computer programming language such as C++.
         - Be sure to use meaningful identifiers, which are the names you will give the variables you use.
         - Be sure to include adequate documentation, which are comment statements written in your program code that do not affect the code itself, but explain what
           specific code fragments are suppossed to do.
  4. Test the Program
         - Run the program and manually check the results.
         - Be thorough:
               - Test all possiblities
               - Test extreme data (invalid data, limit values, empty/null values)
               - Begin BlackBox testing
               - Begin WhiteBox testing
  5. Maintenance
         - Normally geared toward programming in the real world (careers)
         - Update Code
         - Correct "bugs"
         - Edit code to make it more efficient.

With the program development process out of our way, we can now look at the different levels of computer languages. Read on for more...

Levels of Computer Languages

In order to write a program, you must use a computer language. There are three levels of computer languages: machine, symbolic, and high level.

Machine language dates back to the 1940's and is the only language that is directly understood by a computer. All of the code written in machine language is based on binary code, which makes it extremely difficult and time-consuming for writing large programs. Binary code is composed of streams of 1's and 0's. This makes perfect sense considering that the internal circuit of a computer is made of switches, transitors, and other devices that can only be in one state: on or off. The off state is represented by 0, and the on state is represented by 1. A program that took hours to write in machine language could possibly be written in a couple of minutes using a high-level language.

Thanks to the work of mathmatician Grace Hopper in the early 1950's, a new language was developed and named symbolic, or commonly referred to as assembly. Symbolic language made it possible to use commands such as MOV and ADD instead of having to rely primarily on binary code for every command. The symbols represented various machine language instructions, but the symbols still had to be converted to machine language for a computer to understand. A program called an assembler was developed and used for these purposes.

In the late 1950's, a major breakthrough was made and high-level languages were developed. High-level languages broke free of depending directly on the hardware of a computer. Instead, these languages concentrate on the problem being solved.. Program code is written with symbolic names, but the names actually represent memory addresses in the computer. When the program code is run, a compiler translates the code into binary code (internally) so the computer can understand it. High-level languages make it extremely easier to write and understand program code than machine and symbolic languages. Similar to symbolic language, high-level languages require a translator, normally called a compiler, to convert their code into machine language for the computer to interpret.

Examples using machine and high-level languages:

Write a code segment that will add 2 integers and store the result.

**** NOTE: This machine language code segment will not work. It is meant to show what binary code looks like. ****

Machine language code:

0000000 1101001 0010011 0001010
1111011 1010100 1010010 1011101
0000100 1110111 1110111 1010101
0001001 0011001 1111111 1010000

High level code:

sum = firstValue + secondValue;

Ok, so programmers use computer languages to tell a computer what to do, but exactly how do computers work? How do they think, and how do they know how to understand data? The next section deals with the internal representation of data in computers. Read on for more...

Internal Representation of Data

When considering the internal representation of data, you need to understand two things: the units of storage in a computer and the different number systems.

Units of Storage

Smallest unit of storage to greatest:

  • bit (binary digit) [0 or 1]
  • nibble (4 bits)
  • byte (8 bits)
  • word (2 bytes = 16 bits)
  • longword (4 bytes = 32 bits)
  • kilobyte (1024 bytes bytes = 2^10)
  • megabyte (approx. 1 million bytes = 2^20)
  • gigabyte (approx. 1 billion bytes = 2^30)

Note

In a bit, you can store one of 2 possible values: 1 or 0. In a byte, you can store one of 256 possible values.

Number Systems

  • binary (base 2)
  • octal (base 8)
  • decimal (base 10)
  • hexadecimal (base 16)

Hexadecimal Values

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
A corresponds with 10
B corresponds with 11
C corresponds with 12
D corresponds with 13
E corresponds with 14
F corresponds with 15

Converting values from one number system to another is not all that important, but for us geeks, it's more knowledge to add to our cerebral cortex. Plus, it's always good to know how computers interpret data.

Example: 94 (decimal) = ? (binary)

    a - 94 / 2 = 47 which has a remainder of 0
    b - 47 / 2 = 23 which has a remainder of 1
    c - 23 / 2 = 11 which has a remainder of 1
    d - 11 / 2 = 5 which has a remainder of 1
    e - 5 / 2 = 2 which has a remainder of 1
    f - 2 / 2 = 1 which has a remainder of 0
    g - 1 / 2 = 0 which has a remainder of 1

Note: As you perform the divisions, place the remainders in order from right to left.
Answer: 94 (Decimal) = 1011110 (binary)

 

Example: 1011110 (binary) = ? (decimal)

Note: only count those values which are turned on (designated by 1)
    2^6 + 2^4 + 2^3 + 2^2 + 2^1 = 64 + 16 + 8 + 4 + 2 = 94
Answer: 1011110 (binary) = 94 (decimal)

 

Example: 8BC (hex) = ? (decimal)

    8 x 16^2 + 11 x 16^1 + 12 x 16^0 ===>
    8 x 156 + 11 x 16 + 12 = 2236

Answer: 8BC (hex) = 2236 (decimal)

 

Example: 110 (decimal) = ? (hex)

    110 / 16 = 6 which has a remainder of 14 which corresponds to E in hex
    6 / 16 = 0 which has a remainder of 6

Note: As you peform the divisions, place the remainders in order from right to left.
Answer:
110 (decimal) = 6E (hex)

 

Example: 1101111100110001100111 (binary) = ? (hex)

Trick: First divide digits in pairs of four from right to left.

    11  0111  1100  1100  0110  0111 ===>
    0111 = 2^2 + 2^1 + 2^0 = 7
    0110 = 2^2 + 2^1 = 6
    1100 = 2^3 + 2^2 = 12 which corresponds to C
    1100 = 2^3 + 2^2 = 12 which corresponds to C
    0111 = 2^2 + 2^1 + 2^0 = 7
    11 = 2^1 + 2^0 = 3

Answer:
1101111100110001100111 (binary) = 37CC67 (hex)

 

Example: F2C (hex) = ? (binary)

Trick: First divide digits in pairs of four from right to left.

C = 1100
2 = 0010
F = 1111

Answer:
F2C (hex) = 1111 0010 1100 (binary)

Integer Values

A computer stores integer values in two's complement form. For positive integers, you can simply convert the value (should be decimal) into binary and make sure you have the correct capacity. Negative integers are slightly more difficult to calculate. Let's look at an example of each.

Example: - the integer 17, in 1-byte two's complement form would be:
    00010001 [which is simply 17 converted into binary form]

Example: - the integer -17, in 1-byte two's complement form would be:
    Step 1 - convert 17 into binary form: 00010001
    Step 2 - complement (or "flip") each bit: 11101110
    Step 3 - add 1: 11101111

The following example is slightly more difficult.
Let (LSB) denote the least significant byte (or the byte that has the lowest address in memory of a computer).

Example: - Suppose we have a 2-byte integer in memory that looks like the following:      00000110 10110001 (LSB)

In order to figure out the value of this integer, you need to first distinguish which byte is the least significant, which I give you in this example. In this case, 10110001 is the LSB. You then convert that byte which is in binary form into decimal form. 10110001 converted to decimal equals 177. Your next step is to evaluate the byte that you have left over which is 00000110. You must convert this value into decimal form, but remember since you are dealing with a 2-byte integer, the right most bit in 00000110 <--- is in position 256. Therefore, first convert 00000110 into decimal. You should get 6, but you have to multiply that decimal value by 256 to take care of the two-byte integer. Then simply add the two decimal values together. 6 * 256 = 1536 + 177 = 1713, and 1713 is the value of the 00000110 10110001

Note: Because of two's complement form, anytime the left most bit in a value given in binary form is 1, the value must be negative.

Examples:
              11101111 is -17
              10000000 is -128
              10000001 is -127


Real Values (floating-point)

A computer stores a real value, which has a decimal point, by storing 2 integer values (binary mantissa and exponent). In order to store a mantissa integer value and an exponent integer value, the real value must be converted into scientific notation.

Example: Consider a real value 272.8914
    272.8914 converted into scientific notation equals .2728914 X 10^3
    2728914 would be stored as the mantissa and 3 would be stored as the exponent


Character Data (strings)

A computer stores character data by using an ASCII (Amercian Standard Code For Information Interchange) coding scheme. In order to store a character string "mike" internally in a computer, the letters 'm' 'i' 'k' 'e' must be stored numerically by using the ASCII table values. Since the ASCII table is extremely large, I decided to only give the key values for the beginning of uppercase letter, lowercase letters, and numbers.

ASCII Key Values

'A' ---> 65
'a' ---> 97
'0' ---> 48

Note: You may notice that there is a difference of 32 between an uppercase A and a lowercase a. Since there are 26 letters in the alphabet, you might wonder why this is so. The genuises behind ASCII code designed it this way to make internal processing easier. To get from a capital A to a lowercase a in binary form, all you would need to do is flip position 32 of the value.

Example:
    01000001 - A
    01100001 - a

As a beginning programmer, you might find it hard to understand why learning the internal representation of data is important. As a programmer with some experience under my belt, I can tell you that there will be times when finding a solution to a problem will directly depend on knowing how computers interpret data. So if you are serious about programming, think twice before not learning how computers interpret data.

Enough with all the computer science talk. Let's start to dig deep into the C++ language. Read on for more...

C++ Basics

C++ syntax

The syntax of C++ is extremely critical. By definition, syntax is the rules governing the formation of statements in a programming language. In other words, it is the specific language (code) you use to turn algorithms into programs and specifc rules you have to follow when programming with C++. As you will find out, if you make one small mistake when writing code, the compiler will notify you of the syntax error when you try to run it. C++, like all languages, is very picky when it comes to syntax. Let's look at a simple example program written in C++.

// BEGIN PROGRAM [using Dev-C++ Compiler]
// Program to calculate the average of 3 integers
// Programmer: Mike Ware
// Last Updated: 9/5/02

#include <iostream.h>    // header file for input/output stream
#include <stdlib.h>    // header file for standard input/output

int main( )
{
    int number1, number2, number3;    // declaration of variables
    float average;

    cout << endl << endl;
    cout << "Enter first integer: ";
    cin >> number1;
    cout << "Enter second integer: ";
    cin >> number2;
    cout << "Enter third integer: ";
    cin >> number3;

    average = (number1 + number2 + number3) / 3.0;    // calculates the average

    cout << "The average of the three integers is: " << average << endl;

    system("PAUSE");
    return 0;
}    // end main( )

For now, notice how each executable statement in the program is ended with a semi-colon. Every executable statement must always be terminated with a semi-colon, which signals that it is the end of the statement. Any code placed after // will not affect the code; // is used for comments and documentation. Also notice that text is always enclosed in parentheses. Let's dig deep into the code to find out why you need to use certain code statements.

  • cout - console output - It is simply letting the compiler know that something is being sent into the output stream (normally the screen)
  • <<</code> - insertion operator - cout and the insertion operator go hand and hand; you must use it whenever you issue a cout command
  • cin - console input - It is simply letting the compiler know that something is being sent into the input stream (normally into the memory of a computer)
  • >> - extraction operator - cin and the extraction operator go hand and hand; you must use it whenver you issue a cin command
  • endl - end line - basically the same as pressing the return key in a text editor; returns down to the next line

system("PAUSE") - basically specifically designed for Dev's compiler; simply pausing the system during execution of the program so the user can interact with the program through a seperate screen module

Let's take a look at the structure of a C++ program. A program is made up of one or more functions. A function is a subprogram that performs some specific task and perhaps returns a value to the statement that called (invoked) it. The one required function for all programs is main ( ); it serves as the starting point for execution of a program. The main ( ) function may then call other functions which may call other functions and so on. A problem is broken down into smaller pieces from the use of functions.

Definition

function - a subprogram that performs some specific task and perhaps returns a value to the statement that called it.

The following is an outline of a C++ program in the order in which they occur.
    a) an "overview" of program (documentation); comments
    b) #include statements
    c) function prototypes [you don't need a prototype for the main ( )]
    d) main ( ) definition
    e) definitions of other functions

Documentation
The first part of any program should always contain detailed documentation concerning the program. It should include the program's overall purpose, the name of the programmer who wrote the code, the date the program was last updated, and any other information that you or other programmers may need to know in case you or someone else refers to the program code in the future. Comments can be inserted into program code in two ways:

    a - for single line comments, use: // your comments
        Anything placed after ( // ) is considered to be a comment.
    b - for multiple line comments, use : /* your comments */
        Anything placed between ( /* ) and ( */ ) are also considered comments.

#include Statements
The include section holds C++ include files which basically allow the execution of particular C++ code to be compiled and executed. The include files are different for every compiler, but #include <iostream.h> and #include <stdlib.h> are normally required for most compilers. The include statements are installed with the compiler, and it is your responsibility to call upon a statement if you need it. You are also allowed to create include files, but we won't get into that right now.

NOTE: The area after the include statements and before the main ( ) function is called the global area of program code.

Function Prototypes
Function prototypes are placed after the include statements. Every function, except main ( ), must have a function prototype, which is described in detail below.

main ( )
The main ( ) function is the starting point of execution in a C++ program. All other functions and program flow executable statements are basically branched from the main ( ) function.

Definitions of Other Functions
The use of functions in C++ is very important to learn. Just like everything else in C++, functions must be declared, and they have a specific form, which is as follows:

    return-type function_name ( parameter list )
    {
        statement(s)
    }


For now, just focus on the form of functions because I haven't mentioned anything about return-types, variable names, or parameters. We will cover those aspects later.

The function heading is simply:

    return-type function_name( parameter list )

The function body is simply:

    {
        statement(s)
    }


The function definition is simply the function heading plus the function body (the full function).

A function prototype looks exactly the same as the function heading with the addition of a semi-colon at the end.

    return-type function_name( parameter list );

Function prototypes are placed after the include statements but before the main ( ) function in a program. Remember that main ( ) is the only function that dosen't need a prototype; prototypes are required for all other functions. The following is an example of a function that accepts three integers (as input) and returns the average of the three values.

    int getAverage( int num1, int num2, int num3)
    {
        int average;
        average = (num1 + num2 + num3) / 3.0;
        return average;
    }    // end getAverage ( )


The function broken down:

function return type: int
function name: getAverage
parameters: num1, num2, num3

The following is another example of a function. This function will accept a fahrenheit temperature (as input) and will convert the temperature into the equivalent Celsius temperature and then return the Celsius temperature.

    float convertFahrToCelsius( float fahrTemp )
    {
        float celsiusTemp;
        celsiusTemp = ( fahrTemp - 32 ) / 1.8;
        return celsiusTemp;
    }    // end convertFahrToCelsius ( )


It obvious that in order to use this function, we need a complete program. A program whose only purpose is to test one or more functions is usually called a driver program. A function prototype is needed because everything in C++ must be declared before you can use it. In other words, it tells the compiler the name of the function, the return data type, and the number and type of parameters. This is everything a compiler needs to determine if you are using a function properly.

In C++, literals are exactly what they are. They have no special internal meaning. A literal can be a number, a letter, or a string (group of letters). 17, 'a', "name", "mike", "c++", 452, -112, etc.

Variables have an associated memory location and can be modified. For example, say you have a program that will calculate the average of three numbers. In the program code you should have four variables: num1, num2, num3, and average. During execution of the program, the compiler will give each of the variables a memory location in the memory of the computer. If the variables are not set to a default value in the program, as they are placed into memory, they will be assigned some type of garbage value until that value is changed during execution of the program.

Identifiers are the names you give variables, functions, etc. In C++, an identifier must begin with a letter or an underscore and further characters must be letters, underscores, or digits. Examples are STAR_TREK, THX792b4, FifthAvenue, etc.

So now you've seen bits and pieces of a program written in C++. Read on further to take a peek at the different data types...

Data Types

When declaring variables, functions, etc., you will need to specify a data type. That is the reason why you declare something. You are letting the compiler know what type of data it is going to be working with. In C++, data is either integral (integers and characters) or floating-point. Characters are considered integral because the character values will eventually be converted into an ASCII value. There is also a boolean type, which I won't mention too much about in this tutorial. Boolean types can be used when using logical data; when something can be either true or false. A 1 is considered true, and 0 is considered false.

There are several possible sizes and for some of these data types there is a choice of unsigned or signed types. Signed simply means that the data can be negative or positive. Unsigned means the data will be strictly positive. In the following list, the sizes are representative only (compiler dependent).

char 1 byte -128 to 127
unsigned char 1 byte 0 to 255
int 2 bytes -32,768 to 32,767
unsigned int 2 bytes 0 to 65,535
long or (long int) 4 bytes -2,147,483,648 to 2,147,483,647
unsigned long 4 bytes 0 to 4,294,967,295
float 4 bytes 3.4 x 10^-38 to 3.4 x 10^38
double 8 bytes 1.7 x 10^-308 to 1.7 x 10^308
long double 10 bytes 3.4 x 10^-4932 to 3.4 x 10^4932



Note that integer values may be given in decimal, hexadecimal, or octal. Any value that begins with 0x or OX is a hexadecimal number. Any value that begins with 0 is octal. Also note that floating point values may be given in scientific notation. For example, the value 2.693 x 10^23 can be written as 2.693E+23.

The form for declaring a variable is as follows (will see this in next section also):

    data-type identifier;
        Ex: int num1;


Initialization is different from declaring because you declare the variable and also give it a value:

    data-type identifier = value;
        Ex: int num1 = 123;


You can also initialize a variable constant. The form for named constants is:

    const data-type identifier = value; (where value is a literal)
        Ex: const int MAXSIZE = 450;


Why would you want to use named constants? For one, it can help you avoid errors. By declaring something constant, the value of the variable will never be changed during program execution. Secondly, it can make programs easier to modify or correct. Let's say you have a program that processes the income statements for 30000 people, but there is a major lay-off and it is now cut back to 25000. If you had a constant variable for the maximum number of people, it would be very easy to edit the code. Finally, you get away from using "magic numbers", which are numbers that you see in programs and have no clue where they came from.

Note: constants are generally placed after the include statements but before anything else in a program (in the global area).

Now you have data types under your belt. Read on to actually learn how to assign values to variables and how to manipulate data with arithmetic operators...

Arithmetic Operators & Assignment Statements

Assignment Statements
An assignment statement is fairly simple to understand. It is a way of assigning a value to a variable; it stores a value in the indicated location. The form of an assigment statement is:

    variable = expression;


Because you are specifying a variable, the variable must be declared so the compiler knows what type of data will be involved. The expression should therefore match the data type of the variable being assigned to it. Some programmers prefer to use two descriptive words when dealing with these types of statements: declarations and initializations. A declaration is when you declare something. When you initialize something, you declare it and give it a value.

An example of a declaration:    int x;
An example of an initialization:    int y = 100;


Arithmetic Operators
For arithmetic expressions, the fundamental operators are as follows:

    +,- (unary and binary forms: addition/subtraction; positive/negative)
    * (multiplication)
    / (division)
    % (modulus or MOD) ---> returns the integer remainder of a division

NOTE: / will result in an integer value if both operands are integers; otherwise, it will result in an floating point value.
NOTE: % is defined for integers only.

Examples:

        int x = 25;
        int y = 7;
        cout << x / y << endl;        ---> 3
        cout << x % y << endl;        ---> 4
        cout << y % x << endl;        ---> 7

NOTE: the only possible values for an expression of the form x % y are: 0,1,2,...,y - 1

Let's take a look at some examples of code segments you might need when finding a solution of a particular problem.

Example 1: Suppose a line is 76 inches long. Convert this line in dimensions of feet and inches.

    feet = 76 / 12;
    inches = 76 % 12;

Example 2: To determine if a given integer is odd or even.

    number % 2 == 0
    if true ---> number is even
    if false ---> number is odd

Example 3: To determine if a given number is divisble by 3.

    number % 3 == 0
    if true ---> yes
    if false ---> no

NOTE: == is a relational operator which is discussed in later articles [see Logical Expressions (section 3)]

Hopefully you can see how division and modulus can be used for solving problems. The following are some common errors that many beginning programmers run into when programming.

Common Errors:

  1. x = 29 % 4.8;
    Error: the compiler will display something along the lines of "invalid use of floating-point". Remember modulus is defined only for integers.
  2. p + 7 = x + 3;
    Error: the compiler will display something along the lines of "Lvalue required", which means the compiler must find a memory location when assigning values to a variable. In C++, = is an operator; it does not imply equality.

C++ also provides some "shortcut" operators that come in handy when programming. The increment operator, ++, adds 1 to a variable. The decrement operator, --, does the opposite and subtracts 1 from a variable.

Example:

    n = n + 1 is equivalent to n++ and ++n; n = n - 1 is equivalent to n-- and --n

n++ and n-- are called post-increments, which mean they are performed after anything else in the statment. ++n and --n are called pre-increments, which mean they are performed before anything else in the statement.

Example:

    int x = 7;
    int y = 4;
    cout << x++ * --y << endl;     --> 21
    cout << x << " " << y;         --> 8    3


C++ also has shorthand assignment operators or sometimes called compound assignment operators. In general, n += (expression) implies n = n + (expression); n -= (expression)implies n = n - (expression); n *= (expression) implies n = n * (expression); n /= (expression) implies n = n / (expression); n %= (expression)implies n = n % (expression).

n = n + 1    -->    n++    -->    ++n
n = n - 1    -->    n--    -->    --n


Well you now know how to use arithmetic operators and assignment statements, but you might not know exactly how these operations are evaluated in statments. Read on to find out which operators have precedence over others...

Operator Precedence & Operator Associativity

Operator precedence and operator associativity are both very important to understand. The best way to understand these concepts is to examine some examples.

Operator Precedence:

    unary +, - (high precedence)
    *, /, %
    binary +, - (low precedence)

Operator Associativity:

Associativity may be left associative (left to right) or right associative (right to left). All arithmetic ops are left associative; assignment ops (+=, -=, *=, /=, %=, =) are right associative.

Let's examine some examples.

Example 1:

    int a = 6, b = 5, c = 9;
    cout << a + b * c;        --> 6 + ( 5 * 9 ) = 51
    cout << a * b % c;        --> ( 6 * 5 ) % 9 = 3


Example 2:

    int a = b = c = d = 6;    --> intializes a, b, c, and d to a value of 6

Example 3:

    x = y = 5;
    a = 8;
    b = 5
    c = a * b / 3 + a / x;
    cout << c;                --> ( 8 * 5 ) / 3 + ( 8 / 5 ) = 13 + 1 = 14

    c = 9 + a % b;
    cout << c;        --> 12

    c = 10 / 3 * ( 2 + b )
    cout << c;            --> 10 / 3 * ( 7 ) = 3 * 7 = 21;


Example 4:

    17 / 2 / 3 - 125 % 5        --> ( ( 17 / 2 ) / 3 ) - ( 125 % 5 ) = 2

    2.5 * 6 + 3 / 2.0        --> 15.0 + 1.5 = 16.5
    2.5 * ( 6 + 3 ) / 2.0        --> 22.5 / 2.0 = 11.25


Now that we have covered the precedence of operators and operator associativity, we can now move on to something that occurs during assignment statements, sometimes without the programmer asking for it and sometimes by the programmer specifically asking for it. Read on for more about type conversions...

Type Conversions

Type Conversion

When dealing with assignment statements and calculations, data types can sometimes be converted to other data types through something that is called type conversion. Conversions that take place without the programmer specifically asking for it are called implicit conversions. Conversions that are requested by the programmer are called explicit conversions. Programmers run into problems during implicit conversions because they can be unaware of what is actually happening during execution of their program until they examine the code very carfully.

Definition

implicit conversion - conversion that happens without the programmer specifically asking for it

It's pretty clear that programmers can run into problems because of implicit conversions. However, if you fully understand data types and how they are defined for certain arithmetic operators, you should be able to avoid them. For instance, if y and z are float, and a is int, when the statement z = a + y is executed, the value of a will be stored in a temporary memory location as float for use in the rest of the statement. Then, the addition is done (in float form) and finally, the result stored in z. So what happened? [a was implicitly converted into float during the execution of the statement]

Another form of implicit conversion may take place during assignment, but not necessarily from a smaller type to a larger type.

Example:


   int a;
   float b = 3.5;
   float c = 5.0;
   int d = 2;
   a = b * c / d;
   cout << a;        --> 8


What conversion took place in the above example? First of all, d was implicitly converted to 2.0 during b * c / d. The result of that multiplication and division is 8.75, but a was declared int, so a was implicitly converted to 8 during assignment.

Definition

explicit conversion - conversion requested by the programmer; sometimes called typecasting

While programmers are unaware of implicit conversions, they are completely in control of explicit conversions. Explicit conversions, or typecasting, can come in handy as you will see in later problems. Typecasting can be easily accomplished and it has a easy to use form which is: type( variable ) where type is whatever data type you choose and variable is the variable you are converting. It simply takes the value of variable and converts in into the type specified.

Example 1:

   double d;
   int k;
   d = 12.78;
   k = int(d);        --> k = 12
   d = double(k);        --> d = 12.0


Example 2:

   int m = 10, n = 4, r;
   double q;
   q = m / n;        --> q = 2.0
   r = double(m) / n;        --> 2

   r = m / n;        --> 2
   q = double(m) / n        --> 2.5

Note: q = (double)m / n is equivalent to q = double(m) / n;

Ok, so we have convered assignment statements, arithmitic ops, precedence, associativity, type conversions, but we haven't covered how programmers compare expressions in C++. Read on for more about logical and relational expressions...

Logical Expressions

Logcial expressions, somtimes called boolean expressions, are expressions that return a value of true or false. A ( 1 ) is always associated with a true expression; a ( 0 ) is always associated with a false expression. There will be many times when you as a programmer will have to write code to handle certain (true/false) situations. If an expression is true, then you will issue some command. If an expression is false, then you will issue another command. Logical expressions make it possible to test values to see if something is true or false. A relational expression is a special type of logical expression in which some type of comparison is performed. The relational operators are >, <, <=, >=, ==, and !=.

Definition

logical expression - an expression that returns a value of either true ( 1 ) or false ( 0 ).

 

Definition

relational expression - a special type of logical expression in which some type of comparison is performed.

Relational Operators

   > : greater than
   < : less than
   >= : greater than or equal to
   <= : less than or equal to
   == : is equal to
   != : is not equal to

One of the biggest problems many beginning programmers have is distinguishing between = and ==. Experienced programmers can even run into problems with these two operators because it is very easy to mistype them when programming. = is an operator that is only used for assignment purposes; it does not imply equality. == (is equal to) is an relational operator that is used to compare two values to see if they are equal. Be careful not to use = in relational expressions when == is needed.

Examples of relational expressions (also logical expressions):

  1. a< 2 * b - 7
  2. c != -1
  3. b > c + 4 * 7

A relational expression is always a logical expression. Logical expressions are either relational expressions or a combination of multiple expressions joined together by the logical operators: &&, ||, and !.

Logical Operators

   && : logical and
   || : logical or
   ! : logical not

Example of a logical expression (not a relational expression):

(a < b) || ( b < c)

If a = 5, b = 3, and c = 10, the result of this expression is 1 (true).

A quick way to tell if an expression is logical but not relational is if one of the logical operators is being used.

Common Errors:

  1. Using = (assignment operator) when == is needed.
  2. Using an expression such as:

     midTerm || final == 'A' which needs to be changed to    midTerm == 'A' || final == 'A'
  3. Using an expression such as:

       a < b < c which needs to be changed to    a < b && b < c
  4. Using && when || is needed or vice versa:

    Suppose a variable x is being used and x = -1 and x = -99 have special meaning:

       if (x != -1 || x != -99) is wrong and needs to be changed to    if (x != -1 && x != -99)

    In general, the operators have precedence (highest to lowest):

       A - arithmetic
       R - relational
       L - logical

    - the one exception is logical not ( ! ), which is done before the arithmetic operators.

Specifically, the overall precedence for operators in this tutorial is:

   1 : !, +, - : (unary)
   2 : *, /, %
   3 : +, - : (binary)
   4 : <, <=, >, >=
   5 : ==, !=
   6 : &&
   7 : ||

Practical Use Example:

Suppose an insurance company offers a discount on auto insurance for students who have no prior accidents and a GPA of 3.0 or above. What expression could be used to ask this?

   numAccidents == 0 && gpa >= 3.0

Most compilers also have a capability called short-circuit evaluation when evaluating logical expressions. It makes logical operations faster. If a compound logical expression involves one section that is false and an AND ( && ) operation is used, the entire expression must be false. If one section is true and OR ( || ) is involved, the entire expression must be true.

With expressions out of the way, it's time to see how the flow of programs can be controlled with C++. Read on for more about the different control structures...

Controlling Execution With Control Struc

Control Structures

Controlling the flow of a program is a very important aspect of programming. There may be many different ways to find the solution to a particular problem, but you will want to look for the best and fastest way. C++ has four types of control structures: sequential, selection (decision), repetition (loops), and subprograms (functions). These structures simply control the execution of a program.

Sequential
Sequential means the program flow moves from one statement to the next, that statement moves to the next, and so on. It is the simplest form of structures, and it is not likely to use sequential structuring when developing complex programs.

Selection (decision)
Selection structures make decisions and perform commands according to the desicion making. Selection structures involve "if statments" which are statements like "if this, then do that" and "if not this, then do that". With "if statements", we can also include "nested if statements", which are "if statements" inside other "if statements". Another form of selection structures is called a "switch statement", which is very powerful for certain situations but not others. "Switch statements" focus on the value of a particular variable and perform different "cases" accordingly.

Repetition
Repetition structures are used when something needs to be repeated a certain number of times through the use of "loops". A loop is simply a statement that completes iterations or cycles until a certain value is reached and then execution moves to the next executable statement. For instance, if you were to ask the user to enter ten values to find the average of the numbers, you could write a loop that would continue letting the user enter numbers until ten numbers had been entered.

Subprograms (functions)
A function is a subprogram that performs a specific task and may return a value to the statement that called or invoked it. Functions make programming very powerful because once you develop a function to handle a particular situation, you can use that function for other programs. A function can call another function, which may call another function, and so on. It is a great way to organize your program and break your program down into logical steps.

After a very brief intro to the different types of control structures, it's time to move on and find out how you can use each type. We will skip over sequential structures since they are pretty much self-explanatory and elementary and move on to selection statements. Read on for more about selection structures and "if statements"...

Selection Statements

When dealing with selection statments, there are generally three versions: one-way, two-way, and multi-way. One-way decision statements do some particular thing or they don't. Two-way decision statements do one thing or do another. Multi-way decision statements can do many different things depending on the value of an expression.

One-Way Decisions
One-way decisions are handled with an "if statement" and either do some particular thing or do nothing at all. The decision is based on a logical expression which either evaluates to true or false. If the logical expression is evaluated to true, then the corresponding statement is executed; if the logical expression evaluates to false, control goes to the next executable statement. The form of a one-way decision statement is as follows:

   if ( logical expression )
           stmtT;


The stmtT can be a simple or compound statement. Simple involves 1 single statement. Compound involves 1 or more statements enclosed with curly braces { }. A compound statement is called a block statement.

Example 1: simple statement

   int c = 3;
   .
   .
   .
   if ( c > 0 )
           cout << "c = " << c << endl;


Example 2: compound statement

   int c = 10;
   .
   .
   .
   if ( c > 5 )
   {
           b = 2 * b + c;
           cout << c * c * b << endl;
   }


Two-Way Decisions

Two-way decisions are handled with "if / else statements" and either do one particular thing or do another. Similar to one-way decisions, the decision is based on a logical expression. If the expression is true, stmtT will be executed; if the expression is false, stmtF will be executed. The form of a two-way decision is as follows:

   if ( logical expresion )
           stmtT;
   else
           stmtF;


stmtT and stmtF can be simple or compound statements. Remember that compound statments are always enclosed with curly braces { }.

Example 1:

   int c = 10;
   .
   .
   .
   if ( c > 0 )
           cout << "c is a positive integer" << endl;
   else
           cout << "c is a negative integer" << endl;


Example 2:

   int num;
   .
   .
   [num gets a value]
   .
   .
   if ( num % 2 == 0 )
           cout << "num is an even integer" << endl;
   else
           cout << "num is an odd integer" << endl;


Practical use example:

Suppose you are to write code that will calculate the earnings by workers who are paid an hourly wage, with weekly hours greater than 40 being paid "time and a half". Suppose weekly hours and hourly rate are known in the program. Two options of code to handle this situation are as follows:

Option 1 using simple statements:

   if ( weeklyHours <= 40 )
           earnings = hourlyRate * weeklyHours;
   else
           earnings = 40 * hourlyRate + ( weeklyHours - 40 ) * hourlyRate * 1.5;


Option 2 using a simple and compound statement:

   if ( weeklyHours <= 40 )
           earnings = hourlyRate * weeklyHours;
   else
   {
           offHours = weeklyHours - 40;
           regpay = 40 * hourlyRate;
           earnings = regpay + offHours * hourlyRate * 1.5;
   }


Multi-Way Decisions

Multi-way decision statements involve using "if / else if" statements, "nested ifs", or "switch" statments. They are all used to evaluate a logical expression that could have several possible values. "if / else if" statements are often used to choose between ranges of values. Switch statements are discussed in next section [see Switch Statements].

The form of a mult-way decision using "if / else if" statments is as follows:

   if ( logical expression )
           stmtT1;
   else if ( logical expression )
           stmtT2;
   else if ( logcial expression )
           stmtT3;
   .
   .
   .
   else if ( logical expression )
           stmtTN;
   else
           stmtF;


If the first logical expression is evaluated to true, then stmtT1 is executed. If the second logical expression is true, then stmtT2 is executed and so on down the line. If none of the logical expressions are true, then the statement after "else" is executed which is stmtF.

The form of a multi-way decision using "nested ifs" is as follows:

   if ( conditionA )
   {
           if ( conditionB )
                    stmtBT;
           else
                    stmtBF;
   }
   else
           stmtAF;


If conditionA is evaluated to true, then execution moves into the nested if and evaluates conditionB. If conditionA is evaluated to false, then stmtAF is executed.

Example 1:

   int x;
   if ( x > 0 )
           cout << "x is positive" << endl;
   else if ( x = 0 )
           cout << "x is zero" << endl;
   else
           cout << "x is negative" << endl;


Example 2:

   char ch;
   if ( ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' )
           cout << "ch contains a letter" << endl;
   else if ( ch >= '0' && ch <= '9' )
           cout << "ch represents a digit" << endl;
   else if ( ch == ' ' || ch == '\t' || ch == '\n' )
           cout << "ch is white space" << endl;
   else
           cout << "ch is a misc. character" << endl;


Example 3:

   int score;
   .
   .
   [score gets a value]
   .
   .
   if ( score >= 90 )
           grade = 'A';
   else if ( score >= 80 )
           grade = 'B';
   else if ( score >= 70 )
           grade = 'C';
   else if ( score >= 60 )
           grade = 'D';
   else
           grade = 'F';


Example 4:

           if ( x > 25 )
               if ( x < 50 )
                   cout << "x is a mid-range value" << endl;
               else
                   cout << "x is a high value" << endl;
           else
               cout << "x is a low value" << endl;


We've discussed the necessary options for writing code for one-way decisions, two-way decisions, and multi-way decisions, but there is one option I have saved for a section by itself. Read on for more about switch statements...

Switch Statements

A switch statement is yet another option for writing code that deals with multi-way decisions. I saved it for a section by itself because some programmers feel using a switch statment is not good programming style. With 1 semester of programming under my belt and a few years of fooling around with different programming languages, I haven't encountered any problems with using a switch statement. I haven't written too many programs using switch statments, but when I have, the programs have seemed to run smoothly. Maybe I'll see why programmers don't like to use switch statements in later programming experiences. For now, let's explore the ins and outs of a switch statement.

The form of a multi-way decision using a "switch" statement is as follows:

   switch ( int expression )
       stmt;


Where typically the stmt is a block statement containing a series of case labels and optionally a default label. The expression must be integral in nature, which means it must be of type int. Switch statments offer an alternative to an "else if" structure which has multiple possible paths based on the value of a single expression. A revised form for a switch statement is as follows:

   switch ( int expression )
   {
           case label - 1 :
                   stmt-list-1;
                   break;
           case label - 2 :
                   stmt-list-2;
                   break;
           case label - 3 :
                   stmt-list-3;
                   break;
           .
           .
           .
           case label - n :
                   stmt-list-n;
                   break;
           default :
                   stmt-list;
   }


During execution, the expression is evaluated. If the value of the expression matches label - 1, then stmt-list-1 (and perhaps all lists below) will be executed. If not, execution will move on to check label - 2 and so on. If no labels match the expression, default will be executed. Inside each case and at the end of every statment list (except default) there must be a break statement, which terminates the innermost enclosing switch or loop statement (not recommended with loops ).

Here are some final notes about switch statements: the expression being tested must result in an integral value (int or single char), case labels must be integral constants (either literals or named constants), each label within a switch should be unique, and each stmt-list may contain 0 or more stmts which do not need to enclosed with curly braces { }.

Example:

Suppose a variable named score is an int variable that contains the number of points scored on a football play. Consider the following code:

   switch ( score )
   {
           case 1 : cout << "Extra Point" << endl;
                   break;
           case 2 : cout << "Safety of two point conversion" << endl;
                   break;
           case 3 : cout << "Field Goal" << endl;
                   break;
           case 6 : cout << "Touchdown" << endl;
                   break;
           default : cout << "Invalid score on a football play." << endl;
   }

With selection statements out of the way, it's time to talk about iteration (or looping) in a program. This, of course, involves using loop statements. Read on to explore the world of looping...

While Loops

A loop is a statement of code that does something over and over again until the logical expression of the loop evaluates to false. It is simply a way of repeating code over and over again until some particular value is reached. It can also be an effective way of making sure the user is inputting the correct type of input (data validation). A loop is different from an if statement in that if the logical expression is true, the stmt will be executed and control will go back to check the logical expression again, ..., and again, until eventually the logical expression becomes false. One thing to avoid is writing code that produces an infinite loop, which is a loop that never stops until you press ctrl-break on your keyboard. In order to avoid infinite loops, you need a statement(s) inside the body of the loop that changes some part of the logical expression. We will talk about three types of loops: while, do while, and for. The form of a while loop is as follows:

   while ( logical expression )
           stmt;


Example 1:

   int x = 7;
   while ( x > 0 )
   {
           cout << x << endl;
           x--;
   }


Example 2 using data validation:

Suppose the user of a program is to input an integer in the range -10 to 10 (inclusive). Using data validation, write a section of code to input this value:

   int k;
   k = -99;
   while ( k < -10 || k > 10 )
   {
           cout << "Enter an integer value (between -10 and 10): ";
           cin >> k;
   }


Example 3:

Write a section of code that will display all multiples of 5 that are less than 500.

   int x = 5;
   while ( x < 500 )
   {
           cout << x << endl;
           x = x + 5;
   }


That wraps up all I have to say about while loops so let's move on to the next loop of interest. Read on for more about do while loops...

Do While Loops

A do while loop is another type of repetition statement. It is exactly the same as the while loop except it does something and then evaluates the logical expression of the loop to determine what happens next. Normally with a while loop, a part of the logical expression in the loop must be initialized before execution of the loop. A do while loop lets something be performed and then checks the logical expression of the loop. I like to use a do while loop when using data validation during a program. The form of a do while loop is as follows:

   do
           stmt(s);
   while ( logical expression );


The stmt in the loop may be single or complex. Complex statements must be enclosed with curly braces { }. Let's look at a couple of examples of do while loops.

Example 1:

   int number;
   do
   {
           cout << "Enter a postive number greater than 0: ";
           cin >> number;
   }
   while ( number <= 0 || int(number) != number );


Example 2:

   int number;
   do
   {
           cout << "Enter a number between 0 and 100: ";
           cin >> number;
   }
   while ( number <= 0 || number >= 100 );


Having discussed while and do while loops, there is one more loop I would like to cover: for loops. Read on for more about for loops...

For Loops

A for loop is another way to perform something that needs to be repeated in C++ (repetition). Most programmers like to use for loops because the code can be written in a more compact manner compared to the same loop written with a while or do while loop. A for loop is also important to understand because they are used when dealing with arrays and other topics in C++ [for info on arrays see One-Dimensional Arrays (section 8) and Two-Dimensional Arrays (section 8)]. A for loop is generally used when you know exactly how many times a section of code needs to be repeated. The general form of a for loop is as follows:

   for ( expression1; expression2; expression3 )
           stmt(s);


where stmt(s) is a single or compound statment. Expression1 is used to initialize the loop; expression2 is used to determine whether or not the loop will continue; expression3 is evaluated at the end of each iteration or cycle.

NOTE 1: expression2 is tested before the stmt and expression3 are executed; it is possible that the body of the loop is never executed or tested.

NOTE 2: Any or all of the 3 expressions in a for loop can be omitted, but the 2 semi-colons must remain. If expression1 is omitted, there is no initialization done in the loop, but you might not need any initialization for your program. If expression2 is omitted, there is no test section so you will essentially have an infinite loop. If expression3 is omitted, there is no update as part of the for statement, but the update could be done as part of the statement in the body of the loop.

In general, a for loop can be written as an equivalent while loop and vice versa.

   for ( expression1; expression2; expression3 )
           stmt;


is equivalent to...

   expression1;
   while ( expression2 )
   {
           stmt(s);
           expression3;
   }


Example 1:

Write a for loop that will display all odd integers between 1 and 51:

   for ( int k = 1; k <= 51; k += 2 )
           cout << k << endl;


Example 2:

Write a for loop that will display a "countdown" of integers from 20 to 1:

   for ( int k = 20; k >= 1; k-- )
           cout << k << endl;


Now that I've covered while, do while, and for loops, I can introduce you to some methods of controlling repetition in a program. Read on for more about controlling repetition with sentinel values...

Control Structures and Formatting Output

Using Sentinel Values

There are three ways to control repetition during execution of a program: sentinel values, prime reads, and counters. Let's talk about sentinel values now and talk about prime reads and counters later. A sentinel value is a value that is not a legitimate data value for a particular problem (but is of proper type) used to check for a "stopping" value. There may be times when you must let users of your program enter as much information about something as they want to. When the user is done entering information, the user can enter a sentinel value, which would let the program know when the user is done inputting information.

Definition - sentinel value - a value that is not a legitimate data value (but is of proper type) used to check for a "stopping value".

Example 1: [ -1 ] is used as a sentinel value

   int age;
   cout << "Enter an age ( -1 to stop ): ";
   cin >> age;
   while ( age != -1 )
   {
           .
           .
           .
           cout << "Enter an age ( -1 to stop ): ";
           cin >> age;
   }


Example 2: [ -99 ] is used as a sentinel value

Read a list of text scores and calculate their average. An input of -99 for a score denotes end-of-data for the user.

   int numScores, sum, score;
   float average;
   sum = 0;
   numScores = 0;

   cout << "Enter a test score ( -99 to quit ): ";
   cin >> score;
   while ( score != -99 )
   {
           sum += score;
           numScore++;
           cout << "Enter a test score ( -99 to quit ): ";
           cin >> score;
   }

   average = float(sum) / numScores;
   cout << "The average of the " << numScores << " was " << average << endl << endl;


The next article will introduce you to prime reads. Prime reads and sentinel values normally go hand and hand, but not for every situation. Read on to learn more about prime reads...

Using Prime Reads

Another method of controlling repetition is to use a prime read. A prime read and sentinel value often go hand and hand but not always. A prime read is a data input, before the loop statement, that allows the first actual data value to be entered so it can be checked in the loop statement. The variable that is inputted by the user and being tested by the expression in the loop is the prime read; the value of the prime read is what we call a sentinel value [ see Using Sentinel Values].

Example 1: [ age ] is used as a prime read

   int age;
   cout << "Enter an age ( -1 to stop ): ";
   cin >> age;
   while ( age != -1 )
   {
           .
           .
           .
           cout << "Enter an age ( -1 to stop ): ";
           cin >> age;
   }


Example 2: [ score ] is used as a prime read

Read a list of text scores and calculate their average. An input of -99 for a score denotes end-of-data for the user.

   int numScores, sum, score;
   float average;
   sum = 0;
   numScores = 0;

   cout << "Enter a test score ( -99 to quit ): ";
   cin >> score;
   while ( score != -99 )
   {
           sum += score;
           numScore++;
           cout << "Enter a test score ( -99 to quit ): ";
           cin >> score;
   }

   average = float(sum) / numScores;
   cout << "The average of the " << numScores << " was " << average << endl << endl;


We've covered sentinel values and prime reads, but there is one last method used for controlling repetition during execution of a program. Read on to learn more about using counters...

Using Counters

Yet another method for controlling repetition during execution of a program is by using a counter. Using a counter requires knowledge of the exact number of times you need to repeat something. For example, if you were to instruct the user of your program to input ten numbers, you could set a counter variable to 0, and then set up a loop to continue cycles while the value of counter is less than ten (this loop would equal ten cycles: 0, 1, 2, .., 9).

Example 1:

Write a section of code that would output the numbers from 1 to 10:

   int count;
   count = 0;
   int numTimesNeeded = 10;
   while ( count < numTimesNeeded )
   {
           count << count + 1 << endl;
           count++;
   }


Example 2:

Write a section of code that will allow the user to input ten test scores in order to find the average of the scores:

   int count, score;
   float average;
   count = 0;
   int numTimesNeeded = 10;
   int total = 0;
   while ( count < numTimesNeeded )
   {
           cout << "Enter the score for test " << count + 1 << ": ";
           cin >> score;
           total += score;
           count++;
   }

   average = float(total) / numTimesNeeded;
   cout << "The average of the " << numTimesNeeded << " test scores is: " << average << endl;


We have now covered three ways of controlling repetition: using a sentinel value, prime read, or counter. It's time to move on and talk about how programmers control output using output manipulators. Read on for more about ways to spice up your form of output...

Output Manipulators

There may be many reasons why you would want to override the default output behavior. Maybe you are outputting the totals for certain customers at a store and you need to allow just two digits after the decimal point. Output manipulators allow programmers to control the exact form of output. I've found that these manipulators vary from compiler to compiler. I'm basing the information in this tutorial from using Dev's compiler so if you are using a different compiler, you might see some slight changes in the form of your output. Most manipulators are found in the header file    #include <iomanip.h>    but that may also vary from compiler to compiler. Remember that header files are placed at the beginning of your source code after the documentation of your program. The following are some examples of output manipulators:

When turning on certain options, you must use one the following forms before outputting:

   1 - cout.setf( ios::option here | ios::option here );
   2 - cout << setiosflags( ios::option here | ios::option here );

There is also a way to turn off options if you wish to terminate them after using them:

   cout << resetiosflags( ios::option here | ios::option here );

Overview of Statement Commands

cout is an object.
setf is a member function.
ios is a class.
:: is a scope resolution operator.
| is the bitwise OR operator.
object is a class variable.
setflag sets a formatting flag.

Options

showpoint - tells the compiler to display a decimal point and any trailing zeros; only defined for floating point values and never influences an integer value.
showpos - tells the compiler to display a "+" to the left of all positive values.
fixed - tells the compiler how many values to display after the decimal point in order to fulfill default number of significant digits.
scientific - converts values into scientific notation while also having the abilities of fixed
left - sets left justification.
right - sets right justification (default).

Example 1:

Write code to turn on the following flags: showpoint, fixed, left (you have 2 options)

   1 - cout << setiosflags ( ios::showpoint | ios::fixed | ios::left );
   2 - cout.setf ( ios::showpoint | ios::fixed | ios::left )

Other Manipulators

setprecision( ) - If used with fixed it determines how many values to be displayed after the decimal; if used without fixed it determines how many significant digits to be displayed.

setw ( ) - Determines the width of the output field. Its effect does not "stick" throughout the rest of the statement; it is only good for the value it precedes. You must set a width for every value that you want to control.

setfill ( char ) - Specifies a char to be used instead of blank spaces when an output field is larger than the required size. You must turn it off by setting the fill to be spaces if you wish to get back to filling with blank spaces.

The best way to see how manipulators are used is to examine examples and fool around with them yourself. By default, Dev's compiler displays at most 6 significant digits. Let's examine the source code of a complete program and study the program's output.

#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>

void main ( )
{
   float x = 23.012345678;
   float y = 44;
   float z = 3.765;
   float a = 4.5;

   cout << 2345.012345678 << endl;
   cout << 0.005544332211 << endl;
   cout << x << endl;
   cout << y << endl;
   cout << z << endl;
   cout << a << endl;
   cout << x << y << z << a << endl;

   system("PAUSE");
   return 0;
}


Output produced by program:

2345.01
0.00554433
23.01234
44
3.765
4.5
23.0123443.7654.5


Add the following piece of code before the cout statements above:

cout.setf ( ios::showpoint | ios::showpos );

New output produced by program:

+23.0123
+44
+3.76500
+4.50000
+23.0123+44+3.76500+4.50000


Now add the following before the cout statements above:

setprecision (2);


New output produced by program:

+23.
+44
+3.8
+4.5


Change the last cout statement to:

cout << x << setfill ( '*' ) << setw ( 10 ) << y << setfill ( ' ' ) << z << a << endl;

Output produced:

+23.0123*******+44+3.76500+4.50000


Change the last cout statement to:

cout << x << setfill ( '*' ) << setw ( 10 ) << y << setw ( 10 ) << z << setw ( 10 ) << a << endl;

Output produced:

+23.0123*******+44**+3.76500**+4.50000

Example code:

   const float PI = 3.14159;
   cout << setiosflags ( ios::showpoint ) << endl << "The value of PI is: "
       << setprecision ( 4 ) << setw ( 15 ) << setfill ( '-' ) << PI << endl;


Output produced:         ----------3.142

Add the fixed manipulator option to the code:

   setiosflags ( ios::showpoint | ios::fixed );


New output produced:         ---------3.1416

Change code to:

   setiosflags ( ios::showpoint | ios::scientific );

New output produced:         -----3.1416e+00

Change code to:

   setiosflags ( ios::showpoint | ios::scientific | ios::left );


New output produced:         3.1416e+00-----

We covered a good bit of information about output manipulators, but the best way to get familiar with them is to fool around with them yourself. Experiment a little bit. Experimentation gives birth to knowledge. Now it's time to move on to a subject that I find interesting compared to the previous articles: files. I like working with files for the simple fact that the code you write is capable of reading data from your computer's hard drive and also saving data to the hard drive. Read on for more about files...

Text Files, Strings, Random Numbers, and Member Fu

Working with Files

Working with files is not hard and is actually quite fun. The file streams give you access to read and write data to and from your hard drive. After all, that is whole idea of working with files. Your program will either need to read data from a file on your hard drive, or you will need to save data to a file on your hard drive. Files can be text or binary. Text files are first converted into ASCII by the computer before any manipulation can be done. You can create and view text files using a text editor such as notepad on a windows pc. Your compiler should also be able to view and create text files. If your compiler does have this option, you should use it when viewing text files created from your program because it will give you the most accurate representation of what your program actually produced. Binary files are the multimedia files on your computer; they may have extensions such as: obj, exe, gif, jpg, avi (etc). I will only brush through the topics of text files in this section. I may discuss binary files in later articles.

Text Streams
The standard objects cin, cout, and cerr are predefined text streams that are defined in the header file #include <iostream.h>. All other text objects that I will discuss must be explicitly declared to be of type ifstream (input file ) or ofstream (output files).

ifstream - used for input files when you want to read data from a file; the file should exist on the hard drive or an error will occur.

ofstream - used for output files when you want to write data to a file; if the file doesn't exist, one will be created; if the file does exist, it will be overwritten.

Both of these class types are declared in the header file #include <fstream.h> so when you use them in your program, make sure you have included this header file. It is important to remember that before you read from or write to a file, the file must be open. You open a file by attaching a "logical file name" to a "physical file name". The "logical file name" is a variable for the file that you will use throughout the rest of your program. The "physical file name" is the path of the file on disk. Once you have the two names, the file can be opened by using the member function open ( ).

NOTE: When you are specifying the path of a file on your hard drive, you must use a double slash " \\ " instead of the normal single slash. This is done because in C++, when you issue a single slash, the compiler thinks a command will be after it. By using a double slash, the compiler recognizes that a file is being worked with.

Example (declaring and opening files):

ifstream myInputFile;
ofstream myOutputFile;

// Using the open ( ) member function, the following stmt. associates
// the stream variable myInputFile (logical name) with the file NAMELIST.DAT (physical name)
// on drive D: of the computer's hard drive.

myInputFile.open ("D:\\NAMELIST.DAT");

// From this point on in the program, the logical name should be used
// to refer to the input stream

// Using the open ( ) member function again, the following stmt. associates
// the stream variable myOutputFile (logical name) with the file SUMMARY.RPT (physical name)
// on drive D: of the computer's hard dive.

myOutputFile.open ("D:\\SUMMARY.RPT");


You may encounter two errors when opening files: the file does not exist (for input), or there is a disk error such as hard drive corruption. To check for an error during opening, you can use the following piece of code:

ifstream myInputFile;
myInputFile.open ("C:\\DATA.DAT");

if (!myInputFile)
cout << "Error opening file: " << "C:\\DATA.DAT";
else
.
.
use file
.
.


When you are finished using a file, you should close the file stream. Closing a file is extremely important if you are writing to an output file stream. To close a file, you use the member function close ( ) in the form.:

   logicalname.close ( );

Special Note About Output Streams
You open the file in the same way you open an input stream, except you don't necessarily need to check to see if the file exists. When an output stream is opened, the listed physical file will be overwritten; if not, the file will be created. Also note that once an output stream is open, you can "write" to it the same way you do with cout. Instead of using cout, you need to use the logical name of the output file.

The following full program example uses strings and member fucntions which I have not covered yet [for strings see Strings] [for member functions see Member Functions]. Study the following program to see how input files and output files are used in a program.

................
PROBLEM: Suppose a data file (text file) contains the following information on each line for each student in a class:
1 - student name [columns 1 to 30]
2 - number of credit hours [columns 32 to 34]
3 - number of quality points [columns 36 to 38 or 36 to end]
Write a program that will read through such a file and will display, for each student, the following:
   Name
   Credit Hours
   Quality Points
   GPA
````````````````

///////////// BEGIN PROGRAM [PROGRAM WRITTEN WITH DEV-C++ COMPILER] /////////////

#include <iostream.h>
#include <stdlib.h>
#include <fstream.h>

void main ( )
{
   int hours, qualityPoints;
   float gpa;

   ifstream gradeFile;
   char studentName[31];
   char fileName[81];

   cout << endl << endl << endl;
   cout << "Enter the name of the grade file: ";
   cin >> fileName;
   cout << endl << endl;

   gradeFile.open ( filename );
   if ( !gradeFile )
   {
           cout << endl << "Error opening file: " << fileName << endl;
           cout << endl << "Program will be terminated" << endl;
           system ( "PAUSE" );
           exit(1);
   }

   while ( !gradeFile.eof ( ) )
   {
           gradeFile.get ( studentName, 31 );
           gradeFile >> hours >> qualityPoints >> ws;
           gpa = float ( qualityPoints ) / hours;
           cout << studentName << "     " << hours << "     " << qualityPoints << "     " << gpa;
   }

   gradeFile.close ( );

}

///////////// END PROGRAM /////////////

Now that I have introduced you to text files and have shown you how they can be used in programs, you may be wondering how to append a file. That is, how do you add data to a file that already has data on it without erasing all of the previous data. You may think there is a lot to this type of problem, but the code is actually quite simple. Read on for more about appending files...

Appending a File

Despite what some people may first think, appending a file is really not difficult at all. Appending a file means that you want to add data or another file to the end of a file without erasing or deleting any previous data on the file. Some pre-conditions must take place before the appending can be done. If you are appending information to the end of a file, the file must exist. If you are appending a file to the end of another, both of the files must exist. The sample complete program provided will append two files and store the result in a third file. Study the code to see how it is done. The program uses pointers, which I haven't discussed, as parameters [see Using Pointers (section 7) for info]. Also note that the program was compiler using Dev-C++ compiler.



/////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <fstream.h>

void appendFile(char* filename1, char* filename2, char* filename3);

int main()
{
     char filename1[80], filename2[80], filename3[80];

     cout << "Enter the name of the first file: ";
     cin >> filename1;
     cout << "Enter the name of the second file (to be appended to the end of " << filename1 << " ): ";
     cin >> filename2;
     cout << "Enter the name of the file which will contain the appended files: ";
     cin >> filename3;

     appendFile(filename1, filename2, filename3);

     cout << endl
           << "The files were successfully appended to [" << filename3 << "]" << endl << endl;

     system("PAUSE");
     return 0;
}// end main();

void appendFile(char* filename1, char* filename2, char* filename3)
{
     char ch;
     ifstream file1, file2;
     ofstream file3;

     file1.open(filename1);

     if (!file1)
     {
           cout << "Error opening file: " << filename1 << endl;
           exit(1);
     }

     file2.open(filename2);

     if (!file2)
     {
           cout << "Error opening file: " << filename2 << endl;
           exit(1);
     }

     file3.open(filename3);

     while (file1.get(ch))
           file3.put(ch);

     file1.close();

     while (file2.get(ch))
           file3.put(ch);

     file2.close();
     file3.close();
}// end appendFile()

/////////////////////////////////////////////////////////////////////


Well that's all I have to say about working with files. As you can tell, we are starting to get into the good stuff. I know you are probably tired of all this talk about characters and integers so I think it's time to start talking about strings. Read on to explore the wonderful world of strings...

Strings

You can think of a string as a "group" of characters. You can also relate a string and an array because a string can be thought of as an array of characters. Let's first refresh ourselves on how to declare single character variables in C++, and then discuss how to declare and initialize strings.

If you have a program that asks the user if he/she is married, you will most likely store the value of this prompt in a variable declared as char because there is only one answer of two possible values: Y (yes) or N (no). A variable declared as char allocates one char for the variable. In this case a Y would be stored or a N would be stored. If the user were to enter more than one character at the prompt, such as YES, only the Y would be stored in the variable.

While a variable, such as

   char x;

allocates one character for x, a variable declared as

   char x[21];

allocates practically 20 characters for x and room for a null terminater ( '\0' ).

As shown above, char x[21] is a string variable. You can store a value for x that is up to 20 characters long. All strings in C++ end with a null terminator because it denotes the end of the string. Whenever you try to search through strings for certain values or manipulate strings, keep in mind that all strings have a null terminator as the last value. So, if your program asks the user to enter his/her first name, you could declare the string as char firstName[16].

You can initialize a string as follows:

   variable[max num of chars + 1] = "value";

   Ex: firstName[8] = "Michael";

Assignment statements when dealing with strings are different compared to assignment statements dealing with integers and floating-point variables. You can assign a value to a string using the member function:

   strcpy (destination, source);
   EX: strcpy (nameOfLang, "C++");

where source is a group of characters enclosed in quotations or a variable, and destination is the variable that you want the source to be stored in.

For example, if you have a string variable named firstName and a string variable named newFirst, you could assign the contents of firstName to newFirst by using the following statement:

   strcpy (newFirst, firstName);

Let's look at some examples of how strings can be used in programs.

Example 1:

Write a section of code that will calculate and display (to screen) the number of vowels contained in a string identified as mixedString that was given a value earlier in the program:

   int count = 0;
   int k = 0;

   while ( mixedSting[k] != '\0' ) // '\0' is the null terminator
   {
           if ( str[k] == 'A' || str[k] == 'a'
                str[k] == 'E' || str[k] == 'e'
                str[k] == 'I' || str[k] == 'i'
                str[k] == 'O' || str[k] == 'o'
                str[k] == 'U' || str[k] == 'u' )
           count++;
           k++;
   }

cout << "The number of vowels contained in [" << mixedString << "] is: " << count << endl << endl;


Example 2:

Write a section of code that will turn all of the commas in a string identified as string1 into semi-colons:

   int k = 0;

   while ( string1[k] != '\0' )
   {
           if ( string1[k] == ',' )
                string1[k] = ';';
           k++;
   }



//////////////////////////////// Sample Program Compiled with Dev-C++ ////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>
#include <fstream.h>
#include <string.h>

int countVowels(char str[]);

int main()
{
     char myString[25], myString2[25];
     int numberVowels;
     int k = 0;

     cout << endl;
     cout << "Enter a string to be evaluated: ";
     cin.get(myString, 25);
     cin.ignore(80, '\n');

     cout << endl;
     cout << "Enter a second string to be evaluated [any commas will be changed to asterisks]: ";
     cin.get(myString2, 25);
     cin.ignore(80, '\n');

     while (myString2[k] != '\0')
     {
           if (myString2[k] == ',')
                 myString2[k] = '*';
           k++;
     }

     numberVowels = countVowels(myString);

     cout << endl << endl;
     cout << "The number of vowels in " << myString << " is " << numberVowels;
     cout << endl << endl;
     cout << "Your updated second string: " << myString2;
     cout << endl << endl;

     system("PAUSE");
     return 0;
}// end of main()

int countVowels(char str[])
{
     int count = 0, k = 0;

     while (str[k] != '\0')
     {
           if (str[k] == 'a' || str[k] == 'A' || str[k] == 'e' || str[k] == 'E'
                 || str[k] == 'i' || str[k] == 'I' || str[k] == 'o'
                 || str[k] == 'O' || str[k] == 'u' || str[k] == 'U')
               count++;
           k++;
     }// end while

     return count;
}// end countVowels

//////////////////////////////// Sample Program Compiled with Dev-C++ ////////////////////////////////


With strings covered, I would now like to move on to random numbers. Generating random numbers is not difficult at all due to some "built in" functions. Read on for more...

Random Numbers

There may be times when you need to generate and use random numbers in your programs. Maybe you are writing code for a game program in which you need to simulate rolling dice. If you truly want to have a fair game, you should use random numbers. The code to generate random numbers differs depending on your compiler. In this tutorial, I am assuming you are using Dev's C++ compiler. How do random numbers get generated? Well, every computer has an internal clock and this clock makes it possible to seed random numbers. In standard C++, the statement:

   srand(unsigned(time(&t)));

initializes, or "seeds", the random number generator. You must use this code before attempting to get any random number. You should only use this statement once in a program. It does not generate a random number; it simply gets the generator ready.

t must be declared as: time_t t;

You also must include the following two header files:

   #include <stdlib.h> // for srand ( ) and rand ( )
   #include <time.h> // for time ( ) and time_t

To get a random number in the range 0 to 100, you could use:

   int x = rand ( ) % 101;

Suppose you want to generate random numbers in the range -15 to 33. After initializing the random number generator, use:

   var = rand ( ) % 49 - 15;

In general, use the following form depending on the range of random numbers you wish to generate:

   rand ( ) % [number of integers in desired range] + [lowerbound];

Example:

Write code that will fill the contents of an integer array, MAXSIZE of 50 integers, named intArr with randomly chosen numbers in the range -10 to 110. NOTE: If you are not familiar with arrays, see [One-Dimensional Arrays (section 8)].



   srand(unsigned(time(&t)));
   time_t t;

   for (int k = 0; k < 50; k++)
       intArr[k] = rand() % 121 - 10;


Our next topic is the member functions which are built in to C++ that allow programmers to perform operations necessary for programs to complete their overall purpose. Read on for more...

Member Functions

Member functions allow programmers to manipulate areas in programs and perform operations that are necessary for a program to complete its overall purpose. For example, if you try to extract a string using a cin statement and extraction operator >>, extraction will stop at the first white space character. So if you are working with a string that contains nothing but characters, you are fine. But if you are working with a string that contains a space (considered white space), such as a string that would contain a person's full name, you would run into some problems. Obviously, something needs to be done so you can extract the whole string including the space or spaces (considered white space). This is where a member function called get ( ) comes into play.

get ( )
The get ( ) function is a member function of the istream classes. One version has two required parameters and one optional one. The two required parameters are a string variable (array of chars) and a length (max. number of chars to get + 1). The optional one, by default is the new line character '\n' and is called the delimeter. When get ( ) is called during execution, chars will be extracted from the input stream and assigned into the given string until one of the following occurs:
1 - there are no more characters to be extracted (end-of-file has been reached)
2 - the next available character for extraction would be the delimeter
3 - the # of chars extracted is one less than the value of the second parameter

Regardless of which condition stops the extraction, a null char ( '\0' ) will be stored in the string following the last character extracted.
NOTE: even if the delimeter is reached, get ( ) will not extract it.

Example:

Write a section of code that will store the value of input by the user (user's full name) into a variable named fullName that contains at most 40 characters:

   cout << "Enter your full name: ";
   cin.get(fullName, 40);


peek ( )
The peek ( ) function is another member function of the input stream classes. It enables you to "peek" at or look ahead in an input stream. If the input stream contains additional data, peek ( ) will return the value of the next char to be extracted; otherwise, it will return EOF, which represents the end-of-file.

Example:

Write the beginning piece of a section code that will determine if there is more data at a certain point in a text file name gradeFile that is being processed:

   .
   .
   .
   while ( gradeFile.peek ( ) != ' \n ' )
   {
           .
           .
           .


eof ( )
The eof ( ) function is another member function of the input stream classes that returns true if the input stream is at the end-of-file; otherwise, it returns false.

Example:

Write the beginning piece of a section of code that will continue processing a file called gradeFile as long as the end-of-file has not been reached:

   .
   .
   .
   while ( !gradeFile.eof ( ) )
   {
           .
           .
           .


ignore ( )
The ingore ( ) will ignore or "skip" over a specified amount of chars or all chars until '\n' has been found.

Example:

   myInFile.ignore ( 100, '\n' );     ---> will "skip" over next 100 chars


With text files, strings, and member functions out of the way, I feel it is a good time to discuss functions, especially user-defined functions. Read on to learn more about the powerful world of functions...

In-Depth Look at Functions / Pointers

User-Defined Functions

I believe I mentioned something about functions in an article at the beginning of this tutorial, but maybe it is a good idea to brush up on the basics before I discuss some of the more tricky aspects. Earlier, I defined a function as a subprogram that performs some specific task and perhaps returns a value to the calling function. If you wish to see the syntax of a function, go here [see C++ Syntax (section 2)] before continuing. Using functions in a program is how programmers break down the solution to a program into logical steps. A function that is defined for use in one program may be of value in another program that needs to perform the same task. Let me begin by showing an example of a function used in a program that simply determines the average of a set of scores, given that the total of all the scores and the number of scores are passed to the function as parameters.

   float getAverage ( totalScores, number )
   {
           float average;
           average = float ( totalScores) / number;
           return average;
   }

In the above function, the two arguments (called function parameters) are passed to the function by value. Passing by value means that the function receives copies of the arguments and any changes to the values of the variables are not returned to the calling function. In a case when you need to return multiple values to the calling function, you will have to pass your arguments by reference. Passing by reference means that instead of getting a copy of the argument, the function parameter will share the same memory address as the argument. In other words, when passing by reference, any changes that are made to the values of the variables will be sent back to the calling function. This is why passing by reference is useful for returning multiple values a function call. All reference arguments are preceded by the ambersand symbol (&). An example of a function prototype that passes arguments by reference is:

   void myFunction ( int& a, int& b );

Notice that type void is used to declare a function that will be returning multiple values to the calling function. We use void because the function will not be returning one sole value of a specific type, and it could be returning values that are of different types. Void lets us return as many values to the calling function as we want as long as they are passed by reference. Let me show you a complete program that uses a function that passes values by reference.

///////////// BEGIN PROGRAM /////////////

   #include <iostream.h>
   #include <stdlib.h>

   void myFunction ( int a, int& b );

   void main ( )
   {
           int x = 5;, y = 10;
           myFunction ( x, y );
           cout << "x = " << x << endl;
           cout << "y = " << y << endl;
           system("PAUSE");
           return 0;
   }// end main ( )

   void myFunction ( int a, int& b)
   {
           a = b * 3 + a * 2;
           b = 10 * a;
   }// end myFunction ( )

///////////// END PROGRAM /////////////

OUTPUT produced by program:

x = 5
y = 400


Here is another example: Write a function that will peform the integer division of M by N and will return the quotient and the remainder of the division.

   void Divide ( int M, int N, int& quotient, int& remainder )
   {
           quotient = M / N;
           remainder = M % N;
   }// end Divide ( )

Something that may come in handy one day when you are faced with a problem is a function that can swap the values of two variables, whether they are of type int, float, double, char, etc. This can be accomplished through the use of passing functions by reference.

NOTE: You can use the same name for multiple functions in a program as long as those functions have different signatures. A function's signature includes the function's class, name, number of parameters, and types and order of parameters, but does not include its return type. For more info, seek [Function Overloading (Section 11)].

   void swap ( int& x, int& y )
   {
           int temp;
           temp = x;
           x = y;
           y = temp;
   }// end swap ( ) for integers


   void swap ( double& x, double& y )
   {
           double temp;
           temp = x;
           x = y;
           y = temp;
   }// end swap ( ) for double


   void swap ( char& x[], char& y[] )
   {
           char temp[21];
           temp = x;
           x = y;
           y = temp;
   }// end swap ( ) for strings [max number of chars = 21]


Getting used to functions will take some time, and you will only become familiar with them by using them in your programs. Be able to distinguish between an argument being passed by value and an argument being passed by reference. In the next section, I dig into some C++ "built in" functions that are used for specific purposes. Read on for more about "built in" functions...

"Built In" Functions

All of the following "built in" functions are contained in the header file #include <string.h> (using Dev-C++ compiler) so be sure to include it when using these functions. These functions are standard functions that are built into the C++ langauge itself in order to perform some operation that would be much harder to do if a programmer was trying to write a user-defined function to acheive its purpose. There are many built in functions, and I will only cover 11 in this article. NOTE: the last example in this article uses pointers [see Pointers], which is covered in more detail further below.

strcpy ( destination, source );
Performs a char-by-char copy of source into destination; primarily used for string assignment.

strncpy ( destination, source, N );
Copies at most N characters from source into destination; the additional N argument must be present.

strcat ( string1, string2 );
Concatenates string2 onto the end of string1; sort of like saying string1 += string2 ( remember that string1 must be large enough to hold string1 + string2 chars.

strncat ( string1, string2, N );
Concatenates at most N characters from string2 onto the end of string1; the additional N argument must be present.

strcmp ( string1, string2 );
Compares string1 and string2 and returns one of the following (useful for alphabetical order):
    -1 if string1 < string2
    0 if string1 = string2
    1 if string1 > string2

strncmp ( string1, string2, N );
Compares at most N characters of string1 and string2 and returns a result similar to strcmp ( ).

strcmpi ( string1, string2 );
Performs exact operation of strcmp ( ) except it is not case sensitive.

strncmpi ( string1, string2, N );
Performs exact operation of strncmp ( ) except it is not case sensitive.

strlen ( string );
Returns the length of the string not including the null terminator.

strchr ( string, ch );
Returns a pointer to the first occurence of a given character (ch) in string; NULL is returned if ch is not found in the string.

strrchr ( string, ch );
Similar to strchr ( ) except it returns a pointer to the last (right most) occurence of a given char in string.


Suppose we have the following code segment in a program:

char string1[20] = "Programming";
char string2[20] = "Computer";
char string3[20] = "Programs";
char string4[20] = "proGRAMMING";
cout << strlen ( string1 );
cout << strcmp ( string1, string3 );
cout << strcmp ( string2, string3 );
cout << strncmp ( string1, string3, 6 );
cout << strcmpi ( string1, string4 );

OUTPUT produced from the above code segment would be using Dev's compiler:

11
-1
-1
0
0


Example: Suppose char name[30] contains a person's name in the following format:

First I. Last

Write a code segment that will find and print the person's middle initial.

   char midInitial;
   char* temp;

   temp = strchr( name, '.' );
   if ( temp = NULL )
           cout << "There is no middle initial.";
   else
   {
           temp--;
           midInitial = temp[0];
           cout << "The person's middle initial is: " << midInitial << endl << endl;
   }


A pointer is used in the above example. Pointers are useful for allowing direct access to the location of a variable in the memory of a computer. Read on for more about pointers...

Using Pointers

A pointer provides direct access to the memory addresses deep within the mind of a computer. They provide efficient methods and techniques for manipulating data in arrays, they are used in functions as reference parameters, and they are the basis for dynamic allocations of memory.

A pointer is a derived data type. Its value is any of the memory addresses available in a computer for storing and accessing data. A pointer is built on the basic concept of a pointer constant, which is drawn from the set of addresses for a computer and exists by itself. We as programmers cannot change them; we can only use them. Every variable that is declared in C++ has an associated memory location with it; the location value can be thought of as a pointer constant.

Even though addresses are constant, there is no way to know or predict what they will be during each new compile and run of a program. We must still refer to these addresses symbolically to be able to work with them. The address operator ( & ) makes it possible to associate a pointer constant with any named location in memory. For example,

   &name

points directly to the memory location designated for name.

NOTE: When the ampersand ( & ) symbol is used as a prefix to a variable name, it "means" address of variable. When it is used as a suffix to a type, it "means" it is a reference parameter.

For int, float, double, and other types, ( two or four byte ), the first byte in memory location for the specified variable is used as the pointer. This byte is also referred to as the least significant byte ( LSB ). For char types, there is only one byte in memory so therefore that byte serves as the location in memory for a pointer.

When you store the address of a variable into another variable, the variable that holds the address is called a pointer variable. Accessing a variable through a pointer requires use of the indirection operator ( * ) before the pointer variable name. You also use the indirection operator ( * ) to define and declare pointer variables. For example,

   char *p;
   int *a;
   float *s;

One of the most common errors by novices and professionals alike is uninitialized pointers. As with variables, it is possible to initialize pointers when they are declared and defined. For example,

   int x;
   int *p = &x;

However, it is not always recommended to initialize pointers. You could also initialize a pointer to nothing by issuing:

   int *p = NULL;

Pointers are useful with functions. When dealing with passing variables to functions, you can pass by value, reference, or by a pointer which is an alternative for passing by reference. Passing by reference and by pointer will accomplish the same task: changing the values in the calling function's scope, but passing by reference is highly recommended over passing by pointer.

Example 1: Using pointers as parameters in a swap function.

   int a = 5;
   int b = 10;

   swap ( &a, &b );
   .
   .
   .
   void swap ( *px, *py )
   {
           int temp;
           temp = *px;
           *px = *py;
           *py = temp;
   }


Example 2: Suppose char name[30] contains a person's name in the following format:

First I. Last

Write a code segment that will find and print the person's middle initial.

   char midInitial;
   char* temp;

   temp = strchr( name, '.');
   if ( temp = NULL )
           cout << "There is no middle initial.";
   else
   {
           temp--;
           midInitial = temp[0];
           cout << "The person's middle initial is: " << midInitial << endl << endl;
   }


Dynamically Allocating / Releasing Memory

Pointers "point" directly to internal memory locations in a computer's hardware. A problem can arise when you need to assign a value to a pointer that is larger than the pointer can handle. For example, consider the situation where you have a pointer named quote that is declared as follows:

   char* quote;

Now, for instance, let's say that quote will be given a value somewhere in the program as follows:

   quote = "C++ Rocks";

The program assigned "C++ Rocks", disregard the quotes, during execution. Now, consider the situation which arises when we try to assign "C++ Ripped Apart Rocks" to quote, which currently contains "C++ Rocks". With the current value of "C++ Rocks", quote has a size of 13 characters and is generally occupying 13 blocks of internal memory. The value we wish to give it, "C++ Ripped Apart Rocks", has a size of 22 characters, and would generally need to occupy 22 blocks of memory. Thus, if we tried to assign quote the value of "C++ Ripped Apart Rocks" an error would occur because quote currently does not have enough memory capacity to handle it. Therefore, there has to be some way we can handle this type of situation, and the solution ultimately lies in how we can allocate new memory for quote.

We can dynamically allocate and release memory for structured data and non-structured data. Structured data is complex data which may occupy many blocks of memory such as arrays, objects, strings. Non-structured data is simple data which generally occupy 1 block of memory such as integers, floating point values, double values, and character values. In our example, we are using structured data since quote is a pointer initially containing "C++ Rocks", which is a series of character values or a string.

The general form for dynamically allocating or releasing memory for structured data is as follows:


   type* ptrName;     --> declaration of pointer
   .
   .
   // ptrName gets a value
   .
   .
   delete [ ] ptrName;     --> release or "free" previously allocated memory for ptrName
   .
   .
   ptrName = new type[size];     --> allocate memory for "new"; size is the size needed to handle the pointer data ptrName


The general form for dynamically allocating or releasing memory for non-structured data is as follows:


   type* ptrName;     --> declaration of pointer
   .
   .
   // ptrName gets a value
   .
   .
   delete ptrName;     --> release or "free" previously allocated memory for ptrName
   .
   .
   ptrName = new type;     --> allocate memory for "new"


NOTE: When allocating memory, new is an operator that accomplishes two things: it allocates the needed memory for the pointer, and it returns or results the beginning address of the allocated memory.

Our previous quote example, could be handled as illustrated in the following complete program, which was written using Dev-C++'s V.4 compiler.



#include <stdlib.h>
#include <iostream.h>
#include <string.h>

void main( )
{
     char* quote = "C++ Rocks";
     char* secondQuote = "C++ Ripped Apart Rocks";

     cout << "Initially, your quote was " << '"' << quote << '"';
     cout << endl << endl;

     delete [] quote;
     quote = new char[strlen(secondQuote)+1];
     strcpy(quote, secondQuote);

     cout << "Now, you prefer to say " << '"' << quote << '"';
     cout << endl << endl;

     system("PAUSE");
}



In the next article, you will be introduced to an array. An array is necessary for programs that require storage of many values. Read on for more about using arrays in your programs...

Array Type Problems

One-Dimensional Arrays

An array is a group of related data values that are all of the same type. There are a couple of reasons why a programmer might want to use an array in a program. One motivation for using an array is the reduction of identifiers. For example, suppose you have a problem where you need to store the values of fifty test scores. Without arrays, you would have to declare fifty variables in order to keep track of all of their individual scores (test1, test2, test3, ..., test50). With arrays, you could simply declare an array that would hold fifty elements, such as int test[50]. The individual test scores are not given different names in the array. Instead, they are named test as a group of related values. When you want to access a particular text score, you have to use a subscript (or technically speaking, an index value), such as test[0] for the first test, test[1] for the second test, and so on. The first element in an array is also given an index value of 0 and then the index is incremented for each new element.

The form of declaring an array is as follows:

   type arrayName[MAX];

where MAX is the maximum number of elements. In the above example,

   int test[50];

would be an appropriate array declaration. You can also initialize arrays by using curly braces { } as follows:

   int test[5] = { 1, 2, 3, 4, 5 };

This intializes test[0] to 1, test[1] to 2, test[2] = 3, test[3] = 4, and test[4] = 5.

You can initialize all of the elements in an array to 0 as follows:

   int test[50] = { 0 };

Another reason for using an array is if you are faced with a problem where you need to use/access the data involved more than once. In the above example, that could easily be accomplished by using an array because we could use the index value of each test to access each test however many times we need to. When processing an array, it is best (at least from my point of view) to use a for loop. For loops are good to use when processing arrays because when dealing with an array we know exactly how many times when need to run through the loop (MAX - 1).

Example 1: Here is a little example of storing values into an array from a text file and calculating the average value. NOTE: The text file contains one value per line.

////////////////////////////////////////////////////////////////////////////////////

   const int MAXSIZE = 100;
   double salesAmount[MAXSIZE]; // declaration for array
   double aSalesAmount;
   int count = 0;
   double total = 0.0;
   double average;
   ifstream myFile; // declaring input file

   myFile.open ( "C:\\DATA.RPT" );
   if ( !myFile )
   {
           cout << "Error opening input file: C:\\DATA.RPT";
           system("PAUSE");
           exit(1);
   }

   while ( !( myfile.eof ( ) ) )
   {
           myFile >> aSalesAmount >> ws;
           total += aSalesAmount;
           salesAmount[count] = aSalesAmount;
           count++;
   } // end while

   if ( count != 0 )
           average = total / count;
   else
           average = 0.0;

   myFile.close ( );

////////////////////////////////////////////////////////////////////////////////////

Example 2: Having performed the above piece of code, suppose we want to list (and count) the values that are above the average (display to screen).

Add the following to the declaration and initialization section:

   int numAboveAvg = 0;
   int k;

Add the following to existing code:

   cout << "Sales Amount that are above the average [" << average << "] are as follows: " << endl << endl;

   for ( k = 0; k < count; k++ )
           if ( salesAmount[k] > average )
           {
                       numAboveAvg++;
                       cout << salesAmount[k] << endl;
           }

   cout << endl << endl << "There are " << numAboveAvg << " values above the average." << endl;



The following is a simple program demonstrating the use of a one-dimensional array:

/////////////////////////////////////// Compiled Using Dev-C++ Compiler /////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

const int MAXSIZE = 5;

int main()
{
     int accountNumbers[MAXSIZE];
     int customerNum = 1;
     int count = 0;

     cout << endl;
     for (int k = 0; k < MAXSIZE; k++)
     {
           cout << "Enter customer account number (####) for customer " << k+1 << ": ";
           cin >> accountNumbers[k];
     }

     cout << endl << endl;
     while (count < MAXSIZE)
     {
           cout << "Customer " << customerNum << " account number = " << accountNumbers[count] << endl;
           customerNum++;
           count++;
     }
     cout << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

/////////////////////////////////////// Compiled Using Dev-C++ Compiler /////////////////////////////////////


With the very basics of a one dimensional array out of the way, we can now move on and talk about two dimensional arrays, which are more powerful than 1D arrays. Read on for more...

Two Dimensional Arrays

If you are not familiar with one-dimensional arrays, you should back up and read that section before reading this one, [see One-Dimensional Arrays]. While one-dimensional arrays allow data to be placed in an array one row at a time, two-dimensional arrays are capable of storing data in rows and columns. Each row in the array is associated with however many columns you have defined for the array. The only drawback is the fact that the entire array must contain elements of the same type, which is defined when you declare the array. Because of the capability of storing data in rows and columns, it is obvious that two-dimensional arrays can be a very powerful tool in programming compared to one-dimensional arrays. Declaring a two-dimensional array is this simple:

   type arr-name[numRows][numCols];

where type is the data type for the array, arr-name is the variable name for the array, numRows is the maximum number of rows for the array, and numCols is the maximum number of columns for the array. If you wish to declare an integer array of five rows and three columns, you could use:

   int arr[5][3];

If you wish to initialize the values in the array during compile time, you could use:

   int arr[5][3] =
   {
       { 0, 1, 2 }
       { 3, 4, 5 }
       { 6, 7, 8 }
       { 9, 0, 1 }
       { 2, 3, 4 }
   }

The above code initializes arr[0][0] = 0, arr[0][1] = 1, arr[1][0] = 3, arr[1][1] = 4, arr[4][0] = 2, and so on. Because 2D arrays must be filled by row and column, processing a 2D array involves using nested for loops. For instance, in order to fill an array declared numArr[10][10] with user input, you could use the following code:

   for (int rows = 0; rows < 10; row++)
       for (int cols = 0; cols < 10; cols++)
       {
               cout << "Enter a value for row " << row+1 << " col " << col+1 << ": ";
               cin >> numArr[row][col];
       }


If you want to display the contents of the above filled array ten values per line, you could use the following code:

   for (int rows = 0; rows < 10; row++)
   {
       for (int cols = 0; cols < 10; cols++)
               cout << numArr[row][col];
       cout << endl;
   }



Another common use of 2D arrays is storing a collection of strings. When storing strings in a 2D array, the rows of the array are associated with the position of the full string value, but the columns of the array are actually associated with the individual characters of the string. For instance, if the string "c++" was stored in row 0 of stringArr, then stringArr[0][0] would hold 'c', stringArr[0][1] would hold '+', and stringArr[0][2] would hold '+'. For example, suppose we have a 2D array declared as follows:

   char stuNames[30][26];

This array would hold 30 strings with a maximum of 25 characters for each string length (I declared the array with 26 maximum columns to have enough room for the null terminater '\0' that is attached to the end of a string). Suppose we want to fill the array with values from a text file we have previously opened that contains one string per line of 25 characters, and we know the file contains no more than 30 lines of data but we don't know exactly how many. I'll call the logical file name of the input file myFile. If you are not familiar with text files, [see Working with Text Files (section 6)]. Code to take care of this situation is as follows:

   sizeOfFile = 0;
   for (int rowData = 0; rowData < 30; rowData++)
   {
       myFile.get(stuNames[rowData], 26) >> ws;
       sizeOfFile++;
   }


We could display the contents of our filled string array one value per line as follows:

   for (int rowData = 0; rowData < sizeOfFile; rowData++)
       cout << stuNames[rowData] << endl;


2D array Example 1:
Suppose we have a 2D integer array of exactly ten rows and ten columns, and we need to find the sum of the integers along the main diagonal of the array. We could use the following piece of code:

   int arr[10][10];
   .
   .
   arr gets values
   .
   .
   int sum = 0;
   for (int row = 0; row < 10; row++)
       for (int col = 0; col < 10; col++)
               if (row == col)
                       sum += arr[row][col];

   cout << "The sum of the values along the main diagonal is: " << sum << endl;


2D array Example 2:
Suppose the correct answers for a true/false quiz are: T T F F T
Suppose a 2D array contains the student answers with each row representing the responses of a particular student. The first row (0) of the array contains the above answer "key".
Write code to calculate the student's scores on the quiz and store these scores in a one-dimensional array. Assume each question is worth 4 points and there is a maximum of 50 students.
The following code could be used to accomplish this task:

   char quizAnswers[51][5];
   int gradeArray[51]; // row 0 will be unused

   .
   .
   . quizAnswers gets values
   .
   .

   int stuIndex, probIndex, grade;
   for (stuIndex = 1; stuIndex < 51; stuIndex++)
   {
       grade = 0;
       for (probIndex = 0; probIndex < 5; probIndex++)
           if (quizAnswers[stuIndex][probIndex] == quizAnswers[0][probIndex])
               grade += 4;
       gradeArray[stuIndex] = grade;
   }


Ok, now you can work with one-dimensional and two-dimensional arrays, but the most important step to learn about arrays is how to pass them to functions. Since functions are critical for accomplishing tasks and organizing programs, it is vital to learn this key aspect of arrays. Read on for more...

Passing Arrays to Functions

An array is automatically passed by reference unless you declare the parameter as const. If you need to pass the array by value, the function heading would look similer to this:

   void displayArray ( const int arr [ ], int MAXSIZE );

When you pass an array to a function, it is also a good idea to pass the size of the array for processing purposes. If you notice in the above function heading, MAXSIZE is passed to represent the size of the array. In the declaration section of your program, you should initialize a variable to store the size of the array. The following is an example of a function that accepts an array and its size; the function displays the contents of the array to screen.

   void passValue ( int arr [ ], int MAXSIZE )
   {
           for ( int k = 0; k < MAXSIZE; k++)
                       cout << arr[k] << endl;

   } // end passValue ( )


The following is an example of a function that will find and return the position of the smallest value in a given array of integers:

   int positionOfMin ( int arr [ ], int size )
   {
           int positionOfMin = 0;
           int pos;

           for ( pos = 1; pos < size; pos++)
                       if ( arr[pos] < arr[positionOfMin] )
                            positionOfMin = pos;

           return positionOfMin;
   } // end positionOfMin ( )

We can now start to talk about how we may insert and delete elements in an array without causing any problems in the arrays. Let's begin by talking about insertion into an array. Read on for more...

Insertion Into an Array

You may encounter times when you need to insert an element into an array. There are two scenarios. The first is if you need to insert a value into a sorted array. The second is if you simply need to insert a value into a specified location in the array. If you need to insert a value into a specified location, you can simply insert the value into the position and then move all other following elements to the next position and increment the size of the array. If you need to insert a value into a sorted array, you must first find the location of where to put the value so the order will stay in tact. Next, you can insert the value and then move all other following elements to the next position and increment the size of the array. Both scenarios require there be room for the new element in the array. The following is an example of a function that will insert a value into an array sorted previously into ascending order:

void insertElement ( int arr [ ], int& size, int newElement )
{
   int insertPosition, k;

   insertPosition = -1;
   do
           insertPosition++;
   while ( newElement > arr[insertPosition] && insertPosition != size - 1 )

   if ( newElement <= arr[insertPosition] )
   {
           for ( k = size - 1; k >= insertPosition; k-- )
               arr[k+1] = arr[k];
           arr[insertPosition] = newElement;
   }
   else
           arr[size] = newElement;

   size++;
}// insertElement ( )


If you had to insert an element into a specified location in the array, you could easily use the for loop in the above example.. Simply designate the location to be filled as insertPosition, and the loop will take care of moving all following elements to the next position. You can then insert the element and increment the size of the array.



The following demonstrates a program that will allow the user to insert an element into a specified location of a pre-initialized array.

////////////////////////////////////////// Compiled Using Dev-C++ Compiler ///////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

void insertPosition(int arr[], int& size, int element, int position);

int main()
{
     int nums[10] = { 34, 12, 89, 22, 21, 45, 55, 93, 27, 76 };
     int size = 10;
     int element;
     int position;

     for (int k = 0; k < size; k++)
           cout << nums[k] << "  ";

     do
     {
           cout << endl << endl << "Enter the position in the array for insertion (0 - 9): ";
           cin >> position;
     } while (position < 0 || position > 9);

     cout << "Enter the value to be inserted into position [" << position
           << "] of the array: ";
     cin >> element;

     insertPosition(nums, size, element, position);

     cout << endl << endl;
     for (int k = 0; k < size; k++)
           cout << nums[k] << "  ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

void insertPosition(int arr[], int& size, int element, int position)
{
     for (int k = size - 1; k >= position; k--)
           arr[k+1] = arr[k];
     arr[position] = element;

     size++;
}// end insertPosition()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


We have now covered how to insert an element into an array, but what if you need to delete an element from an array? Before moving on the next section, try to devise a function to handle such a situation. After a couple of minutes, read on to learn how to delete an element from an array...

Deletion From an Array

As with inserting an element into an array, there are two scenarios you could face when dealing with deleting an element from an array. The first scenario is if you know the exact location of the value to be deleted. The second is if you know the value to be deleted but have no idea of its location in the array. If you know the exact location of the value to be deleted, you can create a for loop that will shift all of the elements following the location of the value to be deleted to the left one location. You also have to decrement the size since you erased one its elements. If you know the value to be deleted, you must first find the position of the value in the array. After you find the position, you can then perform the same operation as the first scenario. The following is an example of a function that will delete a value in an array given its position in the array:

void deleteElement ( int arr [ ], int& size, int position )
{
   int k;

   if ( position >= size )
           cout << "Error! You are attempting to delete an element beyond the size of the array.";
   else
   {
           for ( k = position; k < size - 1; k++ )
               arr[k] = arr[k+1];
           --size;
   }
}// end deleteElement ( )


The following is an example of segment of code that will find the position of a value in a given array:

   for (int k = 0; k < size; k++ )
           if ( arr[k] == value )
               position = k;



The following demonstrates a program that deletes a value from an array prevously initialized.

/////////////////////////////////////////////// Compiled with Dev-C++ Compiler //////////////////////////////////////

#include
#include

void deleteElement(int arr[], int& size, int position);

int main()
{
     int nums[10] = { 13, 22, 53, 14, 35, 66, 27, 98, 29, 10 };
     int size = 10;
     int position;

     for (int k = 0; k < size; k++)
           cout << nums[k] << "  ";

     cout << endl << endl;
     cout << "Enter the position of the value to be deleted (0 - 9): ";
     cin >> position;

     deleteElement(nums, size, position);

     cout << endl << endl;
     for (int k = 0; k < size; k++)
           cout << nums[k] << "  ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}

void deleteElement(int arr[], int& size, int position)
{
     int k;

     if (position >= size)
     {     cout << "Error: attempting to delete beyone the size of the array!" << endl;
           system("PAUSE");
           exit(1);
     }
     else
     {
           for (k = position; k < size - 1; k++)
                 arr[k] = arr[k+1];

           --size;
     }
}// end deleteElement()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Ok, so now you know how to work with 1D and 2D arrays, pass them to functions, and insert and delete elements. Can you think of anything else you may need to know concerning arrays? What if you need to search for an element in the array. Can you write code to handle this situation? Read on for more...

Searching Arrays

You may face times when you need to search through an entire array to look for specific data, whether it be integer values, floating point values, or characters. Maybe you need to delete the data or you need to move it to another position in the array. Whatever the case may be, I will introduce you to two standard methods for searching arrays: a linear search, and a binary search. Let's start with the linear search.

Linear Search
The definition of linear is of, or relating to a line; from a programmer's perspective, a linear search will evaluate every element in the array to see if it matches the target value, which is the element you are searching for. You can think of the array as a line connected by many dots (rows), and the linear search evaluates each dot (row) in the line. Linear searches start by examining the value in position 0 of the array; if this is the "target" item, return 0; otherwise, move up to position 1 and repeat the process (return 1 if the target is equal to the value in position 1; if not, move on to position 2). Repeat this process until the target is found (return the position it was found in) or the end of the array is reached without finding the target. If the target was not found, you can simply return -1 to indicate a failure.

The following is a function that can be used as a linear search for an array of integers:

   int linearSearch(int arr[], int size, int target)
   {
           int position;
           for (position = 0; position < size; position++)
               if (arr[position] == target)
                   return position;
           return -1;
   }// end linearSearch()


When this function is called, one of two values will be returned: the target's position in the array or -1, which indicates failure. For efficiency sake, for an array of size N, the worst possible case would be N "attempts" while searching the array. The average case is (n+1)/2 "attempts".

Binary Search
It is important to note that for a binary search to work, the array must be sorted into ascending or descending order. This will make sense in a minute. The definition of binary is something made of two components or parts. For our sake, it means the array will be divided in half each cycle through the search. It roughly divides the array in half after each attempt to find the target. Binary searches begin by "checking" the middle element in the array; if this matches the target, the position of the element will be returned. If not, it will restrict the next attempt to either the upper or lower half of the (current) array. This is continued until the target is found (return position) or every position has been logically checked with no target value found (failure). A failure is determined by checking for a "low" position that is greater than the "high" position.

The following function can be used to search an array of integers that was previously sorted into ascending order:

   int binarySearch(int arr[], int size, int target)
   {
       int middlePosition, middleValue;
       int low = 0, high = size - 1;

       while (low <= high)
       {
           middlePosition = (low + high) / 2;
           middleValue = arr[middlePosition];
           if (target == middleValue)
               return middlePosition;
           else if (target < middleValue)
               high = middlePosition - 1;
           else
               low = middlePosition + 1;
       }
       return -1;
   }// end binarySearch()


Now, you have the ability to search arrays, but maybe you would like to shuffle the contents of an array. Read on for more...

Shuffling Array Elements

Writing code to shuffle the contents of an array can take some imagination. Basically, you have two options. One way would be to choose a number at random (which is in the proper range) and place it in the array in position 0; repeat this process and place the next random number in position 1, 2, 3, to size (size of the array) - 1. This option could work, but you must avoid duplicating values when choosing the numbers at random (very inefficient). The second way would be to randomly choose a position (0 to size - 1) and swap the value in that position with the value in the "last" position (size - 1). Next, randomly select another position (0 to size - 2) and swap its value with the one in the next-to-last position. Repeat this process until position 1 is reached. Essentially, this type of shuffle would send the current randomly chosen position value to the end of the array, and then restrict the next shuffle so the previous "locked in" values would not be manipulated. Obviously, the second option is more efficient and would not duplicate values during the shuffle since the values themselves are not being randomly chosen, but rather the positions in the array are being randomly chosen.

The following is code that would shuffle the contents of an integer array:

NOTE: you must use a randomize( ) function to generate random numbers; the specific functions to handle this are different for every compiler; my code is written to work for Dev C++'s compiler [rand( ) is their randomize function].

   void shuffleElements(int theArr[], int size)
   {
       int temporary, randomNum, last;

       for (last = size; last > 1; last--)
       {
           randomNum = rand( ) % last;
           temporary = theArr[randomNum];
           theArr[randomNum] = theArr[last - 1]
           theArr[last - 1] = temporary;
       }
   }// end shuffleElements( )


If you wanted to write code to shuffle the contents of an array to mimic the shuffling of cards in a deck where you could shuffle the cards back into their previous positions as you shuffle, you could use the following piece of code:

   void shuffleCards(int arrDeck[])
   {
       for (int card = 0; card < 52; card++)
       {
           int randNum, temporary;
           randNum = rand( ) % 52;
           temporary = arrDeck[card];
           arrDeck[card] = arrDeck[randNum];
           arrDeck[randNum] = temporary;
       }
   }// end shuffleCards( )


Shuffling the elements in an array is necessary for generating latin squares. Read on for more...

Generating Latin Squares

Two dimensional arrays are necessary for applications involving the generation of Latin squares. A Latin square of order N is an N x N matrix in which each row and col contains each of the values 1, 2, 3, ..., N, exactly one time. The following...

   5  1  2  3  4
   2  3  4  5  1
   4  5  1  2  3
   1  2  3  4  5
   3  4  5  1  2

is an example of a Latin square of order 5. The following Latin square could be generated by the algorithm I depict later:

   5  1  2  3  4
   4  5  1  2  3
   3  4  5  1  2
   2  3  4  5  1
   1  2  3  4  5

Why use Latin squares? Well, they are actually commonly used in the design of statistical experiments. For example, if we wanted to test the effects of 5 different drugs on mice, and there are 5 different groups of mice, each with 5 different sizes, then we could use a latin square of order 5. Each drug will be tested on each group and each size. It turns out that it takes 25 tests to accomplish this; if you wanted to test every drug on each group in each size, it would require 125 tests.

To generate a Latin square of order N, first generate a random permutation of the digits 1 through N. You could generate this permutation by using the "shuffle" function in earlier articles, see [Shuffling Array Elements]. This ordering turns out to be the 1st row of the Latin square. You then take (1st digit - 1) of the permutation to be the index of the next digit to be placed in the matrix. Place this digit in the other rows in the order givin by the permutation (always subtracting one to get the position). Next, rotate the permutation to the left 1 position and repeat this process. You must always go back to the original permutation, which is the first row of the Latin square, to find the digit to be placed throughout the Latin square.

In other words, the (1st digit - 1) of the new permuation gives the index; that position in the original permutation gives the number to be placed; the new permutation gives the ordering; repeat this process until all of the rows are filled.

For example, suppose we want to generate a Latin square of order 6, and we used the "shuffle" function mentioned earlier to generate the random sequence:

   6  3  1  4  2  5

The first digit in this permutation is 6. The index of the value we want to place in the Latin square is (6-1) = 5. Position 5 in the original permutation contains a value of 5. This 5 is the value to be placed in the Latin square in the following manner based on the new permutation:

(6-1) = position 5 in row 0
(3-1) = position 2 in row 1
(1-1) = position 0 in row 2
(4-1) = position 3 in row 3
(2-1) = position 1 in row 4
(5-1) = position 4 in row 5

We then need to rotate the permutation one place to the left. We then end up with...

   3  1  4  2  5  6

The first digit in this new permuation is 3. The index of the value we want to place in the Latin square is (3-1) = 2. Position 2 in the original permutation contains a value of 1. This 1 is the value to be placed in the Latin square in the following manner based on the new permutation:

(3-1) = position 2 in row 0
(1-1) = position 0 in row 1
(4-1) = position 3 in row 2
(2-1) = position 1 in row 3
(5-1) = position 4 in row 4
(6-1) = position 5 in row 5

The new permutation would then be rotated one place to the left and the process would be repeated until all positions in the Latin square are filled. Our completed Latin square would look like this:

   6  3  1  4  2  5
   1  4  5  6  3  2
   5  6  2  1  4  3
   2  1  3  5  6  4
   3  5  4  2  1  6
   4  2  6  3  5  1

You may think that writing code to generate a Latin square would be quite tricky, but once you see the code, you will realize that it is really not that difficult. The following code will produce and display a Latin square of order 5 (code written to be compatitable with DEV C++ compiler).



///////////////////////////////////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <time.h>
#include <iomanip.h>

typedef int tRow[10];
typedef tRow tLatinSquare[10];

int getSize();
void shuffle(int arr[], int size);
void rotate(tRow list, int size);

int main()
{
     tRow sequence, randomList;
     tLatinSquare square;
     int position, value, i, j, size;
     char ch;

     srand((unsigned)time(NULL));
     size = getSize();
     while (size != 0)
     {
           shuffle(randomList, size);
           for (i = 0; i < size; i++)
                 sequence[i] = randomList[i];

           for (i = 0; i < size; i++)
           {
                 position = sequence[0];
                 value = randomList[position - 1];

                 for (j = 0; j < size; j++)
                       square[j][sequence[j] - 1] = value;

                 rotate(sequence, size);
           }

           cout << endl;
           cout << "A Latin Square of Order " << size << " is: " << endl << endl;

           for (i = 0; i < size; i++)
           {
                 for (j = 0; j < size; j++)
                       cout << setw(5) << square[i][j];
                 cout << endl;
           }

           cout << endl << "Press any key to enter a new desired size..."
                 << endl << endl;
           system("PAUSE");
           cout << endl << endl;
           size = getSize();
     }

     cout << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

int getSize()
{
     int size;

     do
     {
           cout << "Enter the desired order of the Latin Square (3 - 10) or 0 to stop: ";
           cin >> size;
     } while ((size < 3 || size > 10) && size != 0);

     return size;
}// end getSize()

void shuffle(int arr[], int size)
{
     int i, temp, ran, last;

     for (i = 0; i < size; i++)
           arr[i] = i + 1;

     for (last = size; last > 1; last--)
     {
           ran = rand() % last;
           temp = arr[ran];
           arr[ran] = arr[last - 1];
           arr[last - 1] = temp;
     }
}// end shuffle()

void rotate(tRow list, int size)
{
     int temp, i;

     temp = list[0];
     for (i = 0; i < size - 1; i++)
           list[i] = list[i+1];
     list[size - 1] = temp;
}// end rotate()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


We are now starting to explore more interesting and challenging information. Well, now that we have touch-based with arrays, we can now talk about how to sort arrays. Yes, there will be times when you will have an array that contains thousands of unsorted integers or other elements. Your job will be to sort this entire array and either put the integers in ascending or descending order. Stop and think for a minute about how you might devise a function to handle this type of situation. Ok, you can stop pondering. Lucky for you, I have already came up with a couple of ways ( standard techniques ) to sort arrays, and I have decided to share them with you. When you see the code, you will wonder why you couldn't develop it. Read on for more about the first sort I will cover: the selection sort...

Sorting Array Data

Selection Sorts

I will cover two technqiues for sorting an individual array and one technique for merging prevously sorted arrays. The first technique, which is called a selection sort, is a sort used for sorting one individual array. The sort itself, depending on if the array is to be sorted into ascending or descending order, will evaluate each element in the array and will lock positions in place while evaluating the array's values. For example, if an array is to be sorted into ascending order, during the first pass through the array, the selection sort will hunt for the element that is the smallest and will lock that element in the first location of the array [0]. During the next pass through, the sort will start hunting from the next unlocked position, find the next smallest value, and lock it into the second location. This process continues during each pass through the array until the whole array is sorted. A selection sort makes use of the swap function we defined earlier. The following is an example of a selection sort. Study the code very carefully and try to think like the compiler during execution of the program.

// selection sort used to sort an array of integers into ascending order
void selectionSort ( int arr[], int size )
{
   int indexOfMin;
   int pass;
   int j;

   for ( pass = 0; pass < size - 1; pass++ )
   {
           indexOfMin = pass;

           for ( j = pass + 1; j < size; j++ )
               if ( arr[j] < arr[pass] )
                   indexOfMin = j;

           swap ( arr[pass], arr[indexOfMin] );
   }
}

// swap function for integers
void swap ( int& x, int& y )
{
   int temp;
   temp = x;
   x = y;
   y = temp;
}



The following is a complete program demonstrating using a selection sort to sort an integer array into ascending order.

////////////////////////////////////////////////// Compiler Using Dev-C++ Compiler /////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

const int MAXSIZE = 10;

void selectionSort(int arr[], int size);
void swap(int& x, int& y);

int main()
{
     int numbers[] = { 13, 5, 1, 7, 9, 11, 3, 17, 19, 15 };
     int k;

     cout << "BEFORE SORT: ";
     for (k = 0; k < MAXSIZE; k++)
           cout << numbers[k] << " ";

     selectionSort(numbers, MAXSIZE);

     cout << endl << endl;
     cout << "AFTER SORT: ";
     for (k = 0; k < MAXSIZE; k++)
           cout << numbers[k] << " ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

void selectionSort(int arr[], int size)
{
     int indexOfMin, pass, j;

     for (pass = 0; pass < size - 1; pass++)
     {
           indexOfMin = pass;

           for (j = pass + 1; j < size; j++)
                 if (arr[j] < arr[indexOfMin])
                       indexOfMin = j;

           swap(arr[pass], arr[indexOfMin]);
     }
}// end selectionSort()

void swap(int& x, int& y)
{
     int temp;
     temp = x;
     x = y;
     y = temp;
}// end swap()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


The above selection sort is a standard technique for sorting arrays. The next technique I will cover, the bubble sort, is another standard sort, but it provides a more efficient way of performing the same sort as a selection sort. Read on for more about bubble sorts...

Bubble Sorts

The bubble sort is another standard technique for sorting data in an array. Instead of making only one swap after a pass through the array like the selection sort, the bubble sort makes several swaps of values depending on whether we want to sort the data into ascending or descending order. Let's describe what happens if we want to use the bubble sort for sorting an array of integers into ascending order. The sort begins by evaluating the first two elements in the array. If the first element is greater than the second element, then a swap will be made. If not, no swap is made. The second element and the third element is evaluated next. If the second element is greater than the third, then a swap is made. If not, no swap is made. As you can see, if the first element is the largest integer in the array, it will continue to be swapped through the array until it reaches the last position in the array. As the selection sort locks values into the beginning of the array, the bubble sort locks the positions at the end of the array. Again, we must use a swap function along with our bubble sort function when sorting an array. The following is a bubble sort used to sort an array of integers into ascending order.

void bubbleSort ( int arr [ ], int size )
{
   int last = size - 2;
   int isChanged = 1;

   while ( last >= 0 && isChanged )
   {
           isChanged = 0;
           for ( int k = 0; k <= last; k++ )
               if ( arr[k] > arr[k+1] )
               {
                   swap ( arr[k], arr[k+1] )
                   isChanged = 1;
               }
           last--;
   }
}

void swap ( int& x, int& y )
{
   int temp;
   temp = x;
   x = y;
   y = temp;
}



The following is a complete program demonstrating using a bubble sort to sort an integer array into descending order.

////////////////////////////////////////////////// Compiler Using Dev-C++ Compiler /////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

const int MAXSIZE = 10;

void bubbleSort(int arr[], int size);
void swap(int& x, int& y);

int main()
{
     int nums[] = { 1, 7, 5, 3, 15, 11, 13, 17, 21, 19 };
     int k;

     cout << "BEFORE SORT: ";
     for (k = 0; k < MAXSIZE; k++)
           cout << nums[k] << " ";

     bubbleSort(nums, MAXSIZE);

     cout << endl << endl;
     cout << "AFTER SORT: ";
     for (k = 0; k < MAXSIZE; k++)
           cout << nums[k] << " ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

void bubbleSort(int arr[], int size)
{
     int last = size - 2;
     int isChanged = 1;

     while (last >= 0 && isChanged)
     {
           isChanged = 0;

           for (int k = 0; k <= last; k++)
                 if (arr[k] < arr[k+1])
                 {
                       swap(arr[k], arr[k+1]);
                       isChanged = 1;
                 }
           last--;
     }
}// end bubbleSort()

void swap(int& x, int& y)
{
     int temp;
     temp = x;
     x = y;
     y = temp;
}// end swap()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


We have now covered two techniques for sorting data in an individual array: selection and bubble sorts. Suppose we need to merge two arrays that are sorted in ascending order into a third array. We want to keep the third array sorted into ascending order as we join the two arrays. Do you have something in mind? Read on to find out more about the merge sort and if your idea is similar to it...

The Merge Sort

You can use a merge sort to combine two sorted arrays into a third array. The third array will need to be sorted also, but we can take care of that while we merge the two arrays into one. A merge sort requires two arrays that have been previously sorted into ascending or descending order. When the sort first begins (for ascending order), the first elements of each of the arrays are evaluated to see which array contains the smaller value. Once the smaller value is found, it will be placed as the first element in the third array. The sort continues to evaluate the elements of both arrays until the end of one of the arrays is found. Depending on which array has ended, the remaining values in the other array will be placed on the end of the third array. The following is a complete program demonstrating how to merge two arrays that are in ascending order:



\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Compiled Using Dev-C++ Compiler \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include <iostream.h>
#include <stdlib.h>

void mergeSort(int arr1[], int arr2[], int arr3[], int size1, int size2, int& size3);
void swap(int& x, int& y);

int main()
{
     int nums1[] = { 3, 7, 9, 15, 19, 23 };
     int nums2[] = { 1, 5, 11, 17, 21, 25, 29, 31 };
     int nums3[15];
     int size1 = 6;
     int size2 = 8;
     int size3;
     int k;

     cout << "ARRAY 1: ";
     for (k = 0; k < size1; k++)
           cout << nums1[k] << " ";

     cout << endl << endl;
     cout << "ARRAY 2: ";
     for (k = 0; k < size2; k++)
           cout << nums2[k] << " ";

     mergeSort(nums1, nums2, nums3, size1, size2, size3);

     cout << endl << endl;
     cout << "MERGED ARRAY 3: ";
     for (k = 0; k < size3; k++)
           cout << nums3[k] << " ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

void mergeSort(int arr1[], int arr2[], int arr3[], int size1, int size2, int& size3)
{
     int pos1 = 0, pos2 = 0, pos3 = 0;

     while (pos1 < size1 && pos2 < size2)
           if (arr1[pos1] < arr2[pos2])
                 arr3[pos3++] = arr1[pos1++];
           else
                 arr3[pos3++] = arr2[pos2++];

     if (pos1 < size1)
           while (pos1 < size1)
                 arr3[pos3++] = arr1[pos1++];
     else
           while (pos2 < size2)
                 arr3[pos3++] = arr2[pos2++];

     size3 = size1 + size2;
}// end mergeSort()

void swap(int& x, int& y)
{
     int temp;
     temp = x;
     x = y;
     y = temp;
}// end swap()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


You now know how to sort individual arrays, and you know how to merge two sorted arrays. However, there may be times when you would like to sort an array without having to directly change the elements in the array. Is it possible? Try to devise a solution to this problem before reading on to see how I came up with a solution. Read on for more...

Tag Sort

I have previously mentioned two standard methods for sorting arrays (selection and bubble sorts), but those two methods directly manipulated and changed the elements in the array. Sometimes, you will want to directly sort the elements in an array, but there may be times when you need to keep the actual array in tact and use a "tag" array to store the correct positioning of the array when it is sorted. When you need to refer to the sorted array, you can call upon this "tagged" array that holds the correct ordering of when the array is sorted. In other words, the actual elements are not being changed during the sort process, rather the positions in the tag array are being changed so they will hold the correct ordering of the sorted elements of the array. For example, consider the following integer array and integer tag array:

   arr[] --> 3  7  1  12  39  4
   tag[] --> 0  1  2  3  4  5

This associates the position of 0 in tag[] to the value of 3 in arr[], the position of 1 in tag[] to the value of 7 in arr[], the position of 2 in tag[] to the value of 1 in arr[], the position of 3 in tag[] to the value of 12 in arr[], the position of 4 in tag[] to the value of 39 in arr[], and the position of 5 in tag[] to the value of 4 in arr[]. After the tag sort (for ascending order) is executed and completed, the arrays would contain the following elements:

   arr[] --> 3  7  1  12  39  4
   tag[] --> 2  0  5  1  3  4

As you can tell, the original elements in arr[] were not changed at all, but the original elements in tag[] have been changed. The tag[] array now holds the correct ordering of the positions in arr[] so the array can be sorted into ascending order when the tag[] array is called upon.

The following is a tag sort function that can be used to sort an array of integers into ascending order. Note that the function will also use a "swap" function so the values can be manipulated during the sort process:

// TAG SORT IS BASED ON SELECTION SORT
void tagSort(int dataArr[], int tagArr[], int size)
{
   int k, indexOfMin, pass;

   for (k = 0; k < size; k++)
       tagArr[k] = k;

   for (pass = 0; pass < size - 1; pass++)
   {
       indexOfMin = pass;

       for (k = pass + 1; k < size; k++)
           if (dataArr[ tagArr[k] ] < dataArr[ tagArr[indexOfMin] ])
               indexOfMin = k;

       if (indexOfMin != pass)
           swap (tagArr[pass], tagArr[indexOfMin]);
   }
}// end tagSort( )

void swap(int& x, int& y)
{
   int temp;
   temp = x;
   x = y;
   y = temp;
}// end swap( )


When you want to use this tag sort in your program, you obviously need to first call the tagSort() function, and then use the tagArr[] when you are displaying the contents of the actual sorted array. For example, if we wanted to display an integer array called arrNums, which has 10 elements, sorted into ascending order we would call our tagSort() function and then use the following code when we want to display the sorted array:

   for (int k = 0; k < 10; k++)
       cout << arrNums[ tagArr[k] ] << endl;



The following is a complete program demonstrating the use of a tag sort to sort an integer array into ascending order.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Compiled Using Dev-C++ Compiler \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include
#include
#include

void TagSort(int tagArr[], int dataArr[], int size);
void swap(int& x, int& y);

int main()
{
     const int MAXSIZE = 5;
     int intArr[MAXSIZE] = { 32, 16, 8, 24, 40 };
     int tagArr[MAXSIZE];

     TagSort(tagArr, intArr, MAXSIZE);

     cout << "ORIGINAL: ";
     for (int k = 0; k < MAXSIZE; k++)
           cout << intArr[k] << " ";

     cout << endl << endl;
     cout << "TAGGED: ";
     for(int j = 0; j < MAXSIZE; j++)
           cout << tagArr[j] << " ";

     cout << endl << endl;
     cout << "SORTED: ";
     for (int i = 0; i < MAXSIZE; i++)
           cout << intArr[tagArr[i]] << " ";

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}// end main()

void TagSort(int tagArr[], int dataArr[], int size)
{
     int k, indexOfMin, pass;

     for (k = 0; k < size; k++)
           tagArr[k] = k;

     for (pass = 0; pass < size - 1; pass++)
     {
           indexOfMin = pass;

           for (k = pass + 1; k < size; k++)
                 if (dataArr[tagArr[k]] < dataArr[tagArr[indexOfMin]])
                       indexOfMin = k;

           if (indexOfMin != pass)
                 swap(tagArr[pass], tagArr[indexOfMin]);
     }
}// end TagSort()

void swap(int& x, int& y)
{
     int temp;
     temp = x;
     x = y;
     y = temp;
}// end swap()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


I have now covered four methods for sorting arrays: a selection sort, bubble sort, merge sort, and tag sort. We can now move on to a better topic: structures. Structures in C++ are very similar to what other programming languages may call records. Read on for more about structures...

User-Defined Types and Header Files / Graphics

Structures

A structure in C++ is similar to a record in Pascal, QBasic, and some other programming languages. A structure, like an array, is a collection of values but structures are different from arrays in that the values stored in an structure may be of different types, and a structure's stored values, called members, are individually named and typed, just like "ordinary" variables. In other words, a structure is a collection of related elements, possibly of different types, having a single referencing name. Each element (member) in a structure is called a field of the structure. All of the elements or data in a structure should be related to one object. Each element in a structure can be individually accessed by using the "dot" ( . ) operator as you will see below.

The form for declaring a structure type is:

   struct type-id
   {
       type1 member1-name;
       type2 member2-name;
       type3 member3-name;
       .
       .
       typeN memberN-name;
   };

NOTE: The semi-colon placed after the closing bracket is required. The proper placement of a structure type is at the beginning of your program in the global area before the main ( ) function.

The above structure would create a type, not a variable. To declare of variable of this type, simply declare it as follows:

   type-id var-id;

Suppose we want to store the following data for an item in an auto parts store:

   supplier #
   part #
   retail price
   wholesale price

We could create a structure type to handle this situation as follows:

   struct tPart
   {
       int supplierNumber;
       int partNumber;
       float retailPrice;
       float wholesalePrice;
   };

To declare a couple of variables of this type, simply do as follows:

   tPart aPart, anotherPart;

We can give values to each individual member as follows:

   aPart.supplierNumber = 223;
   aPart.partNumber = 21;
   aPart.retailPrice = 117.38;
   aPart.wholesalePrice = 54.34;

You are allowed, but not advised, to combine a structure specifier with the declaration of a variable of that structure type. For example,

   struct
   {
       int supplierNumber;
       int partNumber;
       float retailPrice;
       float wholesalePrice;
   } somePart;

can be used instead of creating a structure type and then declaring a variable; in this case, somePart would be immediately associated with the structure type. Obviously, this is not advised because there would be no way to declare multiple variables of that particular structure type. You can also initialize structures at compile time just like you can arrays. For example, with the earlier tPart structure type, we could use:

   tPart somePart = { 223, 21, 117.38, 54.34 };

Unlike arrays, you can directly assign one structure variable the value of another structure that is of the same type. For example, if we had another variable declared tPart newPart, we could simply use the assignment operator to assign newPart the value of somePart:

   newPart = somePart;

You can also declare an array of structure types, and you are also allowed to give structure types arrays as individual members. For example:

   struct STUDENT
   {
       int idNum;
       int testScores[10];
       int finalExam;
   };

   STUDENT classStu[50];

In the above structure type, testScores[] is an array that holds ten values. classStu[] is an array that holds 50 STUDENT structure types. We can access and manipulate the contents of each element in the classStu[] array much like we do ordinary arrays. For example,

   classStu[5].testScores[1] = 88;

would give the second test score of the sixth element in classStu[] a value of 88.

Structure Example:
The following is a program that acts as a computerized ordering program for a auto part store (code was written with DEV C++ copiler):



#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>

struct tAutoPart
{
     int partNumber;
     int idNumber;
     double retailPrice;
     double wholesalePrice;
};

int main()
{
     int MAXORDERSIZE;
     tAutoPart partOrder[MAXORDERSIZE];

     cout << "Enter how many parts are to be ordered: ";
     cin >> MAXORDERSIZE;
     cout << endl;

     for (int k = 0; k < MAXORDERSIZE; k++)
     {
           cout << "Enter the part number for order " << k+1 << ": ";
           cin >> partOrder[k].partNumber;
           cout << "Enter the identification number for order " << k+1 << ": ";
           cin >> partOrder[k].idNumber;
           cout << "Enter the retail price for order " << k+1 << ": ";
           cin >> partOrder[k].retailPrice;
           cout << "Enter the wholesale price for order " << k+1 << ": ";
           cin >> partOrder[k].wholesalePrice;
           cout << endl;
     }

     cout << endl << endl;
     cout << "COMPLETE ORDER" << endl;
     cout << setfill('-') << setw(60) << "-" << setfill(' ') << endl;
     for (int j = 0; j < MAXORDERSIZE; j++)
     {
           cout << "Order " << j+1 << " --> ";
           cout << "Part Number: " << partOrder[j].partNumber << endl;
           cout << setw(12) << " " << "ID Number: " << partOrder[j].idNumber << endl;
           cout << setw(12) << " " << "Retail Price: $" << partOrder[j].retailPrice << endl;
           cout << setw(12) << " " << "Wholesale Price: $" << partOrder[j].wholesalePrice << endl << endl;
           cout << setfill('-') << setw(60) << "-" << setfill(' ') << endl << endl;
     }

     cout << endl << endl << endl;

     system("PAUSE");
     return 0;
}


We can now move on to our next topic: enumerated data types, which simply allow you to give descriptive names to integers. Read on for more...

Enumerated Data Types

Enum is derived from the integer type and therefore enumerated data types are integral in nature. Each integer value is given an identifier called an enumeration constant. In other words, it allows you to give names (labels) to some list of integers. The purpose is to assign names to integers, which in turn makes more readable code. There are two forms: the enumerated constant and the enumerated type. The form of a enumerated constant is as follows:

   enum { enumeration constants };

The form of a enumeration type is as follows:

   enum type_name { enumeration constants };
   type_name var_name;

The list of constants is usually given a number of 0, 1, 2, 3, etc. By default, C++ assigns a 0 to the first constant and then equates each enumerated constant to the next higher integral number, which means you can initialize a constant to whatever integer value you choose. For example,

   enum tWeekDays { sunday, monday, tuesday, wednesday, thursday, friday, saturday };

would assign sunday = 0, monday = 1, tuesday = 2, and so on. However, you could also do something like this:

   enum tFruits { lemon, orange = 5, grape, apple = 4, pear, peach};

This would assign lemon = 0, orange = 5, grape = 6, apple = 4, pear = 5, and peach = 6. Consider the output from the following section of code:

   tFruits fruit;
   fruit = orange;
   cout << fruit << endl;
   fruit = pear;
   cout << fruit << endl;
   cout << ++fruit << endl;

This would produce:

   5
   5
   6

Enumerated data types are commonly used with switch statements. Since switch statements are also integral in nature, you could use the enumerated constant of a enum type as a switch "case" or "cases" in a switch statment. If you are not familiar with switch statements, [see Switch Statements (section 4)]. For example, consider the following switch statement:

   enum tWeekDays { sunday, monday, tuesday, wednesday, thursday, friday, saturday };

   tWeekDays dayOfWeek;

   switch (dayOfWeek)
   {
       case sunday:
           cout << "Today is Sunday." << endl;
           break;
       case monday:
           cout << "Today is Monday." << endl;
           break;
       case tuesday:
           cout << "Today is Tuesday." << endl;
           break;
       case wednesday:
           cout << "Today is Wednesday." << endl;
           break;
       case thursday:
           cout << "Today is Thursday." << endl;
           break;
       case friday:
           cout << "Today is Friday." << endl;
           break;
       case saturday:
           cout << "Today is Saturday." << endl;
           break;
   }


The next section is an introduction to graphics. Read on for more...

Intro To Graphics

Graphics is obviously a very important aspect of programming. In this tutorial, I will only brush on a few topics. It is important to note that everything I explain is geared toward Dev-C++'s Version 4.0 compiler. I've found from my experiences that compilers are particularly different on how they set up their graphics functions so if you are using a different compiler, don't be surprised if these functions don't work for you. To make the DEV compiler "recognize" the graphics function, you must first go to the options menu, choose "compiler options", and check the box which says "Add the following commands when calling compiler". Place the following in the input box:

   -lbgi -lgdi32

The bgi stands for Borland Graphics Interface and gdi stands for graphical device interface (32 bit). The -l preceding the two commands most likely stands for link which tells the compiler to link or load these functions during compilation of programs. Why are we using Borland Graphics Interface? Well, in order to let the DEV compiler incorporate graphics, a programmer a few years back created code based on Borland's graphical interface which would be compatitable with DEV's compiler. Very slick programmer.

The Borland Graphics Interface (BGI) library of functions use the following coordinate system:

   Top-Left of window - (0, 0)
   Top-Right of window - (maxX, 0)
   Bottom-Left of window - (0, maxY)
   Bottom-Right of window - (maxX, maxY)

In "normal" VGA mode, the maxX is 639 and the maxY is 479 which provides a (640 X 480) screen resolution. You must also add the following #include statements when using graphics in your programs:

   #include <iostream.h>
   #include <stdlib.h>
   #include <winbgim.h>

The following is code which can be used as a setup graphics program for you to open when you want to begin coding a new graphics program. The purpose of the code is to initialize the graphics functions and make sure no errors were encountered during initialization. Instead of having to type the code in everytime you want to create a graphics program, you can save this and keep it as a setup graphics program:



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <iostream.h>
#include <stdlib.h>
#include <winbgim.h>

int main()
{
     int graphDriver, graphMode, errorCode;

     // initialize graphic functions
     graphDriver = DETECT; // detects highest resolution possible (640 x 480)
     initgraph(&graphDriver, &graphMode, "");
     errorCode = graphresult();
     if (errorCode != grOk)
     {
           cout << "Error initializing graphics." << endl
                << grapherrormsg(errorCode) << endl;
     }
     else
     {
           // begin creating graphics

     }
     getch();
     closegraph();
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


Let's start to talk about certain BGI functions that you can use in your programs.

   getmaxx( ) and getmaxy( )

These functions return the maximum pixel coordinates in the horizontal and vertical directions, respectively. So, (getmaxx(), getmaxy()) refers to the lower right corner of the graphics window.

   getmaxcolor( )

This function returns the largest available color number. For example, consider the following segment of code which displays 3500 "random" pixels in the graphics window:



// initialize graphics first

int xMax = getmaxx();
int yMax = getmaxy();
int maxColor = getmaxcolor();
int pixel, x, y;

srand((unsigned)time(NULL));

for (pixel = 0; pixel < 3500; pixel++)
{
    x = rand() % (xMax + 1);
    y = rand() % (yMax + 1);
    color = rand() % (maxColor + 1);
    putpixel(x,y,color);
    delay(15);
}

getch();
closegraph();


The function     setviewport()     allows you to limit the drawing that is done to a specified rectangular portion of the graphics window. For example, consider the following code:

   setviewport(50, 100, 350, 300, 1);
   setcolor(WHITE);
   rectangle(0, 0, 299, 199);
   setcolor(RED);
   line(25, 50, 425, 50);

This would draw a white rectangle around the viewport coordinates and a red line from (25,50) to (299,50) in viewport coordinates or from (75,150) to (350,150) in absolute coordinates. After a viewport is defined, all coordinates used will be relative to the viewport settings. The following describes the functions we used in the above code:

setviewport(x1,y1,x2,y2,1)     --> (x1,y1) = top left coordinate of viewport; (x2,y2) = bottom right coordinate of viewport; 1 = clipping is turned on (could also be 0 = clipping turned off)

setcolor(color)     --> where color is a word or number describing the color of choice. I'm not sure but I believe there are 16 possible colors.

rectangle(x1,y1,x2,y2)     --> (x1,y1) = top left coordinate of rectangle; (x2,y2) = bottom right coordinate of rectangle;

line(x1,y1,x2,y2)     --> (x1,y1) = starting coordinate of line; (x2,y2) = ending coordinate of line;

In order to clear all or part of the graphics window (display), you can use the following functions:

cleardevice( )     --> clears the entire graphics window
clearviewport( )     --> clears the currently active viewport

Lines
The following may be used to draw lines:

line(x1, y1, x2, y2)     --> draws a line from (x1, y1) to (x2, y2)

moveto(x,y)     --> changes the current position to (x, y)

lineto(x,y)     --> draws a line in the current style and color from the current position to the given position (x,y) and also sets the current position to (x,y)

linerel(dX, dY)     --> draws a line from the current position (CP) to (CP + dX, CP + dY)

moverel(dX, dY)     --> changes the current position (CP) to (CP + dX, CP + dY)

Customizing Lines
By default, all lines are drawn in a solid line style, one pixel thick. You can change these settings by using the     setlinestyle( )     function, which has three parameters:

   1) int linestyle, which can have one of the following values [ SOLID_LINE (0), DOTTED_LINE (1), CENTER_LINE (2), DASHED_LINE (3), USERBIT_LINE (4) ] (there are more than 5 linestyles; check your compiler).

   2) unsigned upattern, which allows a 16-bit user defined pattern; this value will be ignored if linestyle is not equal to 4.

   3) int thickness, which can have one of the following values [ NORM_WIDTH (1) or THICK_WIDTH (3) ]

NOTE: When creating USERBIT_LINES, consider the following:

   if upattern = 0xAAAA, then every other pixel is "on"
   if upattern = 0xCCCC, then 2 on, 2 off, 2 on, 2 off, etc.

Remember that upattern must be created using a 16-bit pattern, which is basically evaulated at the binary level. 'A' in binary = 1010, 'C' in binary = 1100. NOTE: 1 means "turned on"; 0 means "turned off"

For example, to set the linestyle to a dotted line format with thick width, use the following:

   setlinestyle(DASHED_LINE, 0, THICK_WIDTH);

Rectangles
The rectangle( ) function accepts four parameters: left, top, right, bottom. These parameters specify the upper left and bottom right corners of the rectangle to be drawn.

   rectangle(0, 0, 100, 50);

Circles
The circle( ) function accepts three parameters: xCenter, yCenter, radius. This would draw a circle with the center point being (xCenter, yCenter) and radius of radius.

   circle(50, 50, 10);

The rectangle( ) and circle( ) functions draw only the perimeter (outline) of the specified objects which means the objects are not filled in with color. One way to fill in an object or any closed area is to use the following function:

   floodfill(x, y, borderColor)

The floodfill( ) function has three parameters: the point (x, y), called the seed point, can be any point that is located inside the region to be filled; the borderColor value specifies the color of the border of the region to be filled --> filling will stop when borderColor is reached. The floodfill( ) function will "leak" or "spill out" outside the region, perhaps filling the entire graphics window, if the (x, y) point selected is inside a non-closed region.

There are also two other "fill" functions, which draw and fill an object:

   fillellipse(xCenter, yCenter, xRadius, yRadius);

The fillellipse( ) function has four parameters: (xCenter, yCenter) specifies the center point of the ellipse; xRadius specifes the horizontal radius; yRadius specifies the vertical radius.

   fillpoly(numPoints, polyPoints);

The fillpoly( ) function and corresponding drawpoly( ) function has two parameters: polyPoints should be an array containing the points of the polygon (must pass the first point in again as the last point in the polygon); numPoints specifies how many actual points are in polyPoints. The following shows an example:

   int polyPoints[] = { 3, 5, 25, 33, 250, 76, 123, 123, 3, 5 };
   drawpoly(5, polyPoints);

Drawing ellipses involves using the following function:

   ellipse(x, y, startAngle, endAngle, xRadius, yRadius);

The ellipse( ) function has six paramters: the point (x, y) is the center point of the ellipse; using a startAngle and endAngle other than 0 and 360 allows you to draw a certain sector or portion of an ellipse; xRadius and yRadius are self-explanatory.

Specifying Colors
If you want to change or specify the color and/or pattern used for filling objects and regions, you can use the following function:

   setfillstyle(int pattern, int color);

NOTE: setcolor( ) will specify the color used for the border of objects and normal drawing functions; setfillstyle( ) specifies the interior of objects.

There are 13 different patterns defined for the pattern of filling, including:

   EMPTY_FILL (0)
   SOLID_FILL (1)
   SLASH_FILL (4)
   BK_SLASH_FILL (5)
   HATCH_FILL (7)
   XHATCH_FILL (8)

The following section of code will draw rectangle with a border of white and interior of red:

setcolor(WHITE);
rectangle(20,20,120,100);
setfillstyle(SOLID_FILL, RED);

Displaying Graphical Text
Text can be displayed on the graphics window using the following functions:

   outtext(string);
   outtextxy(x, y, string);

The outtext( ) function displays the given string using the font, direction, and the size set by the function     settextstyle( )    , which has three paramters; int font, int direction, and int size (1 - 10);

   settextstyle(font, direction, size);
   DEFAULT: settextstyle(0, 0, 1);

Direction may be one of the following: HORIZ_DIR (0) or VERT_DIR (1)

You can "stick" with the default, but if you do, your text will be practically unreadable. You should experiment with various settings to see which values you prefer.

By default, outtext( ) positions the text such that the upper left corner of the first character is in the current position. outtextxy( ) is similar to outtext( ) except the upper left corner of the fisrt character is in (x, y).

You can use     settextjustify()     to control the horizontal and vertical position of the text relative to the current position or (x, y). It requires two parameters: horizontal and vertical. Horizontal may be:

   LEFT_TEXT (0)
   CENTER_TEXT (1)
   RIGHT_TEXT (2)

The horizontal parameter determines whether the text starts at the current position, is centered at the current position, or ends at the current position.

The vertical parameter may be:

   BOTTOM_TEXT (0)
   CENTER_TEXT (1)
   TOP_TEXT (2)

The vertical parameter determines whether the text is displayed above the current positoin, centered at the current position, or below the current position. The default values of text justification are LEFT_TEXT and TOP_TEXT.

This wraps up all I want to say concerning graphics. The next section shows how to create header files. Read on for more...

Creating User-Defined Header Files

It is important to note that I am using Dev-C++ V.4 as the compiler for all information depicted in this tutorial. Let me start this topic off by first explaining what a header file actually is. Everytime you write a program you have to specify certain #include statements at the beginning of your program. Why is this? Basically, the #include statement is actually a link to a header file containing pre-defined elements that you will need to use for successful execution of your program. Header files are created so you can use the code contained in them in multiple programs without having to write the code everytime you want to use it in a program. It is all pre-defined in a seperate header file so all you have to do is "include" it in your program.

Take the iostream.h header file for example. Load your compiler and open iostream.h . Peek at what it contains. Do you understand it? Can you figure out what some of it is doing? Probably not at this point, but you should be able to recognize certain function calls and declarations. What you see is actually code used to define the standard input/output streams. By using this header file in your programs, you have access to the input/output streams. Without this convenient header file, if you wanted to have access to the input/output streams, you would first have to define them. Very tedious and time consuming not to mention the extra effort of learning how to define them.

A user-defined header file is a header file that you, as the programmer, create. --> iostream.h and all other header files which are "included" using angle brackets are pre-defined and are provided for by the makers of the Dev-C++ compiler. For example, #include <time.h> and #include <winbgim.h> (notice the brackets). When you create a header file, which I describe below, and "include" it in your program, you will use quotes instead of brackets. For example, suppose guess.h is a header file that you created for a program. You can include this header file in your program by using: #include "guess.h".

For the most part, user-defined header files are used to define constants, user-defined types, and to list function prototypes to be used in a main file. NOTE: header files do not include function definitions --> they will go in a seperate "auxillary" source file that will have a (.cpp) extension. Also note that you shouldn't compile header files individually. There is no need to do this. The header files will be compiled when you compile the actual program which tries to link to it. Also, when you compile the actual program, the only elements in the header file which will be compiled are the elements in which the program will need to use.

The following explains what is required to create a header file using Dev-C++ V.4 compiler:

First, open a new document and clear it. Next, enter the following information (I describe the elements further below):

   #ifndef __NAMEOFHEADER_H
      #define __NAMEOFHEADER_H

   // place constants here

   // place user-defined types here

   // place function prototypes here

   #endif


Notice the (#) symbol preceding commands in the above file. Anytime the (#) is used, it is associated with the compiler directories. It tells the compiler to do something as in turning on/off some compiler setting. It is not an executable statement. It only functions during compilation. NOTE: in the above code, NAMEOFHEADER is the name of the header file; this name can be anything you want it to be, but it should be descriptive and reflect what it actually functions to do.

The     #ifndef     command can be read as "if not defined" which means it checks to see if a given symbol is defined or not. If the symbol is not defined, compilation will move into the body of the header file and do necessary definitions. If the symbol is defined, the header fill will not need to perform definitions. The     #define     is only performed if     #ifndef     returns a value of true, which means the symbol is not defined.     #define     literally does what it reads: it defines the elements contained in the header file for use in the program which called upon it. Basically, these two commands, especially     #ifndef    , gaurd against re-declaration errors if the header file is included in the calling program more than once. In a single application, you may be using multiple source files, and it is possible that more than one of the source files may try to #include the same header file.

Generally speaking, when you create a header file that contains the constants and prototypes for a program, you will also want to create an auxillary file (will have a .cpp extension) that holds all of the function definitions to be used in the program. This means that the only function in the main program will be the main() function. The question may arise as to how the compiler will know how to link the auxillary file to the actual main program file. This can be done through the use of a project file. It is important to note that the main program file, which contains the main() function, and the auxillary file, which contains the function definitions, both have to include the user-defined header file and also any other compiler directives they use. When you include a user-defined header file, you have to use double quotes instead of using brackets. The brackets tell the compiler that it is dealing with pre-defined include files and the quotes tell the compiler it is dealing with a user-defined header file. For example, if I create a header file called myHeader.h, I can include it by using #include "myHeader.h".

Creating Project Files
In order for you to successfully link all of your source files (main file and auxillary file), you will have to create a project and place the files in it. Project files are used to "tie together" mulitple source files into a single executable application or program. Creating a project is extremely easy. The following explains how to create a project using Dev-C++ V. 4 compiler:

If the individual source files already exist, which means you have already created them, click the file menu and select new project. For dos level programs (probably what you want), at the next prompt select console application and make sure C++ project is selected. At the dialog box, enter a descriptive name for the project, which will be given an extension of .dev. It is important to note that none of the source files can have the same base name as the project file. The base name of project will be used as the name of the executable file that is created after successful compilation. Next goto the project menu and select add to project. At the open prompt, select all of the source files to be included in the program (NOTE: do not include the header file itself). Now, you should see an "outline" form of the project on the left side pane. Now, you need to go to the options menu and select Compiler Options. Select directories and place either an "x" or mark the "Add the directory below to be searched for include files..." and enter the full path to the user-defined header file that you created in the text box. This only needs to be done if you didn't place your user-defined header file in the default "include" directory under the Dev-C++ directories. Then, goto the execute menu and select rebuild all. Assuming there are no errors, the executable file should be created (.exe extension with the base project name).

If you have not created any project files, simple choose to create a "New Unit for Project..." instead of "Add unit to project...".

The following are two source files and one user-defined header file that you can use as an example of setting up a project. The program is written for Dev-C++ V.4 compiler.



/////////////////////////////////////////    user-defined header file    /////////////////////////////////////

#ifndef __STUGRADES_H
  #define __STUGRADES_H

// initialize constants
const int possiblePoints = 300;

// function prototypes
void getStudentInfo(char name[25], int& points);
char calculateGrade(int points);
void displayResults(char name[25], char grade);

#endif

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\   end of user-defined header file    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

/////////////////////////////////////////        main source file        /////////////////////////////////////

// PURPOSE: Uses a user-defined header file to calculate a student's letter grade
//          based on three test scores. POSSIBLE POINTS = 300

// PROGRAMMER: Mike Ware
// DATE LAST UPDATED:

#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>
#include "STUGRADES.H"

int main()
{
     char stuName[25], grade;
     int earnedPoints = 0;

     getStudentInfo(stuName, earnedPoints);
     grade = calculateGrade(earnedPoints);
     displayResults(stuName, grade);

     system("PAUSE");
     return 0;
}



\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\    end of main source file     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

/////////////////////////////////////////     auxillary source file      /////////////////////////////////////

// auxilary file for main file --> contains the function definitions

#include <iostream.h>
#include <stdlib.h>
#include <iomanip.h>
#include "STUGRADES.H"

// get student name and total points earned for student
void getStudentInfo(char name[25], int& points)
{
     int test1, test2, test3;

     cout << "Enter the name of the student: ";
     cin.getline(name, 25);

     cout << "Enter the student's earned points for test 1: ";
     cin >> test1;
     cout << "Enter the student's earned points for test 2: ";
     cin >> test2;
     cout << "Enter the student's earned points for test 3: ";
     cin >> test3;

     points = test1 + test2 + test3;
}// end getStudentInfo()

// calculate the letter grade for student based on numberical earned points
char calculateGrade(int points)
{
     char letterGrade;
     int percentage = (points / float(possiblePoints)) * 100;

     if (percentage >= 90)
           letterGrade = 'A';
     else if (percentage >= 80)
           letterGrade = 'B';
     else if (percentage >= 70)
           letterGrade = 'C';
     else if (percentage >= 60)
           letterGrade = 'D';
     else
           letterGrade = 'F';

     return letterGrade;
}// end calculateGrade()

// display student name and letter grade
void displayResults(char name[25], char grade)
{
     cout << endl << endl;
     cout << "STUDENT NAME: " << name << endl;
     cout << setw(45) << setfill('-') << "-" << setfill(' ') << endl;
     cout << setw(14) << "GRADE: " << grade << endl << endl;
}// end displayResults()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  end of auxillary source file  \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\



In the next section, you will be introduced to object oriented programming by using classes. Read on for more...

Classes and Related Topics

Classes

Before I elaborate on classes, I want you to get familiar with object oriented programming (OOP: pronounced "hoop", but with a silent 'h') because when you create classes, you are actually using OOP. What is an object and all this talk about object oriented programming? An object is more or less just some thing that knows how to perform operations (perhaps to itself), and it can also be manipulated. In C++ programming, an object can be called an instance of a class (or object type). An instance is simply a variable of the object type. Since everything is centered around an object, we call this type of programming object oriented. The object type (or class) gives the instance of the class characteristics, such as data members and function members it can use. Some data may be kept entirely private which means it can only be accessed by the object itself, and some other data may be kept public which means that "outside" members can have access to it. OOP makes C++ very powerful, and it is why C++ is capable of building high-quality software.

A class is a combination of data and functions joined together to form an object type. The object type defines the operations that are necessary to manipulate the object. By creating a class, we can take advantage of data hiding, which is very important in programming. Data hiding is physically located data in the class (inside the class object type) that is therefore hidden from the "outside", which is everything in the program not defined in the class type. The data can only be accessed or changed by functions that are apart of the class. The declaration of a class is similar to that of a structure [see Structures in Section 10], except that usually there will be member functions along with data members. There will also be some type of syntax for distinguishing members that are available for public use and ones that will be kept private.

The best way for you to learn is by studying a simple class object type. The following is a very simple class object type:



class myClass
{
    private :
                 int data1;
    public :
                 void setData( int d )
                     {    data1 = d;    }
       
                 void displayData( )
                     {    cout << endl << "Data is " << data1;    }
};


NOTE: Some people claim it is not good programming practice to include function definitions in a class object type. In the above class, I have included definitions for setData( ) and displayData( ) simply because they are small functions and really only have one purpose.

In the above myClass class specification, the only data member is declared as private, which means that nothing outside of the class can directly modify or use this value. The only two member functions are declared as public, which means that "outside" users can call these functions. The public functions provide access to the private data member, and it is the only way that anything "outside" the class, such as an instance declared of the object type, can access the data member.

NOTE: It is not a requirement that all public elements be member functions and all private elements be data members, or vice versa. You may very well place data members in the public domain, and you may also place member functions in the private domain. Also note the semi-colon following the closing bracket of the class specification; this is required.

Since the whole idea of using classes is for programming centered around the object, when you go to call member functions which are defined in the class object type, you must first declare an instance of the class object type. For example, it would make no sense at all to immediately try to call     setData( )     in your program. How would the compiler know where to setData( ) or what to setData( ) for? Obviously, you need to declare an instance of the class, and then use that instance to call the member function for access the private data member. We could use something like this:

   myClass myObject;
   .
   .
   .
   myObject.setData(121); // uses the dot operator "." to access the member function

The above code assigns 121 to myObject.data1. There are two ways you can make the class object available for use in a program:

   1 - Declare the class object type before the main( ) function or earlier in the main( ) function [not recommended inside the main( ) ].
   2 - Put the class object type declaration in a header file and #include the header file in your program [see Creating User-Defined Header Files (section10)].

Some object oriented languages refer to calls to member functions as messages. For example, consider:

   myObject.displayData( ) --> can be thought of as "sending" a displayData message to myObject.

Constructors
In the above example, there was only one way to assign a value into data1, which was a private data member. We had to access the setData( ) public member function and give data1 a value accordingly. Constructors are used to initialize the object when it is created without needing a seperate call to another member function. A constructor can be thought of as an "automatic" initialization of an object. In C++, a constructor is given the same name as the class name and has no return value or even a return type. If it does have an argument list, it will only contain values to initialize the class attributes (data members).

If we wanted to add a constructor to our myClass example, we could add the following code to the class object declaration:



class myClass
{
    private :
                 int data1;
    public :
                 myClass ( )
                     { data1 = 0; }
               
                 myClass ( int dataIn )
                     { data1 = dataIn; }

                 void setData( int d )
                     {    data1 = d;    }
       
                 void displayData( )
                     {    cout << endl << "Data is " << data1;    }
};


Now, when we create an instance of our class in our program, if we do not pass an argument during declaration, the constructor with no arguments will be used to assign 0 to the private data member of the object. If we do provide an argument, the constructor with one argument will be used to assign the private data member of the object the value of the argument. For example, consider the following object declarations:

myClass obj1;     // uses the constructor with no arguments
myClass obj2( 650 );     // uses the constructor with one argument

obj1.data1 will now hold a value of 0
obj2.data1 will now hold a value of 650

Notice that in our updated class specification there are two constructors. You can follow the rule in C++ when using constructors that you can have the same names for functions as long as they have different parameter lists or signatures [see below: Function Overloading]. Although I failed to mention it earlier, when a function definition is placed within a class specification, it is said to be defined inline. An inline function definition is really just a request to the compiler asking for the code to be directly linked with any instances of the class. Since it is just a request, it is not recommended as good programming practice. When a member function is not inline, the actual definition must be placed elsewhere, either after the main( ) function with all other definitions or in a seperate auxillary source file (and used as a project file).

Default Parameters
When variables and expressions are passed to functions as arguments, they are associated with a corresponding parameter in the actual function heading. The parameter must be of the same type as the argument, but it may given a different name and can also be given the oppurtunity to have a default value. Just like regular functions, constructors of a class may have default parameters. For example, consider the following modifications to our previous myClass class:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 )
                     { data1 = d1;
                       data2 = d2;
                       data3 = d3; }
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }
};


If you dilligently look at the class specification, you will notice that it only has one constructor, 2 new private data members have been added, and the arguments in the constructor are being used as default parameters. A default parameter is only used if the argument's value is not being passed to the function. This is beneficial because it enables you to reduce the needed number of constructors. Previously, we had two constructors, now we have one. Our "new" constructor can handle the following situations during the declaration of instances:

myClass obj1, obj2(35), obj3(12, 78), obj4(11, 43, 98);

Overview of Declaration
All private data members in obj1 will have a value of 0.
obj2.data1 would hold 35, but all other data members will have a value of 0.
obj3.data1 would hold 12 and obj3.data2 would hold 78, but obj3.data3 would hold 0.
All private data members is obj4 will been assigned the value of the arguments being passed to the constructor.

As you can see, we can handle four situations by using one simple constructor. Without default parameters, we would have had to create four constructors.

Another convenient way of sending data into a constructor is by using a base/member initialization list. The base/member initialization list is said by some programmers to be slightly faster than other initializations. The general form is as follows:



class className
{
    private :
                 type1 data1;
                 type2 data2;
                 type3 data3;
    public :

                 className (type1 param1, type2 param2, type3 param3) :
                        data1(param1), data2(param2), data3(param3)
                   {  }
};


For another example, consider the following modifications made to our previous myClass class:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                        data1( d1 ), data2( d2 ), data3( d3 )
                  {  }
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }
};


Default Copy Constructor
The default copy constructor provides a way to initialize an object to have the same values as another existing object that is of the same class. You don't even have to define or create the means to handle this situation because the default copy constructor is a "built-in" member function. All classes have their own default copy constructor. The default copy constructor has one argument, which is an instance of the class to be copied to the calling object. There are two ways to use the default copy constuctor:

One way is to simply send the object to be copied as an argument when declaring the instance, such as:

   myClass object1(object2);

Or, you can simply use a form of the assignment statement, such as:

   myClass object1 = object2;

Both of these two methods will perform a member by member copy of values from object2 into object1.

Defining Class Member Functions
In order for the compiler to associate a function as a member of a class, you have to specify that it is a member of the class. This is done by using the class name along with the scope resolution operator ( :: ) in the function heading. Where have you seen the scope resolution operator? You use it when you are setting formatting flags to control the form of output [see Output Manipulators (section 5)]. The scope resolution operator basically tells the compiler that the code following it is to be associated with something. In our case, it will let the compiler know that the function is to be associated with the specified class. For example, suppose we placed a new function called     determineData( )      in our myClass object type. It should now look like this:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                        data1( d1 ), data2( d2 ), data3( d3 )
                  {  }
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }

                 void determineData1( int d1 );
};


Notice that     determineData1( )      is the only member function in myClass which is not inline. We now need to define it. We could use the following code using the scope resolution operator and class name to associate the function with the myClass specification:



void myClass :: determineData( int d1 )
{
    if (d1 < 0)
           cout << "Data1 is of negative value." << endl;
    else if (d1 == 0)
           cout << "Data1 is of neutral value." << endl;
    else
           cout << "Data1 is of positive value." << endl;
}// end myClass :: determineData1( )


Notice the name of the class and the scope resolution operator preceding the name of the member function and parameter list. This is how the compiler knows to associate the     determineData1( )     function as being a member of myClass.

Returning Objects From Functions
To see how functions work with objects, I have provided a simple function to be used with our myClass class that will return the sum of the values of two objects (instances of the class). The function will be called     addData( )    . Update the myClass class specification to the following:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                        data1( d1 ), data2( d2 ), data3( d3 )
                  {  }
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }

                 void determineData1( int d1 );

                 myClass addData( myClass obj );
};


Then put the following member function definition with the other functions either in a main file or in an auxillary file:



myClass myClass :: addData( myClass obj )
{
    myClass temp;

    temp.data1 = data1 + obj.data1;
    temp.data2 = data2 + obj.data2;
    temp.data3 = data3 + obj.data3;

    return temp;
}// end myClass :: addData()


The function above would return the sum of obj (which was passed to the function as an argument) and the object which called upon the addData( ) function which is commonly called the this object. If we wanted to use this function, we would have to "return" the value directly into an object in our program. We could use something like this:

   myClass obj1, ojb2, obj3;

   // obj1 and obj2 get values

   obj3 = obj1.addData(obj2);

This would add the values contained in obj2 and obj1 and store the result in obj3.

Destructors
A destructor is a member function that is called automatically when an object is destroyed. It has the same name as the class of which it is a member (which means that it has the same name as any constructors) except that it is preceded by a tilde ( ~ ). Like constructors, destructors have no return type and no parameters. The most common use of destructors is for deallocating any memory that is dynamically allocated [see Dynamically Allocating / Releasing Memory (section 7)] for the object by the constructor. Until you actually allocate memory for an instance, you don't really need to declare or define a destructor for your classes. However, just for an example, let's add a destructor to our previous smallClass example:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                        data1( d1 ), data2( d2 ), data3( d3 )
                  {  }

                 ~myClass() { }; // this is the destructor
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }

                 void determineData1( int d1 );

                 myClass addData( myClass obj );
};


MEMORY USAGE NOTE: Every instance of a class will get a "copy" of the class's data members; however, all instances of a given class will use the same member functions. The member functions are created and placed into memory only one time (when they are defined). Memory for an instance's data members is allocatted when the object is created.

The following is a simple complete program demonstrating most of the material covered in this section. The code was written using Microsoft Visual C++ V.6 compiler.



\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include <iostream.h>
#include <stdlib.h>

class myClass
{
   private:
       int data1;
       int data2;
       int data3;

   public:
       myClass(int d1 = 0, int d2 = 0, int d3 = 0):
           data1(d1), data2(d2), data3(d3)
           {  }

       void setAll(int d1, int d2, int d3);

       myClass addData(myClass objIn);

       void displayData();

};

void main()
{
   myClass obj1, obj2(2, 4, 6), obj3, obj4;
   int data1, data2, data3;

   cout << "Enter a value for data1: ";
   cin >> data1;
   cout << "Enter a value for data2: ";
   cin >> data2;
   cout << "Enter a value for data3: ";
   cin >> data3;

   obj1.setAll(data1, data2, data3);
   obj4 = obj1.addData(obj2);

   cout << endl << endl;
   cout << "Obj 1" << endl;
   cout << "-----------------------" << endl;
   obj1.displayData();

   cout << "Obj 2" << endl;
   cout << "-----------------------" << endl;
   obj2.displayData();

   cout << "Obj 3" << endl;
   cout << "-----------------------" << endl;
   obj3.displayData();

   cout << "Obj 1 + Obj2" << endl;
   cout << "-----------------------" << endl;
   obj4.displayData();

   cout << endl << endl;
}// end main()

void myClass :: setAll(int d1, int d2, int d3)
{
   data1 = d1;
   data2 = d2;
   data3 = d3;
}// end myclass :: setAll()


void myClass :: determineData(int dataIn)
{

   if (dataIn < 0)
       cout << "Data is currently negative." << endl;
   else if (dataIn > 0)
       cout << "Data is currently positive." << endl;
   else
       cout << "Data is currently neutral." << endl;
}// end myClass :: determineData()


myClass myClass :: addData(myClass objIn)
{
   myClass temp;
   temp.data1 = data1 + objIn.data1;
   temp.data2 = data2 + objIn.data2;
   temp.data3 = data3 + objIn.data3;
   return temp;
}// end myClass :: addData()


void myClass :: displayData()
{
   cout << "      Data 1 = " << data1 << endl;
   cout << "      Data 2 = " << data2 << endl;
   cout << "      Data 3 = " << data3 << endl << endl;
}// end myClass :: displayData()

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Function Overloading

When you use multiple constructors for a class, you are actually overloading functions. You can also overload operators and "regular" non-class functions. To overload a "regular" non-class function, simply create multiple functions all with the same function name but with each having a different signature, which is the name, number of parameters, order and types of the parameters, and the class to which it belongs (does not include the return type); the overloaded functions must have different parameter lists so the compiler knows which function to use depending on the data types of the arguments. For example, consider the following overloaded     swap( )     function:



void swap(int &x, int &y)
{
    int temp;
    temp = x;
    x = y;
    y = temp;
}


void swap(double &x, double &y)
{
    double temp;
    temp = x;
    x = y;
    y = temp;
}


void swap(char x[], char y[])
{
    char temp[25];
    strcpy(temp, x);
    strcpy(x, y);
    strcpy(y, temp);
}


This     swap( )     function is defined for integers, floating point values (double), and strings and all three may be included in the same program. When the function is called within the program, the compiler will determine which function to use by evaluating the parameter list of the calling function.

Operator Overloading

Just as we can overload functions, we can also overload operators. Overloading is the definition of two or more functions or operators with the same identifier. The purpose of overloading an operator is to enable us to write code in a more natural style with intuitive means. If an operator has not been defined or overloaded for a certain class, when you try to use that operator on or with an object of that class type, you will generate some type of error because the compiler will have no clue as to what you are trying to do. The compiler will immediatly try to use the "built-in" meaning for the operator, which will not be able to "handle" the object of the class you are trying to manipulate. For example, consider the addition operator ( + ). This is a built-in operator that is used for addition purposes (actually, just for integer types the built-in + op is defined three different ways: one for short, one for int, and one for long). Suppose we have two instances of a class named obj1 and obj2, and we try to add them together using the "built-in" op by using:

   obj1 + obj2

Since we haven't overloaded the addition operator to handle these instances, the compiler will try to use the "built-in" operator and will generate an error; the "built-in" operator does not have access to the object's data.

Obviously, we need to overload the addition operator so we can use it for instances of our class type. When we overload a particular operator for a class, the overloaded operator function will be called or used whenever that particular operator is used on objects of the class. Overloaded operators always have a name of the following form:

   operatorOP( )

where OP is the operator symbol you want to overload. For example, a member function named     operator+( )     will overload the addition operator for the class.

Before we get into how we can actually overload functions, we need to talk about a few guidelines to follow when overloading operators:

You can overload any C++ operator except the member dot operator ( . ), the conditional expression operator ( ?: ), the pointer to member operator ( .* ), the scope resolution operator ( :: ), and the sizeof operator ( sizeof(int) ).

An overloaded operator must use the same number of operands as the corresponding "built-in" operator.

You cannot change the precedence or associativity of an operator.

You cannot create new operator symbols.

You cannot change the meanings of the operators for any "built-in" type.

Ok, now let's overload. An overloaded operator is "called" whenever you use the corresponding operator symbol with objects of the class type. For example, if fraction1 and fraction2 are instances of a Fraction class, then the statement:

   if (fraction1 == fraction2 )     .
   .
   .

would be using the overloaded ( == ) "is equal to" operator for class Fraction (assuming we have defined it for the class). The actual statement works as follows:

The object to the left of the operator will be the "calling" object (often called the this object), and the object to the right of the operator will be "passed" to the overloaded operator function as an argument. Thus,     fraction1 == fraction2     would be equivalent to (and can also be written as):

   fraction1.operator==(fraction2)

The value returned from the overloaded operator is the value of the expression. For example, the     operator==( )     function must return a value of true ( 1 ) or false ( 0 ) because the expression is a logical ( boolean ) expression. This type of evaluation occurs for all overloaded operators except the expression being evaulated may not be of boolean type.

For an example, let's go back to our previous simplified version of myClass class and attempt to overload some operators. Consider the following class specification:



class myClass
{
    private :
                 int data1;
                 int data2;
                 int data3;

    public :
                 myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                        data1( d1 ), data2( d2 ), data3( d3 )
                  {  }

                 ~myClass() { }; // this is the destructor
               
                 void setData1( int d )
                     {    data1 = d;    }
       
                 void displayData1( )
                     {    cout << endl << "Data is " << data1;    }

                 void determineData1( int d1 );

                 myClass addData( myClass obj );

                 bool operator==( const myClass& objIn );

                 myClass operator+( const myClass& objIn );

                 myClass& operator=( const myClass& objIn );
};


Notice that the class specification now contains three overloaded operator prototypes: "is equal to" ( == ), addition ( + ), and the assignment operator ( = ). Let's overload each operator one by one.

If we want to overload the "is equal to" operator so we can use the ( == ) in a natural style within our program, we would have to place the following function definition with all other definitions:



bool myClass :: operator==( const myClass& objIn )
{
    if ( data1 == objIn.data1 && data2 == objIn.data2 && data3 == objIn.data3 )
          return true;
    else
          return false;
}// end myClass :: operator==() overload


The return type is bool because the function will return a value of 1 (true) or 0 (false). We are passing the argument as const so we can gaurd against any type of accidental change to the object, and also notice that we passed the argument by reference. We must do this because we are passing an object that contains data members and in order to have access to the data members, we must have access to the internal memory locations for them.

We can now use the "is equal to" op in a more "natural" coding style in our program by using:

myClass obj1, obj2;
.
.
// obj1 and obj2 get values
.
.
if ( obj1 == obj2 )
   cout << "Object 1 is equal to Object 2." << endl;

The definition for the overloaded addition ( + ) operator is as follows:



myClass :: myClass operator+( const myClass& objIn )
{
       myClass temp;
       temp.data1 = data1 + objIn.data1;
       temp.data2 = data2 + objIn.data2;
       temp.data3 = data3 + objIn.data3;
       return temp;
}// end myClass :: operator+()


We can now use the addition op in a more "natural" coding style in our program by using:

myClass obj1, obj2, sum;
.
.
// obj1 and obj2 get values
.
.
sum = obj1 + obj3;

When overloading the assignment ( = ) op, we must return a reference to the calling object so we can mimic the "built-in" assignment op. Recall earlier when I said that the "calling" object is commonly referred to as the this object. The this object is simply the currently active object (the object that can directly modify itself). Since we have to return a reference to the "calling" object, we can simply return the this object. The following is the definition for the overloaded assignment op for myClass:



myClass& :: myClass operator=( const myClass& objIn )
{
       data1 = objIn.data1;
       data2 = objIn.data2;
       data3 = objIn.data3;
       return *this;
}// end myClass :: operator=()


We can now use the assignment op in a more "natural" coding style in our program by using:

myClass obj1, obj2, obj3, obj4;
.
.
// obj1 and obj2 get values
.
.
obj3 = obj1;
obj4 = obj2;

A complete program illustrating the use of overloaded operators for our myClass class is provided after the next section. I would like to cover static variables so you can see how they work with objects in the program provided. Read on for more...

Static Variables

Static variables, sometimes referred to as static automatic variables, are used when it is necessary for a function to "remember" the value of a variable from a call of the function to the next call. For example, in the following program, the function     getAverage()     calculates a "running" average by retaining or remembering the sum and the number of values between calls to the function:



///////////////////////////////////// code written with Dev-C++ v.4 compiler ///////////////////////////////////


#include <iostream.h>
#include <stdlib.h>

float getAverage(float newValue);

int main()
{
     float data, average;

     cout << "Enter a numeric value (-999 to stop): ";
     cin >> data;
     while (data != -999)
     {
           average = getAverage(data);
           cout << "Current average is: " << average << endl;
           cout << "Enter a numeric value (-999 to stop): ";
           cin >> data;
     }

     system("PAUSE");
     return 0;
}// end main()

float getAverage(float newValue)
{
     static float total = 0;
     static int count = 0;

     count++;
     total += newValue;
     return (total / count);
}// end getAverage()


A class may contain static data members (any function can have static variables in order to retain their value between function calls). The purpose of static data members in a class is to enable all instances of the class to "share" the same memory location for that member. No matter how many instances of the class are created, there will be only one "copy" of the static data member. Static data members are visible only within the class, but their "lifetime" is the entire execution time of the program.

For example, suppose an object needs to know how many other objects of its type are currently "active" in the program. The class could contain a static data member which will be used to store the needed value and "share" it with all instances. This is illustrated as well as overloaded operators in the following complete program:



////////////////////////////// code written using Micro. Visual C++ V.4 compiler ///////////////////////////////
#include
#include

class myClass
{
   private:
       int data1;
       int data2;
       int data3;

       static int count; // declares static member but does not define it

   public:
       myClass(int d1 = 0, int d2 = 0, int d3 = 0):
           data1(d1), data2(d2), data3(d3)
           { count++; }

       void setAll(int d1, int d2, int d3);
       int getCount();
       void determineData(int dataIn);
       myClass addData(myClass objIn);
       void displayData();
       bool operator==( const myClass& obj );
       bool operator!=( const myClass& obj );
       myClass operator+( const myClass& obj );
       myClass operator-( const myClass& obj );
       myClass operator*( const myClass& obj );
       myClass& operator=( const myClass& obj );

};

int myClass :: count = 0; // defines static member count outside of class specification


void main()
{
   myClass obj1, obj2(2, 4, 6), obj3, obj4;
   myClass add, sub, mult;
   int data1, data2, data3;

   cout << "There are " << obj2.getCount() << " current objects in the program." << endl << endl;

   cout << "Enter a value for data1: ";
   cin >> data1;
   cout << "Enter a value for data2: ";
   cin >> data2;
   cout << "Enter a value for data3: ";
   cin >> data3;

   obj1.setAll(data1, data2, data3);
   obj4 = obj1.addData(obj2);

   cout << endl << endl;
   cout << "Obj 1" << endl;
   cout << "-----------------------" << endl;
   obj1.displayData();

   cout << "Obj 2" << endl;
   cout << "-----------------------" << endl;
   obj2.displayData();

   cout << "Obj 3" << endl;
   cout << "-----------------------" << endl;
   obj3.displayData();

   cout << "Obj 4" << endl;
   cout << "-----------------------" << endl;
   obj4.displayData();

   // use overloaded arithmetic ops
   add = obj1 + obj2;
   sub = obj4 - obj1;
   mult = obj2 * obj3;

   // display results from arithmetic ops

   cout << endl << endl;
   cout << "Obj 1 + Obj 2" << endl;
   cout << "-----------------------" << endl;
   add.displayData();

   cout << "Obj 4 - Obj 1" << endl;
   cout << "-----------------------" << endl;
   sub.displayData();

   cout << "Obj 2 * Obj 3" << endl;
   cout << "-----------------------" << endl;
   mult.displayData();

   obj1 = obj2;

   // perform relational comparisons
   if (obj1 == obj2)
       cout << "Obj 1 is now equal to Obj 2" << endl;

   if (obj3 != obj4)
       cout << "Obj 3 is not equal to Obj 4" << endl;


   cout << endl << endl;
}// end main()

void myClass :: setAll(int d1, int d2, int d3)
{
   data1 = d1;
   data2 = d2;
   data3 = d3;
}// end myclass :: setAll()


void myClass :: determineData(int dataIn)
{

   if (dataIn < 0)
       cout << "Data is currently negative." << endl;
   else if (dataIn > 0)
       cout << "Data is currently positive." << endl;
   else
       cout << "Data is currently neutral." << endl;
}// end myClass :: determineData()


myClass myClass :: addData(myClass objIn)
{
   myClass temp;
   temp.data1 = data1 + objIn.data1;
   temp.data2 = data2 + objIn.data2;
   temp.data3 = data3 + objIn.data3;
   return temp;
}// end myClass :: addData()


void myClass :: displayData()
{
   cout << "      Data 1 = " << data1 << endl;
   cout << "      Data 2 = " << data2 << endl;
   cout << "      Data 3 = " << data3 << endl << endl;
}// end myClass :: displayData()


bool myClass :: operator==( const myClass& obj )
{
   return ( data1 == obj.data1 && data2 == obj.data2 && data3 == obj.data3 );
}// end myClass :: operator==()


bool myClass :: operator!=( const myClass& obj )
{
   return ( data1 != obj.data1 || data2 != obj.data2 || data3 != obj.data3 );
}// end myClass :: operator!=()


myClass myClass :: operator+( const myClass& obj )
{
   myClass temp;
   temp.data1 = data1 + obj.data1;
   temp.data2 = data2 + obj.data2;
   temp.data3 = data3 + obj.data3;
   return temp;
}// end myClass :: operator+()


myClass myClass :: operator-( const myClass& obj )
{
   myClass temp;
   temp.data1 = data1 - obj.data1;
   temp.data2 = data2 - obj.data2;
   temp.data3 = data3 - obj.data3;
   return temp;
}// end myClass :: operator-()


myClass myClass :: operator*( const myClass& obj )
{
   myClass temp;
   temp.data1 = data1 * obj.data1;
   temp.data2 = data2 * obj.data2;
   temp.data3 = data3 * obj.data3;
   return temp;
}// end myClass :: operator*()


myClass& myClass :: operator=( const myClass& obj )
{
   if (this != &obj)
   {
       data1 = obj.data1;
       data2 = obj.data2;
       data3 = obj.data3;
   }
   return *this;
}// end myClass :: operator=()

int myClass :: getCount()
{
   return (count);
}// end myClass :: getCount()

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


You have reached the end of the C++ Ripped Apart tutorial. Up to this point, the tutorial has covered information I have learned throughout my first two programming classes in college. I should have more articles posted when I complete my third programming class this fall; articles should be posted late December 2003. If you have encountered any problems in the tutorial or have a question, simple contact me at the address provided above. Until then, happy coding...

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Linux is only free if your time has no value” - Jamie Zawinski