OPPS with C++



Comments



Description

1UNIT -1 Basic concepts of OOPS and Structure of C++ program In this tutorial you will learn about Objects, Classes, Inheritance, Data Abstraction, Data Encapsulation, Polymorphism, Overloading, Reusability. Before starting to learn C++ it is essential that one must have a basic knowledge of the concepts of Object oriented programming. Some of the important object oriented features are namely: In order to understand the basic concepts in C++, the programmer must have a command of the basic terminology in object-oriented programming. Below is a brief outline of the concepts of Object-oriented programming languages: Objects: Object is the basic unit of object-oriented programming. Objects are identified by its unique name. An object represents a particular instance of a class. There can be more than one instance of an object. Each instance of an object can hold its own relevant data. An Object is a collection of data members and associated member functions also known as methods. 2 Classes: Classes are data types based on which objects are created. Objects with similar properties and methods are grouped together to form a Class. Thus a Class represent a set of individual objects. Characteristics of an object are represented in a class as Properties. The actions that can be performed by objects becomes functions of the class and is referred to as Methods. For example consider we have a Class of Cars under which Santro Xing, Alto and WaganR represents individual Objects. In this context each Car Object will have its own, Model, Year of Manufacture, Colour, Top Speed, Engine Power etc., which form Properties of the Car class and the associated actions i.e., object functions like Start, Move, Stop form the Methods of Car Class. No memory is allocated when a class is created. Memory is allocated only when an object is created, i.e., when an instance of a class is created. Inheritance: Inheritance is the process of forming a new class from an existing class or base class. The base class is also known as parent class or super class, The new class that is formed is called derived class. Derived class is also known as a child class or sub class. Inheritance helps in reducing the overall code size of the program, which is an important concept in object-oriented programming. Data Abstraction: Data Abstraction increases the power of programming language by creating user defined data types. Data Abstraction also represents the needed information in the program without presenting the details. Data Encapsulation: Data Encapsulation combines data and functions into a single unit called Class. When using Data Encapsulation, data is not accessed directly; it is only accessible through the functions present inside the class. Data Encapsulation enables the important concept of data hiding possible. Polymorphism: Polymorphism allows routines to use variables of different types at different times. An operator or function can be given different meanings or functions. Polymorphism refers to a single function or multi-functioning operator performing in different ways. Overloading: Overloading is one type of Polymorphism. It allows an object to have different meanings, depending on its context. When an exiting operator or function begins to operate on new data type, or class, it is understood to be overloaded. 3 Reusability: This term refers to the ability for multiple programmers to use the same written and debugged existing class of data. This is a time saving device and adds code efficiency to the language. Additionally, the programmer can incorporate new features to the existing class, further developing the application and allowing users to achieve increased performance. This time saving feature optimizes code, helps in gaining secured applications and facilitates easier maintenance on the application. The implementation of each of the above object-oriented programming features for C++ will be highlighted in later sections. A sample program to understand the basic structure of C++ //program to read employee details and to output the data #include <iostream.h> → Preprocessor directive class employee → Class Declaration { private: char empname[50]; int empno; public: void getvalue() { cout<<”INPUT EMP NAME:”; cint>>empname; cout<<”INPUT EMP NO:”; cint>>empno; } void displayvalue() { cout<<”EMP NAME:”<<empname; coout<<EMP NO:”<<empno; } }; main() { employee e1; → Creation of Object e1.getvalue(); e1.displayvalu(); } 4 Variables A variable is the storage location in memory that is stored by its value. A variable is identified or denoted by a variable name. The variable name is a sequence of one or more letters, digits or underscore, for example: character _ Rules for defining variable name: A variable name can have one or more letters or digits or underscore for example character _. . White space, punctuation symbols or other characters are not permitted to denote variable name. . A variable name must begin with a letter. . Variable names cannot be keywords or any reserved words of the C++ programming language. . C++ is a case-sensitive language. Variable names written in capital letters differ from variable names with the same name but written in small letters. For example, the variable name EXFORSYS differs from the variable name exforsys. As previously explained, a variable is the storage location in memory that is stored by variable value. The amount of memory allocated or occupied by each variable differs as per the data stored. The amount of memory used to store a single character is different from that of storing a single integer. A variable must be declared for the specific data type. Data Types Below is a list of the most commonly used Data Types in C++ programming language: short int int long int float double long double char bool short int : This data type is used to represent short integer. int: This data type is used to represent integer. 5 long int: This data type is used to represent long integer. float: This data type is used to represent floating point number. double: This data type is used to represent double precision floating point number. long double: This data type is used to represent double precision floating point number. char: This data type is used to represent a single character. bool: This data type is used to represent boolean value. It can take one of two values: True or False. Using variable names and data type, we shall now learn how to declare variables. Declaring Variables: In order for a variable to be used in C++ programming language, the variable must first be declared. The syntax for declaring variable names is data type variable name; The date type can be int or float or any of the data types listed above. A variable name is given based on the rules for defining variable name (refer above rules). Example: int a; This declares a variable name a of type int. If there exists more than one variable of the same type, such variables can be represented by separating variable names using comma. For instance int x,y,z This declares 3 variables x, y and z all of data type int. The data type using integers (int, short int, long int) are further assigned a value of signed or unsigned. Signed integers signify positive and negative number value. Unsigned integers signify only positive numbers or zero. For example it is declared as 6 unsigned short int a; signed int z; By default, unspecified integers signify a signed integer. For example: int a; is declared a signed integer It is possible to initialize values to variables: data type variable name = value; Example: int a=0; int b=5; Constants Constants have fixed value. Constants, like variables, contain data type. Integer constants are represented as decimal notation, octal notation, and hexadecimal notation. Decimal notation is represented with a number. Octal notation is represented with the number preceded by a zero character. A hexadecimal number is preceded with the characters 0x. Example 80 represent decimal 0115 represent octal 0x167 represent hexadecimal By default, the integer constant is represented with a number. The unsigned integer constant is represented with an appended character u. The long integer constant is represented with character l. Example: 78 represent int 85u present unsigned int 78l represent long 7 Floating point constants are numbers with decimal point and/or exponent. Example 2.1567 4.02e24 These examples are valid floating point constants. Floating point constants can be represented with f for floating and l for double precision floating point numbers. Character constants have single character presented between single quotes. Example „c‟ „a‟ are all character constants. Strings are sequences of characters signifying string constants. These sequence of characters are represented between double quotes. Example: “Exforsys Training” is an example of string constant. C++ Objects and Classes An Overview about Objects and Classes In object-oriented programming language C++, the data and functions (procedures to manipulate the data) are bundled together as a self-contained unit called an object. A class is an extended concept similar to that of structure in C programming language, this class describes the data properties alone. In C++ programming language, class describes both the properties (data) and behaviors (functions) of objects. Classes are not objects, but they are used to instantiate objects. Features of Class: Classes contain data known as members and member functions. As a unit, the collection of members and member functions is an object. Therefore, this unit of objects make up a class. 8 How to write a Class: In Structure in C programming language, a structure is specified with a name. The C++ programming language extends this concept. A class is specified with a name after the keyword class. The starting flower brace symbol, {is placed at the beginning of the code. Following the flower brace symbol, the body of the class is defined with the member functions data. Then the class is closed with a flower brace symbol} and concluded with a colon;. class exforsys { data; member functions; …………… }; There are different access specifiers for defining the data and functions present inside a class. Access specifiers: Access specifiers are used to identify access rights for the data and member functions of the class. There are three main types of access specifiers in C++ programming language: private public protected A private member within a class denotes that only members of the same class have accessibility. The private member is inaccessible from outside the class. . Public members are accessible from outside the class. . A protected access specifier is a stage between private and public access. If member functions defined in a class are protected, they cannot be accessed from outside the class but can be accessed from the derived class. When defining access specifiers, the programmer must use the keywords: private, public or protected when needed, followed by a semicolon and then define the data and member functions under it. class exforsys { 9 private: int x,y; public: void sum() { ……… ……… } }; In the code above, the member x and y are defined as private access specifiers. The member function sum is defined as a public access specifier. General Template of a class: General structure for defining a class is: class classname { acess specifier: data member; member functions; acess specifier: data member; member functions; }; Generally, in class, all members (data) would be declared as private and the member functions would be declared as public. Private is the default access level for specifiers. If no access specifiers are identified for members of a class, the members are defaulted to private access. class exforsys { int x,y; public: void sum() { ……… ……… } }; 10 In this example, for members x and y of the class exforsys there are no access specifiers identified. exforsys would have the default access specifier as private. Creation of Objects: Once the class is created, one or more objects can be created from the class as objects are instance of the class. Juts as we declare a variable of data type int as: int x; Objects are also declared as: class name followed by object name; exforsys e1; This declares e1 to be an object of class exforsys. For example a complete class and object declaration is given below: class exforsys { private: int x,y; public: void sum() { ……… ……… } }; main() { exforsys e1; …………… …………… } The object can also be declared immediately after the class definition. In other words the object name can also be placed immediately before the closing flower brace symbol } of the class declaration. 11 For example class exforsys { private: int x,y; public: void sum() { ……… ……… } }e1; The above code also declares an object e1 of class exforsys. It is important to understand that in object-oriented programming language, when a class is created no memory is allocated. It is only when an object is created is memory then allocated. How to Access C++ Class Members It is possible to access the class members after a class is defined and objects are created. General syntax to access class member: Object_name.function_name (arguments); The dot („.‟) used above is called the dot operator or class member access operator. The dot operator is used to connect the object and the member function. This concept is similar to that of accessing structure members in C programming language. The private data of a class can be accessed only through the member function of that class. For example, A class and object are defined below: class exforsys { int a, b; public: void sum(int,int); } e1; Then the member access is written as: 12 e1.sum(5,6); Where e1 is the object of class exforsys and sum() is the member function of the class. The programmer now understands declaration of a class, creation of an object and accessibility of members of a class. It is also possible to declare more than one object within a class: class exforsys { private: int a; public: void sum(int) { ……… ……… } }; main() { exforsys e1,e2; ………… ………… } In these two objects e1 and e2 are declared of class exforsys. By default, the access specifier for members of a class is private. The default access specifier for a structure is public. This is an important difference to recognize and understand in object- oriented C++ programming language. class exforsys { int x; //Here access specifier is private by default }; whereas struct exforsys 13 { int x; //Here access specifier is public by default }; It is not always the case that the member function declaration and definition takes place within the class itself. Sometimes, declaration of member function alone can occur within the class. The programmer may choose to place the definition outside the class. In a situation such as this, it is important to understand the identifying member function of a particular class. This is performed by using the operator :: this is called scope resolution operator. class exforsys { private: int a; public: void getvalues() // Only Member Function declaration is done }; void exforsys :: void getvalues() // Here Member Function is defined { ……………… ……………… } main() { exforsys e1,e2; ………… } So the usage of scope resolution operator is as follows: 14 Overview of the Basic Structure of C++ Programming The next structure present in the program is the class definition. This starts with the keyword class followed by class name employee. Within the class are data and functions. The data defined in the class are generally private and functions are public. These explanations we will be detailed in later sections. The class declaration ends with a semicolon. . main() function is present in all C++ programs. . An object e1 is created in employee class. Using this e1 the functions present in the employee class are accessed and there by data are accessed. . The input namely ename and eno is got using the input statement namely cin and the values are outputted using the output statement namely cout. What is a function? A function is a structure that has a number of program statements grouped as a unit with a name given to the unit. Function can be invoked from any part of the C++ program. Features of Function: To understand why the program structure is written separately and given a name, the programmer must have a clear idea of the features and benefits of function. This will encourage better understanding of function usage: Use of Functions gives a Structured Programming Approach Reduces Program Size: The piece of code that needs to be executed, or the piece of code that is repeated in different parts of the program, can be written separately as a function and stored in a place in memory. Whenever and wherever needed, the programmer can invoke the function and use the code to be executed. Thus, the program size is reduced. Having known about the function and its features let us see how to declare, define and call a function in a C++ program. Declaring a Function: It has been discussed that, in order for a variable to be used, it must be declared. Just like variables, it follows that function definitions must be declared. The general method of declaring a function is to declare the function in the beginning of the program. The general format for declaring a function is 15 return_datatype function name(arguments); Suppose we have a function named as exforsys which return nothing from the function it is declared as void exforsys( ); This declared would inform the compiler that the presence of the function exforsys is there in the program. In the above the return type void tells the compiler that the function has no return value and also the empty braces ( ) indicate that the function takes no arguments. Defining a function: The definition is the place where the actual function program statements or in other words the program code is placed. The general format of the function definition is as follows: return_datatype functionname(arguments) //Declarator { program statements //Function Body …….. ………. } In the above the declarator must match the function declaration already made. That is the function name, the return type and the number of arguments all must be same as the function declaration made. In fact if arguments are declared and defined. The order of arguments defined here must match the order declared in the function declaration. After the declarator the braces starts and the function body is placed. The function body has the set of program statements which are to be executed by the function. Then the function definition ends with } ending braces. For example let us see how to define a function exforsys declared as void that prints first five integers. void exforsys( ) { int i; for (i=1;i<=5;i++) cout<< i; } The output of the function when it is called would be 16 12345 Calling the function: The function must be called for it to get executed. This process is performed by calling the function wherever required. The general format for making the function call would be as follows: functionname(); When the function is called the control, transfers to the function and all the statements present in the function definition gets executed and after which the control, returns back to the statement following the function call. In the above example when the programmer executes the function exforsys, he can call the function in main as follows: exforsys(); Let us see a complete program in C++ to help the programmer to understand the function concepts described above: The output of the above program is 12345 End of Program Concept of Function with Arguments In this C++ Tutorial you will learn about Concept of Function with Arguments - What is an argument Declaring a function, Function Definition and Calling the function. Previous chapters of this tutorial have discussed simple function definition writing, declaration of function and function call. In this chapter, the definition, declaration and call function with arguments will be addressed. What is an argument? An argument is the value that is passed from the program to the function call. This can also be considered as input to the function from the program from which it is called. How to declare a function passed with argument Declaring a function: 17 The general format for declaring the function remains the same as before except the data type passed as arguments in functions are in the same order in which it is defined in function. The format for declaring a function with arguments is: return_datatype functionname(datatype1,datatype2,..); In this example, the data types are the types of data passed in the function definition as arguments to the function. Care must be taken to mention the number of arguments and the order of arguments in the same way as in function definition. For example, suppose a function named exforsys takes two integer values as arguments in its functions definition and returns an integer value. The function declaration of exforsys would be written: int exforsys(int,int); Function Definition: The function definition has the same syntax as the function definition previously defined, but with added arguments. The general format for defining a function with arguments is written as: return_datatype functionname(datatype1 variable1,datatype2 variable2,..) { …………. Program statements …………. return( variable); } In the above example, the return data type defines the data type of the value returned by the function. The arguments are passed inside the function name after parentheses with the data type and the variable of each argument. Care must be taken to mention the number of arguments and the order of arguments in the same way as in function declaration. For example, if the function exforsys takes two integer values x and y and adds them and returns the value z the function definition would be defined as follows: int exforsys(int x,int y) { int z; z=x+y; return(z); } In the above program segment, a return statement takes the general format 18 return(variable) ; This value specified in the return as argument would be returned to the calling program. In this example, the value returned is z, which is an integer value, the data type returned by the function exforsys is mentioned as int. Calling the function: The calling of function takes the same syntax as the name of the function but with value for the arguments passed. The function call is made in the calling program and this is where the value of arguments or the input to the function definition is given. The general format for calling the function with arguments is functionname(value1,value2,…); In the above exforsys function suppose integer value 5 and 6 are passed, the function call would be as follows: exforsys(5,6); As soon as the function call exforsys is made the control, transfers to the function definition and the assignment of 5 to x and 6 to y are made as below. int exforsys(int x,int y) exforsys(5,6); It is possible to store the return value of the function in a variable. In the above example, the assignment of the function call to integer variable b can be produced as follows: int b; b = exforsys(5,6); The above statement assigns the returned value of the function exforsys. The value z is then added to the value of x and y to the variable b. So, variable b takes the value 11. Let us see the whole program to understand in brief the concept of function with arguments The output of the above program would be 19 #include int exforsys(int,int); void main() { int b; int s=5,u=6; b=exforsys(s,u); cout&lt;&lt;”\n The Output is:”&lt; } int exforsys(int x,int y) { int z; z=x+y; return(z); } The Output is:11 C++ Function Passing Types In this C++ tutorial, you will learn about function passing types, two types of arguments passing in functions - passed by value and passed by reference are discussed here. The arguments passed to a function can be performed in two ways: Passed By Value Passed By Reference Passed By Value: In the earlier chapter, all examples for the function with arguments were passed by value. Arguments passed by value are the copies of the values of variables and are passed to the function. The variables defined in the calling function are not passed in this manner. For example: int s=5, u=6; int z; z = exforsys(s,u) would pass values as int exforsys(int x, int y) z = exforsys(5,6); 20 Thus, the copies of the values 5 and 6 are passed to the function definition and not the variables s and u. Thus, any changes in the variable x and y would not have any effect or change the variables s and u. Because s and u are not passed in this manner, only copies of the variables are passed to the function definition. Passed By Reference: Passed by reference indicates a contrast in that the variables are not passed by value. When a variable is passed by reference, it passes the variable to the function definition and not the copies of the value. Any changes to the variable in function definition would effect or reflect corresponding changes in the variables passed as reference. The symbol used for denoting the passed by reference is & (ampersand). For Example: Suppose two integer variables x and y are defined in the calling function, the main program with values x=5 and y=4. Suppose the function exforsys receives the value as passed by reference from this function it is defined and called as follows: #include <iostream.h> void main( ) { void exforsys(int&,int&); //Function Declaration - & denotes passed by reference int x=5,y=4; exforsys(x,y); cout<<”\n The output from Calling program is:”; cout<<”\n x=”<< x; cout<<”\n y=”<< y; } void exforsys(int& s, int& u) //Function Definition - & denotes passed by reference { s=s*10; u=u*10; } 21 In the above example, the reference arguments are indicated by the symbol ampersand & following the data type of the argument. In the above program, since the variables or arguments are passed by reference the assignment is as follows: void exforsys(int& s, int& u) exforsys(x, y); The variable x and y are passed to the called function exforsys where it is associated with variables s and u. Whatever changes were made in the variable s and u effect or reflect on the variables x and y respectively and vice versa. Only in the above, the function multiplied the value of variable s and u by 10 which is reflected in the variable a and b. Thus, in this case, it has not returned any value by using return statement. The programmer must take careful notice and make use of the passed by reference concept by returning more than one value. By using the concept of passing by reference, it is possible to return more than one value. The output of the above program would be The output from Calling program is: x=50 y=40 C++ Inline Functions In this C++ tutorial, you will learn about Inline function, what is inline function, reason for the need of inline function, what happens when an inline function is written, general format of inline function explained with example. What is Inline Function? Inline functions are functions where the call is made to inline functions. The actual code then gets placed in the calling program. Reason for the need of Inline Function: Normally, a function call transfers the control from the calling program to the function and after the execution of the program returns the control back to the calling program after the function call. These concepts of function saved program space and memory space are used because the function is stored only in one place and is only executed when it is called. This concept of function execution may be time consuming since the registers and other processes must be saved before the function gets called. The extra time needed and the process of saving is valid for larger functions. If the function is short, the programmer may wish to place the code of the function in the calling program in order for it to be executed. This type of function is best handled by the inline function. In this situation, 22 the programmer may be wondering “why not write the short code repeatedly inside the program wherever needed instead of going for inline function?” Although this could accomplish the task, the problem lies in the loss of clarity of the program. If the programmer repeats the same code many times, there will be a loss of clarity in the program. The alternative approach is to allow inline functions to achieve the same purpose, with the concept of functions. What happens when an inline function is written? The inline function takes the format as a normal function but when it is compiled it is compiled as inline code. The function is placed separately as inline function, thus adding readability to the source program. When the program is compiled, the code present in function body is replaced in the place of function call. General Format of inline Function: The general format of inline function is as follows: inline datatype function_name(arguments) The keyword inline specified in the above example, designates the function as inline function. For example, if a programmer wishes to have a function named exforsys with return value as integer and with no arguments as inline it is written as follows: inline int exforsys( ) Example: The concept of inline functions: #include <iostream.h> int exforsys(int); void main( ) { int x; cout << “\n Enter the Input Value: ”; cin>>x; cout<<”\n The Output is: “ << exforsys(x); } inline int exforsys(int x1) { return 5*x1; } 23 The output of the above program is: Enter the Input Value: 10 The Output is: 50 The output would be the same even when the inline function is written solely as a function. The concept, however, is different. When the program is compiled, the code present in the inline function exforsys( ) is replaced in the place of function call in the calling program. The concept of inline function is used in this example because the function is a small line of code. The above example, when compiled, would have the structure as follows: #include <iostream.h> int exforsys(int); void main( ) { int x; cout << “\n Enter the Input Value: ”; cin>>x; //The exforsys(x) gets replaced with code return 5*x1; cout<<”\n The Output is: “ << exforsys(x); } When the above program is written as normal function the compiled code would look like below: #include <iostream.h> int exforsys(int); void main( ) { int x; cout << “\n Enter the Input Value: ”; cin>>x; //Call is made to the function exforsys cout<<”\n The Output is: “ << exforsys(x); } int exforsys(int x1) { return 5*x1; } 24 A programmer must make wise choices when to use inline functions. Inline functions will save time and are useful if the function is very small. If the function is large, use of inline functions must be avoided. Scope of Variables in Function In this C++ Tutorial you will learn about Scope of Variables in Function viz Local Variables - Scope of Local Variables, Global Variables - Scope of Global Variables. The scope of the variables can be broadly be classified as Local Variables Global Variables Local Variables: The variables defined local to the block of the function would be accessible only within the block of the function and not outside the function. Such variables are called local variables. That is in other words the scope of the local variables is limited to the function in which these variables are declared. Let us see this with a small example: #include <iostream.h> int exforsys(int,int); void main( ) { int b; int s=5,u=6; b=exforsys(s,u); cout<<”\n The Output is:”<<b; } int exforsys(int x,int y) { int z; z=x+y; return(z); } In the above program the variables x, y, z are accessible only inside the function exforsys( ) and their scope is limited only to the function exforsys( ) and not outside the function. Thus the variables x, y, z are local to the function exforsys. Similarly one would not be able to access variable b inside the function exforsys as such. This is because variable b is local to function main. 25 Global Variables: Global variables are one which are visible in any part of the program code and can be used within all functions and outside all functions used in the program. The method of declaring global variables is to declare the variable outside the function or block. For instance #include <iostream.h> int x,y,z; //Global Variables float a,b,c; //Global Variables void main( ) { int s,u; //Local Variables float w,q; //Local Variables …….. ……………. } In the above the integer variables x, y and z and the float variables a, b and c which are declared outside the block are global variables and the integer variables s and u and the float variables w and q which are declared inside the function block are called local variables. Thus the scope of global variables is between the point of declaration and the end of compilation unit whereas scope of local variables is between the point of declaration and the end of innermost enclosing compound statement. Let us see an example which has number of local and global variable declarations with number of inner blocks to understand the concept of local and global variables scope in detail. #include <iostream.h> int g; void main( ) { int a; { int b; b=25; a=45; g=65; } a=50; exforsys( ); } void exforsys( ) 26 { g = 30; //Scope of g is throughout the program and so is used between function calls } Scope of b is till the first braces shaded as Yellow Scope of a is till the end of main brace shaded as red In the context of scope of variables in functions comes the important concept named as storage class which is discussed in detail in next section. Static Functions - An Introduction The static member functions have a class scope and they do not have access to the 'this' pointer of the class. When a member is declared as static that is a static member of class have only one such data for the entire class even though there are many objects created for the class. The main usage of static function is when the programmer wants to have a function which is accessible even when the class is not instantiated. Defining Static Function: This is done by using the keyword static before the member function which is to be declared as static function. General syntax: static return_data_type fucntionname() //Static function defined with keyword static { statement1; statement2; //Statements for execution inside static function .......... .......... } For example if a function exforsys returning nothing is to be declared as staic function it is done as follows: static void exforsys() { ........; .......; } Accessing Static Function: 27 We have seen in our earlier sections about accessing a normal member function. To recollect a normal member function gets accessed using the object and an operator called as the dot member access operator. The function declared as static or static functions gets accessed using just the class name and the operator called as scope resolution operator which is not possible in case of normal member functions. Let us see an example to understand the declaration of static member function and how to access static member function in detail: #include <iostream.h> class example { private: static int sum; //Static data int x; public: example() //Constructor of the class { sum=sum+1; x=sum; } ~example() //Destructor of the class { sum=sum-1; } static void exforsys() //Static function exforsys( ) defined with keyword static { cout<<"\nResult is: "<<sum; } void number() //Normal member function number( ) { cout<<"\nNumber is: "<<x; } }; void main() { example e1; example::exforsys(); //Static function exforsys() accessed using class name example and the scope resolution operator :: example e2,e3,e4; 28 example::exforsys(); e1.number(); e2.number(); //Normal member function accessed using object e1 and the dot member access operator . e3.number(); e4.number(); } The output of the above program is: Result is: 1 Result is: 4 Number is: 1 Number is: 2 Number is: 3 Number is: 4 In the above we have seen that the function exforsys() s defined as static function and the integer data type sum is declared as static data type. We see that four objects e1, e2, e3 and e4 are created for the class example. The constructor of the class example increments the sum by 1 and the destructor of the class decrements sum by 1. We can see that the static function is accessed using the class name example and the scope resolution operator :: as example::exforsys(); But the normal member function number() is accessed using the object name and the dot member access operator as e1.number() e2.number() e3.number() e4.number() The first time when the static function exforsys() is called there was one object created and this the sum gets incremented by 1 in the constructor printing the result of sum as 1.When the static function exforsys() is called the second time there were three more objects e2,e3 and e4 created which makes the sum to gets incremented thrice from 1 in the constructor of the corresponding class namely example making the value of sum as 4 which is displayed in the second result. From the above explanation it is clear that the static function operate on the class and not in object generally. Also for accessing static function one can use the class name followed by the scope resolution operator as seen in example above. One must note the following while using static member functions: 29 A static member function can access only static member data, static member functions and data and functions outside the class. So one must take essential care not to use static member function like a non-static member function which can access all of the above including the static data member. A non-static member functions can be declared as virtual but care must be taken not to declare a static member function as virtual. One must first understand the concept of static data also while learning the context of static functions. It is possible to declare a data member of a class as static irrespective of it being public or private type in class definition. If a data is declared as static then the static data gets created and initialized only once. That is unlike non-static data members that are created again and again, for each separate object of the class the static data gets created and initialized only once. Just like the concept of static data, in which the variables are shared by all objects of the class in static functions also it apply to all objects of the class A non-static member function can be called only after instantiating the class as an object. But it is not the case with static member functions. Because a static member function can be called, even when a class is not instantiated, Also a static member function cannot have access to the 'this' pointer of the class. C++ Friend Functions In this C++ tutorials, you will learn about friend functions, need for friend function, how to define and use friend function and few important points regarding friend function, explained with example. Need for Friend Function: As discussed in the earlier sections on access specifiers, when a data is declared as private inside a class, then it is not accessible from outside the class. A function that is not a member or an external class will not be able to access the private data. A programmer may have a situation where he or she would need to access private data from non-member functions and external classes. For handling such cases, the concept of Friend functions is a useful tool. What is a Friend Function? A friend function is used for accessing the non-public members of a class. A class can allow non- member functions and other classes to access its own private data, by making them friends. Thus, a friend function is an ordinary function or a member of another class. How to define and use Friend Function in C++: 30 The friend function is written as any other normal function, except the function declaration of these functions is preceded with the keyword friend. The friend function must have the class to which it is declared as friend passed to it in argument. Some important points to note while using friend functions in C++: The keyword friend is placed only in the function declaration of the friend function and not in the function definition. . It is possible to declare a function as friend in any number of classes. . When a class is declared as a friend, the friend class has access to the private data of the class that made this a friend. . A friend function, even though it is not a member function, would have the rights to access the private members of the class. . It is possible to declare the friend function as either private or public. . The function can be invoked without the use of an object. The friend function has its argument as objects, seen in example below. Example to understand the friend function: #include class exforsys { private: int a,b; public: void test() { a=100; b=200; } friend int compute(exforsys e1) //Friend Function Declaration with keyword friend and with the object of class exforsys to which it is friend passed to it }; int compute(exforsys e1) { //Friend Function Definition which has access to private data 31 return int(e1.a+e2.b)-5; } main() { exforsys e; e.test(); cout<<"The result is:"< //Calling of Friend Function with object as argument. } The output of the above program is The result is:295 The function compute() is a non-member function of the class exforsys. In order to make this function have access to the private data a and b of class exforsys , it is created as a friend function for the class exforsys. As a first step, the function compute() is declared as friend in the class exforsys as: friend int compute (exforsys e1) The keyword friend is placed before the function. The function definition is written as a normal function and thus, the function has access to the private data a and b of the class exforsys. It is declared as friend inside the class, the private data values a and b are added, 5 is subtracted from the result, giving 295 as the result. This is returned by the function and thus the output is displayed as shown above. Concept of Pointers: Every storage location of memory has an associated address. Address is a number that grows sequentially. For every program placed in memory, each variable or function in the program has an associated address. The address of operator: The address of operator or Reference operator is denoted by the notation &. When the user wants to get the address of a variable, then the reference operator & can be used. The operator & is used to find the address associated with a variable. The syntax of the reference operator is as follows: &variablename This means that the address of the variablename is achieved. 32 For Example #include <iostream.h> void main() { int exf=200; int test=300; cout<<endl<<&exf <<endl<<&test; } The output of the above program is: 0x92343456 0x78252348 The &exf has the address associated with the integer variable exf and the &test has the address associated with the integer variable test which are displayed using the cout statement. Using the understanding of address of operators, the discussion turns to the concept of pointers. exforsys = 100; test = exforsys; x = &exforsys; Using the above information, the assignment takes place as below: exforsys is an integer variable having the value of 100 stored in memory address location 3501. When the variable exforsys is assigned to the variable test in the second statement: test = exforsys; The value of the variable exforsys 100 is copied to the variable test. In the third statement, the address of the variable exforsys is denoted by reference operator &exforsys is assigned to the variable x as: 33 x = &exforsys; The address of the variable 3501 and not the contents of the variable exforsys is copied into the variable x. The pointers concept fits in this statement. Pointers are the variable that store the reference to another variable. Pointers are variables that store the address of the variable that it is pointed by. Variable x is referred to as the pointer in the above example. The programmer must note that the address operator placed before a variable is not the same as operator & placed after the variable. For example, &x is not same as x&. Variable &x refers to address operator whereas x& refer to reference operator&. Pointer is a variable that holds the address, also called pointer variable. Defining Pointer Variables or Pointer: In order to define pointer variables, the programmer must use the operator denoted as * in C++. The symbol * when placed before a pointer, variable means that it as a pointer to. While defining variables, the data type is placed before it. When the programmer wants to define the integer variable i it is written: int i; A programmer may think that to define pointer variable there is a separate data type. But this is not the case. There is no separate data type for pointer available. When a programmer defines a pointer variable, he or she can point to integer, float, char. The compiler must know the type of data the pointer is pointing to. To define pointer variable is as follows: datatype_of_ variable_pointedto* pointer_varaible; For example: char* ch; This defines that ch is a pointer variable which points to char data type. int* i; This defines that i is a pointer variable which points to int data type. float* f; This defines that f is a pointer variable which points to float data type. C++ Void Pointer and Null Pointer 34 In this C++ tutorial, you will learn about two interesting types of pointers; void pointers and Null Pointer. These pointers will be discussed in conjunction with syntax, usage and example. Pointer to Void General Syntax: void* pointer_variable; Void is used as a keyword. Referring back to pointer definitions and usage, it is known that the data type the pointer variable defines is the same as the data type the pointer points to. The address placed in a pointer must have the same type as the pointer. For example: int i; float f; int* exf; float* test; then exf=&i; Is correct because the address of integer variable is stored in an integer pointer. If a user writes the statement: exf=&f; Then this statement produces an error. The address of the float variable is stored in an integer pointer that is incorrect. Similarly, if the programmer tries to place the address of an integer variable to a float pointer, such as: test=&i; The above statement will also show an error. The Pointer to Void is a special type of pointer that the programmer can use to point to any data type. Using the above example, the programmer declares pointer to void in this manner: void* sample; 35 Using the above example‟s definition and assigning the pointer to void to the address of an integer variable is perfectly correct. sample=&i; Using the above example to define the pointer to void and assign the pointer to void to the address of a float variable as below is also perfectly correct. sample=&f; Pointer to void, or a void pointer, is a special type of pointer that has a great facility of pointing to any data type. There are limitations in the usage of void pointers that are explained below. The concept of dereferencing using the operator * has been explained in an earlier section of this tutorial. The programmer must note that void pointers cannot be de-referenced in the same manner. Direct dereferencing of void pointer is not permitted. The programmer must change the pointer to void as any other pointer type that points to valid data types such as, int, char, float and then dereference it. This conversion of pointer to some other valid data type is achieved by using the concept of type-casting (refer to type-casting section of this tutorial). NULL Pointer: The concept of NULL pointer is different from the above concept of void pointer. NULL pointer is a type of pointer of any data type and generally takes a value as zero. This is, however, not mandatory. This denotes that NULL pointer does not point to any valid memory address. For example: int* exforsys; exforsys=0; The above statement denotes exforsys as an integer pointer type that does not point to a valid memory address. This shows that exforsys has a NULL pointer value. The difference between void pointers and NULL pointers: A Void pointer is a special type of pointer of void and denotes that it can point to any data type. NULL pointers can take any pointer type, but do not point to any valid reference or memory address. It is important to note that a NULL pointer is different from a pointer that is not initialized. For example, if a programmer uses the program below: #include <iostream.h> int *exforsys; 36 void main() { *exforsys=100; } The output of the above program is NULL POINTER ASSIGNMENT The above program will result in a runtime error. This means that the pointer variable exforsys is not assigned any valid address and, therefore, attempting to access the address 0 gives the above error message. UNIT-2 What is Type Conversion It is the process of converting one type into another. In other words converting an expression of a given type into another is called type casting. How to achieve this There are two ways of achieving the type conversion namely: Automatic Conversion otherwise called as Implicit Conversion Type casting otherwise called as Explicit Conversion Let us see each of these in detail: Automatic Conversion otherwise called as Implicit Conversion This is not done by any conversions or operators. In other words value gets automatically converted to the specific type in which it is assigned. Let us see this with an example: #include <iostream.h> void main() { short x=6000; 37 int y; y=a; } In the above example the data type short namely variable x is converted to int and is assigned to the integer variable y. So as above it is possible to convert short to int, int to float and so on. Type casting otherwise called as Explicit Conversion Explicit conversion can be done using type cast operator and the general syntax for doing this is datatype (expression); Here in the above datatype is the type which the programmer wants the expression to gets changed as In C++ the type casting can be done in either of the two ways mentioned below namely: C-style casting C++-style casting The C-style casting takes the synatx as (type) expression This can also be used in C++. Apart from the above the other form of type casting that can be used specifically in C++ programming language namely C++-style casting is as below namely: type (expression) This approach was adopted since it provided more clarity to the C++ programmers rather than the C-style casting. Say for instance the as per C-style casting (type) firstVariable * secondVariable is not clear but when a programmer uses the C++ style casting it is much more clearer as below type (firstVariable) * secondVariable 38 Let us see the concept of type casting in C++ with a small example: #include <iostream.h> void main() { int a; float b,c; cout<< “Enter the value of a:”; cin>>a; cout<< “\n Enter the value of b:”; cin>>b; c = float(a)+b; cout<<”\n The value of c is:”<<c; } The output of the above program is Enter the value of a: 10 Enter the value of b: 12.5 The value of c is: 22.5 Constructors: What is the use of Constructor The main use of constructors is to initialize objects. The function of initialization is automatically carried out by the use of a special member function called a constructor. General Syntax of Constructor Constructor is a special member function that takes the same name as the class name. The syntax generally is as given below: <class name> { arguments}; The default constructor for a class X has the form X::X() In the above example the arguments is optional. The constructor is automatically named when an object is created. A constructor is named whenever an object is defined or dynamically allocated using the "new" operator. There are several forms in which a constructor can take its shape namely: 39 Default Constructor: This constructor has no arguments in it. Default Constructor is also called as no argument constructor. For example: class Exforsys { private: int a,b; public: Exforsys(); ... }; Exforsys :: Exforsys() { a=0; b=0; } Copy constructor: This constructor takes one argument. Also called one argument constructor. The main use of copy constructor is to initialize the objects while in creation, also used to copy an object. The copy constructor allows the programmer to create a new object from an existing one by initialization. For example to invoke a copy constructor the programmer writes: Exforsys e3(e2); or Exforsys e3=e2; Both the above formats can be sued to invoke a copy constructor. For Example: 40 #include <iostream.h> class Exforsys() { private: int a; public: Exforsys() { } Exforsys(int w) { a=w; } Exforsys(Exforsys& e) { a=e.a; cout<<” Example of Copy Constructor”; } void result() { cout<< a; } }; void main() { Exforsys e1(50); Exforsys e3(e1); cout<< “\ne3=”;e3.result(); } In the above the copy constructor takes one argument an object of type Exforsys which is passed by reference. The output of the above program is Example of Copy Constructor e3=50 Some important points about constructors: A constructor takes the same name as the class name. The programmer cannot declare a constructor as virtual or static, nor can the programmer declare a constructor as const, volatile, or const volatile. No return type is specified for a constructor. The constructor must be defined in the public. The constructor must be a public member. 41 Overloading of constructors is possible. This will be explained in later sections of this tutorial. Destructors What is the use of Destructors Destructors are also special member functions used in C++ programming language. Destructors have the opposite function of a constructor. The main use of destructors is to release dynamic allocated memory. Destructors are used to free memory, release resources and to perform other clean up. Destructors are automatically named when an object is destroyed. Like constructors, destructors also take the same name as that of the class name. General Syntax of Destructors ~ classname(); The above is the general syntax of a destructor. In the above, the symbol tilda ~ represents a destructor which precedes the name of the class. Some important points about destructors: Destructors take the same name as the class name. Like the constructor, the destructor must also be defined in the public. The destructor must be a public member. The Destructor does not take any argument which means that destructors cannot be overloaded. No return type is specified for destructors. For example: class Exforsys { private: …………… public: Exforsys() { } ~ Exforsys() { } } Operator Overloading 42 Operator overloading is a very important feature of Object Oriented Programming. Curious to know why!!? It is because by using this facility programmer would be able to create new definitions to existing operators. In fact in other words a single operator can take up several functions as desired by programmers depending on the argument taken by the operator by using the operator overloading facility. After knowing about the feature of operator overloading now let us see how to define and use this concept of operator overloading in C++ programming language. We have seen in previous sections the different types of operators. Broadly classifying operators are of two types namely: Unary Operators Binary Operators Unary Operators: As the name implies takes operate on only one operand. Some unary operators are namely ++ called as Increment operator, -- called as Decrement Operator, ! , ~, unary minus. Binary Operators: The arithmetic operators, comparison operators, and arithmetic assignment operators all this which we have seen in previous section of operators come under this category. Both the above classification of operators can be overloaded. So let us see in detail each of this. Operator Overloading – Unary operators As said before operator overloading helps the programmer to define a new functionality for the existing operator. This is done by using the keyword operator. The general syntax for defining an operator overloading is as follows: return_type classname :: operator operator symbol(argument) { ………….. statements; } Thus the above clearly specifies that operator overloading is defined as a member function by making use of the keyword operator. 43 In the above: return_type – is the data type returned by the function class name - is the name of the class operator – is the keyword operator symbol – is the symbol of the operator which is being overloaded or defined for new functionality :: - is the scope resolution operator which is used to use the function definition outside the class. The usage of this is clearly defined in our earlier section of How to define class members. For example Suppose we have a class say Exforsys and if the programmer wants to define a operator overloading for unary operator say ++, the function is defined as Inside the class Exforsys the data type that is returned by the overloaded operator is defined as class Exforsys { private: ………. public: void operator ++( ); ………… }; So the important steps involved in defining an operator overloading in case of unary operators are namely: Inside the class the operator overloaded member function is defined with the return data type as member function or a friend function. The concept of friend function we will 44 define in later sections. If in this case of unary operator overloading if the function is a member function then the number of arguments taken by the operator member function is none as seen in the below example. In case if the function defined for the operator overloading is a friend function which we will discuss in later section then it takes one argument. The operator overloading is defined as member function outside the class using the scope resolution operator with the keyword operator as explained above Now let us see how to use this overloaded operator member function in the program #include <iostream.h> class Exforsys { private: int x; public: Exforsys( ) { x=0; } //Constructor void display(); void Exforsys ++( ); }; void Exforsys :: display() { cout<<”\nValue of x is: “ << x; } void Exforsys :: operator ++( ) //Operator Overloading for operator ++ defined { ++x; } void main( ) { Exforsys e1,e2; //Object e1 and e2 created cout<<”Before Increment” cout <<”\nObject e1: ”<<e1.display(); cout <<”\nObject e2: ”<<e2.display(); ++e1; //Operator overloading applied ++e2; cout<<”\n After Increment” cout <<”\nObject e1: ”<<e1.display(); cout <<”\nObject e2: ”<<e2.display(); } The output of the above program is: 45 Before Increment Object e1: Value of x is: 0 Object e1: Value of x is: 0 Before Increment Object e1: Value of x is: 1 Object e1: Value of x is: 1 In the above example we have created 2 objects e1 and e2 f class Exforsys. The operator ++ is overloaded and the function is defined outside the class Exforsys. When the program starts the constructor Exforsys of the class Exforsys initialize the values as zero and so when the values are displayed for the objects e1 and e2 it is displayed as zero. When the object ++e1 and ++e2 is called the operator overloading function gets applied and thus value of x gets incremented for each object separately. So now when the values are displayed for objects e1 and e2 it is incremented once each and gets printed as one for each object e1 and e2. This is how unary operators get overloaded. We will see in detail how to overload binary operators in next section Operator Overloading – Binary Operators Binary operators, when overloaded, are given new functionality. The function defined for binary operator overloading, as with unary operator overloading, can be member function or friend function. The difference is in the number of arguments used by the function. In the case of binary operator overloading, when the function is a member function then the number of arguments used by the operator member function is one (see below example). When the function defined for the binary operator overloading is a friend function, then it uses two arguments. Binary operator overloading, as in unary operator overloading, is performed using a keyword operator. Binary operator overloading example: 46 #include <iostream.h> class Exforsys { private: int x; int y; public: Exforsys() //Constructor { x=0; y=0; } void getvalue( ) //Member Function for Inputting Values { cout << “\n Enter value for x: “; cin >> x; cout << “\n Enter value for y: “; cin>> y; } void displayvalue( ) //Member Function for Outputting Values { cout <<”value of x is: “ << x <<”; value of y is: “<<y } Exforsys operator +(Exforsys); }; Exforsys Exforsys :: operator + (Exforsys e2) //Binary operator overloading for + operator defined { int x1 = x+ e2.x; int y1 = y+e2.y; return Exforsys(x1,y1); } void main( ) { Exforsys e1,e2,e3; //Objects e1, e2, e3 created cout<<\n”Enter value for Object e1:”; e1.getvalue( ); cout<<\n”Enter value for Object e2:”; e2.getvalue( ); e3= e1+ e2; //Binary Overloaded operator used cout<< “\nValue of e1 is:”<<e1.displayvalue(); cout<< “\nValue of e2 is:”<<e2.displayvalue(); 47 cout<< “\nValue of e3 is:”<<e3.displayvalue(); } The output of the above program is: Enter value for Object e1: Enter value for x: 10 Enter value for y: 20 Enter value for Object e2: Enter value for x: 30 Enter value for y: 40 Value of e1 is: value of x is: 10; value of y is: 20 Value of e2 is: value of x is: 30; value of y is: 40 Value of e3 is: value of x is: 40; value of y is: 60 In the above example, the class Exforsys has created three objects e1, e2, e3. The values are entered for objects e1 and e2. The binary operator overloading for the operator „+‟ is declared as a member function inside the class Exforsys. The definition is performed outside the class Exforsys by using the scope resolution operator and the keyword operator. The important aspect is the statement: e3= e1 + e2; The binary overloaded operator „+‟ is used. In this statement, the argument on the left side of the operator „+‟, e1, is the object of the class Exforsys in which the binary overloaded operator „+‟ is a member function. The right side of the operator „+‟ is e2. This is passed as an argument to the operator „+‟ . Since the object e2 is passed as argument to the operator‟+‟ inside the function defined for binary operator overloading, the values are accessed as e2.x and e2.y. This is added with e1.x and e1.y, which are accessed directly as x and y. The return value is of type class Exforsys as defined by the above example. There are important things to consider in operator overloading with C++ programming language. Operator overloading adds new functionality to its existing operators. The programmer must add proper comments concerning the new functionality of the overloaded operator. The program will be efficient and readable only if operator overloading is used only when necessary. Some operators cannot be overloaded: scope resolution operator denoted by :: member access operator or the dot operator denoted by . the conditional operator denoted by ?: and pointer to member operator denoted by .* 48 C++ Memory Management operators Need for Memory Management operators The concept of arrays has a block of memory reserved. The disadvantage with the concept of arrays is that the programmer must know, while programming, the size of memory to be allocated in addition to the array size remaining constant. In programming there may be scenarios where programmers may not know the memory needed until run time. In this case, the programmer can opt to reserve as much memory as possible, assigning the maximum memory space needed to tackle this situation. This would result in wastage of unused memory spaces. Memory management operators are used to handle this situation in C++ programming language What are memory management operators? There are two types of memory management operators in C++: new delete These two memory management operators are used for allocating and freeing memory blocks in efficient and convenient ways. New operator: The new operator in C++ is used for dynamic storage allocation. This operator can be used to create object of any type. General syntax of new operator in C++: The general syntax of new operator in C++ is as follows: pointer variable = new datatype; In the above statement, new is a keyword and the pointer variable is a variable of type datatype. For example: int *a=new int; In the above example, the new operator allocates sufficient memory to hold the object of datatype int and returns a pointer to its starting point. The pointer variable a holds the address of memory space allocated. 49 Dynamic variables are never initialized by the compiler. Therefore, the programmer should make it a practice to first assign them a value. The assignment can be made in either of the two ways: int *a = new int; *a = 20; or int *a = new int(20); delete operator: The delete operator in C++ is used for releasing memory space when the object is no longer needed. Once a new operator is used, it is efficient to use the corresponding delete operator for release of memory. General syntax of delete operator in C++: The general syntax of delete operator in C++ is as follows: delete pointer variable; In the above example, delete is a keyword and the pointer variable is the pointer that points to the objects already created in the new operator. Some of the important points the programmer must note while using memory management operators are described below: The programmer must take care not to free or delete a pointer variable that has already been deleted. . Overloading of new and delete operator is possible (to be discussed in detail in later section on overloading). . We know that sizeof operator is used for computing the size of the object. Using memory management operator, the size of the object is automatically computed. . The programmer must take care not to free or delete pointer variables that have not been allocated using a new operator. . Null pointer is returned by the new operator when there is insufficient memory available for allocation. 50 Example: to understand the concept of new and delete memory management operator in C++: #include <iostream.h> void main() { //Allocates using new operator memory space in memory for storing a integer datatype int *a= new a; *a=100; cout << " The Output is:a="<<a; //Memory Released using delete operator delete a; } The output of the above program is The Output is:a=100 In the above program, the statement: int *a= new a; Holds memory space in memory for storing a integer datatype. The statement: *a=100 This denotes that the value present in address location pointed by the pointer variable a is 100 and this value of a is printed in the output statement giving the output shown in the example above. The memory allocated by the new operator for storing the integer variable pointed by a is released using the delete operator as: delete a; C++ Dereference Operator In this C++ tutorial, you will learn how to access the value of variables pointed by the pointer variables using pointer concept discussed in detail. It is possible to access the value of variables pointed by the pointer variables using pointer. This is performed by using the Dereference operator in C++ which has the notation *. 51 The general syntax of the Dereference operator is as follows: *pointer_variable In this example, pointer variable denotes the variable defined as pointer. The * placed before the pointer_variable denotes the value pointed by the pointer_variable. For example: exforsys = 100; test = exforsys; x = &exforsys; y=*x; In the above example, the assignment takes place as below: In the above example, the exforsys is a integer variable having the value of 100 stored in memory address location 3501. The variable exforsys is assigned to the variable test in the second statement: test = exforsys; The value of the variable exforsys is 100 and is then copied to the variable test. 52 In the third statement, the address of the variable exforsys denoted by reference operator &exforsys is assigned to the variable x as: x = &exforsys; The address of the variable 3501 and not the contents of the variable exforsys is then copied into the variable x. The fourth statement makes use of the deference operator: y=*x This means that the value pointed to by the pointer variable x gives the value 100 to y. Some points for the programmer to note: The programmer must note that the x refers to the address 3501 whereas *x refers to the value stored in the address 3501 namely 100. . The reference operator is denoted by & and deference operator denoted by * . Both differ in their meaning and functionality. The reference operator denotes the address of. The dereference operator denotes the value pointed by. In short, a deference variable can be denoted as referenced. . If the programmer wants to define more than two pointer variables, then comma operator may be used in this instance. The programmer must carefully place pointer symbol * before each pointer variable. For instance, if the user wishes to define two integer pointer variables, e1 and e2, this can be done as follows: int *e1,*e2; If the programmer declares: int *e1,e2; This means that e1 is a pointer variable pointing to integer data type but e2 is only an integer data type and not a pointer type. The programmer must ensure to place * before each pointer variable. The dereferencing or indirect addressing is performed using the indirection operator * used to access the value stored in an address. The defining of pointer variable: 53 int* exf; The definition of pointer variable as in the above case is the pointer variable exf. It is also possible to assign value to pointer variable using indirection operator. This gives the same effect as working with variables. For Example: #include void main() { int example,test; int* exforsys; exforsys=&example; example=200; test=200; *exforsys=100; test=*exforsys; cout< cout< } The output of the above program is 100 100 In the above example, the pointer variable exforsys points to integer variable example and takes the address of the variable example as: test and example have initial values of 200. 54 The statement: *exforsys=100 Sets the value of the variable pointed by pointer variable exforsys as 100. This is equivalent to setting example=100. The assignment is as follows: example test The statement test=*exforsys; sets the value of variable test with the value pointed by the pointer variable exforsys which is 100 as seen in the above example. Test also becomes 100 and the assignment becomes: Both results are displayed having the value 100. 55 UNIT-3 C++ Inheritance Introduction What is Inheritance? Inheritance is the process by which new classes called derived classes are created from existing classes called base classes. The derived classes have all the features of the base class and the programmer can choose to add new features specific to the newly created derived class. For example, a programmer can create a base class named fruit and define derived classes as mango, orange, banana, etc. Each of these derived classes, (mango, orange, banana, etc.) has all the features of the base class (fruit) with additional attributes or features specific to these newly created derived classes. Mango would have its own defined features, orange would have its own defined features, banana would have its own defined features, etc. This concept of Inheritance leads to the concept of polymorphism. Features or Advantages of Inheritance: Reusability: Inheritance helps the code to be reused in many situations. The base class is defined and once it is compiled, it need not be reworked. Using the concept of inheritance, the programmer can create as many derived classes from the base class as needed while adding specific features to each derived class as needed. Saves Time and Effort: The above concept of reusability achieved by inheritance saves the programmer time and effort. Since the main code written can be reused in various situations as needed. I ncreases Program Structure which results in greater reliability. Polymorphism General Format for implementing the concept of Inheritance: class derived_classname: access specifier baseclassname For example, if the base class is exforsys and the derived class is sample it is specified as: 56 class sample: public exforsys The above makes sample have access to both public and protected variables of base class exforsys. Reminder about public, private and protected access specifiers: If a member or variables defined in a class is private, then they are accessible by members of the same class only and cannot be accessed from outside the class. . Public members and variables are accessible from outside the class. . Protected access specifier is a stage between private and public. If a member functions or variables defined in a class are protected, then they cannot be accessed from outside the class but can be accessed from the derived class. Inheritance Example: class exforsys { public: exforsys(void) { x=0; } void f(int n1) { x= n1*5; } void output(void) { cout<<x; } private: int x; }; class sample: public exforsys { public: sample(void) { s1=0; } void f1(int n1) { s1=n1*10; } void output(void) { exforsys::output(); cout << s1; 57 } private: int s1; }; int main(void) { sample s; s.f(10); s.output(); s.f1(20); s.output(); } The output of the above program is 50 200 In the above example, the derived class is sample and the base class is exforsys. The derived class defined above has access to all public and private variables. Derived classes cannot have access to base class constructors and destructors. The derived class would be able to add new member functions, or variables, or new constructors or new destructors. In the above example, the derived class sample has new member function f1( ) added in it. The line: sample s; creates a derived class object named as s. When this is created, space is allocated for the data members inherited from the base class exforsys and space is additionally allocated for the data members defined in the derived class sample. The base class constructor exforsys is used to initialize the base class data members and the derived class constructor sample is used to initialize the data members defined in derived class. The access specifier specified in the line: class sample: public exforsys Public indicates that the public data members which are inherited from the base class by the derived class sample remains public in the derived class. 58 Inheritance leads to various issues such as: What is Inherited types of inheritance accessibility modes (public, private and protected) in inheritance Friend function and inheritance etc. #include <iostream> using namespace std; class Cpolygon { protected: int width, height; public: void input_values (int one, int two) { width=one; height=two; } }; class Crectangle: public Cpolygon { public: int area () { return (width * height); } }; class Ctriangle: public Cpolygon { public: int area () { return (width * height / 2); } }; int main () { Crectangle rectangle; Ctriangle triangle; rectangle.input_values (2,2); 59 triangle.input_values (2,2); cout << rectangle.area() << endl; cout << triangle.area() << endl; return 0; } In the example above we have used the protected members of the class Cpolygon in the class Crectangle and in the Ctriangle class. This is only possible through Inheritance. What is inherited? When inheritance is done, various links and tables (index, virtual etc) are created which are used to provide the accessibility of the members of the base class in derived class and in other class hierarchy. This means saying “public members are inherited” is better to say as “public members become accessible”. A derived class inherits every member of a base class except: its constructor and its destructor its friends its operator=() members Types of I nheritance There are five different inheritances supported in C++: (1) Simple / Single (2) Multilevel (3) Hierarchical (4) Multiple (5) Hybrid 60 Multiple I nheritance Multiple inheritance is achieved whenever more than one class acts as base classes for other classes. This makes the members of the base classes accessible in the derived class, resulting in better integration and broader re-usability. Take a look at an example: #include <iostream> using namespace std; class Cpolygon { protected: int width, height; public: void input_values (int one, int two) { width=one; height=two; } }; class Cprint { public: void printing (int output); 61 }; void Cprint::printing (int output) { cout << output << endl; } class Crectangle: public Cpolygon, public Cprint { public: int area () { return (width * height); } }; class Ctriangle: public Cpolygon, public Cprint { public: int area () { return (width * height / 2); } }; int main () { Crectangle rectangle; Ctriangle triangle; rectangle.input_values (2,2); triangle.input_values (2,2); rectangle.printing (rectangle.area()); triangle.printing (triangle.area()); return 0; } Note:the two public statements in the Crectangle class and Ctriangle class. Accessibility modes and I nheritance We can use the following chart for seeing the accessibility of the members in the Base class (first class) and derived class (second class). 62 Here X indicates that the members are not inherited, i.e. they are not accessible in the derived class. C++ Polymorphism Introduction Polymorphism is the ability to use an operator or function in different ways. Polymorphism gives different meanings or functions to the operators or functions. Poly, referring to many, signifies the many uses of these operators and functions. A single function usage or an operator functioning in many ways can be called polymorphism. Polymorphism refers to codes, operations or objects that behave differently in different contexts. Below is a simple example of the above concept of polymorphism: 6 + 10 The above refers to integer addition. The same + operator can be used with different meanings with strings: "Exforsys" + "Training" The same + operator can also be used for floating point addition: 7.15 + 3.78 Polymorphism is a powerful feature of the object oriented programming language C++. A single operator + behaves differently in different contexts such as integer, float or strings referring the concept of polymorphism. The above concept leads to operator overloading. The concept of 63 overloading is also a branch of polymorphism. When the exiting operator or function operates on new data type it is overloaded. This feature of polymorphism leads to the concept of virtual methods. Polymorphism refers to the ability to call different functions by using only one type of function call. Suppose a programmer wants to code vehicles of different shapes such as circles, squares, rectangles, etc. One way to define each of these classes is to have a member function for each that makes vehicles of each shape. Another convenient approach the programmer can take is to define a base class named Shape and then create an instance of that class. The programmer can have array that hold pointers to all different objects of the vehicle followed by a simple loop structure to make the vehicle, as per the shape desired, by inserting pointers into the defined array. This approach leads to different functions executed by the same function call. Polymorphism is used to give different meanings to the same concept. This is the basis for Virtual function implementation. In polymorphism, a single function or an operator functioning in many ways depends upon the usage to function properly. In order for this to occur, the following conditions must apply: All different classes must be derived from a single base class. In the above example, the shapes of vehicles (circle, triangle, rectangle) are from the single base class called Shape. The member function must be declared virtual in the base class. In the above example, the member function for making the vehicle should be made as virtual to the base class. Features and Advantages of the concept of Polymorphism: Applications are Easily Extendable: Once an application is written using the concept of polymorphism, it can easily be extended, providing new objects that conform to the original interface. It is unnecessary to recompile original programs by adding new types. Only re-linking is necessary to exhibit the new changes along with the old application. This is the greatest achievement of C++ object-oriented programming. In programming language, there has always been a need for adding and customizing. By utilizing the concept of polymorphism, time and work effort is reduced in addition to making future maintenance easier. Helps in reusability of code. Provides easier maintenance of applications. Helps in achieving robustness in applications. Types of Polymorphism: C++ provides three different types of polymorphism. Virtual functions Function name overloading Operator overloading 64 In addition to the above three types of polymorphism, there exist other kinds of polymorphism: run-time compile-time ad-hoc polymorphism parametric polymorphism Other types of polymorphism defined: run-time: The run-time polymorphism is implemented with inheritance and virtual functions. compile-time: The compile-time polymorphism is implemented with templates. ad-hoc polymorphism: If the range of actual types that can be used is finite and the combinations must be individually specified prior to use, this is called ad-hoc polymorphism. parametric polymorphism: If all code is written without mention of any specific type and thus can be used transparently with any number of new types it is called parametric polymorphism. In general, there are two main categories of Polymorphism namely Ad Hoc Polymorphism Pure Polymorphism Overloading concepts fall under the category of Ad Hoc Polymorphism and Virtual methods. Templates or parametric classes fall under the category of Pure Polymorphism. Virtual Functions What are Virtual Functions? Virtual, as the name implies, is something that exists in effect but not in reality. The concept of virtual function is the same as a function, but it does not really exist although it appears in needed places in a program. The object-oriented programming language C++ implements the concept of virtual function as a simple member function, like all member functions of the class. The functionality of virtual functions can be over-ridden in its derived classes. The programmer must pay attention not to confuse this concept with function overloading. Function overloading is 65 a different concept and will be explained in later sections of this tutorial. Virtual function is a mechanism to implement the concept of polymorphism (the ability to give different meanings to one function). Need for Virtual Function: The vital reason for having a virtual function is to implement a different functionality in the derived class. For example: a Make function in a class Vehicle may have to make a Vehicle with red color. A class called FourWheeler, derived or inherited from Vehicle, may have to use a blue background and 4 tires as wheels. For this scenario, the Make function for FourWheeler should now have a different functionality from the one at the class called Vehicle. This concept is called Virtual Function. Properties of Virtual Functions: Dynamic Binding Property: Virtual Functions are resolved during run-time or dynamic binding. Virtual functions are also simple member functions. The main difference between a non-virtual C++ member function and a virtual member function is in the way they are both resolved. A non-virtual C++ member function is resolved during compile time or static binding. Virtual Functions are resolved during run-time or dynamic binding Virtual functions are member functions of a class. Virtual functions are declared with the keyword virtual, detailed in an example below. Virtual function takes a different functionality in the derived class. Declaration of Virtual Function: Virtual functions are member functions declared with the keyword virtual. For example, the general syntax to declare a Virtual Function uses: class classname //This denotes the base class of C++ virtual function { public: virtual void memberfunctionname() //This denotes the C++ virtual function { ............. ............ } }; 66 Referring back to the Vehicle example, the declaration of Virtual function would take the shape below: class Vehicle //This denotes the base class of C++ virtual function { public: virtual void Make() //This denotes the C++ virtual function { cout <<"Member function of Base Class Vehicle Accessed"<<endl; } }; After the virtual function is declared, the derived class is defined. In this derived class, the new definition of the virtual function takes place. When the class FourWheeler is derived or inherited from Vehicle and defined by the virtual function in the class FourWheeler, it is written as: class Vehicle //This denotes the base class of C++ virtual function { public: virtual void Make() //This denotes the C++ virtual function { cout <<"Member function of Base Class Vehicle Accessed"<<endl; } }; class FourWheeler : public Vehicle { public: void Make() { cout<<"Virtual Member function of Derived class FourWheeler Accessed"<<endl; } }; void main() { Vehicle *a, *b; a = new Vehicle(); a->Make(); b = new FourWheeler(); b->Make(); } 67 In the above example, it is evidenced that after declaring the member functions Make() as virtual inside the base class Vehicle, class FourWheeler is derived from the base class Vehicle. In this derived class, the new implementation for virtual function Make() is placed. The programmer might be surprised to see the function call differs and the output is then printed as above. If the member function has not been declared as virtual, the base class member function is always called because linking takes place during compile time and is therefore static. In this example, the member function is declared virtual and the address is bounded only during run time, making it dynamic binding and thus the derived class member function is called. To achieve the concept of dynamic binding in C++, the compiler creates a v-table each time a virtual function is declared. This v-table contains classes and pointers to the functions from each of the objects of the derived class. This is used by the compiler whenever a virtual function is needed. C++ Pure Virtual Function and Virtual Base Class In this C++ tutorial, you will learn about pure virtual function, declaration of pure virtual function and virtual base class, virtual base class and how to implement a virtual base class, explained with examples. What is Pure Virtual Function: Pure Virtual Function is a Virtual function with no body. Declaration of Pure Virtual Function: Since pure virtual function has no body, the programmer must add the notation =0 for declaration of the pure virtual function in the base class. General Syntax of Pure Virtual Function takes the form: class classname //This denotes the base class of C++ virtual function { public: virtual void virtualfunctioname() = 0 //This denotes the pure virtual function in C++ }; The other concept of pure virtual function remains the same as described in the previous section of virtual function. 68 To understand the declaration and usage of Pure Virtual Function, refer to this example: class Exforsys { public: virtual void example()=0; //Denotes pure virtual Function Definition }; class Exf1:public Exforsys { public: void example() { cout<<"Welcome"; } }; class Exf2:public Exforsys { public: void example() { cout<<"To Training"; } }; void main() { Exforsys* arra[2]; Exf1 e1; Exf2 e2; arra[0]=&e1; arra[1]=&e2; arra[0]->example(); arra[1]->example(); } Since the above example has no body, the pure virtual function example() is declared with notation =0 in the base class Exforsys. The two derived class named Exf1 and Exf2 are derived from the base class Exforsys. The pure virtual function example() takes up new definition. In the main function, a list of pointers is defined to the base class. 69 Two objects named e1 and e2 are defined for derived classes Exf1 and Exf2. The address of the objects e1 and e2 are stored in the array pointers which are then used for accessing the pure virtual function example() belonging to both the derived class EXf1 and EXf2 and thus, the output is as in the above example. The programmer must clearly understand the concept of pure virtual functions having no body in the base class and the notation =0 is independent of value assignment. The notation =0 simply indicates the Virtual function is a pure virtual function as it has no body. Some programmers might want to remove this pure virtual function from the base class as it has no body but this would result in an error. Without the declaration of the pure virtual function in the base class, accessing statements of the pure virtual function such as, arra[0]->example() and arra[1]->example() would result in an error. The pointers should point to the base class Exforsys. Special care must be taken not to remove the statement of declaration of the pure virtual function in the base class. Virtual Base Class In the above example, there are two derived classes Exf1 and Exf2 from the base class Exforsys. As shown in the above diagram, the Training class is derived from both of the derived classes Exf1 and Exf2. In this scenario, if a user has a member function in the class Training where the user wants to access the data or member functions of the class Exforsys it would result in error if it is performed like this: class Exforsys { protected: int x; }; class Exf1:public Exforsys { }; 70 class Exf2:public Exforsys { }; class Training:public Exf1,public Exf2 { public: int example() { return x; } }; The above program results in a compile time error as the member function example() of class Training tries to access member data x of class Exforsys. This results in an error because the derived classes Exf1 and Exf2 (derived from base class Exforsys) create copies of Exforsys called subobjects. This means that each of the subobjects have Exforsys member data and member functions and each have one copy of member data x. When the member function of the class Training tries to access member data x, confusion arises as to which of the two copies it must access since it derived from both derived classes, resulting in a compile time error. When this occurs, Virtual base class is used. Both of the derived classes Exf1 and Exf2 are created as virtual base classes, meaning they should share a common subobject in their base class. For Example: class Exforsys { protected: int x; ; class Exf1:virtual public Exforsys { }; class Exf2:virtual public Exforsys { }; class Training:public Exf1,public Exf2 { 71 public: int example() { return x; } }; In the above example, both Exf1 and Exf2 are created as Virtual base classes by using the keyword virtual. This enables them to share a common subobject of their base class Exforsys. This results in only one copy that the member function example() of Class Training can access the member data x. C++ Polymorphism and Abstract Base Class Before you start reading this C++ tutorial on polymorphism you should have a good understanding of class inheritance and pointers. I ntroduction to Polymorphism Polymorphism is by far the most important and widely used concept in object oriented programming. Some of the widely used technologies and libraries like COM, MFC etc. have polymorphism as their foundation. If you look at all the original design patterns, almost every pattern uses polymorphism in its structure. Polymorphism is a mechanism that allows you to implement a function in different ways. Pointers to base class We have seen that it is possible to derive a class from a base class and that we can add functionality to member functions. One of the features of derived classes is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism takes advantage of this feature. Let‟s take a look at an example of a base class and derived classes: #include <iostream> using namespace std; class CPolygon { protected: int width, height; public: 72 void setup (int first, int second) { width= first; height= second; } }; class CRectangle: public CPolygon { public: int area() { return (width * height); } }; class CTriangle: public CPolygon { public: int area() { return (width * height / 2); } }; int main () { CRectangle rectangle; CTriangle triangle; CPolygon * ptr_polygon1 = &rectangle; CPolygon * ptr_polygon2 = &triangle; ptr_polygon1->setup(2,2); ptr_polygon2->setup(2,2); cout << rectangle.area () << endl; cout << triangle.area () << endl; return 0; } As you can see, we create two pointers (ptr_polygon1 and ptr_polygon2) that point to the objects of class CPolygon. Then we assign to these pointers the address of (using the reference 73 ampersand sign) the objects rectangle and triangle. Both rectangle and triangle are objects of classes derived from CPolygon. In the cout statement we use the objects rectangle and triangle instead of the pointers ptr_polygon1 and ptr_polygon2. We do this because ptr_polygon1 and ptr_polygon2 are of the type CPolygon. This means we can only use the pointers to refer to members that CRectangle and CTriangle inherit from Cpolygon. If we want to use the pointers to class CPolygon then area() should be declared in the class CPolygon and not only in the derived classes CRectangle and Ctriangle. The problem is that we use different versions of area() in the derived classes – CRectangle and Ctriangle – so we can‟t implement one version of area() in the base class CPolygon. (If they were the same we had no problem.) We can fix this by using virtual members. Virtual Members A virtual member is a member of a base class that we can redefine in its derived classes. To declare a member as virtual we must use the keyword virtual. Let‟s change our previous example: #include <iostream> using namespace std; class CPolygon { protected: int width, height; public: void setup (int first, int second) { width= first; height= second; } virtual int area() { return (0); } }; class CRectangle: public CPolygon { 74 public: int area() { return (width * height); } }; class CTriangle: public CPolygon { public: int area() { return (width * height / 2); } }; int main () { CRectangle rectangle; CTriangle triangle; CPolygon polygon; CPolygon * ptr_polygon1 = &rectangle; CPolygon * ptr_polygon2 = &triangle; CPolygon * ptr_polygon3 = &polygon; ptr_polygon1->setup(2,2); ptr_polygon2->setup(2,2); ptr_polygon3->setup(2,2); cout << ptr_polygon1->area () << endl; cout << ptr_polygon2->area () << endl; cout << ptr_polygon3->area () << endl; return 0; } Because of the change – adding area() as a virtual member of CPolygon – now all the three classes have all the same members (width, height, setup() and area().) A class that declares or inherits a virtual function is called a polymorphic class. 75 Abstract Base Classes (ABC) At the design level, an abstract base class (ABC) corresponds to an abstract concept. For instance: if you ask to draw a shape, I will probably ask what kind of shape. The term “shape” is an abstract concept, it could be a circle, a square, etc, etc. You could say in C++ that class CShape is an abstract base class (ABC) and class circle (etc) could be a derived class. As we look at the C++ language we could say that an abstract base class has one or more pure virtual member functions. In the example above we put an implementation (return (0);) in the virtual member function area(). If we want to change it into a pure virtual member function we use =0; instead of the return (0). So the class will look like this: class CPolygon { protected: int width, height; public: void setup (int first, int second) { width= first; height= second; } virtual int area() = 0; }; This pure virtual function area() makes CPolygon an abstract base class. But you have to remember the following: by adding a pure virtual member to the base class, you are forced to also add the member to any derived class. So our example should now look like this: #include <iostream> using namespace std; class CPolygon { protected: int width, height; public: void setup (int first, int second) { 76 width= first; height= second; } virtual int area() = 0; }; 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 rectangle; CTriangle triangle; CPolygon * ptr_polygon1 = &rectangle; CPolygon * ptr_polygon2 = &triangle; ptr_polygon1->setup(2,2); ptr_polygon2->setup(2,2); cout << ptr_polygon1->area () << endl; cout << ptr_polygon2->area () << endl; return 0; } Note: there is also an extra void in the derived classes CRectangle and CTriangle. Using a unique type of pointer (CPolygon*) we can point to objects of different but related classes. 77 We can make use of that. For instance: we could implement an extra function member in the abstract base class CPolygon that can print the result of the area() function. (Remember that CPolygon itself has no implementation for the function area() and still we can use it, isn‟t it cool.) After implementation of such a function the example will look like this: #include <iostream> using namespace std; class CPolygon { protected: int width, height; public: void setup (int first, int second) { width= first; height= second; } virtual int area(void) = 0; void onscreen(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 () { 78 CRectangle rectangle; CTriangle triangle; CPolygon * ptr_polygon1 = &rectangle; CPolygon * ptr_polygon2 = &triangle; ptr_polygon1->setup(2,2); ptr_polygon2->setup(2,2); ptr_polygon1->onscreen(); ptr_polygon2->onscreen(); return 0; } As you can see this can be very useful. Dynamic Allocation In an earlier tutorial we already looked at dynamic allocation using the „new‟ operator. (So we don‟t have to explain that again). In this last section we will change the previous example and we will dynamically allocate the objects. Take a look at the next example: #include <iostream> using namespace std; class CPolygon { protected: int width, height; public: void setup (int first, int second) { width= first; height= second; } virtual int area(void) = 0; void onscreen(void) { cout << this->area() << endl; } }; 79 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 * ptr_polygon1 = new CRectangle; CPolygon * ptr_polygon2 = new CTriangle; ptr_polygon1->setup(2,2); ptr_polygon2->setup(2,2); ptr_polygon1->onscreen(); ptr_polygon2->onscreen(); delete ptr_polygon1; delete ptr_polygon2; return 0; } Typecasting Typecasting is the concept of converting the value of one type into another type. For example, you might have a float that you need to use in a function that requires an integer. 80 I mplicit conversion Almost every compiler makes use of what is called automatic typecasting. It automatically converts one type into another type. If the compiler converts a type it will normally give a warning. For example this warning: conversion from „double‟ to „int‟, possible loss of data. The problem with this is, that you get a warning (normally you want to compile without warnings and errors) and you are not in control. With control we mean, you did not decide to convert to another type, the compiler did. Also the possible loss of data could be unwanted. Explicit conversion The C and C++ languages have ways to give you back control. This can be done with what is called an explicit conversion. Sure you may still lose data, but you decide when to convert to another type and you don‟t get any compiler warnings. Let‟s take a look at an example that uses implicit and explicit conversion: #include <iostream> using namespace std; int main() { int a; double b=2.55; a = b; cout << a << endl; a = (int)b; cout << a << endl; a = int(b); cout << a << endl; } Note: the output of all cout statements is 2. The first conversion is an implicit conversion (the compiler decides.) As explained before, the compiler should give a warning. The second conversion is an explicit typecast, in this case the C style explicit typecast. 81 The third conversion is also explicit typecast, in this case the C++ style explicit typecast. Four typecast operators The C++ language has four typecast operators: static_cast reinterpret_cast const_cast dynamic_cast Static_cast Automatic conversions are common in every C++ program. You have: Standard conversion. For instance: from short to int or from int to float. User defined conversions (Class conversions.) Conversion from derived class to base class. (Take a look at the inheritance tutorial) The static_cast can be used for all these types of conversion. Take a look at an example: int a = 5; int b = 2; double out; // typecast a to double out = static_cast<double>(a)/b; It may take some time to get used to the notation of the typecast statement. (The rumour goes that Bjarne Stroustrup made it difficult on purpose, to discourage the use of typecasting.) Between the angle brackets you place to which type the object should be casted. Between the parentheses you place the object that is casted. It is not possible to use static_cast on const objects to non-const objects. For this you have to use const_cast. (Further down we take a look at const_cast.) If an automatic conversion is valid (from enum to int for instance) then you can use static_cast to do the opposite (from int to enum.) For instance: enum my_numbers { a=10, c=100, e=1000 }; 82 const my_numbers b = static_cast<my_numbers> (50); const my_numbers d = static_cast<my_numbers> (500); Note: We add some new values (b and d). These are type-cast from int to enum. Reinterpret_cast The reinterpret_cast is used for casts that are not save: Between integers and pointers Between pointers and pointers Between function-pointers and function-pointers For instance the typecast from an integer to a character pointer: char *ptr_my = reinterpret_cast<char *>(0xb0000); Note: the example above uses a fixed memory location. If we use the reinterpret_cast on a null-pointer then we get a null-pointer of the asked type: char *ptr_my = 0; int *ptr_my_second = reinterpret_cast<int *>(ptr_my); The reinterpret_cast is almost as dangerous as an “old fashion” cast. The only guaranty that you get is that if you cast an object back to the original data-type (before the first cast) then the original value is also restored (of course only if the data-type was big enough to hold the value.) The only difference with an old fashion cast is that const is respected. This means that a reinterpret_cast can not be used to cast a const object to non-const object. For instance: char *const MY = 0; // This is not valid because MY is a const!! int *ptr_my = reinterpret_cast<int *>( MY); 83 Const_cast The only way to cast away the const properties of an object is to use const_cast. Take a look at an example: void a(Person* b); int main() { const Person *ptr_my = new Person("Joe"); a( const_cast<Person *>(ptr_my) ); } The use of const_cast on an object doesn‟t guarantee that the object can be used (after the const is cast away.) Because it is possible that the const-objects are put in read-only memory by the program. The const_cast can not be used to cast to other data-types, as it is possible with the other cast functions. Take a look at the next example: int a; const char *ptr_my = "Hello"; a = const_cast<int *>(ptr_my); a = reinterpret_cast<const char*>(ptr_my); a = reinterpret_cast<int *>(const_cast<char *>(ptr_my) ); Note: casting from const char * to int * isn‟t very good (not to say a very dirty trick.) Normally you won‟t do this. The first statement (const_cast) will give an error, because the const_cast can‟t convert the type. The second statement (reinterpret_cast) will also give an error, because the reinterpret_cast can‟t cast the const away. The third statement will work (mind the note. It is a dirty trick, better not use it.) That is all for this tutorial. In typecasting part 2 we will look at RTTI, dynamic_cast, typeid and type_info. 84 RTTI, dynamic_cast, typeid and type_info Before you start this C++ programming tutorial on RTTI, dynamic_cast, typeid and type_info, make sure you fully understand the previous tutorial on static_cast, const_cast and reinterpret_cast. Runtime Type I nformation (RTTI ) Runtime Type Information (RTTI) is the concept of determining the type of any variable during execution (runtime.) The RTTI mechanism contains: The operator dynamic_cast The operator typeid The struct type_info RTTI can only be used with polymorphic types. This means that with each class you make, you must have at least one virtual function (either directly or through inheritance.) Compatibility note: On some compilers you have to enable support of RTTI to keep track of dynamic types. So to make use of dynamic_cast (see next section) you have to enable this feature. See you compiler documentation for more detail. Dynamic_cast The dynamic_cast can only be used with pointers and references to objects. It makes sure that the result of the type conversion is valid and complete object of the requested class. This is way a dynamic_cast will always be successful if we use it to cast a class to one of its base classes. Take a look at the example: class Base_Class { }; class Derived_Class: public Base_Class { }; Base_Class a; Base_Class * ptr_a; Derived_Class b; Derived_Class * ptr_b; ptr_a = dynamic_cast<Base_Class *>(&b); ptr_b = dynamic_cast<Derived_Class *>(&a); The first dynamic_cast statement will work because we cast from derived to base. The second dynamic_cast statement will produce a compilation error because base to derived conversion is not allowed with dynamic_cast unless the base class is polymorphic. 85 If a class is polymorphic then dynamic_cast will perform a special check during execution. This check ensures that the expression is a valid and complete object of the requested class. Take a look at the example: // dynamic_cast #include <iostream> #include <exception> using namespace std; class Base_Class { virtual void dummy() {} }; class Derived_Class: public Base_Class { int a; }; int main () { try { Base_Class * ptr_a = new Derived_Class; Base_Class * ptr_b = new Base_Class; Derived_Class * ptr_c; ptr_c = dynamic_cast< Derived_Class *>(ptr_a); if (ptr_c ==0) cout << "Null pointer on first type-cast" << endl; ptr_c = dynamic_cast< Derived_Class *>(ptr_b); if (ptr_c ==0) cout << "Null pointer on second type-cast" << endl; } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();} return 0; } In the example we perform two dynamic_casts from pointer objects of type Base_Class* (namely ptr_a and ptr_b) to a pointer object of type Derived_Class*. If everything goes well then the first one should be successful and the second one will fail. The pointers ptr_a and ptr_b are both of the type Base_Class. The pointer ptr_a points to an object of the type Derived_Class. The pointer ptr_b points to an object of the type Base_Class. So when the dynamic type cast is performed then ptr_a is pointing to a full object of class Derived_Class, but the pointer ptr_b points to an object of class Base_Class. This object is an incomplete object of class Derived_Class; thus this cast will fail! Because this dynamic_cast fails a null pointer is returned to indicate a failure. When a reference type is converted with dynamic_cast and the conversion fails then there will be an exception thrown out instead of the null pointer. The exception will be of the type bad_cast. 86 With dynamic_cast it is also possible to cast null pointers even between the pointers of unrelated classes. Dynamic_cast can cast pointers of any type to void pointer(void*). Typeid and typ_info If a class hierarchy is used then the programmer doesn‟t have to worry (in most cases) about the data-type of a pointer or reference, because the polymorphic mechanism takes care of it. In some cases the programmer wants to know if an object of a derived class is used. Then the programmer can make use of dynamic_cast. (If the dynamic cast is successful, then the pointer will point to an object of a derived class or to a class that is derived from that derived class.) But there are circumstances that the programmer (not often) wants to know the prizes data-type. Then the programmer can use the typeid operator. The typeid operator can be used with: Variables Expressions Data-types Take a look at the typeid example: #include <iostream> #include <typeinfo> using namespace std; int main () { int * a; int b; a=0; b=0; if (typeid(a) != typeid(b)) { cout << "a and b are of different types:\n"; cout << "a is: " << typeid(a).name() << '\n'; cout << "b is: " << typeid(b).name() << '\n'; } return 0; } Note: the extra header file typeinfo. 87 The result of a typeid is a const type_info&. The class type_info is part of the standard C++ library and contains information about data-types. (This information can be different. It all depends on how it is implemented.) A bad_typeid exception is thrown by typeid, if the type that is evaluated by typeid is a pointer that is preceded by a dereference operator and that pointer has a null value. UNIT-4 What is a Stream? A stream is an object where a program can either insert or extract characters to or from it. The standard input and output stream objects of C++ are declared in the header file iostream. Standard Input Stream Generally, the device used for input is the keyboard. For inputting, the keyword cin is used, which is an object. The overloaded operator of extraction, >>, is used on the standard input stream, in this case: cin stream. Syntax for using the standard input stream is cin followed by the operator >> followed by the variable that stores the data extracted from the stream. For example: int prog; cin >> prog; In the example above, the variable prog is declared as an integer type variable. The next statement is the cin statement. The cin statement waits for input from the user‟s keyboard that is then stored in the integer variable prog. The input stream cin wait before proceeding for processing or storing the value. This duration is dependent on the user pressing the RETURN key on the keyboard. The input stream cin waits for the user to press the RETURN key then begins to process the command. It is also possible to request input for more than one variable in a single input stream statement. A single cin statement is as follows: cin >> x >> y; is the same as: cin >> x; cin >> y; 88 In both of the above cases, two values are input by the user, one value for the variable x and another value for the variable y. // This is a sample program This is a comment Statement #include <iostream.h> Header File Inclusion Statement void main() { int sample, example; cin >> sample; cin >> example; } If a programmer wants to write comments in C++ program, the comments should follow after a pair of slashes denoted by //. All the characters after the // are ignored by C++ compiler and the programmer can choose to comment after the // In the above example, two integer variables are input with values. The programmer can produce input of any data type. It is also possible to input strings in C++ program using cin. This is performed using the same procedures. The vital point to note is cin stops when it encounters a blank space. When using a cin, it is possible to produce only one word. If a user wants to input a sentence, then the above approach would be tiresome. For this purpose, there is a function in C++ called getline. Standard Output Stream By default, the device used for output is the screen of the computer. For outputting values the keyword cout is used, which is an object. The insertion operator << is used on the standard output cout stream. The syntax for using the standard output stream is cout followed by the operator << followed by the value to be inserted or output by the insertion operator. For example: int prog; cin >> prog; cout << prog; In the above example, the variable prog is declared as an integer type variable. The next statement is the cin statement that waits for input from the user‟s keyboard. This information is then stored in the integer variable prog. The value of prog is displayed on the screen by the standard output stream cout. It is also possible to display a sentence as follows: cout << “ Training given by Exforsys”; 89 The above gives output as: Training given by Exforsys If a programmer chooses to use constant strings of characters, they must be enclosed between double quotes “ ”. In this situation, it is important to note the difference between the two statements below: cout << "exforsys"; cout << exforsys; In the above, the first statement displays on the screen as exforsys. The second statement outputs the value of the variable exforsys. As previously explained, the extraction operator >> can be used more than once in a single cin statement. Similarly, it is possible to use the insertion operator << more than once in a cout statement. For example cout << "Exforsys" << "gives" << "excellent training"; This produces output on the screen as: Exforsys gives excellent training The above concept is mainly used if the programmer chooses to print string constants followed by variables. In this next example, the programmer chooses to display a combination of string constants and variables. For example int a=50; cout << "Exforsys has given" << a << "numbers of trainings"; This produces the output as: Exforsys has given 50 numbers of trainings Below is one more example: 90 cout << “Exforsys”; cout << “Training”; The above produces output as: Exforsys Training An important point to note from the above example is cout does not give a line break unless specified. If the programmer chooses to display output in a new line, it must be explicitly specified in cout by using the \n which denotes newline character. for example cout << "Exforsys\n "; cout << "Training"; gives output as Exforsys Training There is also another way for specifying newline that is by using endl manipulator. For example cout << "Exforsys" << endl; cout << "Training"; gives the same output as Exforsys Training Example to demonstrate the use of input and output streams // Example to demonstrate the use of Input and Output streams #include <iostream.h> void main() { int a,b; cout << “Enter the value of a:”; cin >> a; b=a+10; 91 cout << “Value of b is:”<<b; } File IO in C++ (text and binary files) This C++ programming language tutorial will be in two parts. The first (this one) will cover the theory behind IO and in the second tutorial we will look at some examples. I nput-Output Input – Output is a process of transfer of data from one computer device to another or from one part of the computer to another. There are three categories of Input-Output: Standard IO Memory IO Network IO Standard IO is used frequently for which C++ provides cin, cout, cerr and clog streams. IO in C++ is done through stream classes, which are having the following inheritance hierarchy: We can use the ifstream, ofstream and fstream classes to perform file IO. (cin is an object of class istream and cout is an object of class ostream.) 92 File IO means transfer of data from secondary memory (hard disk) to main memory or vice-versa. A schematic showing the flow of data and classes involved is as follows: Note: The arrows indicate the flow of data. Text and binary files The C++ language supports two types of files: Text files Binary files The basic difference between text files and binary files is that in text files various character translations are performed such as “\r+\f” is converted into “\n”, whereas in binary files no such translations are performed. By default, C++ opens the files in text mode. In the tables below we will see the various steps and operations that can (or must) be performed to use files in C++: 1)Creating or opening a file For writing data Text Files ofstream out (”myfile.txt”); or ofstream out; Binary Files ofstream out (”myfile.txt”,ios::binary); or ofstream out; 93 out.open(”myfile.txt”); out.open(”myfile.txt”, ios::binary); For Appending (adding text at the end of the existing file) Text Files ofstream out(”myfile.txt”,ios::app); or ofstream out; out.open(”myfile.txt”, ios::app); Binary Files ofstream out (”myfile.txt”,ios::app|ios::binary); or ofstream out; out.open(”myfile.txt”, ios::app | ios::binary); For reading data Text Files ifstream in (”myfile.txt”); or ifstream in ; in.open(”myfile.txt”); Binary Files ifstream in (”myfile.txt”, ios::binary); or ifstream in ; in.open(”myfile.txt”, ios::binary); 2) Closing Files (after reading or writing) ofstream object “out” Ifstream object “in” out.close(); in.close(); 3) Reading / Writing Data to and from files Data Functions for reading file Function for writing into file 94 char get(); put(); 1 word >> (extraction operator) << (insertion operator) >=1 word getline(); << (insertion operator) Objects read() write() Binary data Same as above Same as above 4) Functions that can be used to perform special tasks Operation function Description Checking end of file. eof() Used to check eof during the reading of file Check if an operation fails. bad() Returns true if a reading or writing operation fails. Check if an operation fails. Fail() Returns true in the same cases as bad(), but also in the case that a format error happens. Checking for opened file. is_open(); Checks if the file is opened or not, returns true if the file is opened else false Number of bytes already read. gcount() Returns count of the bytes read from the file 95 Ignoring characters during file read. ignore() Ignores n bytes from the file. (get pointer is positioned after n character) Checking next character. peek() Checks the next available character, will not increase the get pointer to next character. Random access (only for binary files). seekg() seekp() tellg() tellp() In case of binary files random access is performed using these functions. They either give or set the position of get and put pointers on the particular location Read and Write characters (text file) In the example below we will write and read characters from a text file. We will write a character (given by the user) to a file test.txt as long as the answer to the question “Continue?:” is replayed with y (from yes). If we answer with something else the program must close the test.txt file. The last part of the program will read all characters in the file test.txt. #include<iostream> #include<fstream> using namespace std; int main(int argc, char *argv[]) { //code to write characters in a file char c, ans; ans='y'; //Open an output stream ofstream out ("test.txt"); if(out.is_open()) { //Loop will continue until something other then y is entered 96 while (ans=='y') { cout <<endl << "Continue ?"; cin >> ans; if(ans=='y') { cout << endl << "Enter Character :"; cin >> c; out.put(c); } } } out.close(); //code for reading file completely ifstream in("test.txt"); if(in.is_open()) { while(!in.eof()) { c = in.get(); if(!in.eof()) cout << c; } } in.close(); } Special flags The table below shows the special flags that can be used to manage files. (These flags are used during opening of the files). ios::app Opens the file in append mode ios::ate Opens the file and set the cursor at end of the file ios::binary Opens the file in binary mode ios::in Opens the file for reading ios::out Opens the file for writing ios::trunc Opens the file and truncates all the contents from it 97 Binary copy In the next example we will copy a binary file to another binary file. We will open two files in binary mode, one for reading and one for writing. If both are open then we will copy the content of one file to another. #include<iostream> #include<fstream> using namespace std; int main(int argc, char *argv[]) { //Open an input and output stream in binary mode ifstream in("myimage.jpg",ios::binary); ofstream out("myimage1.jpg",ios::binary); if(in.is_open() && out.is_open()) { while(!in.eof()) { out.put(in.get()); } } //Close both files in.close(); out.close(); } Note: of course you need to supply the myimage.jpg file to copy (or any other jpg file). Reading and writing an object In this last example we will write and read an object (student object) to and from a file: #include<iostream> #include<fstream> using namespace std; class Student 98 { char name[20]; int mark; public: void GetStudentData(); void ShowStudentData(); }; void Student :: GetStudentData() { cout << "Enter Student Name:" << endl; cin >> name; cout << "Enter Student Mark:" << endl; cin >> mark; } void Student :: ShowStudentData() { cout << "Student Details are:" << endl; cout << "Name: " << name << endl << "Mark: " << mark << endl; } int main(int argc, char *argv[]) { char ans='y'; Student sobj; //We open student.dat in append mode ofstream out("student.dat", ios::app); if(out.is_open()) { //Loop will continue until something other then y is entered while( ans == 'y') { cout << endl << "Continue ?"; cin >> ans; if(ans == 'y') { sobj.GetStudentData(); out.write((char*) & sobj, sizeof(sobj)); } } } out.close(); 99 ifstream in("student.dat"); if(in.is_open()) { while(!in.eof()) { in.read((char*) &sobj, sizeof(sobj)); if(!in.eof()) { sobj.ShowStudentData(); } } } in.close(); } As you can see there are many ways to do file IO in the C++ programming language. IOstream Library (Standard Input / Output Streams Library ) The iostream library is an object-oriented library that provides input and output functionality using streams. A stream is an abstraction that represents a device on which input and ouput operations are performed. A stream can basically be represented as a source or destination of characters of indefinite length. 100 Streams are generally associated to a physical source or destination of characters, like a disk file, the keyboard, or the console, so the characters gotten or written to/from our abstraction called stream are physically input/output to the physical device. For example, file streams are C++ objects to manipulate and interact with files; Once a file stream is used to open a file, any input or output operation performed on that stream is physically reflected in the file. To operate with streams, C++ provides the standard iostream library, which contains the following elements: Basic class templates The base of the iostream library is the hierarchy of class templates. The class templates provide most of the functionality of the library in a type-independent fashion. This is a set of class templates, each one having two template parameters: the char type (charT) parameter, that determines the type of elements that are going to be manipulated and the traits parameter, that provides additional characteristics specific for a particular type of elements. The class templates in this class hierarchy have the same name as their char-type instantiations but with the prefix basic_. For example, the class template which istream is instantiated from is called basic_istream, the one from which fstream is is called basic_fstream, and so on... The only exception is ios_base, which is by itself type-independent, and therefore is not based on a template, but is a regular class. Class template instantiations The library incorporates two standard sets of instantiations of the entire iostream class template hierarchy: one is narrow-oriented, to manipulate elements of type char and another one, wide-oriented, to manipulate elements of type wchar_t. The narrow-oriented (char type) instantiation is probably the better known part of the iostream library. Classes like ios, istream and ofstream are narrow-oriented. The diagram on top of this page shows the names and relationships of narrow-oriented classes. The classes of the wide-oriented (wchar_t) instatiation follow the same naming conventions as the narrow-oriented instantiation but with the name of each class and object prefixed with a w character, forming wios, wistream and wofstream, as an example. Standard objects As part of the iostream library, the header file <iostream> declares certain objects that are used to perform input and output operations on the standard input and output. 101 They are divided in two sets: narrow-oriented objects, which are the popular cin, cout, cerr and clog and their wide-oriented counterparts, declared as wcin, wcout, wcerr and wclog. Types The iostream classes barely use fundamental types on their member's prototypes. They generally use defined types that depend on the traits used in their instantiation. For the default char and wchar_t instantiations, types streampos, streamoff and streamsize are used to represent positions, offsets and sizes, respectively. Manipulators Manipulators are global functions designed to be used together with insertion (<<) and extraction (>>) operators performed on iostream stream objects. They generally modify properties and formatting settings of the streams. endl, hex and scientific are some examples of manipulators. Organization The library and its hierarchy of classes is split in different files: <ios>, <istream>, <ostream>, <streambuf> and <iosfwd> aren't usually included directly in most C++ programs. They describe the base classes of the hierarchy and are automatically included by other header files of the library that contain derived classes. <iostream> declares the objects used to communicate through the standard input and output (including cin and cout). <fstream> defines the file stream classes (like the template basic_ifstream or the class ofstream) as well as the internal buffer objects used with these (basic_filebuf). These classes are used to manipulate files using streams. <sstream>: The classes defined in this file are used to manipulate string objects as if they were streams. <iomanip> declares some standard manipulators with parameters to be used with extraction and insertion operators to modify internal flags and formatting options. Compatibility notes The names, prototypes and examples included in this reference for the iostream classes mostly describe and use the char instantiations of the class templates instead of the templates themselves, even though these classes are only one of their possible instantiations. We believe this provides a better readability and is arguably as easy to obtain the names and prototypes of the basic template from the char instantiation as the opposite. Elements of the iostream library (char instantitation) Classes: ios_base Base class with type-independent members for the standard stream classes (class) ios Base class with type-dependent members for the standard stream classes (class) 102 istream Input stream (class) ostream Output Stream (class) iostream Input/Output Stream (class) ifstream Input file stream class (class) ofstream Output file stream (class) fstream Input/output file stream class (class) istringstream Input string stream class (class) ostringstream Output string stream class (class) stringstream Input/output string stream class (class) streambuf Base buffer class for streams (class) filebuf File stream buffer (class) stringbuf String stream buffer (class) Objects: cin Standard input stream (object) cout Standard output stream (object) cerr Standard output stream for errors (object) clog Standard output stream for logging (object) Types: fpos Stream position class template (class template) streamoff Stream offset type (type) streampos Stream position type (type) streamsize Stream size type (types) Manipulators: boolalpha Alphanumerical bool values (manipulator function) dec Use decimal base (manipulator function) endl Insert newline and flush (manipulator function) 103 ends Insert null character (manipulator function) fixed Use fixed-point notation (manipulator function) flush Flush stream buffer (manipulator function) hex Use hexadecimal base (manipulator function) internal Adjust field by inserting characters at an internal position (manipulator function) left Adjust output to the left (manipulator function) noboolalpha No alphanumerical bool values (manipulator function) noshowbase Do not show numerical base prefixes (manipulator function) noshowpoint Do not show decimal point (manipulator function) noshowpos Do not show positive signs (manipulator function) noskipws Do not skip whitespaces (manipulator function) nounitbuf Do not force flushes after insertions (manipulator function) nouppercase Do not generate upper case letters (manipulator function) oct Use octal base (manipulator function) resetiosflags Reset format flags (manipulator function) right Adjust output to the right (manipulator function) scientific Use scientific notation (manipulator function) setbase Set basefield flag (manipulator function) setfill Set fill character (manipulator function) setiosflags Set format flags (manipulator function) setprecision Set decimal precision (manipulator function) 104 setw Set field width (manipulator function) showbase Show numerical base prefixes (manipulator function) showpoint Show decimal point (manipulator function) showpos Show positive signs (manipulator function) skipws Skip whitespaces (manipulator function) unitbuf Flush buffer after insertions (manipulator function) uppercase Generate upper-case letters (manipulator function) ws Extract whitespaces (manipulator function) What is a Manipulator? Manipulators are operators used in C++ for formatting output. The data is manipulated by the programmer‟s choice of display. There are numerous manipulators available in C++. Some of the more commonly used manipulators are provided here below: endl Manipulator: This manipulator has the same functionality as the „\n‟ newline character. For example: cout << "Exforsys" << endl; cout << "Training"; 105 produces the output: Exforsys Training setw Manipulator: This manipulator sets the minimum field width on output. The syntax is: setw(x) Here setw causes the number or string that follows it to be printed within a field of x characters wide and x is the argument set in setw manipulator. The header file that must be included while using setw manipulator is <iomanip.h> #include <iostream.h> #include <iomanip.h> void main( ) { int x1=12345,x2= 23456, x3=7892; cout << setw(8) << ”Exforsys” << setw(20) << ”Values” << endl << setw(8) << “E1234567” << setw(20)<< x1 << end << setw(8) << “S1234567” << setw(20)<< x2 << end << setw(8) << “A1234567” << setw(20)<< x3 << end; } The output of the above example is: setw(8) setw(20) Exforsys Values E1234567 12345 106 S1234567 23456 A1234567 7892 setfill Manipulator: This is used after setw manipulator. If a value does not entirely fill a field, then the character specified in the setfill argument of the manipulator is used for filling the fields. #include <iostream.h> #include <iomanip.h> void main( ) { cout << setw(10) << setfill('$') << 50 << 33 << endl; } The output of the above program is $$$$$$$$5033 This is because the setw sets 10 width for the field and the number 50 has only 2 positions in it. So the remaining 8 positions are filled with $ symbol which is specified in the setfill argument. setprecision Manipulator: The setprecision Manipulator is used with floating point numbers. It is used to set the number of digits printed to the right of the decimal point. This may be used in two forms: fixed scientific These two forms are used when the keywords fixed or scientific are appropriately used before the setprecision manipulator. The keyword fixed before the setprecision manipulator prints the 107 floating point number in fixed notation. The keyword scientific before the setprecision manipulator prints the floating point number in scientific notation. #include <iostream.h> #include <iomanip.h> void main( ) { float x = 0.1; cout << fixed << setprecision(3) << x << endl; cout << sceintific << x << endl; } The above gives ouput as: 0.100 1.000000e-001 C++ Tutorial – Namespaces and anonymous namespaces Namespaces are used in the C++ programming language to create a separate region for a group of variables, functions and classes etc. Namespaces are needed because there can be many functions, variables for classes in one program and they can conflict with the existing names of variables, functions and classes. C++ uses namespace to avoid the conflicts. The concept can be depicted using the following diagram: 108 The following will list the primary reasons due to which a conflict can occur: Between User defined variables/functions/classes and built in Library variables/functions/classes (e.g. sqrt(), abs() etc.) Between the separately included library and built in library Between the variables/functions/classes of the one separately included library and the same of the other separately included library Creating Namespaces The C++ language include the keyword “namespace” for creating namespaces. namespace { members of namespace; } Take a look at an example: #include<iostream> using namespace std; namespace myconstants { const double pi = 3.141592; } namespace myshapes { double area; double perimeter; void AreaOfCircle(double radius) { area = myconstants::pi * radius * radius; } void PerimeterOfCircle(double radius) { perimeter = 2 * myconstants::pi * radius; } } int main(int argc, char * argv[]) { 109 double r; cout << endl << "Enter Radius:"; cin >> r; myshapes::AreaOfCircle(r); cout << endl << "Area of the Circle is :" << myshapes::area; myshapes::PerimeterOfCircle(r); cout << endl << "Perimeter of the Circle is :" << myshapes::perimeter; } Namespaces have the following important points: 1) We can have more than one namespace of the same name. This gives the advantage of defining the same namespace in more than one file (although they can be created in the same file as well). Take a look at an example: #include<iostream> using namespace std; namespace mynamespace { int x; } namespace mynamespace { int y; } int main(int argc, char * argv[]) { mynamespace::x = mynamespace::y =5; cout << mynamespace::x << endl << mynamespace::y; } 2)We can have anonymous namespaces (namespace with no name). They are directly usable in the same program and are used for declaring unique identifiers. It also avoids making global static variable. The “anonymous” namespace you have created will only be accessible within the file you created it in. 110 Take a look at an example: /*This is file1.cpp*/ #include<iostream> using namespace std; namespace { int local; } void func(); int main() { local = 1; cout << "Local=" << local << endl; func(); cout << "Local=" << local << endl; return 0; } /* This is file2.cpp */ namespace { // Should not collide with other files int local; } void func() { local = 2; } Note: you will need to include both the files in the same project. The result of this program should be: Local = 1 Local = 1 111 It should not be: Local = 1 Local = 2 3) C++ has a default namespace named std, which contains all the default library of the C++ included using #include directive. Using Namespaces Namespaces are used with the „using‟ keyword, which makes all the members of the namespace available in the current program and the members can be used directly, without taking reference of the namespace. Namespaces are also usable by taking their name reference with scope resolution operator. This method allows distinguishing between the members of the same name in two different namespaces. Take a look at an example: #include<iostream> using namespace std; namespace mynamespace { int i,j; } using namespace mynamespace; int main(int argc, char *argv[]) { cout << endl << i << ", " << j; } Or without the „using‟ keyword: #include<iostream> using namespace std; namespace mynamespace { 112 int i,j; } int main(int argc, char *argv[]) { std::cout << endl << mynamespace::i << ", " << mynamespace::j; } Namespace alias It is also possible to declare an alternate name for an existing namespace. We can use the following format: namespace new_name = current_name; Namespace std All the files in the C++ standard library declare all of its entities within the std namespace. For that reason we include the „using namespace std;‟ statement in most programs that is using any entity defined in iostream. UNIT-5 Unit-5 C++ Function templates are those functions which can handle different data types without separate code for each of them. For a similar operation on several kinds of data types, a programmer need not write different versions by overloading a function. It is enough if he writes a C++ template based function. This will take care of all the data types. There are two types of templates in C++, viz., function templates and class templates. This article deals with only the function templates. There are lot of occasions, where we might need to write the same functions for different data types. A favorite example can be addition of two variables. The variable can be integer, float or double. The requirement will be to return the corresponding return type based on the input type. If we start writing one function for each of the data type, then we will end up with 4 to 5 different functions, which can be a night mare for maintenance. 113 C++ templates come to our rescue in such situations. When we use C++ function templates, only one function signature needs to be created. The C++ compiler will automatically generate the required functions for handling the individual data types . This is how a programmer's life is made a lot easier. C++ Template functions - Details: Let us assume a small example for Add function. If the requirement is to use this Add function for both integer and float, then two functions are to be created for each of the data type (overloading). int Add(int a,int b) { return a+b;} // function Without C++ template float Add(float a, float b) { return a+b;} // function Without C++ template If there are some more data types to be handled, more functions should be added. But if we use a c++ function template, the whole process is reduced to a single c++ function template. The following will be the code fragment for Add function. template <class T> T Add(T a, T b) //C++ function template sample { return a+b; } This c++ function template definition will be enough. Now when the integer version of the function, the compiler generates an Add function compatible for integer data type and if float is called it generates float type and so on. Here T is the typename. This is dynamically determined by the compiler according to the parameter passed. The keyword class means, the parameter can be of any type. It can even be a class. C++ Template functions - Applicability: C++ function templates can be used wherever the same functionality has to be performed with a number of data types. Though very useful, lots of care should be taken to test the C++ template functions during development. A well written c++ template will go a long way in saving time for programmers. C++ Class Templates are used where we have multiple copies of code for different data types with the same logic. If a set of functions or classes have the same functionality for different data types, they becomes good candidates for being written as Templates. 114 One good area where this C++ Class Templates are suited can be container classes. Very famous examples for these container classes will be the STL classes like vector, list etc., Once code is written as a C++ class template, it can support all data types. Though very useful, It is advisable to write a class as a template after getting a good hands-on experience on the logic (by writing the code with normal data types). There are cases where we need specialization for writing optimized code for specific data types. This C++ class template Specialization article gives a brief description. This article describes how to declare, define and use the C++ Class Templates in practice. This tries to build a very preliminary Queue, using the STL::Vector container class. This code is written and tested with Microsoft Visual C++ 5.00. Declaring C++ Class Templates: Declaration of C++ class template should start with the keyword template. A parameter should be included inside angular brackets. The parameter inside the angular brackets, can be either the keyword class or typename. This is followed by the class body declaration with the member data and member functions. The following is the declaration for a sample Queue class. //Sample code snippet for C++ Class Template template <typename T> class MyQueue { std::vector<T> data; public: void Add(T const &d); void Remove(); void Print(); }; The keyword class highlighted in blue color, is not related to the typename. This is a mandatory keyword to be included for declaring a template class. Defining member functions - C++ Class Templates: If the functions are defined outside the template class body, they should always be defined with the full template definition. Other conventions of writing the function in C++ class templates are the same as writing normal c++ functions. template <typename T> void MyQueue<T> ::Add(T const &d) { data.push_back(d); } template <typename T> void MyQueue<T>::Remove() { 115 data.erase(data.begin( ) + 0,data.begin( ) + 1); } template <typename T> void MyQueue<T>::Print() { std::vector <int>::iterator It1; It1 = data.begin(); for ( It1 = data.begin( ) ; It1 != data.end( ) ; It1++ ) cout << " " << *It1<<endl; } The Add function adds the data to the end of the vector. The remove function removes the first element. These functionalities make this C++ class Template behave like a normal Queue. The print function prints all the data using the iterator. Full Program - C++ Class Templates: //C++_Class_Templates.cpp #include <iostream.h> #include <vector> template <typename T> class MyQueue { std::vector<T> data; public: void Add(T const &); void Remove(); void Print(); }; template <typename T> void MyQueue<T> ::Add(T const &d) { data.push_back(d); } template <typename T> void MyQueue<T>::Remove() { data.erase(data.begin( ) + 0,data.begin( ) + 1); } template <typename T> void MyQueue<T>::Print() { 116 std::vector <int>::iterator It1; It1 = data.begin(); for ( It1 = data.begin( ) ; It1 != data.end( ) ; It1++ ) cout << " " << *It1<<endl; } //Usage for C++ class templates void main() { MyQueue<int> q; q.Add(1); q.Add(2); cout<<"Before removing data"<<endl; q.Print(); q.Remove(); cout<<"After removing data"<<endl; q.Print(); } Advantages of C++ Class Templates: One C++ Class Template can handle different types of parameters. Compiler generates classes for only the used types. If the template is instantiated for int type, compiler generates only an int version for the c++ template class. Templates reduce the effort on coding for different data types to a single set of code. Testing and debugging efforts are reduced. Templates Function templates Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function. These function templates can use these parameters as if they were any other regular type. 117 The format for declaring function templates with type parameters is: template <class identifier> function_declaration; template <typename identifier> function_declaration; The only difference between both prototypes is the use of either the keyword class or the keyword typename. Its use is indistinct, since both expressions have exactly the same meaning and behave exactly the same way. For example, to create a template function that returns the greater one of two objects we could use: template <class myType> myType GetMax (myType a, myType b) { return (a>b?a:b); } Here we have created a template function with myType as its template parameter. This template parameter represents a type that has not yet been specified, but that can be used in the template function as if it were a regular type. As you can see, the function template GetMax returns the greater of two parameters of this still-undefined type. To use this function template we use the following format for the function call: function_name <type> (parameters); For example, to call GetMax to compare two integer values of type int we can write: int x,y; GetMax <int> (x,y); When the compiler encounters this call to a template function, it uses the template to automatically generate a function replacing each appearance of myType by the type passed as the actual template parameter (int in this case) and then calls it. This process is automatically performed by the compiler and is invisible to the programmer. Here is the entire example: // function template #include <iostream> 6 10 118 using namespace std; template <class T> T GetMax (T a, T b) { T result; result = (a>b)? a : b; return (result); } int main () { int i=5, j=6, k; long l=10, m=5, n; k=GetMax<int>(i,j); n=GetMax<long>(l,m); cout << k << endl; cout << n << endl; return 0; } In this case, we have used T as the template parameter name instead of myType because it is shorter and in fact is a very common template parameter name. But you can use any identifier you like. In the example above we used the function template GetMax() twice. The first time with arguments of type int and the second one with arguments of type long. The compiler has instantiated and then called each time the appropriate version of the function. As you can see, the type T is used within the GetMax() template function even to declare new objects of that type: T result; Therefore, result will be an object of the same type as the parameters a and b when the function template is instantiated with a specific type. In this specific case where the generic type T is used as a parameter for GetMax the compiler can find out automatically which data type has to instantiate without having to explicitly specify it within angle brackets (like we have done before specifying <int> and <long>). So we could have written instead: int i,j; GetMax (i,j); 119 Since both i and j are of type int, and the compiler can automatically find out that the template parameter can only be int. This implicit method produces exactly the same result: // function template II #include <iostream> using namespace std; template <class T> T GetMax (T a, T b) { return (a>b?a:b); } int main () { int i=5, j=6, k; long l=10, m=5, n; k=GetMax(i,j); n=GetMax(l,m); cout << k << endl; cout << n << endl; return 0; } 6 10 Notice how in this case, we called our function template GetMax() without explicitly specifying the type between angle-brackets <>. The compiler automatically determines what type is needed on each call. Because our template function includes only one template parameter (class T) and the function template itself accepts two parameters, both of this T type, we cannot call our function template with two objects of different types as arguments: int i; long l; k = GetMax (i,l); This would not be correct, since our GetMax function template expects two arguments of the same type, and in this call to it we use objects of two different types. We can also define function templates that accept more than one type parameter, simply by specifying more template parameters between the angle brackets. For example: template <class T, class U> 120 T GetMin (T a, U b) { return (a<b?a:b); } In this case, our function template GetMin() accepts two parameters of different types and returns an object of the same type as the first parameter (T) that is passed. For example, after that declaration we could call GetMin() with: int i,j; long l; i = GetMin<int,long> (j,l); or simply: i = GetMin (j,l); even though j and l have different types, since the compiler can determine the appropriate instantiation anyway. Class templates We also have the possibility to write class templates, so that a class can have members that use template parameters as types. For example: template <class T> class mypair { T values [2]; public: mypair (T first, T second) { values[0]=first; values[1]=second; } }; The class that we have just defined serves to store two elements of any valid type. For example, if we wanted to declare an object of this class to store two integer values of type int with the values 115 and 36 we would write: mypair<int> myobject (115, 36); this same class would also be used to create an object to store any other type: mypair<double> myfloats (3.0, 2.18); 121 The only member function in the previous class template has been defined inline within the class declaration itself. In case that we define a function member outside the declaration of the class template, we must always precede that definition with the template <...> prefix: // class templates #include <iostream> using namespace std; template <class T> class mypair { T a, b; public: mypair (T first, T second) {a=first; b=second;} T getmax (); }; template <class T> T mypair<T>::getmax () { T retval; retval = a>b? a : b; return retval; } int main () { mypair <int> myobject (100, 75); cout << myobject.getmax(); return 0; } 100 Notice the syntax of the definition of member function getmax: template <class T> T mypair<T>::getmax () Confused by so many T's? There are three T's in this declaration: The first one is the template parameter. The second T refers to the type returned by the function. And the third T (the one between angle brackets) is also a requirement: It specifies that this function's template parameter is also the class template parameter. 122 Template specialization If we want to define a different implementation for a template when a specific type is passed as template parameter, we can declare a specialization of that template. For example, let's suppose that we have a very simple class called mycontainer that can store one element of any type and that it has just one member function called increase, which increases its value. But we find that when it stores an element of type char it would be more convenient to have a completely different implementation with a function member uppercase, so we decide to declare a class template specialization for that type: // template specialization #include <iostream> using namespace std; // class template: template <class T> class mycontainer { T element; public: mycontainer (T arg) {element=arg;} T increase () {return ++element;} }; // class template specialization: template <> class mycontainer <char> { char element; public: mycontainer (char arg) {element=arg;} char uppercase () { if ((element>='a')&&(element<='z')) element+='A'-'a'; return element; } }; int main () { mycontainer<int> myint (7); mycontainer<char> mychar ('j'); cout << myint.increase() << endl; cout << mychar.uppercase() << endl; return 0; } 8 J 123 This is the syntax used in the class template specialization: template <> class mycontainer <char> { ... }; First of all, notice that we precede the class template name with an emptytemplate<> parameter list. This is to explicitly declare it as a template specialization. But more important than this prefix, is the <char> specialization parameter after the class template name. This specialization parameter itself identifies the type for which we are going to declare a template class specialization (char). Notice the differences between the generic class template and the specialization: template <class T> class mycontainer { ... }; template <> class mycontainer <char> { ... }; The first line is the generic template, and the second one is the specialization. When we declare specializations for a template class, we must also define all its members, even those exactly equal to the generic template class, because there is no "inheritance" of members from the generic template to the specialization. Non-type parameters for templates Besides the template arguments that are preceded by the class or typename keywords , which represent types, templates can also have regular typed parameters, similar to those found in functions. As an example, have a look at this class template that is used to contain sequences of elements: // sequence template #include <iostream> using namespace std; template <class T, int N> class mysequence { T memblock [N]; public: void setmember (int x, T value); T getmember (int x); }; template <class T, int N> 100 3.1416 124 void mysequence<T,N>::setmember (int x, T value) { memblock[x]=value; } template <class T, int N> T mysequence<T,N>::getmember (int x) { return memblock[x]; } int main () { mysequence <int,5> myints; mysequence <double,5> myfloats; myints.setmember (0,100); myfloats.setmember (3,3.1416); cout << myints.getmember(0) << '\n'; cout << myfloats.getmember(3) << '\n'; return 0; } It is also possible to set default values or types for class template parameters. For example, if the previous class template definition had been: template <class T=char, int N=10> class mysequence {..}; We could create objects using the default template parameters by declaring: mysequence<> myseq; Which would be equivalent to: mysequence<char,10> myseq; Templates and multiple-file projects From the point of view of the compiler, templates are not normal functions or classes. They are compiled on demand, meaning that the code of a template function is not compiled until an instantiation with specific template arguments is required. At that moment, when an instantiation is required, the compiler generates a function specifically for those arguments from the template. When projects grow it is usual to split the code of a program in different source code files. In these cases, the interface and implementation are generally separated. Taking a library of functions as example, the interface generally consists of declarations of the prototypes of all the 125 functions that can be called. These are generally declared in a "header file" with a .h extension, and the implementation (the definition of these functions) is in an independent file with c++ code. Because templates are compiled when required, this forces a restriction for multi-file projects: the implementation (definition) of a template class or function must be in the same file as its declaration. That means that we cannot separate the interface in a separate header file, and that we must include both interface and implementation in any file that uses the templates. Since no code is generated until a template is instantiated when required, compilers are prepared to allow the inclusion more than once of the same template file with both declarations and definitions in a project without generating linkage errors.
Copyright © 2024 DOKUMEN.SITE Inc.