Warning: Cannot modify header information - headers already sent by (output started at /home/warebiz/public_html/msw/cpp/wp-content/plugins/lht_adsense.php:104) in /home/warebiz/public_html/msw/cpp/wp-includes/feed-rss2.php on line 8
An Introduction to Programming with C++ http://www.mikeware.us/cpp For beginning C++ programmers Wed, 31 Dec 2008 02:05:58 +0000 http://wordpress.org/?v=2.5.1 en Data Representation http://www.mikeware.us/cpp/?p=63 http://www.mikeware.us/cpp/?p=63#comments Mon, 26 May 2008 17:23:44 +0000 admin http://www.mikeware.us/cpp/?p=63 Understanding how data is represented within a computer requires an individual to learn two things:

  • The units of storage in a computer
  • The different number systems

Units of Storage

From the smallest unit of storage to the greatest:

  1. bit (i.e., 0 or 1)
  2. nibble (4 bits)
  3. byte (8 bits)
  4. word (2 bytes = 16 bits)
  5. longword (4 bytes = 32 bits)
  6. kilobyte (1024 bytes = 2^10)
  7. megabyte (about 1 million bytes = 2^20)
  8. gigabyte (about 1 billion bytes = 2^30)

In a bit, you can store one of 2 (2^1 = 2) possible values: 1 or 0. In a byte, you can store one of 256 (2^8 = 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, and F corresponds with 15.

Converting decimal values to binary values

To convert a value in the decimal system to a value in the binary system, do the following:

Example: 94 (decimal) = ? (binary)

  1. 94 / 2 = 47, which has a remainder of 0
  2. 47 / 2 = 23, which has a remainder of 1
  3. 23 / 2 = 11, which has a remainder of 1
  4. 11 / 2 = 5, which has a remainder of 1
  5. 5 / 2 = 2, which has a remainder of 1
  6. 2 / 2 = 1, which has a remainder of 0
  7. 1 / 2 = 0, which has a remainder of 1

As you perform the divisions, place the remainders in order from right to left:

Answer: 94 (Decimal) = 1011110 (binary)

Converting binary values to decimal values

To convert a number in the binary system to a number in the decimal system, do the following:

Example: 1011110 (binary) = ? (decimal)

Only count those values which are turned on (designated by 1). The least possible exponent is 0.

2^6 + 2^4 + 2^3 + 2^2 + 2^1 = 64 + 16 + 8 + 4 + 2 = 94

Answer: 1011110 (binary) = 94 (decimal)

Converting hexadecimal values to decimal values

To convert a number in the hexadecimal system to a number in the decimal system, do the following:

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

Expand:		8 x 16^2 + 11 x 16^1 + 12 x 16^0

Compute:		8 x 156 + 11 x 16 + 12 = 2236

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

Converting decimal values to hexadecimal values

To convert a number in the decimal system to a number in the hexadecimal system, do the following:

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

As you perform the divisions, place the remainders in order from right to left:

Answer: 110 (decimal) = 6E (hex)

Converting binary values to hexadecimal values

To convert a number in the binary system to a number in the hexadecimal system, do the following:

Example: 1101111100110001100111 (binary) = ? (hex)

First divide the binary digits in pairs of four from right to left:

     11  0111  1100  1100  0110  0111

Now, compute the binary values of the individual groups of digits:

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)

Converting hexadecimal values to binary values

To convert a number in the hexadecimal system to a number in the binary system, you would do the following:

Example: F2C (hex) = ? (binary)

C = 12 = 1100
2 = 0010
F = 15 = 1111

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

Integer Values

A computer stores integer values in two’s complement form. For positive integers, it converts (if needed) the value into binary and performs a capacity check. Negative integers are slightly more difficult to calculate.

Here is the integer 17, in 1-byte two’s complement form:

00010001

We compute 17 by adding the bits that are “turned on” (i.e., 2^0 + 2^4 = 17). Here is the integer -17, in 1-byte two’s complement form:

  1. Convert 17 into binary form
  2. Complement (or “flip”) each bit
  3. Add 1
00010001	// step 1

11101110	// step 2

11101111	// step 3

The following example is more difficult. Let (LSB) denote the least significant byte (or the byte that has the lowest memory address of a computer). 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, the least significant byte must first be determined. In this case, 10110001 is the LSB. The LSB should then be converted into decimal form. 10110001 converted to decimal equals 177. The next step is to evaluate the byte that is left over: 00000110. This byte must be converted to decimal, but since it’s a 2-byte integer, the right most bit in the value 00000110 <--- is in position 256.

Thus, first convert 00000110 into decimal. The result should be 6, but it must be multiplied by 256 since it is the second byte. Then, add the two decimal values together:

6 * 256 = 1536 + 177 = 1713

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.

11101111	// -17

10000000	// -128

10000001	// -127

Real Values (floating-point)

A computer stores a real value, which has a decimal point, by storing 2 integer values:

  • A binary mantissa
  • An exponent

In order to store a mantissa integer value and an exponent integer value, the real value must be converted into scientific notation. Consider a real value 272.8914:

272.8914	// converted into scientific notation equals .2728914 X 10^3

mantissa = 2728014
exponent = 3

Character Data (strings)

A computer stores character data using ASCII (Amercian Standard Code For Information Interchange). In order to store a character string “c++” internally in a computer, the characters ‘c’, ‘+’, and ‘+’ must be stored numerically using ASCII.

There is a difference of 32 between an uppercase ‘A’ and lowercase ‘a’. ASCII is designed this way to make internal processing easier. To get from a capital ‘A’ to a lowercase ‘a’ in binary form, only bit position 32 of the value needs to be flipped (CPU chips can do this very fast).

01000001	// 'A'

01100001	// 'a'

You’re now ready to write your first C++ program.

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=63
Programs http://www.mikeware.us/cpp/?p=62 http://www.mikeware.us/cpp/?p=62#comments Mon, 26 May 2008 17:22:46 +0000 admin http://www.mikeware.us/cpp/?p=62 Note: This article discusses the software life cycle at its most basic level. For a more realistic discussion, buy a good book on software engineering.

Programmers create programs to solve problems. When writing programs, good programmers follow a development process. Almost any software development process consists of five phases:

  • Understanding the problem
  • Designing a solution
  • Writing a program
  • Testing the program
  • Maintaining the program

Understanding the problem

First, the problem must be analyzed in order to form a precise specification. At a minimum, the specification should include the input that is required and the type of output that must be produced. Input refers to the specific data that is consumed by a program. Output refers to the exact answer that must be produced by the program.

Designing a solution

It’s important that the solution is developed before writing any code. When developing the solution, an algorithm (or many of them) should be devised. During this step, the programmer must ensure:

  • The problem is being solved correctly (verification).
  • The correct problem is being solved (validation).

Writing a program

After a solution has been developed, the next step of the process is to write program code. Writing code essentially means implementing an algorithm using some programming language. An appropriate programming language must be selected based on the problem domain. Program code should be well-structured, include adequate documentation, and follow industry standard best practices.

Testing the program

The most basic testing can be done by running the program and manually checking the results. Three types of testing normally take place during this step:

  • Unit testing
  • White box testing
  • Black box testing

Unit testing is normally preformed by programmers as code is written. With unit testing, specific code units are tested in isolation. Unit testing can also be performed by a group of individuals on a regular basis as updates are made to a program.

White box testing refers to testing done by a person who has knowledge of the program’s code.

Black box testing, on the other hand, refers to testing done by someone who has no idea of how the code is written.

Maintaining the program

After code has been thoroughly tested, the fifth and final step is maintaining the program. The programmer maintains it by modifying the code for a variety of reasons:

  • Increase performance
  • Add features
  • Fix problems (flaws, bugs, and security vulnerabilities)
  • Adapt the program to a new environment

That’s an extremely brief introduction to how the most simplistic programs are developed. If you have an engineer’s mindset (and you should!), buy a good book on the topic. In the next article, we’ll cover how data is interpreted by a computer. Read on for more…

Next: Data Representation

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=62
Languages http://www.mikeware.us/cpp/?p=61 http://www.mikeware.us/cpp/?p=61#comments Mon, 26 May 2008 17:21:26 +0000 admin http://www.mikeware.us/cpp/?p=61 Writing a program requires the use of a programming language. Essentially, there are three levels of computer languages: machine, symbolic, and high-level.

Machine Language

Machine language dates back to the 1940’s and is the only language that is directly understood by a computer. Machine language code is actually binary code, which makes it extremely difficult to read and also write. Binary code is composed of streams of 1’s and 0’s. The internal circuit of a computer consists of switches, transistors, 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.

The following is an example of machine language:

1101001
1111011
0000100
0001001

Sybolic Language

Thanks to the work of Grace Hopper in the early 1950’s, symbolic language was developed. Symbolic language, or assembly language, made it possible to use commands such as MOV and ADD instead of having to write binary code for every operation. Symbols represented various machine language instructions, but they still had to be converted to machine language for a computer to understand them. A program called an assembler was developed and used for these purposes.

The following is an example of symbolic language:

MOV AX, firstValue
MOV BX, secondValue
ADD AX, BX
MOV sum, AX

High-Level Language

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, program code is written with symbolic names, but the names actually represent memory addresses in a computer. When a high-level program is executed, a compiler translates the code into binary code so a computer can understand it.

High-level languages make it extremely easier to write code than machine and symbolic language. Similar to symbolic language, high-level languages require a translator, normally called a compiler, to convert code into machine language for the computer to interpret. A program that takes an hour to write in machine language or symbolic language could possibly be written in a couple of minutes using a high-level language.

The following is an example of high-level language (much like C++):

sum = firstValue + secondValue;

Converting Pseudocode to a High-Level (C++) Program

The following program implements the pseudocode from the previous article. Notice that all of the pseudocode is left as program comments to increase readability of the code. It’s not important to understand how this program was written — you’ll learn all about that in later articles. For now, just learn to appreciate how easy reading high-level programming language code is compared to symbolic and machine languages.

View Example Program

Language Paradigms

While the level of a language refers to the level of abstraction at which a program can be written using it, the type of a language refers to the specific domain a language is designed for usage.

There are five main types of languages:

  • imperative (C++)
  • logical (Prolog)
  • functional (Lisp)
  • object-oriented (Java)
  • scripting (XML)

Each type of language can be used to program in a certain way. Imperative languages are good for sequential style programming, logical languages are good for reaching conclusions, functional languages are good for executing recursion, object-oriented languages are good for modeling the real-world, and scripting languages are good for describing content.

Of course, languages are used to write programs.

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=61
Algorithms in Computer Science http://www.mikeware.us/cpp/?p=60 http://www.mikeware.us/cpp/?p=60#comments Mon, 26 May 2008 17:20:06 +0000 admin http://www.mikeware.us/cpp/?p=60 Algorithms are arguably the cornerstone of computer science. Informally, an algorithm is a step-by-step plan for solving a problem in a finite amount of time. Algorithms are designed by thinking about how to solve a problem. Often, the first idea that comes to mind isn’t the best.

There are standard ways to specify algorithms. The following two methods are most effective for me:

  • pseudocode
  • UML

Pseudocode is a verbal description of a solution involving a mixture of English and a programming-like language. If you ever take a tradictional computer science course, pseudocode will become your second language.

UML is a modern way to represent algorithms. With UML, complex models can be depicted in an intuitive manner.

A Pseudocode Example

The following is an algorithm that will, given the name of a student, determine if the student is a senior:

student = prompt the user for the student's identifier

if isValid(student)
      then studentClass = findClassForStudent(student)

if studentClass is senior
      then output = student is a senior
      else output = student is not a senior

// definition of utilities

define isValid(student):
      for every name in listOfStudentNames
            if name is student
                 return true
      return false

define findClassForStudent(student):
      return student's class

While the algorithm shown above solves a problem, it is very abstract. To make it concrete, you would write a program that executes it using some programming language.

This leads into our next topic: Languages

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=60
What is Computer Science (CS)? http://www.mikeware.us/cpp/?p=59 http://www.mikeware.us/cpp/?p=59#comments Mon, 26 May 2008 17:17:56 +0000 admin http://www.mikeware.us/cpp/?p=59 If you want to write programs, it’s important to first learn a few computer science (CS) concepts.

CS is not about:

  • Learning how to use a computer (kids are learning this in kindergarten these days)
  • Learning how to use software
  • Building computer hardware components (this is what electrical engineers do)

CS is about:

  • Devising algorithms that process information (i.e., interpret information)
  • Devising algorithms that transform information (i.e., do something with the information)
  • Developing software tools that execute the above algorithms in an automated manner

In this light, I think of CS as being mostly a combination of math and logic. If you don’t like math or logic, then you probably won’t enjoy writing programs. You probably won’t enjoy reading this tutorial much either.

A Science or Not?

Suprisingly, there’s been a debate since the very existence of computer science about whether or not it is actually a science. If you’re interested, here’s a good article discussing the issue.

Not Just Programming

It’s important to realize that computer science is much more than just programming. Often times, problems that require a programming solution cut across different domains of knowledge or fields, such as the medical field, telecommunications, banking, or the sports arena. Because of this, it’s more challenging for developers to devise a solution to a specific problem than to actually implement (or program) it. Indeed, projects usually fail because a team of programmers did not solve a problem good enough, not because the problem was impossible to solve using technology. Computer scientists should be thought of as problem solvers rather than programmers.

Perhaps the best term to describe the programming aspect of computer science is software engineering. Yet, programming is a small piece of software engineering. In addition to construction (i.e., programming), software engineers deal with software requirements, software design, and software testing — all of which require knowledge, time, and money.

A Builder’s Mindset

When you write a program, you are building something (even though that something is abstract). Because software is abstract, a single problem can have many different software solutions. This is why building software is difficult. It is also why it should be thought about as an engineering discipline — where teams of people figure out how to build high-quality, secure software constrained by resource constraints.

This leads into our next topic: Algorithms

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=59
Using Random Numbers http://www.mikeware.us/cpp/?p=58 http://www.mikeware.us/cpp/?p=58#comments Mon, 26 May 2008 02:30:37 +0000 admin http://www.mikeware.us/cpp/?p=58 Using random numbers in programs is a fairly common practice. Maybe a game program needs to simulate the rolling of dice. Maybe an encryption algorithm needs a random digit to perform an operation. Maybe a login session simply uses a unique random number to identify an active user. There are many other examples.

Please note that there is no way to truly generate a random number using a computer. For this reason, random number generators are commonly called pseudo-random number generators. Advanced courses in encryption will explain why this is so in great detail.

The code to generate random numbers may differ from compiler to compiler. In this tutorial, I assume the use of Dev’s C/C++ V.4 compiler.

How are random numbers generated? Every computer has an internal clock that makes it possible to seed random numbers.

In standard C++, the following statement initializes, or “seeds”, the random number generator:

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

The generator must be initialized before it can generate a random number. The initialization statement only needs to be executed once in the lifetime of a program.

The initialization statement does not generate a random number; it simply gets the generator ready.

t must be declared as:

time_t t;

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

The following header files must be included:

#include    // for srand() and rand()
#include      // for time() and time_t

An alternative method for preparing the random generator is by using the following statement:

srand((unsigned)time(NULL));

Instead of using the time_t t variable, the above code sets the internal values to NULL until a value is attempted to be generated.

After seeding the random generator, random numbers are generated using the rand() function.

To generate a random number in the range 0 to 100, use:

int x = rand( ) % 101;

Suppose random numbers need to be generated in the range -10 to 50.

After initializing the random number generator, use:

var = rand( ) % 61 - 10;

In general, use the following form depending on the range of random numbers that need generated:

rand() % [number_of_integers_in_desired_range] + [lower_bound];

Example:

Write code that will fill the contents of an integer array, which has a MAXSIZE of 50 integers, named intArr with randomly chosen numbers in the range -110 to 110.

NOTE: If not familiar with arrays, [see One-Dimensional Arrays (Section 6)].

srand((unsigned)time(NULL));

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

For an example of how to generate random numbers in a program, consider the following small but complete program:

View Example Program

For a more advanced random number program, consider the following program. It simulates the rolling of dice 50,000 times and records the sum of each throw:

View Example Program

In the next section, we take an in-depth look at user-defined functions. Read on for more…

Next: Introduction to Graphic

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=58
Creating User-Defined Header Files http://www.mikeware.us/cpp/?p=57 http://www.mikeware.us/cpp/?p=57#comments Mon, 26 May 2008 02:29:22 +0000 admin http://www.mikeware.us/cpp/?p=57 Every time a program is written, certain #include statements must be specified at the beginning of the program. The #include statement is actually a link to a header file containing pre-defined elements that need to be used for successful execution of the program.

Header files are created so that the code contained in them can be used in multiple programs without having to write the code every time. It is all pre-defined in a separate header file so all you have to do is “include” it.

Consider the iostream.h header file. Load your compiler and open iostream.h. 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.

What is a user-defined header file?

A user-defined header file is a header file that you, as the programmer, created. iostream.h and all other header files which are included using angle brackets (<>) are pre-defined.

For example, the following header files are pre-defined:

#include 
#include 

When you create a header file, which is described below, and “include” it in a 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"

User-defined header files are commonly used to define constants, user-defined types, and to list function prototypes to be used in a main file.

Header files do not normally include function definitions. Definitions will normally be placed in a separate “auxiliary” source file that will have a (.cpp) extension. Also note that you shouldn’t compile header files individually. The header files will be compiled when you compile the actual program that tries to link to it.

Also, when you compile the actual program, the only elements in the header file that will be compiled are the elements in which the program will need to use.

The following explains how to create a header file using the Dev-C++ V 4.9.9.2 compiler:

First, open a new document and clear it. Next, enter the following information (the elements are described 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 (#) symbol is used, it is associated with the compiler directories. It tells the compiler to do something as in turning on/off some compiler setting.

Any statement preceded with the (#) symbol is not an executable statement. It only functions during compilation. In the above code, NAMEOFHEADER is the name of the header file. This name can be anything you would like it to be, but it should be descriptive and reflect its contents.

The #ifndef command can be read as “if not defined”. #ifndef 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 include necessary definitions.

If the symbol is defined, the header fill will not need to include the 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.

Essentially, these two commands (#ifndef and #define), guard 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 auxiliary 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 auxiliary 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 auxiliary file, which contains the function definitions, both have to include the user-defined header file and also any other compiler directives they use.

As mentioned earlier, 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.

Creating Project Files

In order to successfully link all of your source files (main file and auxiliary file), you will have to create a project and place the files in it. Project files are used to “tie together” multiple source files into a single executable application or program. Creating a project is extremely easy. The following explains how to create a project using the Dev C/C++ V 4.9.9.2 compiler:

  1. if the individual source files already exist, which means you have already created them, click the file menu and select new project
  2. for DOS level programs (probably what you want), at the next prompt select console application and make sure C++ project is selected
  3. at the dialog box, enter a descriptive name for the project, which will be given an extension of .dev
  4. go to the project menu and select add to project
  5. at the open prompt, select all of the source files to be included in the program (NOTE: do not include the header file itself)
  6. go to the options menu and select Compiler Options. Select directories and enter the full path to the user-defined header file that you created in the text box (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)
  7. go to the execute menu and select rebuild all

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. Assuming there are no errors, once the program is built, the executable file should be created (.exe extension with the base project name).

If you have not created any project files, simply choose to create a “New Unit for Project…” instead of “Add unit to project…”.

Note: If all source files are located in the current working directory, then there is no need to create a project file.

The following are two source files and one user-defined header file that can be used as an example of setting up a project. The program is written for Dev C/C++ V 4.9.9.2 compiler.

Here is the header file:

View Example Program

Here is the main source file:

View Example Program

Here is the auxiliary file:

View Example Program

In the next section, we’ll cover object oriented programming and classes. Read on for more…

Next: Generating Random Numbers

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=57
Using Static Variables http://www.mikeware.us/cpp/?p=56 http://www.mikeware.us/cpp/?p=56#comments Mon, 26 May 2008 02:25:54 +0000 admin http://www.mikeware.us/cpp/?p=56 Static variables are used when it is necessary for a function to remember or track the value of a variable from the first call of the function to the next call.

For example, in the following program, the function getAverage() calculates a running average by remembering the sum and the number of values between calls to a function:

float getAverage(float newValue)
{
   static float total = 0;
   static int count = 0;

   count++;
   total += newValue;
   return (total / count);
}

Here is the complete example:

View Example Program

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 only be 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 alive in a program. The class could contain a static data member used to store the tracking value and share it with all instances.

This concept as well as overloaded operators is illustrated in the following complete program that manipulates complex numbers.

The variable named count is used as the static (tracking) variable:

class ComplexClass // specification
{
	private:
		int data1;
		int data2;
		int data3;

		// declares static member but does not define it
		static int count;

	public:
		ComplexClass(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);
		ComplexClass addData(ComplexClass objIn);
		void displayData();
		bool operator==(const ComplexClass& obj);
		bool operator!=(const ComplexClass& obj);
		ComplexClass operator+(const ComplexClass& obj);
		ComplexClass operator-(const ComplexClass& obj);
		ComplexClass operator*(const ComplexClass& obj);
		ComplexClass& operator=(const ComplexClass& obj);
};

View Example Program

Next: User-Defined Header Files

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=56
Virtual Functions http://www.mikeware.us/cpp/?p=55 http://www.mikeware.us/cpp/?p=55#comments Mon, 26 May 2008 02:23:59 +0000 admin http://www.mikeware.us/cpp/?p=55 It is necessary to learn about inheritance before learning the concept of polymorphism. Polymorphism is the idea that a method (or function) in a derived class having the same name as a method in the parent class can act differently or perform different operations in each class.

The easiest way to see this is by an example. We will expand our example in the inheritance article by overloading the output stream << operator and making our previous displayData method virtual.

To refresh our memory, consider our Student class specification:

const int NAMELEN = 41;
const int ADDRLEN = 61;
const int CITYLEN = 51;
const int STATELEN = 41;

class Student
{
	protected:
		char  name[NAMELEN];
		char  streetAddr[ADDRLEN];
		char  city[CITYLEN];
		char  state[STATELEN];
		int   age;

	public:
		Student();
		Student(char*, char*, char*, char*, int);

		void  setName(char inName[]);
		void  setStreet(char inStreet[]);
		void  setCity(char inCity[]);
		void  setState(char inState[]);
		void  setAge(int a);
		char* getName();
		char* getStreet();
		char* getCity();
		char* getState();
		int   getAge();
		void  displayData();
};

To create more natural code, we will overload the output stream << operator so that we can use it to invoke the displayData method.

Let’s begin by adding the following inline definition to the private section of our Student class specification:

friend ostream& operator<<(ostream& ostrm, Student& inStu)
{
	inStu.displayData();
	return ostrm;
}

The concept of overloading the insertion operator << is the same as overloading all other operators. For more information on how to overload other operators, see the operator overloading article.

The only aspect that may be confusing is the keyword friend preceding the function heading. A friend function can access the private members of a class even if it is not a class member. Making this function a friend of the class let’s us easily invoke the displayData method.

Our updated specification should now look like the following:

const int NAMELEN = 41;
const int ADDRLEN = 61;
const int CITYLEN = 51;
const int STATELEN = 41;

class Student
{
	protected:
		char  name[NAMELEN];
		char  streetAddr[ADDRLEN];
		char  city[CITYLEN];
		char  state[STATELEN];
		int   age;

	public:
		Student();
		Student(char*, char*, char*, char*, int);

		void  setName(char inName[]);
		void  setStreet(char inStreet[]);
		void  setCity(char inCity[]);
		void  setState(char inState[]);
		void  setAge(int a);
		char* getName();
		char* getStreet();
		char* getCity();
		char* getState();
		int   getAge();

		void  displayData();
		friend ostream& operator<<(ostream& ostrm,
			Student& inStu)
		{
			inStu.displayData();
			return ostrm;
		}
};

So far, we have not made any methods virtual. To demonstrate the idea of polymorphism, let’s first see why there is need for virtual functions. The following complete program example demonstrates the use of our updated Student class specification:

(Open source code in new window)

We successfully overloaded the output stream operator <<, but the Student class displayData method was invoked during both calls in the main function:

int main()
{
	Student regularStudent("Joe Smoe", "My Street",
		"My City", "My State", 21);
	College collegeStudent;

	// prompt for student data
	collegeStudent.setAllStuData();

	// prompt for class weights and grades
	collegeStudent.getClassInfo();

	// calculate gpa for student
	collegeStudent.calcGpa();

	cout << regularStudent;
	cout << endl;
	cout << collegeStudent;

	cout << endl << endl;
	return EXIT_SUCCESS;
}

Why? Well, it really comes down to when the type of the calling object can be determined. Up to this point, we have been using compile time binding. This means that the type of a calling object is determined at compile time. So, like we saw in our example, no matter which type of object tries to use the overloaded output stream operator <<, the displayData method of the base class will always be invoked because the output stream operator << had its reference at compile time.

With the help of virtual functions, we can take advantage of run-time binding so that the type of an object is determined at run-time. We do this by simply placing the virtual keyword at the beginning of a function’s prototype. Since we want the displayData method of a class invoked when we use the overloaded output stream operator <<, we will make it virtual by making the following modifications to its prototype:

class Student
{
		// omitted for brevity

		virtual void displayData();

		// omitted for brevity
};

The only modification we made was adding the keyword virtual to its prototype. By making it virtual, the displayData method will serve as a default definition for that function. It will be used in any derived class that does not override it with another definition. We are overriding it with another definition so this does not apply to our example.

The following complete program example incorporates our virtual change and solves our initial displayData method problem. Compile, build, and run it to examine the results:

(Open source code in new window)

You should now be able to understand the power of polymorphism. A great exercise would be to try to add a virtual function to the above example that will calculate the GPA (grade point average) of an object of a particular class.

Read on for more…

Next: Static Variables

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=55
Inheritance http://www.mikeware.us/cpp/?p=54 http://www.mikeware.us/cpp/?p=54#comments Mon, 26 May 2008 02:21:13 +0000 admin http://www.mikeware.us/cpp/?p=54 Inheritance is a very important concept of OOP that allows a programmer to create portable classes (classes that can be re-used in different applications). The idea of inheritance is that one class can be used in the building of another class. We can derive a child class from a parent class so that the derived class will “inherit” all (or some) of the data members of the parent class. The child should be a variation of the parent class.

For example, we could have a parent class named Vehicle, and derive a class named Car. In most cases, the child class will inherit the parent class’ data members while also defining other data and operations for its own use.

For an example, consider a Student class. We can define this class to be rather generic for all types of students by defining it as follows:

const int NAMELEN = 41;
const int ADDRLEN = 61;
const int CITYLEN = 51;
const int STATELEN = 41;

class Student
{
	protected:
		char  name[NAMELEN];
		char  streetAddr[ADDRLEN];
		char  city[CITYLEN];
		char  state[STATELEN];
		int   age;

	public:
		Student();
		Student(char*, char*, char*, char*, int);

		void  setName(char inName[]);
		void  setStreet(char inStreet[]);
		void  setCity(char inCity[]);
		void  setState(char inState[]);
		void  setAge(int a);
		char* getName();
		char* getStreet();
		char* getCity();
		char* getState();
		int   getAge();
		void  displayData();
};

Our Student class defines data that applies to all types of students, whether it be elementary students, high school students, or college students. What if we were instructed to develop a college student class? Should we simply use our Student class and add more data and operations to it? We could, but that would steer away from developing portable classes (classes that can be used in building other classes).

Using the idea of inheritance, we could simply define a College class to be a child of the Student class. Then, the College class would inherit all of the Student class data members, and we could add more members to satisfy requirements of the College class.

For an example, assume our College class is defined as follows:

const int INSTITLEN = 61;
const int IDLEN = 6;
const int MAXNUMOFCLASSES = 10;

enum tStuType { FULL_TIME, PART_TIME };
enum tStuStatus { REGISTERED, UNREGISTERED };

class College : public Student
{
	private:
		tStuType stuType;
		tStuStatus stuStatus;
		char  institution[INSTITLEN];
		char  id[IDLEN];
		float curSemesterGpa;
		float cumulativeGpa;
		int   curNumOfHours;
		int   curNumOfClasses;
		char  curGrades[MAXNUMOFCLASSES];
		int   curClassWeights[MAXNUMOFCLASSES];

	public:
		College();
		College(char*, char*, float,
			float, int, tStuType, tStuStatus);

		void  setAllStuData();
		void  setStuType(tStuType t);
		void  setStuStatus(tStuStatus s);
		void  getStuType();
		void  getStuStatus();
		void  getClassInfo();
		int   getClassPoints(char code);
		void  calcGpa();
		void  displayData();
};

Notice the College class has data members that are not defined in the Student class. These members are specifically for the College class. Also, notice the class heading:

class College : public Student

This defines the College class to be derived from the Student class. Thus, the College class inherits all data members of the Student class and possesses other members of its own.

The general form for deriving a class is as follows:

class  ChildClass : childType ParentClass
{
	private: // or protected

	public:
};

The childType specifier is normally public for all practical purposes. A childType of public allows a derived class to access both public and protected members of the parent class.

Also notice that in our Student class, all members are either protected or public. Class members defined as private cannot be accessed by the outside world (except by friend functions).

We must therefore declare our members within the parent class (which would normally be private) as protected. This makes them accessible by derived classes and friend functions but not the outside world. So what about constructors for derived classes? Derived class constructors are handled slightly different from normal classes.

Since derived classes normally have unique data members (only known to it), they will need a constructor of their own to handle default initializations since the parent class only initializes its own data members. This is accomplished by using a base-member initialization list.

The base-member initialization list makes a call to the parent class constructor so its inherited members can be initialized. A derived constructor for our Student and College classes could be defined as the following:

Student :: Student(char* inName, char* inStreetAddr,
	char* inCity, char* inState, int inAge)
{
	strcpy(name, inName);
	strcpy(streetAddr, inStreetAddr);
	strcpy(city, inCity);
	strcpy(state, inState);
	age = inAge;
}

College :: College() : Student("", "", "", "", 0)
{
	strcpy(institution, "");
	strcpy(id, "");
	curSemesterGpa = 0.0;
	cumulativeGpa = 0.0;
	curNumOfHours = 0;
	curNumOfClasses = 0;
	stuType = FULL_TIME;
	stuStatus = UNREGISTERED;
}

Notice the call to the Student class constructor in the heading. To see how the base-member initialization works, consider the following modifications:

College :: College() : Student("a", "b", "c", "d", 0)
{
	strcpy(institution, "");
	strcpy(id, "");
	curSemesterGpa = 0.0;
	cumulativeGpa = 0.0;
	curNumOfHours = 0;
	curNumOfClasses = 0;
	stuType = FULL_TIME;
	stuStatus = UNREGISTERED;
}

When this constructor is called, the members of the College class will be initialized normally. The specified Student constructor will then be called and will make the following assignments to the inherited members:

strcpy(name, "a");
strcpy(streetAddr, "b");
strcpy(city, "c");
strcpy(state, "d");
age = 0;

Now, all of our College class members are initialized, and all of our inherited Student class members have been initialized.

If you are having a hard time understanding how inheritance works, don’t worry. It is fairly straightforward in principle but the syntax can be tricky.

Study the following complete program example demonstrating most of the techniques described in this article before reading further in this section:

(Open source code in new window)

In the next article, we’ll explore the idea of polymorphism. Read on for more…

Next: Virtual Functions

]]>
http://www.mikeware.us/cpp/?feed=rss2&p=54