Object Oriented Programming Using C++
Object Oriented Programming Using C++ Basic Idea About Languages WHAT IS LANGUAGE? You are aware with the term language. It is a system of communication between you and me. Some of the basic natural languages that we are familiar with are English, Hindi, Oriya etc. These are the languages used to communicate among various categories of persons. But how you will communicate with your computer. Your computer will not understand any of these natural languages for transfer of data and instruction. So there are programming languages specially developed so that you could pass your data and instructions to the computer to do specific job. You must have heard names like FORTRAN, BASIC, COBOL etc. These are programming languages. So instructions or programs are written in a particular language based on the type of job. As an example, for scientific application FORTRAN and C languages are used. On the other hand COBOL is used for business applications. Programming Languages There are two major types of programming languages. These are Low Level Languages and High Level Languages. Low Level languages are further divided in to Machine language and Assembly language. Low Level Languages The term low level means closeness to the way in which the machine has been built. Low level languages are machine oriented and require extensive knowledge of computer hardware and its configuration. (a) Machine Language Machine Language is the only language that is directly understood by the computer. It does not needs any translator program. We also call it machine code and it is written as strings of 1's (one) and 0’s (zero). When this sequence of codes is fed to the computer, it recognizes the codes and converts it in to electrical signals needed to run it. For example, a program instruction may look like this: 1011000111101 It is not an easy language for you to learn because of its difficult to understand. It is efficient for the computer but very inefficient for programmers. It is considered to the first generation language. It is also difficult to debug the program written in this language.
1
Object Oriented Programming Using C++ Advantage The only advantage is that program of machine language run very fast because no translation program is required for the CPU. Disadvantages 1. It is very difficult to program in machine language. The programmer has to know details of hardware to write program. 2. The programmer has to remember a lot of codes to write a program which results in program errors. 3. It is difficult to debug the program. (b) Assembly Language It is the first step to improve the programming structure. You should know that computer can handle numbers and letter. Therefore some combination of letters can be used to substitute for number of machine codes. The set of symbols and letters forms the Assembly Language and a translator program is required to translate the Assembly Language to machine language. This translator program is called `Assembler'. It is considered to be a second-generation language. Advantages: 1. The symbolic programming of Assembly Language is easier to understand and saves a lot of time and effort of the programmer. 2. It is easier to correct errors and modify program instructions. 3. Assembly Language has the same efficiency of execution as the machine level language. Because this is one-to-one translator between assembly language program and its corresponding machine language program. Disadvantages: 1. One of the major disadvantages is that assembly language is machine dependent. A program written for one computer might not run in other computers with different hardware configuration. HIGH LEVEL LANGUAGES You know that assembly language and machine level language require deep knowledge of computer hardware where as in higher language you have to know only the instructions in English words and logic of the problem irrespective of the type of computer you are using.
2
Object Oriented Programming Using C++ Higher level languages are simple languages that use English and mathematical symbols like +, -, %, / etc. for its program construction. You should know that any higher level language has to be converted to machine language for the computer to understand. Higher level languages are problem-oriented languages because the instructions are suitable for solving a particular problem. For example COBOL (Common Business Oriented Language) is mostly suitable for business oriented language where there is very little processing and huge output. There are mathematical oriented languages like FORTRAN (Formula Translation) and BASIC (Beginners All-purpose Symbolic Instruction Code) where very large processing is required. Thus a problem oriented language designed in such a way that its instruction may be written more like the language of the problem. For example, businessmen use business term and scientists use scientific terms in their respective languages. Advantages of High Level Languages Higher level languages have a major advantage over machine and assembly languages that higher level languages are easy to learn and use. It is because that they are similar to the languages used by us in our day to day life. COMPILER It is a program translator that translates the instruction of a higher level language to machine language. It is called compiler because it compiles machine language instructions for every program instructions of higher level language. Thus compiler is a program translator like assembler but more sophisticated. It scans the entire program first and then translates it into machine code. The programs written by the programmer in higher level language is called source program. After this program is converted to machine languages by the compiler it is called object program.
Higher Level Language --> (Compile) ---> Program --> Machine Language Program Fig. Compile A compiler can translate only those source programs, which have been written, in that language for which the compiler is meant for. For example FORTRAN compiler will not compile source code written in COBOL language.
3
Object Oriented Programming Using C++ Object program generated by compiler is machine dependent. It means programs compiled for one type of machine will not run in another type. Therefore every type of machine must have its personal compiler for a particular language. Machine independence is achieved by using one higher level language in different machines. INTERPRETER An interpreter is another type of program translator used for translating higher level language into machine language. It takes one statement of higher level languages, translate it into machine language and immediately execute it. Translation and execution are carried out for each statement. It differs from compiler, which translate the entire source program into machine code and does involve in its execution. The advantage of interpreter compared to compiler is its fast response to changes in source program. It eliminates the need for a separate compilation after changes to each program. Interpreters are easy to write and do not require large memory in computer. The disadvantage of interpreter is that it is time consuming method because each time a statement in a program is executed then it is first translated. Thus compiled machine language program runs much faster than an interpreted program. What is C++ C++ is a general-purpose, platform-neutral programming language that supports objectoriented programming and other useful programming paradigms, including procedural programming, object-based programming, generic programming, and functional programming. History • • • • • •
Developed by Dr. Bjarne Stroustrup at AT&T Bell labs in the early 80’s Originally called C with Classes 1985 First external C++ release 1990 first Borland C++ release, The Annotated C++ Reference Manual 1995 Initial draft standard released 1997 Formally approved international C++ standard is accepted – ISO/ANSI C++
Why C++? Popular and relevant (used in nearly every application domain): end-user applications (Word, Excel, PowerPoint, Photoshop, Acrobat, Quicken, games) operating systems (Windows 9x, NT, XP; IBM’s K42; some Apple OS X) large-scale web servers/apps (Amazon, Google) central database control (Israel’s census bureau; Amadeus; Morgan-Stanley financial modeling) communications (Alcatel; Nokia; 800 telephone numbers; major transmission nodes in Germany and France) numerical computation / graphics (Maya) 4
Object Oriented Programming Using C++ device drivers under real-time constraints Stable, compatible, scalable Design goals of C++ Backward compatibility with C (almost completely – every program in K&R is a C++ program) Simplicity, elegance (few built-in data types, e.g., no matrices) Support for user-defined data types (act like built-in types; N.B. Standard Template Library (STL)) No compromise in efficiency, run-time or memory (unless “advanced features” used) Compilation analysis to prevent accidental corruption of data (type-checking and data hiding) Support object-oriented style of programming Some C++ concepts constructor / destructor / copy constructor initialization list inheritance exceptions overloading operators (e.g., assignment operator) namespace const virtual function pure virtual (abstract) function friend template standard template library (STL) pass by value, pass by reference composition versus derivation Major C++ Enhancements 1. C++ supports object-oriented programming features e.g., abstract classes, inheritance, & virtual methods 2. C++ supports data abstraction & encapsulation e.g., the class mechanism & name spaces
5
Object Oriented Programming Using C++ 3. C++ supports generic programming e.g., parameterized types 4. C++ supports sophisticated error handling e.g., exception handling 5. C++ supports identifying an object’s type at runtime e.g., Run-Time Type Identification (RTTI) The C++ Compiler Translates C++ source code into the machine language for the hardware platform output of the compiler are object files (.obj) which are partially ready to run object files must be linked together using the system linker to make an executable file (.exe) .exe files are specific for the hardware platform C++ is portable only at the source level C++ include statements indicate to the compiler preprocessor which files (source) to include in the file to be compiled. Included files are actually copied into the source file and replace the #include statement. C++ Application classes are/can be used by a C++ application; but the application is not itself an instantiated class (like Java) main() is the name of the function where the executable file starts execution C++ classes do not have main() functions C++ objects are initialized via class constructors, C++ applications are initialized in the main() function.
6
Object Oriented Programming Using C++ C++ application development
C++ Source File
compiler
C++ Include Files
Object File
Linker
.EXE File
System Object Files
Advantages of C++ for scientific and engineering problems include: • •
• •
•
•
Availability. C++ can be used on a variety of computers ranging from personal computers to supercomputers. Portability. C++ is in the last stages of standardization, and this process has been closely followed by compiler vendors, ensuring quick availability of a common standard language. Speed. Even with the speed of modern computers there are problems for which efficiency of language is of paramount importance. C++ inherits the speed of C. Generality. Science and engineering now extends beyond computation and into realms of graphics and robotics. The C++ language is not only suited for formulas and computations (FORTRAN's strong suit), it can be used effectively in data base and business applications (COBOL's strong suit). Reusability. Clearly it would be an advantage to use previously written code if it can be inserted into one's current project. The capability of easy re-use and modification of existing code to fit new problems is an important feature of the C++ language. Object Orientation. As computers have become larger and faster, they have been programmed to solve larger and more complex problems. Object Oriented Programming (OOP) is a strategy for simplifying the programming task, and is supported by the language features of C++.
Some Disadvantages Include: •
•
Cryptic Syntax. While having the English-like capability of a high level language, the speed of execution in C++ is inherited from C, and C++ retains some rather cryptic syntax to maintain compatibility. Black box features. Some features, such as input/output operations, are difficult for the novice to get a handle on.
7
Object Oriented Programming Using C++ •
•
A large overhead. C++ was designed for large and very large applications. Learning a language, on the other hand, proceeds by building small programs at first. It sometimes seems that a programming problem is being attacked with a meat cleaver when a judicious scalpel might be more appropriate! Difficult debugging. Debugging C and C++ programs can be very frustrating. Errors in a C++ program may be very difficult to find because the compiler may not catch them until many lines later.
C++ - HelloWorld #include
int main() { cout << “Hello World” << “\n”; } Stream Input/Output Stream Input
Keyboard, Disk file, etc.
Stream Output
Hello, World!
Video display, Disk file, etc.
Hello, World!
Some streams in cout cin cerr Operator << Operator >>
8
Object Oriented Programming Using C++
Identifiers: Identifiers refer to the names of variables, functions, arrays, classes, etc. created by the programmer. They are the functional requirement of any language. Each language has its own rules for naming these Identifiers. The following rules for naming these identifiers are: • • • •
Only alphabetic characters, digits and underscores are permitted. The name cannot start with a digit. Uppercase and lowercase are distinct. A declared keyword cannot be used as variable name.
Keywords: The Keywords implement specific C++ language features. They are explicitly reserved identifiers and cannot be used as names for the program variables or other user-defined program elements. For eg. Break, double, new, switch, operator, this, char etc. Constants: Like variables, constants are data storage locations. But variables can vary; constants, on the other hand and as you might have guessed, do not vary. You must initialize a constant when you create it, and you cannot assign a new value later; after a constant is initialized, its value is, in a word, constant. Literal Constants C++ has two types of constants: literal and symbolic. A literal constant is a value typed directly into your program wherever it is needed. For example: int myAge = 39; myAge is a variable, of type int; 39 is a literal constant. You can't assign a value to 39, and its value can't be changed.
9
Object Oriented Programming Using C++ Symbolic Constants A symbolic constant is a constant that is represented by a name, just as a variable is. Unlike a variable, however, after a constant is initialized, its value can't be changed. If your program has one integer variable named students and another named classes, you could compute how many students you have, given a known number of classes, if you knew there were 15 students per class: students = classes * 15; NOTE : * indicates multiplication. In this example, 15 is a literal constant. Your code would be easier to read and easier to maintain if you substituted a symbolic constant for this value: students = classes * studentsPerClass If you later decided to change the number of students in each class, you could do so where you define the constant studentsPerClass without having to make a change every place you used that value. Defining Constants with #define To define a constant the old-fashioned, evil, politically incorrect way, you would enter: #define studentsPerClass 15 Note that studentsPerClass is of no particular type (int, char, and so on). #define does a simple text substitution. Every time the preprocessor sees the word studentsPerClass, it puts 15 in the text. Because the preprocessor runs before the compiler, your compiler never sees your constant; it sees the number 15. Defining Constants with const Although #define works, there is a better, less fattening, and more tasteful way to define constants in C++: const unsigned short int studentsPerClass = 15; This example also declares a symbolic constant named studentsPerClass, but this time studentsPerClass is typed as an unsigned short int.
10
Object Oriented Programming Using C++ This takes longer to type, but offers several advantages. The biggest difference is that this constant has a type, and the compiler can enforce that it is used according to its type. Characters and Strings There also exist non-numerical constants, like: 'z' 'p' "Hello world" "How do you do?" The first two expressions represent single characters, and the following two represent strings of several characters. Notice that to represent a single character we enclose it between single quotes (') and to express a string of more than one character we enclose them between double quotes ("). When writing both single characters and strings of characters in a constant way, it is necessary to put the quotation marks to distinguish them from possible variable identifiers or reserved words. Notice this: x 'x' x refers to variable x, whereas 'x' refers to the character constant 'x'. Character constants and string constants have certain peculiarities, like the escape codes. These are special characters that cannot be expressed otherwise in the source code of a program, like newline (\n) or tab (\t). All of them are preceded by an inverted slash (\). Here you have a list of such escape codes: \n \r \t \v \b \f \a \' \" \? \\
Newline carriage return Tabulation vertical tabulation Backspace page feed alert (beep) single quotes (') double quotes (") question (?) inverted slash (\)
11
Object Oriented Programming Using C++
For example: '\n' '\t' "Left \t Right" "one\ntwo\nthree" Additionally, you can express any character by its numerical ASCII code by writing an inverted slash bar character (\) followed by the ASCII code expressed as an octal (radix8) or hexadecimal (radix-16) number. In the first case (octal) the number must immediately follow the inverted slash (for example \23 or \40), in the second case (hexadecimal), you must put an x character before the number (for example \x20 or \x4A). [Consult the document ASCII Code for more information about this type of escape code]. Constants of string of characters can be extended by more than a single code line if each code line ends with an inverted slash (\): "string expressed in \ two lines" You can also concatenate several string constants separating them by one or several blank spaces, tabulators, newline or any other valid blank character: "we form" "a single" "string" "of characters"
User Defined Data Types User Defined Data Types: Structures and Classes Struct Type Struct student { char name[20]; int roll_no; float total_marks; }; The Keyword struct declares student as a new data type that can hold three fields of different data type. These fields are known as structure members or elements. The identifier student, which is referred to as structure name or structure tag, can be used to create variables of type student. 12
Object Oriented Programming Using C++
Example: Struct student A; //C declaration Student A; // C++ declaration Note: C++ structures support all the features of structures as defined in ‘C’ , But C++ has expanded its capabilities further to suit its OOP philosophy. In C++, the structure can have both variables and functions as members. Class Type The class type enables us to create sophisticated user defined types. We provide data items for the class type and the operations that can be performed on the data. For example, to create a square class that has a data item for size, and provides draw and resize operations: class square { int size; public: square(); ~square(); void draw(); bool resize(int newSize); }; square s;//c++ declaration Enumeration Type An enumeration type is a user defined type that enables the user to define the range of values for the type. Named constants are used to represent the values of an enumeration, for example: enum weekday {monday, tuesday, wednesday, thursday, friday, saturday, sunday}; weekday currentDay = wednesday; if(currentDay==tuesday){ // do something } The default values assigned to the enumeration constants are zero-based, so in our example above monday == 0, tuesday == 1, and so on.
13
Object Oriented Programming Using C++ The user can assign a different value to one or more of the enumeration constants, and subsequent values that are not assigned a value will be incremented. For example: enum fruit {apple=3, banana=7, orange, kiwi}; Here, orange will have the value 8 and kiwi 9. Derived Data Types Arrays An array is a data structure which allows a collective name to be given to a group of elements which all have the same type. An individual element of an array is identified by its own unique index (or subscript). An array can be thought of as a collection of numbered boxes each containing one data item. The number associated with the box is the index of the item. To access a particular item the index of the box associated with the item is used to access the appropriate box. The index must be an integer and indicates the position of the element in the array. Thus the elements of an array are ordered by the index Declaration of Arrays An array declaration is very similar to a variable declaration. First a type is given for the elements of the array, then an identifier for the array and, within square brackets, the number of elements in the array. The number of elements must be an integer. For example data on the average temperature over the year in Britain for each of the last 100 years could be stored in an array declared as follows: float annual_temp[100]; This declaration will cause the compiler to allocate space for 100 consecutive float variables in memory. The number of elements in an array must be fixed at compile time. It is best to make the array size a constant and then, if required, the program can be changed to handle a different size of array by changing the value of the constant, const int NE = 100; float annual_temp[NE]; then if more records come to light it is easy to amend the program to cope with more values by changing the value of NE. This works because the compiler knows the value of the constant NE at compile time and can allocate an appropriate amount of space for the array. It would not work if an ordinary variable was used for the size in the array declaration since at compile time the compiler would not know a value for it. 14
Object Oriented Programming Using C++ Functions: Functions are segments of code that allow you to better organize your code. You can think of a function as a small program, and of a program as a collection of functions. This is a function for the "Hello World" program: #include void print_hello() {
// This line declares the function cout << "Hello World!\n"; // This is the body, which defines the function
} void main() { print_hello();
// This is how the function is called
}
The print_hello() function The function is void because it does not return any value once it has finished. Pointers: Pointers are variables that hold addresses in C and C++. They provide much power and utility for the programmer to access and manipulate data in ways not seen in some other languages. They are also useful for passing parameters into functions in a manner that allows a function to modify and return values to the calling routine. Introduction As a program is executing all variables are stored in memory, each at its own unique address or location. Typically, a variable and its associated memory address contain data values. For instance, when you declare: int count = 5; The value "5" is stored in memory and can be accessed by using the variable "count". A pointer is a special type of variable that contains a memory address rather than a data value. Just as data is modified when a normal variable is used, the value of the address
15
Object Oriented Programming Using C++ stored in a pointer is modified as a pointer variable is manipulated. Usually, the address stored in the pointer is the address of some other variable. int *ptr; ptr = &count // Stores the address of count in ptr // The unary operator & returns the address of a variable To get the value that is stored at the memory location in the pointer it is necessary to dereference the pointer. Dereferencing is done with the unary operator "*". int total; total = *ptr; // The value in the address stored in ptr is assigned to total Declaration and Initialization Declaring and initializing pointers is fairly easy. int main() { int j; int k; int l; int *pt1; // Declares an integer pointer int *pt2; // Declares an integer pointer/ float values[100]; float results[100]; float *pt3; // Declares a float pointer float *pt4; // Declares a float pointer j = 1; k = 2; pt1 = &j; // pt1 contains the address of the variable j pt2 = &k; // pt2 contains the address of variable k pt3 = values; // pt3 contains the address of the first element of values pt3 = &values[0]; // This is the equivalent of the above statement return 0; }
16
Object Oriented Programming Using C++
What Is a Variable? In C++ a variable is a place to store information. A variable is a location in your computer's memory in which you can store a value and from which you can later retrieve that value. Notice that this is temporary storage. When you turn the computer off, these variables are lost. Permanent storage is a different matter. Typically, variables are permanently stored either to a database or to a file on disk. Storing to a file on disk is discussed on Day 16, "Advanced Inheritance." Declaration of Variables: Say you want to store data for temporary use in a program. You will have to declare variables to input the data. Luckily, declaring variables in C++ is very easy. This simple program gets the user's age as input and displays it back to the screen. Here is the source code: #include using namespace std; int main() { int age; cout << "My Second C++ Program" <<' endl; cout << "Please enter your age in years followed by : "; cin >> age; cout << "\nYou are " << age << " years old." << endl; return 0; } To declare the variable "age," simply type the data type (int) in front of it. Notice that you can specify the type of data you want the function main to return. This next program uses a few more variables. It retrieves the user's age, as well as their height, weight, hair color, and eye color.
17
Object Oriented Programming Using C++ #include #include int main() { int age, feet, inches, pounds; string hair, eye; cout << "My third c++ program.\n\n"; cout << "Please type your answer to each question" << "followed by ." << endl; cout << "What is your age (years)? "; cin >> age; cout << "\nHow tall are you (ft in)? "; cin >> feet >> inches; cout << "\nHow much do you weigh (lbs)? "; cin >> pounds; cout << "\nWhat color is your hair? "; cin >> hair; cout << "\nWhat color are your eyes? "; cin >> eye; cout << endl << "\nYou are " << age << " years old, " << feet << " feet " << inches << " inches tall." weigh " << pounds << " pounds, << " have " << hair << " colored hair and " << eye << " colored eyes." << endl; return 0;
18
<< "\nYou
Object Oriented Programming Using C++ } Notice that it's possible to declare different variables of the same type in one line. Simply separate them by a comma. The previous code also includes a file called string and uses strings to store characters. Unlike C, declarations in C++ are statements. As declarations are statements in C+ +, variables may be declared anywhere within the program. In the following example, the scope of the variable counter is valid for the duration of the compound statement, initialized by the counted loop. for (int counter=0; counter<10; counter++) { cout << counter << endl; } If there is already a declaration of counter, earlier in the function, then it will not have scope until the compound statement has been completed, whereby it will retain its original value before the counted loop was executed. Preprocessor Directives Preprocessor directives are orders that we include within the code of our programs that are not instructions for the program itself but for the preprocessor. The preprocessor is executed automatically by the compiler when we compile a program in C++ and is in charge of making the first verifications and digestions of the program's code. All these directives must be specified in a single line of code and they do not have to include an ending semicolon;. #define At the beginning of this tutorial we have already spoken about a preprocessor directive: #define, that serves to generate what we called defined constantants or macros and whose form is the following: #define name value Its function is to define a macro called name that whenever it is found in some point of the code is replaced by value. For example:
19
Object Oriented Programming Using C++ #define MAX_WIDTH 100 char str1[MAX_WIDTH]; char str2[MAX_WIDTH]; It defines two strings to store up to 100 characters. #define can also be used to generate macro functions: #define getmax(a,b) a>b?a:b int x=5, y; y = getmax(x,2); after the execution of this code y would contain 5. #undef #undef fulfills the inverse functionality of #define. It eliminates from the list of defined constants the one that has the name passed as a parameter to #undef: #define MAX_WIDTH 100 char str1[MAX_WIDTH]; #undef MAX_WIDTH #define MAX_WIDTH 200 char str2[MAX_WIDTH]; #ifdef, #ifndef, #if, #endif, #else and #elif These directives allow to discard part of the code of a program if a certain condition is not fulfilled. #ifdef allows that a section of a program is compiled only if the defined constant that is specified as the parameter has been defined, independently of its value. Its operation is: #ifdef name // code here #endif For example: #ifdef MAX_WIDTH char str[MAX_WIDTH]; #endif In this case, the line char str[MAX_WIDTH]; is only considered by the compiler if the defined constant MAX_WIDTH has been previously defined, independently of its value. If it has not been defined, that line will not be included in the program.
20
Object Oriented Programming Using C++ #ifndef serves for the opposite: the code between the #ifndef directive and the #endif directive is only compiled if the constant name that is specified has not been defined previously. For example: #ifndef MAX_WIDTH #define MAX_WIDTH 100 #endif char str[MAX_WIDTH]; In this case, if when arriving at this piece of code the defined constant MAX_WIDTH has not yet been defined it would be defined with a value of 100. If it already existed it would maintain the value that it had (because the #define statement won't be executed). The #if, #else and #elif (elif = else if) directives serve so that the portion of code that follows is compiled only if the specified condition is met. The condition can only serve to evaluate constant expressions. For example: #if MAX_WIDTH>200 #undef MAX_WIDTH #define MAX_WIDTH 200 #elsif MAX_WIDTH<50 #undef MAX_WIDTH #define MAX_WIDTH 50 #else #undef MAX_WIDTH #define MAX_WIDTH 100 #endif char str[MAX_WIDTH]; Notice how the structure of chained directives #if, #elsif and #else finishes with #endif. #line When we compile a program and errors happen during the compiling process, the compiler shows the error that happened preceded by the name of the file and the line within the file where it has taken place. The #line directive allows us to control both things, the line numbers within the code files as well as the file name that we want to appear when an error takes place. Its form is the following one: #line number "filename"
21
Object Oriented Programming Using C++ Where number is the new line number that will be assigned to the next code line. The line number of successive lines will be increased one by one from this. filename is an optional parameter that serves to replace the file name that will be shown in case of error from this directive until another one changes it again or the end of the file is reached. For example: #line 1 "assigning variable" int a?; This code will generate an error that will be shown as error in file "assigning variable", line 1. #error This directive aborts the compilation process when it is found returning the error that is specified as the parameter: #ifndef __cplusplus #error A C++ compiler is required #endif This example aborts the compilation process if the defined constant __cplusplus is not defined. #include This directive has also been used assiduously in other sections of this tutorial. When the preprocessor finds an #include directive it replaces it by the whole content of the specified file. There are two ways to specify a file to be included: #include "file" #include The only difference between both expressions is the directories in which the compiler is going to look for the file. In the first case where the file is specified between quotes, the file is looked for in the same directory that includes the file containing the directive. In case that it is not there, the compiler looks for the file in the default directories where it is configured to look for the standard header files. If the file name is enclosed between angle-brackets <> the file is looked for directly where the compiler is configured to look for the standard header files.
22
Object Oriented Programming Using C++ #pragma This directive is used to specify diverse options to the compiler. These options are specific for the platform and the compiler you use. Consult the manual or the reference of your compiler for more information on the possible parameters that you can define with #pragma. Comments Comments are pieces of source code discarded from the code by the compiler. They do nothing. Their purpose is only to allow the programmer to insert notes or descriptions embedded within the source code. C++ supports two ways to insert comments: // line comment /* block comment */ The first of them, the line comment, discards everything from where the pair of slash signs (//) is found up to the end of that same line. The second one, the block comment, discards everything between the /* characters and the next appearance of the */ characters, with the possibility of including several lines. We are going to add comments to our second program: /* my second program in C++ with more comments */ #include int main () { cout << "Hello World! ";
// says Hello World!
cout << "I'm a C++ program"; // says I'm a C++ program return 0; }
23
Object Oriented Programming Using C++ Hello World! I'm a C++ program If you include comments within the source code of your programs without using the comment characters combinations //, /* or */, the compiler will take them as if they were C++ instructions and, most likely causing one or several error messages. C++ Operators Operators Introduced to C++ The following operators are introduced in C++. New Operators Operator Description New
Used to allocate memory
delete
Used to de-allocate memory
::
Scope resolution operator, used to bind a method to a class
->*
Class member pointer operator
.*
Class member pointer operator
C++ Operator Examples The following shows how the new operators are used. // Allocate memory for five integers iPtr = new int[5]; // Deallocate the memory delete [] iPtr; // Define a display function for the class, Rectangle void Rectangle::display() // Output a pointer to a data member cout << x.*ptrToMember; // Output a pointer to a data member of a class pointer
24
Object Oriented Programming Using C++ cout << xPtr->*ptrToMember; C++ Arithmetic Operators The following table shows the basic C++ arithmetic operators. Arithmetic Operators Operator Description ~
1's compliment negation. This operator is overloaded in C++ as a destructor.
+
Unary plus or addition.
-
Unary minus or subtraction.
*
Multiplication, or indirection.
/
Division.
%
Modulus (remainder of a division).
++
Increment (pre-increment if placed before the variable, post-increment if placed after).
--
Decrement (pre-decrement if placed before the variable, post-decrement if placed after).
Arithmetic Operator Examples The following illustrates how to use the arithmeic operators. // Negate the value of a result = ~a; // Caluclate the sum of a and b total = a + b; // Calculate the difference between a and b difference = a - b; // Calculate the product of a and b
25
Object Oriented Programming Using C++
product = a * b; // Calculate the quotient of a and b quotient = a / b; // Calculate the remainder when a is divided by b remainder = a % b; // Increment the value of a a++; // Decrement the value of b b--; // The pre-inrecrement version num = ++a; // The pre-decrement version num = --b; Self Assignment If an assignment is made that requires the value of the variable being assigned to, C++ offers a convenient shortcut by placing the operator immediately before the assignment operator. total += a;
// Equivalent to total = total + a;
difference -= a; // Equivalent to difference = difference - a; product *= a;
// Equivalent to product = product * a;
quotient /= a; // Equivalent to quotient = quotient / a; remainder %= a; // Equivalent to remainder = remainder % a; C++ Relational Operators
26
Object Oriented Programming Using C++ The following table shows the basic C++ relational operators. Relational Operators Operator Description <
Less than (eg. x < 10).
<=
Less than or equal to (eg. x <= 10).
==
Equivalence (eg. x == 10).
>
Greater than (eg. x > 10).
>=
Greater than or equal to (eg. x >= 10).
!=
Not equivalent (eg. x != 10).
C++ Logical Operators The following table shows the basic C++ logical operators. Logical Operators Operator Description !
Logical Not. For example, (!x) will evaluate to True if x is zero.
&&
Logical And. For example, (x > 4 && y <= 10) will evaluate to true if x is greater than four and y is less than or equal to ten.
||
Logical Or. For example, (x > 10 || y > 10) will evaluate to true if either x or y are greater than ten
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 carefully. Definition implicit conversion - conversion that happens without the programmer specifically asking for it
27
Object Oriented Programming Using C++ 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 as it was declared int, so it 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:
28
Object Oriented Programming Using C++ 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; Control structures Although C++ is an object-oriented programming language, it is really a hybrid language as it still supports structured programming techniques. With an objectoriented approach, instead of breaking the problem domain up in terms of functionality, you identify the objects in terms of data and behavior. This section concentrates on the structured capabilities of C++. Structured programming is based around three basic constructs: Sequence, Selection and Iteration. The Sequence construct is the simplest, whereby statements are executed in the order they're found in the code. Both the Selection and Iteration constructs require a Logical Test. Logical tests can be performed on variables, literal values, calculations, and results of functions The test will evaluate to either true or false. Selection Constructs The Selection constructs are used when the flow of execution may flow down two or more paths. The if Statement The if statement is used to control the flow of execution down one of two or more paths, depending on the result of a logical test. Further conditions may be specified with else if, should previous conditions be false, and else may be used should none of the above be true. There is no semicolon at the end of if statement. if (grade < 40) cout << "Failed" << endl;
29
Object Oriented Programming Using C++
else if (grade < 75) cout << "Passed" << endl; else cout << "Excellent Pass" << endl; If statements can be nested, and the combined with logical operators. If more than one line should be executed should a condition be true, the lines should be enclosed within curly braces. if (x == 10) { cout << "That's an excellent result" << endl; cout << "You should be extremely proud of yourself!" << endl; } Note: A common mistake is to use the assignment operator instead of the equality operator when checking for equivalence. The following statement will always evaluate to True as x is assigned the value 10 which is a non-zero number. if (x = 10) The switch Statement When there are many conditions, the switch statement can be used if the values are discrete. The switch statement is easier to read than multiple if statements when there are a large number of discrete values. Iteration Constructs The Iteration constructs are used when a block of code is required to be executed continually until a condition is met. Pre-Condition Loops
30
Object Oriented Programming Using C++ Pre-condition loops allow for a condition to be tested at the start of the loop. This type of loop is used when you only want to execute the block of code should the condition be true. If there is more than one line to be included in the iteration, it should be contained within curly braces. A semicolon is not added to the end of the while statement. If one is added in error, then the block of code will be executed exactly once. The following example uses a pre-condition loop to calculate the factorial of a number (a number that is multiplied by every integer number below itself down to 1). factorial.cpp #include int main() { int num; long int factorial=1; cout << "Enter a number: "; cin >> num; cout << "The factorial of " << num << " is: "; // Use a pre-condition loop to calculate the factorial while (num > 1) factorial *= num--;
cout << factorial << endl; return 0; } Post-Condition Loops
31
Object Oriented Programming Using C++ Post-condition loops allow for a condition to be tested at the end of the loop. This type of loop is used when you want to execute the block of code at least once and is often used for validation. If there is more than one line to be included in the iteration, it should be contained within curly braces. The following example uses a post-condition loop to ensure that a number is between 1 and 10. validate.cpp #include int main() { int num; // Use a post-condition loop to validate the number entered do { cout << "Enter a number between 1 and 10: "; cin >> num; } while (num < 1 || num > 10); cout << num << " is between 1 and 10\n"; return 0; } Counted Loops Counted loops are used when you know how many times you want to execute a statement or a list of statements. A semicolon is not added to the end of the for statement. If one is added in error, then the block of code will be executed exactly once. The following is the general syntax of a counted loop. for (initialisation_section; condition; increment_section) { 32
Object Oriented Programming Using C++ // List of statements } The parameters that the counted loop operates on are in three sections separated by semicolons. The first section is for initialising any variables, the second section has the condition, and the third section has any increments required to implement the loop. If more than one variable is to be used in either the initialisation_section or the increment_section, then they are separated by commas. As declarations are statements in C++, control variables may be declared in the initialisation section of the counted loop. The condition must evaluate to either true or false. Compounded conditions must be created by using the logical operators. The following counted loop prints the prime numbers (a number divisible only by itself and 1) between 1 and a number enetered by the user. prime.cpp #include int main() { int num; cout << "Enter a number: "; cin >> num;
for (int counter = 1; counter < num; counter++) { int prime, test; test = counter; prime = 1; while (test-- > 2)
33
Object Oriented Programming Using C++
if ((counter % test) == 0) prime = 0; if (prime == 1) cout << counter << " is a prime number\n"; } return 0; } Structures Structures are a way of storing many different variables of different types under the same name. This makes it a more modular program, which is easier to modify because its design makes things more compact. It is also useful for databases. The format for declaring a structure(in C++, it is different in C) is struct NAME { VARIABLES; }; Where NAME is the name of the entire type of structure. To actually create a single structure the syntax is NAME name_of_single_structure; To access a variable of the structure it goes name_of_single_structure.name_of_variable; For example: struct example { int x; }; example an_example; //Treating it like a normal variable type
34
Object Oriented Programming Using C++ an_example.x=33;
//How to access it
Here is an example program: struct database { int id_number; int age; float salary; }; int main() { database employee; //There is now an employee variable that has modifiable //variables inside it. employee.age=22; employee.id_number=1; employee.salary=12000.21; return 0; } The struct database declares that database has three variables in it, age, id_number, and salary. You can use database like a variable type like int. You can create an employee with the database type as I did above. Then, to modify it you call everything with the 'employee.' in front of it. You can also return structures from functions by defining their return type as a structure type. Example: struct database fn(); I suppose I should explain unions a little bit. They are like structures except that all the variables share the same memory. When a union is declared the compiler allocates enough memory for the largest data-type in the union. It’s like a giant storage chest where you can store one large item, or a bunch of small items, but never the both at the same
35
Object Oriented Programming Using C++ time. The '.' operator is used to access different variables inside a union also. As a final note, if you wish to have a pointer to a structure, to actually access the information stored inside the structure that is pointed to, you use the -> operator in place of the . operator. A quick example: #include struct xampl { int x; }; int main() { xampl structure; xampl *ptr; structure.x=12; ptr=&structure; //Yes, you need the & when dealing with structures //and using pointers to them cout<x; //The -> acts somewhat like the * when used with pointers //It says, get whatever is at that memory address //Not "get what that memory address is" return 0; } Union A union is a user-defined data or class type that, at any given time, contains only one object from its list of members (although that object can be an array or a class type). The
36
Object Oriented Programming Using C++ member-list of a union represents the kinds of data the union can contain. A union requires enough storage to hold the largest member in its member-list. Declaring a Union Begin the declaration of a union with the union keyword, and enclose the member list in curly braces: union DATATYPE // Declare union type { char ch; int
i;
long l; float f; double d; } var1;
// Optional declaration of union variable
Using a Union A C++ union is a limited form of the class type. It can contain access specifiers (public, protected, private), member data, and member functions, including constructors and destructors. It cannot contain virtual functions or static data members. It cannot be used as a base class, nor can it have base classes. Default access of members in a union is public. A C union type can contain only data members. In C, you must use the union keyword to declare a union variable. In C++, the union keyword is unnecessary: union DATATYPE var2; // C declaration of a union variable DATATYPE var3;
// C++ declaration of a union variable
A variable of a union type can hold one value of any type declared in the union. Use the member-selection operator (.) to access a member of a union: var1.i = 6; var2.d = 5.327;
// Use variable as integer // Use variable as double
37
Object Oriented Programming Using C++ You can declare and initialize a union in the same statement by assigning an expression enclosed in curly braces. The expression is evaluated and assigned to the first field of the union. For example: #include
union NumericType { int
iValue;
long
lValue;
double
dValue;
}; void main() { union NumericType Values = { 10 }; // iValue = 10 printf("\n%d", Values.iValue); Values.dValue = 3.1416; printf("\n%f", Values.dValue); } The NumericType union is arranged in memory (conceptually) as shown in Figure 8.1.
Figure: Storage of Data in Numeric Type Union
38
Object Oriented Programming Using C++ Enumerations (enum) Enumerations serve to create data types to contain something different that is not limited to either numerical or character constants nor to the constants true and false. Its form is the following: enum model_name { value1, value2, value3, . . } object_name; For example, we could create a new type of variable called color to store colors with the following declaration: enum colors_t {black, blue, green, cyan, red, purple, yellow, white}; Notice that we do not include any fundamental data type in the declaration. To say it another way, we have created a new data type without it being based on any existing one: the type color_t, whose possible values are the colors that we have enclosed within curly brackets {}. For example, once declared the colors_t enumeration in the following expressions will be valid: colors_t mycolor; mycolor = blue; if (mycolor == green) mycolor = red; In fact our enumerated data type is compiled as an integer and its possible values are any type of integer constant specified. If it is not specified, the integer value equivalent to the first possible value is 0 and the following ones follow a +1 progression. Thus, in our data type colors_t that we defined before, black would be equivalent to 0, blue would be equivalent to 1, green to 2 and so on. If we explicitly specify an integer value for some of the possible values of our enumerated type (for example the first one) the following values will be the increases of this, for example: enum months_t { january=1, february, march, april,
39
Object Oriented Programming Using C++ may, june, july, august, september, october, november, december} y2k; in this case, variable y2k of the enumerated type months_t can contain any of the 12 possible values that go from january to december and that are equivalent to values between 1 and 12, not between 0 and 11 since we have made january equal to 1. Functions in C++ Functions allow the programmer to modularise the program. All variables declared within functions are called local variables, in that they are known only in the function to which they have been defined. Some functions have a parameter list that provides a communication method between the function, and the module that called the function. The parameters are also local variables, in that they are not available outside of the function. The programs covered so far all have main, which is a function. The following example uses a function called square which writes the square (a number multiplied by itself) of the numbers between 1 and 10. factorial.cpp #include #include // Function prototype int square(int x); int main() { for (int counter=1; counter<=10; counter++) cout << "Square of " << setw(4) << counter << " is " << setw(4) << square(counter) << endl; return 0; }
40
Object Oriented Programming Using C++
// Define the function 'square' int square(int x) { return x * x; } The function prototype, "square", declares a function that takes an integer parameter and returns an integer. When the compiler reaches the function call to square in the main program, it is able to check the function call against the function's definition. When the program reaches the line that calls the function square, the program jumps to the function and executes that function before resuming its path through the main program. Programs that do not have a return type should be declared using void. The Factorial program is an example of a recursive function, a function that calls itself. Pass by Value Parameters to a function may either be pass by value, or pass by reference. If parameters are passed by value, only a copy of the variable has been passed to the function. Any changes to the value will not be reflected back to the calling function. The above function uses pass by value, as the square function doesn't need to alter the value of the parameter passed to it. The program, Number To Words, is another example of pass by value functions. Default Arguments C++ Allows you to specify default values for the arguments of functions. The default value is specified in the function prototype and not the function definition, unless the function definition is also the declaration. It is not necessary to assign a default value for every argument, but from the point the first default value is defined, default values will be required for the remaining arguments of the function. The following are valid function prototypes for functions with three arguments. // Three default arguments
41
Object Oriented Programming Using C++
void func1(int first=0, int second=0, int third=0); // Two default arguments void func2(int first, int second=0, int third=0); // One default argument void func3(int first, int second, int third=0); // No default arguments void func4(int first, int second, int third); If no default argument is provided, then the argument must be provided when the function is called. Default values are specified from left to right, as shown in the following example. default.cpp #include void display(int first=0, int second=0, int third=0); int main() { display(); display(23); display(12, 50);
// Displays 0 0 0 // Displays 23 0 0 // Displays 12 50 0
display(20, 1, 1964); // Displays 20 1 1964 return 0; } void display(int first, int second, int third) {
42
Object Oriented Programming Using C++
cout << first << " " << second << " " << third << endl; } Pass by Reference A function that uses call-by-reference arguments reflects any changes to the values of the arguments to the calling function. In traditional C, this is achieved by passing the address of the variable, and using pointers to de-reference the arguments. C++ introduces a reference operator (&), that allows true call-byreference functions, eliminating the need to dereference arguments passed to the function. The reference operator is placed before the variable name of arguments in the parameter list of the function. The function can then be called without passing the address, and without dereferencing within the function. The following example contains a function to swap two values. As the values require changing in the calling function, the values are passed by reference. swap.cpp #include // The arguments x, and y are passed by reference // Therefore changes made to these variables will // be reflected to where they were called. void swap(int &x, int &y); int main() { int x=6, y=9; cout << "Before the swap:\n"; cout << "X: " << x << endl; 43
Object Oriented Programming Using C++
cout << "Y: " << y << endl; swap(x, y); cout << "\nAfter the swap:\n"; cout << "X: " << x << endl; cout << "Y: " << y << endl; return 0; } void swap(int &x, int &y) { int temp = x; x = y; y = temp; } Function Overloading C++ allows both functions and operators to be overloaded. An overloaded function, is a function with the same name as another function, but with different parameter types. This promotes programming flexibility. Overloaded functions allow the following Use the same function name • Carry out operations using different data types • The programmer using the function need not be concerned with how the function has been implemented. •
For example, suppose we were required to find the product of two numbers, either of which may be of type int or double. All we would have to do is write four functions, each with the same name and defined to ensure that we get the correct
44
Object Oriented Programming Using C++ result, regardless of the types of the arguments we use in the function call. The appropriate function is selected depending on the data types of the parameters. The following shows you the function prototypes. int product(int x, int y); double product(int x, double y); double product(double x, int y); double product(double x, double y); The compiler will then choose the appropriate function. From a readability point of view, function overloading should only be used when the functionality is the same. Overloaded functions must adhere to the following two rules: 1. The compiler does not use the return type of the function to distinguish between function instances. 2. The argument list of each of the function instances must be different. The following example overloads a swap function for integers, and floating point numbers. overload.cpp #include // Overload the swap function for int and float void swap(int &a, int &b); void swap(float &a, float &b); int main() { int i1 = 3, i2 = 5; float f1 = 3.14159f, f2 = 1.23f;
45
Object Oriented Programming Using C++
cout << "Integers before swap:" << endl; cout << i1 << ", " << i2 << endl; swap(i1, i2); cout << "Integers after swap:" << endl; cout << i1 << ", " << i2 << endl; cout << "Floating points before swap:" << endl; cout << f1 << ", " << f2 << endl; swap(f1, f2); cout << "Floating points after swap:" << endl; cout << f1 << ", " << f2 << endl; return 0; } // An integer swap function void swap(int &a, int &b) { int temp = a; a = b; b = temp; } // An overloaded float swap function void swap(float &a, float &b) {
46
Object Oriented Programming Using C++
float temp = a; a = b; b = temp; } Inline functions An inline function is a request to the compiler to optimise the function call by substituting the function called at the point where the function call is made. If the request is successful, the overhead associated with a normal function call is eliminated. To make an inline function, the keyword, "inline" precedes the function prototype and function definition. As it's just a request, there is no guarantee that the function will be called inline. Functions containing the following would not be suitable for an inline function: • • • • •
Static Variables Iteration Constructs A switch Statement Arrays Recursive calls to istelf
The following example uses an inline function to determine if a given year is a leap year. If the request is successful, the function definition will be expanded in the main part of the program, eliminating the need for a function call. leap.cpp #include // Prototype declaration inline int leap(int year); int main() {
47
Object Oriented Programming Using C++ int year; cout << "Enter a year: "; cin >> year; if (leap(year)) cout << "The year " << year << " is a leap year" << endl; else cout << "The year " << year << " is not a leap year" << endl; return 0; } // Definition of leap inline int leap(int year) { if (((year % 4) == 0) && (((year % 100) != 0) || ((year%400) == 0))) return 1; return 0; } An array is a set of variables, represented by a single name. The individual variables are called elements and are identified by index numbers. The following example declares an array with ten elements. int x[10]; The first element in the array has an index number of zero. Therefore, the above array has ten elements, indexed from zero to nine. Accessing the Elements
48
Object Oriented Programming Using C++ To access an individual element in the array, the index number follows the variable name in square brackets. The variable can then be treated like any other variable in C++. The following example assigns a value to the first element in the array. x[0] = 16; The following example reads a value from the keyboard into the last element of an The following example prints the value of the third element in an array. cout << x[2] << endl; array with ten elements. cin >> x[9]; Initialising Array Elements Arrays can be initialised like any other variables by assignment. As an array contains more than one value, the individual values are placed in curly braces, and seperated with commas. The following example initialeses a ten dimensional array with the first ten values of the three times table. int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30}; This saves assigning the values individually as in the following example. int x[10]; x[0] = 3; x[1] = 6; x[2] = 9; x[3] = 12; x[4] = 15; x[5] = 18; x[6] = 21;
49
Object Oriented Programming Using C++
x[7] = 24; x[8] = 27; x[9] = 30; Looping through an Array As the array is indexed sequentially, we can use the for loop to display all the values of an array. The following example displays all the values of an array. It then sorts the array into ascending order, and displays the sorted array. sort.cpp #include #include int main() { int x[10] = {1, 37, 3, 7, 4, 9, 2, 33, 19, 5}; int counter; cout << "Before Sorting:\n\n"; cout << "Element" << setw(10) << "Content" << endl; // Display the contents of the initial array for (counter=0; counter<10; counter++) cout << setw(7) << counter << setw(10) << x[counter] << endl; // Sort the array for (int outer=0; outer<10; outer++) for (int inner=outer+1; inner<10; inner++) if (x[inner] < x[outer])
50
Object Oriented Programming Using C++ { // Swap the values int temp = x[inner]; x[inner] = x[outer]; x[outer] = temp; } cout << "After Sorting:\n\n"; cout << "Element" << setw(10) << "Content" << endl; // Display the contents of the sorted array for (counter=0; counter<10; counter++) cout << setw(7) << counter << setw(10) << x[counter] << endl; return 0; } Multidimensional Arrays An array can have more than one dimension. By allowing the array to have more than one dimension provides greater flexibility. For example, spreadsheets are built on a two dimensional array; an array for the rows, and an array for the columns. The following example uses a two dimensional array with two rows, each containing five columns.
multi.cpp #include #include int main()
51
Object Oriented Programming Using C++ { // Declare a 2 x 5 multidimensional array int x[2][5] = { {1, 2, 3, 4, 5}, {2, 4, 6, 8, 10} }; // Display the rows for (int row=0; row<2; row++) { // Display the columns for (int column=0; column<5; column++) cout << setw(6) << x[row][column]; cout << endl; } return 0; } We have already seen how variables are memory cells that we can access by an identifier. But these variables are stored in concrete places of the computer memory. For our programs, the computer memory is only a succession of 1 byte cells (the minimum size for a datum), each one with a unique address. A good simile for the computer memory can be a street in a city. On a street all houses are consecutively numbered with an unique identifier so if we talk about 27th of Sesame Street we will be able to find that place without trouble, since there must be only one house with that number and, in addition, we know that the house will be between houses 26 and 28. In the same way in which houses in a street are numbered, the operating system organizes the memory with unique and consecutive numbers, so if we talk about location 1776 in the memory, we know that there is only one location with that address and also that is between addresses 1775 and 1777. POINTERS
52
Object Oriented Programming Using C++
A pointer is a variable that represents the location (rather than the value) of a data item, such as a variable or an array element. Suppose v is a variable that represents some particular data item. The compiler will automatically assign memory cells for this data item. The data item can then be accessed if we know the location (i.e. the address) of the first memory cell. The address v’s memory location can be determined by the expression &v, where & is a unary operator, called the address operator, that evaluates the address of its operand. pv = &v This new variable pv is called pointer to v, since it “point” to the location where v is stored in memory. As pv represents v’s address not its value, it is referred to as a pointer variable.
Value of v
Address of pv
pv v The data item represented by v (ie the data item stored in v’s memory cells) can be accessed by the expression *pv, where * is a unary operator, called the indirection operator, that operates only on a pointer variable. Therefore, *pv and v both represent the sane data item,(ie the contents of the same memory cells). If we write pv = &v and u=*pv, then u and v will both represent the same value; ie, the value of v will indirectly be assigned to u. void main() { int u =3, v; int *pu, *pv; pu = &u; // pointer to an integer v = *pu; // assign value of u to v pv = &v; // assign address of v to pv cout << “u = “<< u; cout <<”&u = “ << &u; cout <<”pu = “<
53
Object Oriented Programming Using C++ Pointer Declarations All pointer variables must be declared before they may be used. The interpretation of a pointer declaration differs, however, from the interpretation of other variable declarations. Pointer variable must be preceded by an asterisk(*). This identifies the fact that the variable is a pointer. data-type *ptvar; where ptvar is the name of the pointer variable, and data-type refers to the data type of the pointer’s object. Passing Pointers To A Function Pointers are often passed to a function as arguments. This allows data items within the calling portion of the program to be accessed by the function, altered within the function, and then returned to the calling portion of the program in altered form. WE refer to this use of pointers as passing arguments by reference (or by address or by location). When an item is passed by reference, however (i.e., when a pointer is passed to a function), the address of a data or within the calling function. The contents of that address can be accessed freely, either within the function or within the calling routine. Any change made to the data item (i.e., to the contents of the address) will be recognized in both the function and the calling routine. When pointers are used as arguments to a function, each formal pointer arguments must be preceded by an asterisk. Function prototypes are written in the same manner. If a function declaration does not include variable names, the data type of each pointer argument must be followed by an asterisk. Eg. void main() { void funct1(int u, int v); void funct2(int *pu, int *pv); int u = 1, v = 3; cout <<”Before calling funct1 : u = “<
54
Object Oriented Programming Using C++ void funct1(int u, int v) { u=0, v=0; cout << “within funct1 : u = “<
55
Object Oriented Programming Using C++ // display an array element cout << “ i = “ << i << “ x[i] = “ << x[i] << “ *(x + i) = “ << *(x + i); // display the corresponding array address cout << “ &x[i] = “<< &x[i] << “ (x + i) = “ << (x + i); } } // end of main Operations On Pointers In arrays, an integer value can be added to an array name in order to access an individual array element. The integer value is interpreted as an array subscript; it represents the location of the desired array element relative to the first element in the array. This works because all of the array elements are of the same data type, and therefore each array element occupies the same number of memory cells (i.e., the same number of bytes or words). The actual number of memory cells that separate the two array elements will depend on the data type of the array. This concept can be extended to pointer variables in general. Thus, an integer value can be added to or subtracted from a pointer variable, though the resulting expression must be interpreted very carefully. Suppose, for example, that px is a pointer variable that represents the address of some variable x. We can write expressions such as ++px, --px, (px + 3), (px + i) and (px – i), where i is an integer variable. Each expression will represent an address that is located some distance from the original address represented by px. The exact distance will be the product of the integer quantity and the number of bytes or words associated with the data item to which px points. Suppose, for example, that px points to an integer quantity, and each integer quantity requires two bytes of memory. Then the expression (px + 3) will result in an address that is six bytes beyond the integer to which px points. This new address will not necessarily represent the address of another data item, particularly if the data items stored between the two addresses involve different data types.
Px
|2bytes| (px+3)
Eg. void main() { int *px; int i =1; float = 0.3;
// pointer to an integer
56
Object Oriented Programming Using C++ double d = 0.005; char c = ‘*’; px = &i; cout <<”Values : i = “<< i <<”f = “<
d =0.005000 c = * &f=1180 &d=1186 px+1=1180 px+2=1182
&c=118E px+3=1184
If px is defined as a pointer to a different type of object(eg, a character or a floating-point quantity), then any integer constant that is added to or subtracted from the pointer will be interpreted differently. In particular, each integer value will represent an equivalent number of individual bytes if px points to a character, or a corresponding number of fourbyte multiples if px points to a floating-point quantity. One pointer variable can be subtracted from another provided both variables point to elements of the same array. The resulting value indicates the number or bytes separating the corresponding array elements.
57
Object Oriented Programming Using C++ eg. void main() { int *px, *py; //interger pointers static int a[6] = {1,2,3,4,5,6}; px = &a[0]; py = &a[5]; cout <<”px = “<= py) (px == py) (px != py) (px == NULL) These expressions can be used as any other logical expression. For example, if (px < py) cout << “px < py”; else cout << ”px >= py”; Expressions such as (px < py) indicate whether or not the element associated with px is ranked ahead of the element associated with py (i.e., whether or not the subscript associated with *px is less than the subscript associated witj *py).
58
Object Oriented Programming Using C++ The operations discussed previously are the only operations that can be carried out on pointers. These permissible operations are summarized below. 1. A pointer variable can be assigned the address of an ordinary variable (e.g., pv = &v). 2. A pointer variable can be assigned the value of another pointer variable (e.g., pv = px) provided both pointers point to objects of the same data type. 3. A pointer variable can be assigned a null(zero) value (e.g., pv = NULL, where NULL is a symbolic constant that represents the value 0). 4. An integer quantity can be added to or subtracted from a pointer variable (e.g., pv + 3, ++pv, etc.) 5. One pointer variable can be subtracted from another provided both pointers point to elements of the same array. 6. Two pointer variables can be compared provided both pointers point to objects of the same data type. Other arithmetic operations on pointers are not allowed. Thus, a pointer variable cannot be multiplied by a constant; two pointer variables cannot be added; and so on. An ordinary variables cannot be assigned an arbitrary address (i.e., an expression such as &x cannot appear on the left side of an assignment statement). Pointers And Multidimensional Arrays A two dimensional array, for example, is actually a collection of one-dimensional arrays. Therefore, we can define a two-dimensional array as a pointer to a group of contiguous one-dimensional arrays. Thus a two-dimensional array declaration can be written as data-type (*ptvar) [expression2]; rather than data-type array[expression1][expression2]; This concept can be generalized to higher-dimensional arrays; that is; data-type (*ptvar)[expression2][expression3]………..[expression n]; replaces data-type array[expression1][expression2]………..[expression n]; In this declarations data-type refers to the data type of the array, ptvar is the name of the pointer variable, array is the corresponding array name, and expression1, expression2, ……expression n are poisitve-valued integer expression that indicate the maximum number of array elements associated with each subscript. Notice that parentheses that surround the array name and the preceding asterisk in the pointer version of each declaration. These parentheses must be present. Without them we would be defining an array of pointers rather than a pointer to group of arrays, since these particular symbols (i.e., the square brackets and the asterisk) would normally be evaluated right to left.
59
Object Oriented Programming Using C++ Eg1. Suppose x is a two-dimensional integer having 10 rows and 20 columns. We can declare x as int (* x) [20]; rather than int x[10][20]; In the first declaration, x is defined to be a pointer to a group of contiguous, onedimensional, 20-element integer arrays. Thus, x points to the first 20-element arrays, which is actually the first row (i.e., row 0) of the original two-dimensional array. Similarly, (x + 1) points to the second 20-element array, which is the second row (row 1) of the original two-dimensional array, and so on. x 1st one-dimensional aray (x + 1) 2nd one dimensional array …………………. (x + 9) 10th one-dimensional array Eg. 2 Consider a three dimensional floating point array t. This array can be defined as float (*t)[20][30]; rather than float t[10][20][30]; In the first declaration, t is defined as a pointer to a group of contiguous two-dimenional, 20 * 30 floating-point arrays. Hence t points to the first 20 * 30 array, (t + 1) points to the second 20 * 30 array, and so on. An individual array element within a multidimensional array can be accessed by the repeated use of the indirection operator. Usually, however, this procedure is more awkward than the conventional method for accessing an array element. Eg. Suppose x is a 2-d integer array having 10 rows and 20 columns. The item in row 2, column 5 can be accessed by writing either x[2][5] or *(*(x + 2) + 5) In the second form (x + 2) is a pointer to row 2. Therefore the object of this pointer. *(x + 2), refers the entire row. Since row 2 is a one-dimensional array, *(x + 2) is actually a pointer to the first element in row 2. The object of this pointer, *(*(x + 2) + 5), therefore refers to the item in column 5 of row 2, which is x[2][5].
60
Object Oriented Programming Using C++
x 1st one dimensional array
(x + 1) 2nd one dimensional array
(x + 2) 3rd one dimensional array *(x+2)
*(x+2)+5 *(*(x+2)+5)
Programs that make use of multidimensional arrays can be written in several different ways. In particular, there are different ways to define the arrays, and different ways to process the individual array elements. Arrays Of Pointers A multidimensional array can be expressed in terms of an array of pointers rather than a pointer to a group of contiguous arrays. Each pointer will indicate the beginning of a separate (n-1)-dimensional array. In general terms, a two-dimensional array can be defined as a one-dimensional array of pointers by writing data-type *array[expression 1]; rather than the conventional array definition. data-type array[expression1][expression2]; similarly, an n-dimensional array can be defined as an (n-1)-dimensional array of pointers by writing data-type *array[expression1][expression2]….[expression n-1]; rather than data-type array[expression1][expression2]….[expression n]; In these declarations data-type refers to the data type of the original n-dimensional array, array is the array name, and expression 1, expression 2,………expression n are positive valued integer expressions that indicate the maximum number of elements associated with each subscript.
61
Object Oriented Programming Using C++ Notice that the array name and its proceeding asterisk are not in closed in parentheses in this type of declaration. Thus, a right to left rule first associates the pairs of square brackets with array defining the named object as an array. The preceding asterisk than establishes that the array will contain pointers. Moreover, the last (i.e. the leftmost) expression is omitted when defining an array of pointers, whereas the first (i.e. leftmost) expression is omitted when defining a pointer to a group of arrays. When an n-dimensional arrays is expressed in this manner, an individual array element within the n-dimensional array can be accessed by a single use of the indirection operator. e.g. Suppose x is a two dimensional integer array having 10 rows and 20 columns, we can define x as a one dimensional array of pointers of writing. i n t * x [10] Hence x [0] points to the beginning of the first row, x[1] points to the beginning of the second row and so on .note that the number of elements within each is not explicitly specified.
X [0]
1st one-dimensional array
X [1]
2nd one -dimensional array
X [2]
3rd one -dimensional array (X [2]+ 5) *(X [2] + 5)
X [9]
10th one -dimensional array
An individual array element, such as x [2][5] can be accessed by writing *(X [2] + 5) In this expression x[2] is a pointer to the first element in a row 2, so that (x[2]+5) points to element 5 (actually , the sixth element ) within row 2. The object of this pointer *(x[2]+5) therefore refers to x[2][5].
62
Object Oriented Programming Using C++
Now consider a three dimensional floating point array t. Suppose the dimensionality of t is 10 * 20 * 30. This array can be expressed as a two dimensional array of pointers by writing float *t [10][20]; Therefore we have 200 pointer (10 rows, 20columns), each pointing to one-dimensional array. An individual array element such as t[2][3][5], can be accessed by writing *(t [2][3] +5) In this expression t[2][3] is a pointer to the first element in the one dimensional array represented t[2][3]. Hence (t[2][3] +5) points to element 5 (the sixth element) within this array. The object of this pointer, *(t[2][3] +5), therefore represented t[2][3][5]. Passing Functions To Other Functions A pointer to a function can be passed to another function as an argument. This allows one function to be transferred to another, as though the first function were a variable. Let us refer to the first function as the guest function, and the second function as the host function. Thus, the guest is passed to the host, where it can be accessed. Successive calls to the host function can pass different pointers (i.e., different guest functions) to the host. When a host function accepts the name of a guest function as an argument, the formal argument declaration must identify that argument as a pointer to the guest function. In its simplest form, the formal argument declaration can be written as data-type (*function-name)() where data-type refers to the data type of the quantity returned by the guest and functionname is the name of the guest. The formal argument declaration can also be written as data-type (*function-name)(type1, type2,…….) or as data-type (*function-name)(type1 arg1, type2 arg2,…….) where type1, type2,….. refer to the data types of the arguments associated with the guest, and arg1, arg2,…refers to the name of the arguments associated with the guest. The guest function can be accessed within the host by means of the indirection operator. To do so, the indirection operator must precede the guest function name (i.e., the formal argument). Both the indirection operator and the guest function name must be enclosed in parentheses; i.e., (*function-name)(argument1, argument2, …., argument n); Where argument1, argument 2, ………, argument n refer to the arguments that are required in the function call. Now consider the function declaration for the host function. It may be written as funct-data-type funct-name(arg-data-type (*)(type1, type2,….), data types of other funct |-----pointer to guest function -----|
63
Object Oriented Programming Using C++ args); where funct-data-type refers to the data type of the quantity returned by the host function; funct-name refers to the name of the host function; arg-data-type refers to the data type of the quantity returned by the guest function, and type1, type2,….. refer to the data types of guest function’s arguments. Notice that the indirection operator appears in parentheses, to indicate that they are function arguments. When full function prototyping is used, the host function declaration is expanded as follows: funct-data-type funct-name(arg-data-type (*ptr-var)(type1 arg1, type2 arg2,…..….), |-----pointer to guest function -----| data types of other funct args); The notation is the same as above, except that pt-var refers to the pointer variables pointing to the guest function, and type1 arg1, type2 arg2,…. refer to the data types and the corresponding names of the guest function’s argument. More About Pointer Declarations One difficulty is the dual use of parentheses. In particular, parentheses are used to indicate functions, and they are used for nesting purposes (to establish precedence) within more complicated declarations. Thus, the declaration int *p(int a); indicates a function that accepts an integer argument, and returns a pointer to an integer. On the other hand, the declaration int *p(int a); indicates a pointer to a function that accepts an integer argument and returns an integer. In this last declaration, the first pair of parentheses is used for nesting, and the second pair is used to indicate a function. The interpretation of more complex declarations can be increasingly troublesome. For example, consider the declaration int *(*p)(int (*a)[]); In this declaration, (*p)(…..) indicates a pointer to a function. Hence int *(*p) (…….) indicates a pointer to a function that returns a pointer to an integer within the last pair of parentheses (the functions argument specification ), (*a) [] indicates a pointer to an array. Therefore, int(*a)[] represents a pointer to an array of integers. Putting the pieces together, (*p) (int (*a)[]) represents a pointer to a function whose argument is a pointer to an array of integers. And finally, the entire declaration int *(*p) (iunt (*a)[]); represents a pointer to a function that accepts a pointer to an array of integers as an argument, and returns a pointer to an integer. Remember that a left parenthesis immediately following an identifier name indicates that the identifier represents a function. Similarly, a left square bracket immediately following an identifier name indicates that the identifier represents an array. Parentheses that identify functions and square brackets that identify arrays have a higher precedence than
64
Object Oriented Programming Using C++ the unary indirection operation. Therefore, additional parentheses are required when declaring a pointer to a function or a pointer to an array. Several declarations involving pointer are shown below. int *p;
// p is a pointer to an integer quantity
int *p[10];
// p is a 10 element array of pointer to integer quantities
int (*p)[10];
// p is a pointer to a 10 element integer array
int *p(void);
// p is a function that returns a pointer to an integer quantity
int p(char *a);
/* p is a function that accepts an argument which is a pointer to a character and returns an integer quantity */
int *p(char a*);
/* p is a function that accepts an argument which is a pointer to a character returns a pointer to an integer quantity */
int (*p) (char *a);
/* p is a function that accepts an argument which is a pointer to a character and returns an integer quantity */
int (*p(char *a))[10]; /* p is a function that accepts an argument which is a pointer to a character and returns a pointer to a 10-element integer array */ int p(char (*a)[]);
/* p is a function that accepts an argument which is an array of pointers to characters returns an integer quantity */
int *p (char a []);
/*p is a function that accept an argument which is a character array returns a pointer to an integer quantity */
int *p (char (*a) []);
/*p is a function that accepts an argument which is a pointer to a character array returns a pointer to an integer quantity */
int *p (char *a []);
/*p is a function that accepts an argument which is an array of pointers to characters returns a pointer to an integer quantity */
int (*p)(char(*a)[]); /*p is a pointer to a function that accepts an argument which is a pointer to a character array returns an integer quantity */ int *(*p) (char (*a) []); /*p is a pointer to a function that accepts an argument which is a pointer to a character array returns a pointer to an integer quantity */ int *(*p)(char *a[]);
/*p is a pointer to a function that accepts an argument which is an array of pointers to characters returns a pointer to an integer quantity */
65
Object Oriented Programming Using C++
int (*p[10])(void);
/ * p is a 10 element array of pointers to functions; each function returns an integer quantity */
int (*p[10])(char a);
/ * p is a 10 element array of pointers to functions; each function accepts an argument which is a character, and returns an integer quantity */
int *(*p[10])(char a); /* p is a 10 element array of pointers to functions; each function accepts an argument which is a character, and returns a pointer to an integer quantity */ int *(*p[10])(char *a); function
/* p is a 10 element array of pointers to functions; each accepts an argument which is a pointer to a character, and returns a pointer to an integer quantity */
Operators new and new[ ] In order to request dynamic memory, the operator new exists. new is followed by a data type and optionally the number of elements required within brackets []. It returns a pointer to the beginning of the new block of assigned memory. Its form is: pointer = new type or pointer = new type [elements] The first expression is used to assign memory to contain one single element of type. The second one is used to assign a block (an array) of elements of type. For example: int * bobby; bobby = new int [5]; in this case, the operating system has assigned space for 5 elements of type int in a heap and it has returned a pointer to its beginning that has been assigned to bobby. Therefore, now, bobby points to a valid block of memory with space for 5 int elements.
66
Object Oriented Programming Using C++ You could ask what is the difference between declaring a normal array and assigning memory to a pointer as we have just done. The most important one is that the size of an array must be a constant value, which limits its size to what we decide at the moment of designing the program before its execution, whereas the dynamic memory allocation allows assigning memory during the execution of the program using any variable, constant or combination of both as size. The dynamic memory is generally managed by the operating system, and in multitask interfaces it can be shared between several applications, so there is a possibility that the memory exhausts. If this happens and the operating system cannot assign the memory that we request with the operator new, a null pointer will be returned. For that reason it is recommended to always check to see if the returned pointer is null after a call to new. int * bobby; bobby = new int [5]; if (bobby == NULL) { // error assigning memory. Take measures. }; Operator delete. Since the necessity of dynamic memory is usually limited to concrete moments within a program, once it is no longer needed it should be freed so that it becomes available for future requests of dynamic memory. The operator delete exists for this purpose, whose form is: delete pointer; or delete [] pointer; The first expression should be used to delete memory alloccated for a single element, and the second one for memory allocated for multiple elements (arrays). In most compilers both expressions are equivalent and can be used without distinction, although indeed they are two different operators and so must be considered for operator overloading (we will see that on section 4.2). How many numbers do you want to type // rememb-o-matic in? 5 Enter number : 75 #include Enter number : 436 Enter number : 1067 #include Enter number : 8 Enter number : 32 You have entered: 75, 436, 1067, 8, 32,
67
Object Oriented Programming Using C++
int main () { char input [100]; int i,n; long * l; cout << "How many numbers do you want to type in? "; cin.getline (input,100); i=atoi (input); l= new long[i]; if (l == NULL) exit (1); for (n=0; n
This simple example that memorizes numbers does not have a limited amount of numbers that can be introduced, thanks to us requesting to the system to provide as much space as is necessary to store all the numbers that the user wishes to introduce. 68
Object Oriented Programming Using C++ NULL is a constant value defined in manyfold C++ libraries specially designed to indicate null pointers. In case that this constant is not defined you can do it yourself by defining it to 0: #define NULL 0 It is indifferent to put 0 or NULL when checking pointers, but the use of NULL with pointers is widely extended and it is recommended for greater legibility. The reason is that a pointer is rarely compared or set directly to a numerical literal constant except precisely number 0, and this way this action is symbolically masked Dynamic Memory Allocation in C++ C++ provides the new and delete operators for memory allocation, instead of using C's malloc and free functions. They cannot be interchanged with each other. For example, it's not possible to free memory allocated by malloc with delete, and it's not possible to delete memory allocated by new with the free function. Allocating Memory Memory allocation is achieved using the new operator. The following allocates memory for one an integer, and sets the initial value to 42. newint.cpp #include int main() { int *iPtr; iPtr = new int(42); cout << *iPtr << endl; // Free the memory delete iPtr; return 0;
69
Object Oriented Programming Using C++
} The memory allocated with new is released using the delete keyword. Allocating Memory for Arrays When allocating memory for an array, the size of the array is specified within square brackets using the new operator. The next example allocates memory for five integers. fiveint.cpp #include int main() { int *iPtr; iPtr = new int[5]; // Initialise the array for (int counter=0; counter<5; counter++) iPtr[counter] = 0; // Free the memory delete [] iPtr; return 0; } When deleting an array, the [] token must prefix the array name to delete. When allocating arrays, it's a good idea to check that it was successful. If the memory is unable to be allocated, new returns a NULL value.
70
Object Oriented Programming Using C++ checkmem.cpp #include int main() { double *dPtr; if ((dPtr = new double[5000000]) == NULL) cout << "Not enough memory!\n"; else cout << "Memory allocated successfully\n"; delete [] dPtr; return 0; } CLASSES Classes A class in C++ is a collection of some functions and the data on which they operate. In C, as structure is a collection of various types of data, in C++ a class is a collection of data and member functions required for their input, processing and output. The functions and the data on which they operate are grouped together and given a name. This is known as a class. It allows the data (and functions) to be hidden, if necessary, from external use. When defining a class, we are creating a new abstract data type that can be treated like any other built-in data type. Declration Of A Class
A class can be declared as given below: class cl_name { private :
71
Object Oriented Programming Using C++ data_type var1; data_type var2; …………….. …………….. public : data_type m_function1(); data_type m_function2(); ………………………… ………………………… }; Where cl_name – is the name of the class data_type – is the type of data var_1, var2 are the names of the variables m_function1(), m_function2() are the member functions
The class declaration starts with the keyword class followed by the class name. Then are declared the variable which form the data and the data type for each. The member functions for processing the data are also declared along with return type for each. The class declaration ends with the closing brace (}) followed by the semi-colon. There are two more key words inside the class declaration – private and public. The data or the member functions which follow the keyword private (with a colon(:) thereafter) cannot be accessed from outside the class. They can be accessed by some member functions from within the class. Data or member functions declared as public (with a colon(:) thereafter) can however be accessed from outside the class. By default data is always private and member functions are public. Even then it is recommended to explicitly specify the private and public members in a class. This is a result of how classes are used. The data is hidden so it will be safe from accidental manipulation, while the functions that operate on the data are public so they can be accessed from outside the class. However, there is no rule that data must be private and functions public; in some circumstances there is a need to use private functions and public data. Eg. Suppose we want to read the side of a triangle, evaluate it’s area and display the result. We can declare a class as given below class triangle { private: 72
Object Oriented Programming Using C++ float side1; float side2; float side3; float area; public: void read_data(); void area_triangle(); void display(); }; It declares a class named triangle. The data consists of side1, side2, side3, the side of the triangle and its area, all declared private. The member function include read_data() to read information, area_tringle() to evaluate it’s area and display() to show any information. Note that we can not assign value to variables side1, side2, side3 by means other than the function read_data(). Member function area_triangle() has been planned to evaluate area assign value to variable area. The information about the triangle can be obtained only through member function display(). Objects in C++ Object represents the basic run – time entity in an object-oriented system; they occupy space in memory that keeps its state and is operated on by the defined operations on the object. Each object when created gets a private copy of the instance variables, and when an operation defined on the class is performed on the object, it is performed on the state of the particular object. When program is executed, the objects interact by sending message passing to one another. Each object contains data, and code to manipulate the data. Objects can interact without having to know details of each other’s data or code. The figure below shows the notation of object oriented analysis and design. Object: Account DATA side1 side2 side3 area FUNCTION read_data () area_triangle() display()
In the above section we declared a class named triangle. Each member of this class will consist of four real number to store three sides and the area of the triangle. In addition to
73
Object Oriented Programming Using C++ data we will declare three member function of process the data. Declaration of a class, as in case of declaration of a structure does not allocate any memory for storing data. It just defines a combination of a data and member function, which will be grouped together. An object in C++ define combination of data and member function which conform to the definition of a class. We have defined above a class named triangle. Suppose ABC and PQR are two triangles. Each of them will have three sides and an area. They will be using member function read _data(), area _triangle() and display() declared in the class definition. Both ABC and PQR are objects belonging to class triangle. The syntax of declaring objects is as under: class cl _name obj1, obj_2,………… Where cl _name is the of the class obj_1,obj_2 are the names of the objects The following is valid declaration of object class triangle ABC, PQR; Definition of Member Functions in a Class Eg. Suppose it is required to assign the values of 10 and 13 to the X and Y coordinates of a point and display the same. class point { private: int x; int y; public: void getxy (int x_cord, int y_cord); void showxy(); }; Here the member functions getxy() and shoxy() have been incorporated as member functions of the class. The member functions can be declared in two ways: i)
outside the class definition
ii)
inside the class definition
Irrespective of the place of definition, the function should perform the same task. Therefore, the code for the function body would be identical in both the cases.
74
Object Oriented Programming Using C++ Outside the Class Definition Member functions that are declared inside a class have to be defined separately outside the class. Their definitions are very much like the normal functions. They should have a function header and a function body. An important difference between a member function and a normal function is that a member function incorporates a membership ‘identity label’ in the header. This ‘label’ tells the compiler which class the function belongs to. The general form of a member function definition is : return-type class-name :: function-name (argument declaration) { Function body } The membership label class-name :: tells the compiler that the function function-name belongs to the class class-name. That is, the scope of the function is restricted to the class-name specified in the header line. The symbol :: is called the scope resolution operator.
Eg. class point { private: int x; int y; public: void getxy (int x_cord, int y_cord); void showxy(); }; // member functions defined outside the class definition void point :: getxy (int x_cord, int y_cord) { x = x_cord; y = y_cord; } 75
Object Oriented Programming Using C++ void point :: showxy() { cout << “ X coordinate – “ << x << endl; cout << “ Y coordinate – “ << y << endl; } Since these functions do not return any value, their return type is void. The member functions have some special characteristics that are often used in the program development. These characteristics are: •
Several different classes can use the same function name. The ‘membership label’ will resolve their scope.
•
Member functions can access the private data of the class. A non-member function cannot do so. (However, an exception to this rule is a friend function).
•
A member function can call another member function directly, without using the dot operator.
Inside the Class Definition Another method of defining a member function is to replace the function declaration by the actual function definition inside the class. Small functions may be defined inside the class definition. Eg. class point { private: int x; int y; public: void getxy (int x_cord, int y_cord) { x = x_cord; y = y_cord; } void showxy() { 76
Object Oriented Programming Using C++ cout << “ X coordinate – “ << x << endl; cout << “ Y coordinate – “ << y << endl; } }; Note that as the member functions are defined inside the class definition, there are no semicolons after the function definitions. When a function is defined inside a class, it is treated as an inline function. Eg. #include class point { private: int x; int y; public: void getxy (int x_cord, int y_cord) { x = x_cord; y = y_cord; } void showxy(); }; // end of class void point :: showxy() { cout << “ X coordinate – “ << x << endl; cout << “ Y coordinate – “ << y << endl; } void main() { point p;
77
Object Oriented Programming Using C++ point q; p.getxy(10,12); p.showxy(); q.getxy(15,25); q.showxy(); } //end of main This program features the class point. This class contains two private variables and two public functions. The member function getxy() has been defined inside the class and therefore behaves like an inline function. This function supplies values to both the variables. x = x_cord; in the function definition of getxy(). This shows that the member functions can have direct access to private data items. The member function showxy() displays the values of the private variables x and y. The program creates two objects, p and q in two different statements. This can be combined in one statement. point p, q;
Here is the output of program Object p X coordinate – 10 Y coordinate – 12
Object q X coordinate – 15 Y coordinate – 25 Making an Outside Function Inline We can define a member function outside the class definition and still make it inline by just using the qualifier inline in the header line of function definition. Example: class point { ……………….
78
Object Oriented Programming Using C++ ……………… public : void getxy(int x_cord, int y_cord);
//declaration
};
inline void point :: getxy(int x_cord, int y_cord) //definition { x = x_cord; y = y_cord; } Constructors Suppose it is required to read a number and test if it is a prime number. Suppose there is a class to store integer numbers n (number to be tested) and prime (to be assigned the value of 1 if n is prime and 0 otherwise). Lets initialize prime with 1 which will be updated to 0 if number n happens to be divisible by any other than 1 and n. class anynumber { private : int n; int prime = 1; //this is illegal public : void getno(int k) { n = k; } void isprime(); void display(); }; The above declaration is illegal. You cannot initialize a data member of a class inside the class declaration. The declaration int prime = 1 is illegal. Data members in a class can be initialized through a special type of function known as constructor. C++ provides a special member function called the constructor which enables an object to initialize itself when it is created. This is known as automatic initialization of objects. It also provides another member function called the destructor that destroys the objects when they are no longer required. A constructor is a ‘special’ member function whose task is to initialize the objects of its class. It is special because its anme is the same as the class name. The constructor is 79
Object Oriented Programming Using C++ invoked whenever an object of its associated class is created. It is called constructor because it constructs the values of data members of the class. The only restriction on a constructor is that it cannot return a value. //class with a constructor class point { private : int x, y; public : point (void); // constructor declared ………….. …………. }; point :: point (void) // constructor defined { x = 0; y = 0; } When a class contains a constructor like the one defined above, it is guaranteed that an object created by the class will be initialized automatically. For example point p1; //object p1 created not only creates the object p1 of type point but also initializes its data members x and y to zero. There is no need to write to any statement to invoke the constructor function (as we do with the normal functions). If a ‘normal’ member function is defined for zero initialization, we would need to invoke this function for each of the objects separately. A constructor that accepts no parameters is called default constructor. The default constructor for class A is A::A() . The constructor functions have some special characteristics. These are : • They should be declared in public section. • They are invoked automatically when the objects are created. • They do not have return types, not even void and therefore, and they cannot return values. • They cannot be inherited, though a derived class can call the base class constructor. • Like other C++ functions, they can have default arguments. • Constructors cannot be virtual. • We cannot refer to their addresses. • An object with a constructor (or destructor) cannot be used as a member of a union. • They make ‘implicit calls’ to the operators new and delete when memory allocation is required.
80
Object Oriented Programming Using C++ Constructors With Arguments The constructors that can take arguments are called parameterized constructors. Example : class student { private: int roll_no; char name[30]; int age; public : student (int x, int y); // constructor declared }; student :: student (int x, int y) { roll_no = x; age = y; } Objects of such classes having parameterized constructors can be created with an implicit/explicit call to constructors. Eg.. student s1 = student(3, 17); // explicit call student s1(3, 17); // implicit call Remember, when the constructor is parameterized, we must provide appropriate arguments for the constructor. Multiple Constructors in A Class C++ allows us to declare multiple constructors inside a class. point (); // no arguments point(int, int) // two arguments In the first case, the constructor itself supplies the data values and no values are passed by the calling program. In the second case, the function call passes the appropriate values from main(). C++ permits us to use both these constructors in the same class. For example, class num { private : int m, n;
81
Object Oriented Programming Using C++ public : num() {
// constructor1 m = 0; n = 0;
} num (int a, int b) { m =a; n =b; } num (num &i) { m = i.m; n = i.n; }
// constructor 2
// constructor 3 // passing object as an argument
}; The process of sharing the same name by two or more functions is referred to as function overloading. Similarly, defining more than one constructor is known as constructor overloading. Either of these constructors may be called explicitly/implicitly when creating an object. Example: num n1; num n2 (5,6); num n3(n2);
// will call constructor1 // will call constructor 2 // will call constructor 3
Constructor 3 copies the values of n2 into n3. In other words, it sets the value of every data element of n3 to the value of the corresponding data element of n2. Such a constructor is called copy constructor. When defining multiple constructors in a class, we must also define the “do-nothing” implicit constructor such as Num() { }; This constructor will do nothing and is defined just to satisfy the compiler. Constructors with Default Arguments It is possible to define constructors with default arguments. For example, the constructor point() can be declared as follows: point (int x, int y = 0); The default value of the argument y is zero. Then the statement point P(7); assigns the value 7 to the x variable and 0 to y (by default). However, the statement point P(5 , 7);
82
Object Oriented Programming Using C++ assigns 5 to x and 7 to the y. The actual parameter, when specified, overrides the default value. It is important to distinguish between the default constructor A::A() and the default argument constructor A::A(int x = 0). The default argument constructor can be called with either one argument or no arguments. When called with no arguments, it becomes a default constructor. Copy Constructor We used the copy constructor num(int &i); as one of the overloaded constructors. A copy constructor is used to declare and initialize an object from another object. For example, the statement num n2(n1); would define the object n2 and at the same time initialize it to the value of n1. Another form of this statement is num n2 = n1; The process of initializing through a copy constructor is known as copy initialization. Statement n2 = n1; will not invoke the copy constructor. If n1 and n2 are objects, this statement is legal and simply assigns the values of n1 to n2, member by member. This is the task of the overloaded assignment operator. Example: #include class code { private : int id; public : code() { }; code (int a) { id = a} code (code &x) { id = x.id} void display() { cout << id;
} }; void main() { code A(100); // object A is created and initialized code B(A); // copy constructor called code C = A; // copy constructor called again
83
Object Oriented Programming Using C++ code D; D = A;
// D is created but not initialized // A is copied over D but constructor is not called
cout << “\n id of A = “; A.display(); cout << “\n id of B = “; B.display(); cout << “\n id of C = “; C.display(); cout << “\n id of D = “; D.display(); } Destructors A destructor is used to destroy the objects that have been created by a constructor. Like a constructor, the destructor is a member function whose name is the same as the class name but is preceded by a tilde(~). Eg. classname :: ~classname(){
}
A destructor never takes any argument nor does it return any value. It will be invoked implicitly by the compiler upon exit from the program to clear up the storage that is no longer accessible. If new is used to allocate memory in the constructor then we should use delete to free that memory. Example matrix :: ~ matrix() { for( int i = 0; i < n; i++) delete p[i]; delete p; } If you don’t do so, your program may fail to call the destructor. Friend Function Private data can be accessed by member functions of a class. However, some times it is required that a function which is not a member of the class should have access to private data. This is possible by defining the non-member function as a friend function. A friend function will have access to all private and protected members of the class of which it is a friend. To declare a friend function it is required to write the prototype of the function within the class with the keyword friend preceding the function prototype.
84
Object Oriented Programming Using C++ class ABC { private: ………….. ………….. public: ………….. ………….. friend void xyz(); };
// declaation
The function declaration should be preceded by the keyword friend. The function is defined elsewhere in the program like a normal C++ function. The function definition does not use either the keyword friend or the scope resolution ::. The functions that are declared with the keyword friend are known as friend functions. A function can be declared as a friend in any number of classes. A friend function, although not a member function, has full access rights to the private members of the class. A friend function possesses certain special characteristics: • It is not in the scope of the class to which it has been declared as friend. • Since it is not in the scope of the class, it cannot be called using the object of that class. • It can be invoked like a normal function without the help of any object. • Unlike member functions, it cannot access the member names directly and has to use an object name and dot membership operator with each member name (e.g. A.x). • It can be declared either in the public or the private part of a class without affecting its meaning. • Usually, it has the object as arguments. Example: #include class sample { private: int a, b; public: void setvalue() { a = 25; b = 40; } friend float mean(sample s); }; float mean(sample s) { 85
Object Oriented Programming Using C++ return float(s.a + s.b)/2.0; } Void main() { sample X; X.setvalue(); cout << “Mean value = “ << mean(X) ; } Output Mean value = 32.5 The friend function accesses the class variable a and b by using the dot operator and the object passed to it. The function call mean(X) passes the object X by value to the friend function. Forward reference for declaration of friend function Suppose a rectangular field measures 15 * 12 m2 with a circle of radius 5 m at its center. It is required to find the area of the field outside the circle. Suppose there are two classes rectangle and circle to evaluate the area of the figures. We have to define a friend function (rest()) which will have access to the areas of both the figures. class circle { private: float r, ar; public : circle (float x) { r = x; ar = PIE * r * r; } friend float rest(rectangle R, circle C); }; class rectangle { private: float l, b, ar; public: rectangle(float x, float y)
86
Object Oriented Programming Using C++ { l = x; b = y; ar = I * b; } friend float rest (rectangle R, circle C); }; The program will fail to compile as, in class circle we have defined friend function as : friend float rest (rectangle R, circle C); We have, as yet, not defined class rectangle. This leads to an error. The problem will not be solved by declaring rectangle before circle as the friend function needs to be declared in both the classes. The problem is solved by what is known as foreward reference in C++. #include #define PIE 3.14159 class rectangle; //forward reference class circle { private: float r, ar; public : circle (float x) { r = x; ar = PIE * r * r; } friend float rest(rectangle R, circle C); }; class rectangle { private: float l, b, ar; public: rectangle(float x, float y) { l = x; b = y; ar = I * b; } friend float rest (rectangle R, circle C); }; float rest (rectangle R, circle C)
87
Object Oriented Programming Using C++ { float a; a=R.ar – C.ar; return(a); } void main() { Float a; Circle C(5); Rectangle R(12, 15); a = rest(R, C); cout << “Area of the lawn = “ <
// member function of X
}; class Y { ……………… ……………… friend int x :: fun1(); ………………
// fun1() of X is friend of Y
}; The function fun1() is a member of class X and a friend of class Y. Example #include #define PIE 3.14159 class rectangle; //forward reference class circle { private: float r, ar;
88
Object Oriented Programming Using C++ public : circle (float x) { r = x; ar = PIE * r * r; } float rest(rectangle R, circle C); }; class rectangle { private: float l, b, ar; public: rectangle(float x, float y) { l = x; b = y; ar = I * b; } friend float circle :: rest(rectangle R); }; float circle :: rest (rectangle R) { float a; a=R.ar – ar; return(a); } void main() { float a; circle C(5); rectangle R(12, 15); a = C.rest(R); cout << “Area of the lawn = “ <
89
Object Oriented Programming Using C++ class Z { ……………… friend class X;
// all member functions of X are friends to Z
}; Example: #include #define PIE 3.14159 class circle { private: float r, ar; public : circle (float x) { r = x; ar = PIE * r * r; } friend class rectangle; }; class rectangle { private: float l, b, ar; public: rectangle(float x, float y) { l = x; b = y; ar = l * b; } float diff(circle C) { float a; a = ar – C.ar; return(a); } };
// friend class declaration
void main() { float left; circle C(5); rectangle R(12, 15); left = R.diff ( C );
90
Object Oriented Programming Using C++ cout << “Area of the lawn = “ < class twonos { private: static int p; int q; public: void setdata(int i, int j) { p = i; q = j; }; void show() { cout << “static int p = “ <
91
Object Oriented Programming Using C++ X are assigned values of 5 and 6 respectively. In object Y they are assigned respective values of 8 and 10. Output : In object X Static int p = 5 int q = 6 In object X Static int p = 8 int q = 6 In object Y Static int p = 8 int q = 10 Operator Overloading C++ allows us to specify some new jobs for an already existing C++ operators. The mechanism of giving some extra jobs to an operator is known as operator overloading. Operator overloading provides a flexible option for the creation of new definitions for most of the C++ operators. Almost all the operators can be overloaded in C++, except the following: i) Class member access operators (., .*) ii) Scope resolution operator (::) iii) Size operator (sizeof) iv) Conditional operator (? :) When an operator is overloaded, its original meaning is not lost. Although the semantics of an operator can be extended, we cannot change its syntax, the grammatical rules that govern its use such as the number of operands, precedence and associativity. Defining Operator Overloading The additional task of an operator can be specified with the help of a special function, called operator function. The operator function must be a member of a class so that the additional task of the operator is limited by the concerned class. The general form of an operator function is : return type classname :: operator op(arglist) { Function body // task defined }
92
Object Oriented Programming Using C++ return type : is the type of value returned by the specified operation op : is the operator being overloaded The op is preceded by the keyword operator. Operator op is the function name. Operator function must be either a non static member function or a friend function. The two functions will be treated differently. A friend function will have one argument for unary operators and two for binary operators, while a member function has no arguments for unary operators. This is because the object used to invoke the member function is passed implicitly and therefore is available for the member function. This is not the case with friend function. Arguments may be passed either by value or by reference. Operator functions are declared in the class using prototypes as follows: vector operator+(vector); vector operator-(vector); friend vector operator+(vector, vector); friend vector operator-(vector); int operator ==(vector); friend int operator==(vector, vector)
// vector addition // unary minus // vector addition // vector minus // comparison // comparison
Vector is a data type of class and may represent both magnitude and direction. The process of overloading involves the following steps: 1. Create a class that defines the data type that is to be used in overloading operation. 2. Declare the operator function operator op() in the public part of the class. It may be either a member function of a friend function 3. Define the operator function to implement the required operations. Overloaded operator functions can be invoked by expressions such as op x or x op for unary operators and x op y for binary operators. op x (or x op) would be interpreted as operator op (x) for friend functions. Similarly, the expression x op y would be interpreted as either x.operator op(y) in case of member functions, or operator op(x, y) in case of friend functions. When both the forms are declared, standard argument matching is applied to resolve any ambiguity.
93
Object Oriented Programming Using C++ Overloading Unary Operators A unary operator is one which operates on a single operand. Example : overloading unary minus #include class space { private: int x, y, z; public: void getdata(int a, int b, int c); void display(); void operator–(); //overload unary minus }; void space :: getdata(int a, int b, int c) { x = a; y = b; z = c; } void space :: display() { cout << x ; cout << y; cout << z; } void space :: operator–() { x = -x; y = -y; z = -z; } void main() { space S; S.getdata(10, -20, 30); cout << “S : “; S.display(); -S;
// activates operator-() function
94
Object Oriented Programming Using C++ cout << “S : “; S.display(); } Output : S : 10 -20 S : -10 20
30 -30
The function operator –() takes no argument. It changes the sign of data members of the object S. Since this function is a member function of the same class, it can directly access the members of the object which activated it. Statement S2 = -S1 will not work, the function operator –() does not return any value. It can work if the function is modified to return an object. Using friend function friend void operator –(space &s);
// declaration
void operator –(space &s) { s.x = -s.x; s.y = -s.y; s.z = -s.z; }
// definition
Here the argument is passed by reference. It will not work if we pass argument by value because only a copy of the object that activated the call is passed to operator –(). Therefore, the changes made inside the operator function will not reflect in the called object. Overloading Binary Operaotrs Binary operators can be used to add (+), subtract (-), multiply (*) and divide (/) the objects. When we use binary operator for overloading it takes first argument (A) internally, and passes second argument (B) as an argument and return a value to store in C, where C may be object as well as a data member/ variable. If we use binary operator function as friend function; it takes two arguments. Example : overload + operator #include class complex { private:
95
Object Oriented Programming Using C++ float x, y; public: complex() { }; complex(float real, float imag) { x = real; y = imag; } complex operator +(complex); void display();
//constructor 1 // constructor 2
}; complex complex :: operator +(complex c) { complex temp; // temporary temp.x = x + c.x; // these are temp.y = y + c.y; // float additions return(temp); } void complex :: display () { cout << x << + j” << y ; } void main() { complex c1, c2, c3; c1 = complex(2.5, 3.5); c2 = complex(1.6, 2.7); c3 = c1 + c2; cout << “ C1 = “<
96
Object Oriented Programming Using C++ 3. It is a member function of complex. The function is expected to add two complex values and return a complex value as the result but receives only one value as argument. Where does the other value come from? C3 = C1 + C2; // invokes operator +() function INHERITANCE Inheritance is one of the most powerful features of objected oriented programming. It promotes software reusability by creating new base classes from existing classes. The new classes created, in addition to some additional features over and above the capabilities of the base class from which they are derived, posses all the attributes of the base class. When you derive a new class, it inherits all the attributes of the base class. You may incorporate additional attributes into the new class. But you do not have to touch the member functions of the base class. Each derived class itself becomes the base class for creation of further derived classes. Thus it is possible to create class libraries. Software groups can develop their own libraries and can use class libraries used by other groups. The C++ classes can be reused in several ways. Once a class has been written and tested, it can be adapted by other programmers to suit their requirements. This is basically done by creating new classes, reusing the properties of the existing ones. The mechanism of deriving a new class from an old one is called inheritance (or derivation). The old class is referred to as the base class and the new one is called the derived class or subclass. The derived class inherits some or all of the traits from the base class. A class can also inherit properties from more than one class or from more than one level. A derived class with only one base class, is called single inheritance and one with several base classes is called multiple inheritance. The traits of one class may be inherited by more than one class. This process is known as hierarchical inheritance. The mechanism of deriving a class from another ‘derived class’ is known as multilevel inheritance.
97
Object Oriented Programming Using C++
A
A
B
C Multiple Inheritance
B Single Inheritance
A
A
B
C
B
D
Hierarchical Inheritance
C
A Multileverl Inheritance
C
B
D
Hybrid Inheritance
98
Object Oriented Programming Using C++
A derived class can be defined by specifying its relationship with the base class in addition to its own details. The general form of defining a derived class is : class derived-class-name { ……………….. ………………. . ……………….. };
:
visibility-mode base-class-name
// // //
members of derived class
The colon indicates that the derived-class-name is derived from the base-class-name. The visibility mode is optional and, if present, may be either private or public. The default visibility-mode is private. Visibility mode specifies whether the features of the base class are privately derived or publicly derived. Examples class ABC : private XYZ { Members of ABC };
// private derivation
class ABC : public XYZ { Members of ABC };
// public derivation
class ABC : XYZ { Members of ABC };
// [private derivation by default
Points to Remember : 1. In private derivation, public members of base class becomes the private members of the derived class. class B class D : private B { { int a; int d; public : public: int b, c; display(); getdata(); }; mul(); };
99
Object Oriented Programming Using C++ Class D private : int d; int b, c; getdata(); mul(); public: display();
In public derivation, public members of a base class becomes the public members of the derived class. Class D private : int d; public: int b, c; getdata(); mul(); display();
2. In each case, no private members of a base class is accessible to the derived class. Example of Single Inheritance #include class B { int a; // private : not inheritable public : // public : ready for inheritance int b; void get_ab(); int get_a(); void show_a();
100
Object Oriented Programming Using C++ }; class D : public B { int c; public : void mul(); void display(); }; void B :: get_ab() { a = 5; b = 10; int B :: get_a() { return a;
}
}
void B :: show_a() { cout << “a = “ << a;
}
void D :: mul() { c = b * get_a();
}
void D :: display() { cout <<” a = “ << get_a; cout <<” b = “ << b; cout <<” c = “ << c; } void main() { D d; d.get_ab(); d.mul(); d.show_a(); d.display(); d.b = 20; d.mul(); d.display(); } Output : A=5 A=5
a=5 b = 20
b = 10 c = 100
c = 50
101
Object Oriented Programming Using C++
The class D is a public derivation of the base class B. Therefore, D inherits all the public members of B and retians their visibility. Thus a public member of the base class B is also a public member of the derived class D. The private members of B cannot be inherited by D. The class D, in effect, will have more members than what it contains at the time of declaration Class D private : int c; public: int b; get_ab(); get_a() show_a(); mul(); display();
NOTE : Although the data member a is private in B and cannot be inherited, pbjects of D are able to access it through an inherited member function of B. Example : Single Inheritance : Private #include class B { int a; // private : not inheritable public : // public : ready for inheritance int b; void get_ab(); int get_a(); void show_a(); }; class D : private B { int c; public : void mul();
// private derivation
102
Object Oriented Programming Using C++ void display(); }; void B :: get_ab() { cout << “Enter values for a and b : “; cin >> a >> b; } int B :: get_a() { return (a);
}
void B :: show_a() { cout << “a = “ << a;
}
void D :: mul() { c = b * get_a();
// ‘a’ cannot be used directly }
void D :: display() { show_a(); cout <<” b = “ << b; cout <<” c = “ << c; }
// outputs value of ‘a’
void main() { D d; // d.getab() ; wont work d.mul(); // d.show_a(); it wont work d.display()’ // d.b = 20; it wont work, b has become private d.mul(); d.display(); } Output : Enter values for a and b : 5 10 A=5 b = 10 c = 50 Enter values for a and b : 12 20 A = 12 b = 20 c = 240 Being private members of D, the members of B, get_ab() and show_a() can not be accessed directly through main().
103
Object Oriented Programming Using C++
Class D private : int c; int b; get_ab(); get_a() show_a(); public: mul(); display();
Making a Private Member Inheritable A private member of a base class can be inherited when declared as protected. A private member of a class when declared protected, it becomes accessible by the member functions within its class and any class immediately derived from it. It cannot be accessed by the functions outside these two classes. A class can now use all the three visibility modes: class alpha { private : ………. ………. protected : ………. ………. public : ………. ………. };
//optional // visible to member functions // within its class // visible to member functions // of its own and derived class // visible to all functions // in the program
When a protected member is inherited in public mode, it becomes protected in the derived class too and therefore is accessible by the member functions of the derived class. A protected member, inherited in the private mode derivation, becomes private in the derived class. Although it is available to the member functions of the derived class, it is not available for further inheritance (since private members cannot be inherited).
104
Object Oriented Programming Using C++ Class B not inheritable X
X not inheritable
Private Protected Public
Class D2 : private B Private
Class D1 : public B Private
Protected
Protected
Public
Public
Class X : public D1 : protected D2
Private Protected Public The keywords private, protected, and public may appear in any order and any number of times in the declaration of a class. For example class beta { protected : …………. public : ………….. private : …………. public : ………….. }; is a valid class definition.
105
Object Oriented Programming Using C++ It is possible to inherit a base class in protected mode (known as protected declaration). In protected derivation, both the public and protected members of the base class become protected members of the derived class. What are the various functions that can have access to these members? They could be: 1. A function that is a friend of the class. 2. A member function of a class that is a friend of the class. 3. A member function of a derived class. While the friend functions and the member functions of a friend class can have direct access to both the private and protected data, the member functions of a derived class can directly access only the protected data. However, they can access the private data through the member functions of the base class.
Visibility of inherited members Base Class Visibility
Derived Class Visibility Public Derivation
Private Derivation
Protected Derivation
Private
Not inherited
Not Inherited
Not Inherited
Protected
Protected
Private
Protected
Public
public
Private
Protected
Multilevel Inheritance Base Class
A
Grandfather
Intermediate Base Class
B
Father
Derived Class
C
Child
106
Object Oriented Programming Using C++
In the above figure class A servers as a base class for the derived class B, which in turn serves as a base class for the derived class C. The class B is known as intermediate base class since it provides a link for the inheritance between A and C. The chain ABC is known as inheritance path. A derived class with multilevel inheritance is declared as follows: class A { …………………. }; class B : public A { ……….}; class C : public B { ……….};
// Base class // B derived from A // C derived from B
This process can be extended to any number of levels; Example class student { protected: int roll_number; public : void get_number(int); void put_number(); }; void student :: get_number(int a) { roll_number = a; } void student :: put_number() { cout << “roll number : “ << roll_number ; } class test : public student // first level derivation { protected: float sub1, sub2; public : void get_marks(float, float); void put_marks(); } void test :: get_marks(float x, float y) { sub1 = x ; sub2 = y ; } void test :: put_marks() { cout << “Marks in sub1 = “ << sub1; cout << “Marks in sub2 = “ << sub2; } class result : public test { float total; public : void display(); };
// second level derivation
107
Object Oriented Programming Using C++ Polymorphism Before getting into this section, it is recommended that you have a proper understanding of pointers and class inheritance. If any of the following statements seem strange to you, you should review the indicated sections: Statement: Explained in: Int a::b(c) {}; Classes a->b Pointers class a: public b; Friendship and inheritance Pointers to base class One of the key features of derived classes is that a pointer to a derived class is typecompatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature, that brings Object Oriented Methodologies to its full potential. We are going to start by rewriting our program about the rectangle and the triangle of the previous section taking into consideration this pointer compatibility property: // pointers to base class
20
#include
10
using namespace std;
class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } };
class CRectangle: public CPolygon {
108
Object Oriented Programming Using C++
public: int area () { return (width * height); } };
class CTriangle: public CPolygon { public: int area () { return (width * height / 2); } };
int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } In function main, we create two pointers that point to objects of class CPolygon (ppoly1 and ppoly2). Then we assign references to rect and trgl to these pointers, and because both are objects of classes derived from CPolygon, both are valid assignations.
109
Object Oriented Programming Using C++ The only limitation in using *ppoly1 and *ppoly2 instead of rect and trgl is that both *ppoly1 and *ppoly2 are of type CPolygon* and therefore we can only use these pointers to refer to the members that CRectangle and CTriangle inherit from CPolygon. For that reason when we call the area() members at the end of the program we have had to use directly the objects rect and trgl instead of the pointers *ppoly1 and *ppoly2. In order to use area() with the pointers to class CPolygon, this member should also have been declared in the class CPolygon, and not only in its derived classes, but the problem is that CRectangle and CTriangle implement different versions of area, therefore we cannot implement it in the base class. This is when virtual members become handy: Virtual members A member of a class that can be redefined in its derived classes is known as a virtual member. In order to declare a member of a class as virtual, we must precede its declaration with the keyword virtual: // virtual members
20
#include
10
using namespace std;
0
class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area () { return (0); } };
class CRectangle: public CPolygon { public:
110
Object Oriented Programming Using C++
int area () { return (width * height); } };
class CTriangle: public CPolygon { public: int area () { return (width * height / 2); } };
int main () { CRectangle rect; CTriangle trgl; CPolygon poly; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; CPolygon * ppoly3 = &poly; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly3->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; cout << ppoly3->area() << endl; return 0;
111
Object Oriented Programming Using C++
} Now the three classes (CPolygon, CRectangle and CTriangle) have all the same members: width, height, set_values() and area(). The member function area() has been declared as virtual in the base class because it is later redefined in each derived class. You can verify if you want that if you remove this virtual keyword from the declaration of area() within CPolygon, and then you run the program the result will be 0 for the three polygons instead of 20, 10 and 0. That is because instead of calling the corresponding area() function for each object (CRectangle::area(), CTriangle::area() and CPolygon::area(), respectively), CPolygon::area() will be called in all cases since the calls are via a pointer whose type is CPolygon*. Therefore, what the virtual keyword does is to allow a member of a derived class with the same name as one in the base class to be appropriately called from a pointer, and more precisely when the type of the pointer is a pointer to the base class but is pointing to an object of the derived class, as in the above example. A class that declares or inherits a virtual function is called a polymorphic class. Note that despite of its virtuality, we have also been able to declare an object of type CPolygon and to call its own area() function, which always returns 0. Abstract base classes Abstract base classes are something very similar to our CPolygon class of our previous example. The only difference is that in our previous example we have defined a valid area() function with a minimal functionality for objects that were of class CPolygon (like the object poly), whereas in an abstract base classes we could leave that area() member function without implementation at all. This is done by appending =0 (equal to zero) to the function declaration. An abstract base CPolygon class could look like this: // abstract class CPolygon class CPolygon { protected: int width, height;
112
Object Oriented Programming Using C++
public: void set_values (int a, int b) { width=a; height=b; } virtual int area () =0; }; Notice how we appended =0 to virtual int area () instead of specifying an implementation for the function. This type of function is called a pure virtual function, and all classes that contain at least one pure virtual function are abstract base classes. The main difference between an abstract base class and a regular polymorphic class is that because in abstract base classes at least one of its members lacks implementation we cannot create instances (objects) of it. But a class that cannot instantiate objects is not totally useless; We can create pointers to it and take advantage of all its polymorphic abilities. Therefore a declaration like: CPolygon poly; would not be valid for the abstract base class we have just declared, because tries to instantiate an object. Nevertheless, the following pointers: CPolygon * ppoly1; CPolygon * ppoly2; would be perfectly valid. This is so for as long as CPolygon includes a pure virtual function and therefore it's an abstract base class. However, pointers to this abstract base class can be used to point to objects of derived classes. Here you have the complete example: // abstract base class
20
113
Object Oriented Programming Using C++
#include
10
using namespace std;
class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; };
class CRectangle: public CPolygon { public: int area (void) { return (width * height); } };
class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } };
114
Object Oriented Programming Using C++
int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; return 0; } If you review the program you will notice that we refer to objects of different but related classes using a unique type of pointer (CPolygon*). This can be tremendously useful. For example, now we can create a function member of the abstract base class CPolygon that is able to print on screen the result of the area() function even though CPolygon itself has no implementation for this function: // pure virtual members can be called
20
// from the abstract base class
10
#include using namespace std;
class CPolygon { protected: int width, height; public:
115
Object Oriented Programming Using C++
void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; void printarea (void) { cout << this->area() << endl; } };
class CRectangle: public CPolygon { public: int area (void) { return (width * height); } };
class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } };
int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl;
116
Object Oriented Programming Using C++
ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); return 0; } Virtual members and abstract classes grant C++ the polymorphic characteristics that make object-oriented programming such a useful instrument in big projects. Of course, we have seen very simple uses of these features, but these features can be applied to arrays of objects or dynamically allocated objects. Let's end with the same example again, but this time with objects that are dynamically allocated: // dynamic allocation and polymorphism
20 10
#include using namespace std;
class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; void printarea (void)
117
Object Oriented Programming Using C++
{ cout << this->area() << endl; } };
class CRectangle: public CPolygon { public: int area (void) { return (width * height); } };
class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } };
int main () { CPolygon * ppoly1 = new CRectangle; CPolygon * ppoly2 = new CTriangle; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); delete ppoly1; delete ppoly2;
118
Object Oriented Programming Using C++
return 0; } Notice that the ppoly pointers: CPolygon * ppoly1 = new CRectangle; CPolygon * ppoly2 = new CTriangle; are declared being of type pointer to CPolygon but the objects dynamically allocated have been declared having the derived class type directly. Input/Output with Files C++ provides the following classes to perform output and input of characters to/from files: • • •
ofstream: Stream class to write on files ifstream: Stream class to read from files fstream: Stream class to both read and write from/to files.
These classes are derived directly or indirectly from the classes istream, and ostream. We have already used objects whose types were these classes: cin is an object of class istream and cout is an object of class ostream. Therfore, we have already been using classes that are related to our file streams. And in fact, we can use our file streams the same way we are already used to use cin and cout, with the only difference that we have to associate these streams with physical files. Let's see an example: // basic file operations
[file example.txt]
#include
Writing this to a file
#include using namespace std;
int main () {
119
Object Oriented Programming Using C++
ofstream myfile; myfile.open ("example.txt"); myfile << "Writing this to a file.\n"; myfile.close(); return 0; } This code creates a file called example.txt and inserts a sentence into it in the same way we are used to do with cout, but using the file stream myfile instead. But let's go step by step: Open a file The first operation generally performed on an object of one of these classes is to associate it to a real file. This procedure is known as to open a file. An open file is represented within a program by a stream object (an instantiation of one of these classes, in the previous example this was myfile) and any input or output operation performed on this stream object will be applied to the physical file associated to it. In order to open a file with a stream object we use its member function open(): open (filename, mode); Where filename is a null-terminated character sequence of type const char * (the same type that string literals have) representing the name of the file to be opened, and mode is an optional parameter with a combination of the following flags: ios::in Open for input operations. ios::out Open for output operations. ios::binary Open in binary mode. Set the initial position at the end of the file. ios::ate If this flag is not set to any value, the initial position is the beginning of the file. All output operations are performed at the end of the file, appending the ios::app content to the current content of the file. This flag can only be used in streams open for output-only operations. If the file opened for output operations already existed before, its previous ios::trunc content is deleted and replaced by the new one.
120
Object Oriented Programming Using C++ All these flags can be combined using the bitwise operator OR (|). For example, if we want to open the file example.bin in binary mode to add data we could do it by the following call to member function open(): ofstream myfile; myfile.open ("example.bin", ios::out | ios::app | ios::binary); Each one of the open() member functions of the classes ofstream, ifstream and fstream has a default mode that is used if the file is opened without a second argument: class default mode parameter ofstream ios::out ifstream ios::in fstream ios::in | ios::out For ifstream and ofstream classes, ios::in and ios::out are automatically and respectivelly assumed, even if a mode that does not include them is passed as second argument to the open() member function. The default value is only applied if the function is called without specifying any value for the mode parameter. If the function is called with any value in that parameter the default mode is overridden, not combined. File streams opened in binary mode, perform input and output operations independently of any format considerations. Non-binary files are known as text files, and some translations may occur due to formatting of some special characters (like newline and carriage return characters) Since the first task that is performed on a file stream object is generally to open a file, these three classes include a constructor that automatically calls the open() member function and has the exact same parameters as this member. Therefor, we could also have declared the previous myfile object and conducted the same opening operation in our previous example by writing: ofstream myfile ("example.bin", ios::out | ios::app | ios::binary); Combining object construction and stream opening in a single statement. Both forms to open a file are valid and equivalent. To check if a file stream was successful opening a file, you can do it by calling to member is_open() with no arguments. This member function returns a bool value of true 121
Object Oriented Programming Using C++ in the case that indeed the stream object is associated with an open file, or false otherwise: if (myfile.is_open()) { /* ok, proceed with output */ }
Closing a file When we are finished with our input and output operations on a file we shall close it so that its resources become available again. In order to do that we have to call the stream's member function close(). This member function takes no parameters, and what it does is to flush the associated buffers and close the file: myfile.close(); Once this member function is called, the stream object can be used to open another file, and the file is available again to be opened by other processes. In case that an object is destructed while still associated with an open file, the destructor automatically calls the member function close(). Text files Text file streams are those where we do not include the ios::binary flag in their opening mode. These files are designed to store text and thus all values that we input or output from/to them can suffer some formatting transformations, which do not necessarily correspond to their literal binary value. Data output operations on text files are performed in the same way we operated with cout: // writing on a text file
[file example.txt]
#include
This is a line.
#include
This is another line.
using namespace std;
int main () { ofstream myfile ("example.txt");
122
Object Oriented Programming Using C++
if (myfile.is_open()) { myfile << "This is a line.\n"; myfile << "This is another line.\n"; myfile.close(); } else cout << "Unable to open file"; return 0; } Data input from a file can also be performed in the same way that we did with cin: // reading a text file
This is a line.
#include
This is another line.
#include #include using namespace std;
int main () { string line; ifstream myfile ("example.txt"); if (myfile.is_open()) { while (! myfile.eof() ) {
123
Object Oriented Programming Using C++
getline (myfile,line); cout << line << endl; } myfile.close(); }
else cout << "Unable to open file";
return 0; } This last example reads a text file and prints out its content on the screen. Notice how we have used a new member function, called eof() that returns true in the case that the end of the file has been reached. We have created a while loop that finishes when indeed myfile.eof() becomes true (i.e., the end of the file has been reached). Checking state flags In addition to eof(), which checks if the end of file has been reached, other member functions exist to check the state of a stream (all of them return a bool value): bad() Returns true if a reading or writing operation fails. For example in the case that we try to write to a file that is not open for writing or if the device where we try to write has no space left. fail() Returns true in the same cases as bad(), but also in the case that a format error happens, like when an alphabetical character is extracted when we are trying to read an integer number. eof() Returns true if a file open for reading has reached the end. good() It is the most generic state flag: it returns false in the same cases in which calling any of the previous functions would return true.
124
Object Oriented Programming Using C++ In order to reset the state flags checked by any of these member functions we have just seen we can use the member function clear(), which takes no parameters. get and put stream pointers All i/o streams objects have, at least, one internal stream pointer: ifstream, like istream, has a pointer known as the get pointer that points to the element to be read in the next input operation. ofstream, like ostream, has a pointer known as the put pointer that points to the location where the next element has to be written. Finally, fstream, inherits both, the get and the put pointers, from iostream (which is itself derived from both istream and ostream). These internal stream pointers that point to the reading or writing locations within a stream can be manipulated using the following member functions: tellg() and tellp() These two member functions have no parameters and return a value of the member type pos_type, which is an integer data type representing the current position of the get stream pointer (in the case of tellg) or the put stream pointer (in the case of tellp). seekg() and seekp() These functions allow us to change the position of the get and put stream pointers. Both functions are overloaded with two different prototypes. The first prototype is: seekg ( position ); seekp ( position ); Using this prototype the stream pointer is changed to the absolute position position (counting from the beginning of the file). The type for this parameter is the same as the one returned by functions tellg and tellp: the member type pos_type, which is an integer value. The other prototype for these functions is: seekg ( offset, direction ); seekp ( offset, direction ); Using this prototype, the position of the get or put pointer is set to an offset value relative to some specific point determined by the parameter direction. offset is of the member type off_type, which is also an integer type. And direction is of type seekdir, which is an
125
Object Oriented Programming Using C++ enumerated type (enum) that determines the point from where offset is counted from, and that can take any of the following values: ios::beg offset counted from the beginning of the stream offset counted from the current position of the stream ios::cur pointer ios::end offset counted from the end of the stream The following example uses the member functions we have just seen to obtain the size of a file: // obtaining file size
size is: 40 bytes.
#include #include using namespace std;
int main () { long begin,end; ifstream myfile ("example.txt"); begin = myfile.tellg(); myfile.seekg (0, ios::end); end = myfile.tellg(); myfile.close(); cout << "size is: " << (end-begin) << " bytes.\n"; return 0; }
Binary files In binary files, to input and output data with the extraction and insertion operators (<< and ]>>) and functions like getline is not efficient, since we do not need to format any
126
Object Oriented Programming Using C++ data, and data may not use the separation codes used by text files to separate elements (like space, newline, etc...). File streams include two member functions specifically designed to input and output binary data sequentially: write and read. The first one (write) is a member function of ostream inherited by ofstream. And read is a member function of istream that is inherited by ifstream. Objects of class fstream have both members. Their prototypes are: write ( memory_block, size ); read ( memory_block, size ); Where memory_block is of type "pointer to char" (char*), and represents the address of an array of bytes where the read data elements are stored or from where the data elements to be written are taken. The size parameter is an integer value that specifies the number of characters to be read or written from/to the memory block. // reading a complete binary file
the complete file content is in memory
#include #include using namespace std;
ifstream::pos_type size; char * memblock;
int main () { ifstream file ("example.txt", ios::in| ios::binary|ios::ate); if (file.is_open()) { size = file.tellg(); memblock = new char [size]; file.seekg (0, ios::beg);
127
Object Oriented Programming Using C++
file.read (memblock, size); file.close();
cout << "the complete file content is in memory";
delete[] memblock; } else cout << "Unable to open file"; return 0; } In this example the entire file is read and stored in a memory block. Let's examine how this is done: First, the file is open with the ios::ate flag, which means that the get pointer will be positioned at the end of the file. This way, when we call to member tellg(), we will directly obtain the size of the file. Notice the type we have used to declare variable size: ifstream::pos_type size; This is the correct way of declaring size since ifstream::pos_type is the type returned by file.tellg(). But since this type is an integer type, it can be converted to an int. Therefore, we could also declare size as a variable of type int or some other integer type capable of holding the size of a file and call tellg as: int size; size = (int) file.tellg(); Once we have obtained the size of the file, we request the allocation of a memory block large enough to hold the entire file:
128
Object Oriented Programming Using C++
memblock = new char[size]; Right after that, we proceed to set the get pointer at the beginning of the file (remember that we opened the file with this pointer at the end), then read the entire file, and finally close it: file.seekg (0, ios::beg); file.read (memblock, size); file.close(); At this point we could operate with the data obtained from the file. Our programs simply announces that the content of the file is in memory and then terminates. Buffers and Synchronization When we operate with file streams, these are associated to an internal buffer of type streambuf. This buffer is a memory block that acts as an intermediary between the stream and the physical file. For example, with an ofstream, each time the member function put (which writes a single character) is called, the character is not written directly to the physical file with which the stream is associated. Instead of that, the character is inserted in that stream's intermediate buffer. When the buffer is flushed, all the data contained in it is written to the physical medium (if it is an output stream) or simply freed (if it is an input stream). This process is called synchronization and takes place under any of the following circumstances: •
• • •
When the file is closed: before closing a file all buffers that have not yet been flushed are synchronized and all pending data is written or read to the physical medium. When the buffer is full: Buffers have a certain size. When the buffer is full it is automatically synchronized. Explicitly, with manipulators: When certain manipulators are used on streams, an explicit synchronization takes place. These manipulators are: flush and endl. Explicitly, with member function sync(): Calling stream's member function sync(), which takes no parameters, causes an immediate synchronization. This function returns an int value equal to -1 if the stream has no associated buffer or in case of failure. Otherwise (if the stream buffer was successfully synchronized) it returns 0.
129