C
C
1. The basic reason for the change in programming methodologies is the increasing complexity of programs.
2. OOP took the best ideas of structured programming and combined them with several new concepts. ` 3. What is an object? Objects are basic run time entities (real life entities) such as an invoice, a car etc.. It can also be defined as an entity able to save a state (info) and which offers a number of operations (behaviour) to either examine or affect this state. Ex. Person 4. What is object orientation? It is a technique for system modeling. It offers a number of concepts
OOP concepts : The basic concepts which are common to all OOP Languages are:
Encapsulation: The wrapping of data and functions into a single unit is known as encapsulation. The single unit is called Class. Polymorphism: Means the ability to take more than one form Ex: Operation addition exhibiting different behavior in different instances.
Inheritance: It is the process by which an object of one class acquires the properties of objects of another class.
Inheritance is important bcoz it supports the concept of classification.
Without this classification each object should define all its characteristics explicitly.
The concept of Inheritance provides the idea of reusability.
In St.P, Large programs are divided to smaller programs known as functions, In OOP they are divided into objects.
Sample C++ Program
An action is referred to as an expression, An expression terminated by a semicolon is referred to as a statement. A statement is the smallest independent unit in a C++ Program
A first look at input/output #include <iostream.h> main() { cout<< C++ is an extension of C; } The above statement introduces 2 new features cout and <<
The identifier cout is a predefined object that represents the standard output stream in C++.
The standard output stream represents the screen.
It inserts the contents of the variable on its right to the object on its left.
The identifier cin is a predefined object that represents the standard input stream in C++. The standard input stream represents the keyboard. The operator >> is called the extraction or get-from operator.
It extracts the value from the keyboard and assigns it to the variable on its right .
The preprocessor directives #define, #ifdef and #endif
i) Simply tell the preprocessor that the flag is defined, without specifying a value: #define FLAG ii) or give it a value (which is the typical C way to define a constant): #define PI 3.14159 2. In either case, the label can now be tested by the preprocessor to see if it has been defined: #ifdef FLAG will yield a true result, and the code following the #ifdef will be included in the package sent to the compiler. This inclusion stops when the preprocessor encounters the statement
#endif
or
#endif
// FLAG
3 4
Any non-comment after the #endif on the same line is illegal, even though some compilers may accept it. The complement of #define is #undef (short for un-define), which will make an #ifdef statement using the same variable yield a false result. #undef will also cause the preprocessor to stop using a macro. The complement of #ifdef is #ifndef, which will yield a true if the label has not been defined. (Used in header files) #ifndef HEADER_FLAG #define HEADER_FLAG
5 6
C++ Datatypes Pointer type: A Pointer is a variable which holds the address of another variable. A pointer is defined by prefixing the identifier with the dereference operator (*)
int *p = 0; //comment
int *p, ival; p = ival; //comment
A pointer can hold a value of zero, indicating that it points to no object. A pointer cannot hold a non address value. void *gp; Void pointer is called a generic pointer, which can hold the address value of any data type. But may not be dereferenced.
For Ex: void *gp; int *ip; gp = ip; // assign int pointer to void pointer But *ip = *gp; //illegal Pointer arithmetic: A number can be added to a pointer. A number can be subtracted from a pointer
A pointer when incremented always points to an immediate next location of its type.
String type: C++ provides two string representations:
The C-style originated within C language and is supported within C++ The string is stored within a character array and is manipulated thro a char* pointer. The C-style character string can be of zero length. String Class type : String class supports the following operations a) Initialize a string object both with a sequence of characters and with a second string object. b) Copy one string to another. c) Compare two strings for equality. d) To append two strings.
The address of a constant object can be assigned only to a pointer to a constant object. double dval = 3.14; const double *pc = 0; const double wage = 9.6; pc = &wage; pc = &dval;
//comment
Here pointer is itself not a constant so we can reassign pointer to point to a different object but cannot modify the object pointer addresses. Int *const curerr = &errnum; int errnum = 0; *curerr = 5; //comment A constant pointer to non constant object can modify the value of the object but cannot change the address Const double pi = 3.14159; const double *const pi_ptr = π
A constant pointer to a constant object can neither modify the value of the object nor the address itself can be changed.
Reference Types: A reference provides an alias for a previously defined object. The general form is: Data-type & reference-name = variable-name For Ex: float total = 100; float &sum = total; Both the variables refer to the same data object in the memory. Cout<<total<<\n<<sum; //Both print 100 total = total +10; // changes the value of both to 110 int &rerefval = &ival //comment
Although a reference serves as a pointer, it is not correct to initialize it to the address of an object
Once defined a reference cannot be made to refer to another object that is why it must be initialized. Each definition of a reference must be preceded by the address-of operator. int ival = 1024, ival1 = 2048; int &rval = ival, rval1 = ival2; //defines one reference int &rval = ival, &rval1 = ival2; // defines 2 references int ival = 1024, ival2 = 2048; int *pi = &ival, *pi2 = &ival2; pi = pi2; int &ri = ival, &ri2 = ival2; ri = ri2; The difference between a reference and a pointer is that a reference is internally treated as a constant pointer.
Bool Type: A bool object can be assigned literal values true or false. For Ex: bool found = false;
short bool found = false; //comment Bool cannot be declared to be either signed, unsigned, short or long. bool found = false; int occ_count = 0; while( ) { found = look_for(); occ_count += found; }
Bool objects and Bool literals are implicitly promoted to type int when an arithmetic value is necessary. False becomes 0 and true 1.
Arithmetic and pointer values are implicitly converted to a value of type bool. For Ex: int find( int value );
Enumerated Data type: It is a user-defined data type. The syntax of enum statement is similar to that of struct For Ex: enum shape{ circle, square, triangle}; The tag name becomes new type name and new variables can be declared using these tag names. For Ex: shape ellipse; Enum color {red, blue, green}; color bkground; color bkground = blue; color bkground = 7; //comment int c = red; //comment C++ does not permit an int value to be automatically converted to an enum value but an enum value can be used in place of an int value.
Integer values can be assigned to enumerators explicitly. For Ex: enum color{red, blue = 4, green}
Anonymous enums are enums without tagnames. For Ex: enum {off, on}; int switch1 = off; Enumeration is used to define symbolic constants for a switch statement. { case circle: enum shape - - - - - - - break; {circle, rectangle, triangle}; case rectangle: Main() -- - - -; { int code; case triangle: cout<<Enter shape code:; -- - - -; cin>>code; cout<<Enter shape code:; While(code>=circle && code<=triangle) cin>>code; { switch(code) } }
int staff_size = 27; const int buff_size = 512; int get_size(); char input_buffer[buf_size]; double salaries[staff_size]; int test_scores[get_size( )]; //comment The dimension must be a const expression. A non constant variable cannot be used to specify the dimension of an array. It must be possible to evaluate the value of the dimension at compile time. But the access to non-const object is accomplished only at run time and so it cannot be used as a dimension.
int b[ ] = {1, 2, 3}
An array can be explicitly initialized and such an array need not specify a dimension value
If the dimension is greater than the number of elements, the non initialized elements are set to zero int a[5] = {0, 1, 2}; // a= {0, 1, 2, 0, 0}
A character array can be initialized as either a list of commaseparated characters or a string literal
const char ca1[ ] = {c, +, +}; const char ca2[ ] = C++ ; const char ch3[3] = C++ ;
The string const contains an additional terminating null character. int ia[ ] = {0, 1, 2}; int ia3[10]; int ia1[ ] = ia; ia3 = ia; //comment
An array cannot be initialized with another array, nor one array can be assigned to another.
int getindex( );
a[ getindex( ) ] = val;
Any expression that results in an integral value can be used to index into an array int *ap[ ] = {&ix, &jx, &kx }; int &ar[ ] = {&I, &j, &k}; An array of pointers is permitted but array of references is not permitted.
Complex Number Type: The complex number class is a part of standard library. To use it its associated header file <complex.h> must be included
Using this class it is possible to initialize one complex object with another Ex: complex <double> purei2(purei1);
An array of complex objects can be declared
Ex: complex <double> con[2] = { complex <double> (2, 3), complex <double> (2, 3) };
Complex numbers support addition, subtraction, multiplication, division and test for equality. Ex: complex <double> a; complex <double> b;
These are the unary operators to perform dynamic memory allocation and de-allocation
New operator is used to create objects. The General Form is:
The general form of using delete delete pointer-variable; For Ex: delete p; To free dynamically allocated array delete [size] pointer-variable For Ex: delete [10] p; Recent version of C++ does not require the size to be specified delete [ ] p; If sufficient memory is not available for allocation, new returns a null pointer
FUNCTIONS
1. A Function is a self contained block of statements that performs a specific task 2. Operands of function definition Parameters 3. Operands of function call arguments
4. The function definition is composed of the function return type, the name of the function, the parameter list and the function body.
5. A function that does not return a value has a return type of void.
6. A function is evaluated whenever the functions name is followed by the call operator ( ) followed by a semicolon.
1. A function call can cause one of the two things to happen a. If the function has been declared inline, the body of the function may be expanded at the point of its call during compilation. b. Else the function is invoked at runtime and the control is transferred to the function being invoked. 2. When evaluation of the called function is complete, the suspended function resumes execution at the point immediately following the call. 3. A function must be declared bfore its called otherwise a compile time error results.
Function Prototype:
1. A function prototype consists of the function return type, the name of the function and the parameter list.
2. Need not specify name of the parameters Ex: int min(int,int)
3. A function prototype describes the functions interface(the number and type of parameters)
4. int[10] calc();
6. Function declarations are best placed within header files. The header files can be included in every file where functions are called
Parameter type checking 1. Int gcd(int,int); gcd(hello, world); // comment gcd(243); // comment gcd(3.14,6.29); // comment 2. The function parameter list provides the compiler the necessary information to perform type checking. 3. Y a function cannot be called until it has first been declared. 4. In case of a type mismatch, an implicit conversion is applied. If an implicit conversion is not possible then a compile time error is issued.
Argument Passing: 1. Functions parameters are allocated storage on the programs run-time stack.
2. The storage remains associated with the function until the function terminates.
3. The storage size is determined by its type
Default arguments:
1. Default arguments can be specified using initialization syntax with the parameter list 2. Ex: float amt(float principal, int time, float rate=0.15); 3. Functions providing default arguments can be invoked with or without an argument for this parameter. If an argument is provided it overloads the default argument value.
4. Arguments to the call are resolved by position.(cannot supply argument for time without supplying argument for principal)
5. Only the trailing arguments can have default values. We must add defaults from right to left 6. Ex: mul(int I, int j=5, int k=10); // comment 7. Mul(int I=15, int j, int k); //comment
1. A parameter can have its default argument specified only once in a file. Ex: // ff.h // ff.c int ff(int=0); #include ff.h int ff(int i=0) { }
2. By convention, the default argument is specified in the function declaration contained in the public header file. 3. A default argument can be any expression Ex: int set(int); int val;
Does the built-in printf() function provide the compiler the necessary information to perform type-checking.
Then how parameter type checking is performed???
Ellipses:
1. It is sometimes impossible to list the type and no. of all arguments that might be passed to a function. In these cases ellipses () can be specified in a function parameter list. 2. Ellipses suspend type-checking. Their presence tells the compiler that when the function is called, 0 or more arguments may follow and the types of the arguments are unknown.
3. Ellipses take either of the two forms void fun(param-list, ); void fun();
4. In first case, for arguments that correspond to parameters explicitly declared , type checking is performed and for arguments that correspond to ellipses, type checking is suspended.
6. No they are not equivalent. First accepts no arguments, Second accepts 0 or more arguments 7. Fun(); // invokes which above function; 8. Fun(val); // invokes which above function;.
Returning a value:
4. Return by Reference: int& max(int &x,int &y) { if(x>y) return x; else return y; }
Array Parameters: 1. Arrays are never passed by value, rather is passed as a pointer to zeroth element 2. Void putvalues(int*); Void putvalues(int[]); Void putvalues(int[10]); 3. All the above three are equivalent declarations. 4. Passing an array as a pointer implies that the changes to an array parameter within the called function are made to the array argument itself and not to a local copy
1. . The size of an array is not part of its parameter type. 2. When the compiler applies parameter type checking on the argument type, there is no checking of the array sizes. . 3. Void putvalues(int[10]); void main() { int i ,j[2]; putvalues(&i); //comment putvalues(j); //comment }
4. Parameter type checking confirms that both calls of putvalues() provide an argument of type int*
5. Another mechanism is to declare the parameter as a reference to an array
1. void putvalues(int (&arr)[10]); void main() { int i ,j[2]; putvalues(i); //comment putvalues(j); //comment } 2. When the parameter is a reference to an array type, the array size becomes part of the parameter and argument types
3. The compiler checks that the size of the array argument matches the one specified in the function parameter type.
4. Void putvalues(const int[10]); // comment
5. Indicates that the function cannot change the array elements on declaring the elements in the parameter type as const
1. If a #include directive is enclosed within the braces of a compound statement LD, the linkage directive applies to all the declarations within the header file. 2. Int main() { extern c double sqrt(double); }
3. The above code results in a compile time error. A linkage directive cannot appear within a function body 4. A linkage directive is most appropriately placed within a header file.
Inline Functions
1. The general form: 2. Inline function-header { function body } 3. Ex: inline int square(int a) { return a*a };
6. Compiler may ignore the request if the function definition is too long, and compile the function as a normal function.
Recursion A function that calls itself is referred to as a recursive function. A recursive function must always define a stopping condition. Otherwise the function will recurse forever Ex: int factorial(int val) { if(val>1) return val * factorial(val-1); return 1; }
Overloaded Functions
1. Function overloading allows multiple functions that provide a common operation on different parameter types to share a common name
2. Ex: The expression 1 + 3 invokes the addition operation for integer operands and the expression 1.3 + 3.2 invokes the addition operation that handles floating point operands. 3. It is the job of the compiler to invoke the appropriate function.
2. When a function name is declared more than once in a particular scope, the compiler interprets the second declaration as follows. a) If the parameter lists of the functions differ either in number or type, the functions are considered to be overloaded. int max(int, int); float max(float, float, float); b) If both the return type and the parameter list of the function declarations match exactly, the second is treated as redeclaration. int max(int, int); int max(int, int); c) If the parameter lists of the function declarations match exactly, but the return types differ, the 2nd is treated as erroneous redeclaration. int max(int, int); float max(int, int); d) If the parameter lists of the functions differ only in default arguments, the second is treated as redeclaration.
int max(int, int); int max(int, int =10); 1. Typedef double Doll; extern Doll calc(Doll); extern double calc(double); 2. If of the function parameter lists, one uses typedef and the other uses the type to which the typedef corresponds, then the parameter lists are not different.
3. Typedef double Doll; extern Doll calc(Doll); extern int calc(double); 4. Void f(int); void f(const int);
5. The const and volatile qualifiers are not taken into account in identifying different functions
6. Void f(int*); void f(const int*); 7. Const or volatile qualifier is taken into account to identify the declarations if they are applied to a pointer or a reference.
1. Whenever the different function names provide information that would make the program easier to understand For Ex: void setdate(Date&, int, int, int); void printdate(Const Date&); Date& convertdate(Const String&);
2. All of the above functions share the same datatype, namely class Date but do not share the same operation
1. All the functions in a set of overloaded functions are declared in the same scope.
2. A locally declared function hides a function declared at global scope. void print(double); void print(const string&); // comment void fun(int val) { extern void print(int);// comment print(value:); // comment print(val); // comment }
1. A set of overloaded functions can also be declared with in a namespace. Eg, namespace Development { extern void print (const char &); extern void print(const char &, int); } 2. The functions that are members of distinct namespaces do not overload one another.
3. Using declarations and using directives are mechanisms by which namespace members can be made visible in other scopes.
Extern C and overloaded functions
1. Can some functions in the overloaded set be C++ while other are C functions?
2. A Linkage Directive can be specified for only one function in a set of overloaded functions
For Ex: extern C void print(const char*); extern C void print(int); // error 3. Of the set of overloaded functions one is C function and the others are C++ functions. 4. The C function can be called from both C programs and C++ programs. 5. The additional functions can be called only from C++ programs. 6. The linkage directive does not influence the selection of a function for a function call. 7. For Ex: Class Emp{..}; Class stud{..}s1; extern c double cal(double); extern emp cal(const emp&); extern stud cal(const stud&); Int main() { cal(34); cal(s1); }
7. When no function matches the pointers type exactly, the initialization results in a compile time error
8. The initialization of pf2 results in an error.
12. No Type conversion between pointer to function types. Initialization of *pc2 results in error.
1. Function Overload resolution is the process by which a function call is associated with one function in a set of overloaded functions.
2. The following is the example to explain the 3 steps. void f(); void main() void f(int); { void f(double, double = 3.4); f(5.6); void f(char*, char*); return 0; }
b) Select the functions from the set of overloaded functions that can be called with the arguments specified in the call.
c) Select the function that best matches the call.
2. A candidate function is a function with the same name as the function that is called..
3. All the 4 functions of the considered example.
9. For this, the conversions used to convert the arguments to the corresponding parameter types are ranked. 10. The best viable function is the function for which the conversions applied to the arguments are no worse than the conversions necessary to call any other viable function
11. When the viable function f(int) is considered, the conversion applied (double to int) is a standard conversion.
12. An exact match is better than a standard conversion. Bcoz Not to do a conversion is better than to do any conversion. 13. In the example considered the best viable function is f(double,double).
14. If the 3rd step of function overload resolution finds no best viable function, then the function call is ambiguous.
c) No match Bcoz no type conversion exists void print(unsigned int); void print(const char*); void print(char); int *ip; print(ip); 1. For the argument to be an exact match, the argument need not exactly match the type of the parameter. 2. There are some minor conversions that can be applied to the argument 3. The possible conversions in exact match category are: a) Lvalue-to-rvalue conversion b) Array-to-pointer conversion c) Function-to-pointer conversion
d) Qualification conversions
Lvalue-to-rvalue conversion
1. An lvalue is an object that a program can address from which a value can be fetched and its value can be modified unless the object is declared const.
2. An rvalue is an expression that denotes a value or is an expression that denotes a temporary object that the user cannot address and that cannot have its value modified. 3. Ex: int main() { int val, res; val = 5; res = calc(val); return 0; } 4. In the first expression val is the lvalue, and 5 is the rvalue. 5. In the second expression res is the lvalue and the temporary that holds the return value of the function call to calc() is the rvalue.
11. When a function expects a pass by value argument, an lvalue-torvalue conversion is performed when the argument is an lvalue.
12. Ex: void print(int); int main() { int val=5; print(val); return 0; }
Array-to-pointer conversion 1. A function parameter never has an array type. True or false. 2. True, Instead the parameter is transformed to a pointer to the first element of the array. 3. This type of conversion is an array-to-pointer conversion. 4. Int a[3]; void putvalues(int *); int main() { putvalues(a); return 0; } 5. Even though putvalues has a pointer parameter and even though an array-to-pointer conversion takes place on the argument, the argument is an exact match for the call to putvalues.
Function-to-pointer conversion
1. As with a parameter of array type, a parameter of function type is automatically transformed to a pointer to the function.
2. This conversion is called function-to-pointer conversion.
3. Even though this conversion takes place, the argument is an exact match for a parameter of type pointer to function.
4. Typedef int (*pf1)(int &, int &);
int fun(int &, int &); void samp(int *, int *, pf1); void main() { int a[2], b[3]; samp(a, b, fun); }
Qualification conversions
3. Int *pi, *pj; bool fun(const int *, const int *); void main() { if (fun(pi, pj)) ..; }
4. The arguments pi and pj are converted from the type pointer to int to the type pointer to const int.
5. This conversion which adds a const qualification to the type to which the pointer points, is a qualification conversion.
6. The 3 conversions in exact match category (lvalue to rvalue, array to pointer, function to pointer) are referred to as lvalue transformations
1. An exact match with an lvalue transformation is ranked better than an exact match requiring a qualification conversion.
2. The possible conversions can be grouped into three categories: Promotions, Standard conversions, and user defined conversions.
Details of a promotion
2. Enum stat{fail, pass}; void ff(int); void ff(char); int main() { ff(pass); ff(0); return 0; } 3. In the first function call enumeration const is promoted to int.
4. Representation of an enumeration type depends on the values of the enumeration constants.
6. Enum e1{a1,b1,c1}; enum e2{a2, b2, c2=0x80000000}; void format(int); void format(unsigned int); int main() { format(a1); format(a2); return 0; } 7. The representation chosen for e1 is char. Bcoz a1,b1,c1 with values 0,1,2 can be represented by type char. 8. The representation chosen for e2 is unsigned int. bcoz one of the enumeration constants has a value of 0x80000000 which cannot be represented by char. 9. Format(a1); calls--- void format(int); 10. Format(a2); calls---- void format(unsigned int);
c) Floating integral conversions: From floating point to integral or from integral to floating point types
d) Pointer conversions: Zero to pointer type, and conversion of pointer of any type to the type void* e) Bool conversions: From any integral type, floating point type, enumeration type, or pointer type to type bool.
Ex: void print(void*); void print(double); void main() { int i; print(i); print(&i); } 2. i is converted from int to double in first call and &i is converted from int* to void* in second call. 3. void print(int); void print(void*); void set(const char*); void set(char*);
void main() { print(0); set(0); } 4. 1st call matches print(int); 2nd call is ambiguous bcoz matches both
Pointers to Functions
8. An initialization or assignment is legal only if the parameter list and return type of both the pointers (left-hand side and right-hand side) match exactly.
Invocation 1. Once a pointer to a function has been declared, it can be used to call the function to which it refers. 2. Int min(int*, int); int (*pf) (int*, int) = min; int a[5], size; void main() { min(a, size); // direct call pf(a, size); // in-direct call } 3. Both a direct call to the function using functions name and indirect call to the function using a pointer can be written same way.
4. The call pf(a, size); can also be written using the longhand explicit pointer notation. (*pf)(a, size); 5. . Int min(int*, int); int (*pf) (int*, int); const int size=5; int a[size]; void main() { min(a, size); pf(a, size); } 6. The second function call results in error bcoz pf has a value zero.
7. Only pointers that have been initialized or assigned to refer to a function can invoke a function.
Arrays of pointers to functions
Generic Functions
1. A generic function defines a general set of operations that can be applied to various types of data.
2. For Ex: Quicksort algorithm is the same whether it is applied to an array of integers or array of floats. 3. By creating a generic function, one can define the nature of the algorithm independent of any data. 4. The compiler will automatically generate the correct code for the type of data that is used when executing the function. 5. In general creating a generic function means creating a function that can automatically overload itself.
1. Template <class Ttype> ret-type func-name(parameter list) { //function body } 2. Ttype is a placeholder name for a data type used by the function. The compiler will automatically replace it with actual datatype. 3. Template <class X> void swap(X &a, X &b) { X temp; temp = a; a = b; b = temp; } int main() { int I = 10; j = 20; double y = 10.1, z = 23.2; swap(i, j); swap(y, z); return 0; }
1. template <class X> void swap(X &a, X &b) tells the compiler two things: a) A template is being created b) X is the generic type. 2. In the above example the compiler creates two versions of swap(). One that will exchange integer values and the other that will exchange floating point values.
3. Important terms related to templates: i)A function definition preceded by a template statement is called a template function. ii)Generated function: A specific instance of a template function generated by the compiler. 4. The template clause of generic function does not have to be on the same line For Ex: template <class x> void swap(x &a, x &b) { ,.,.,.,.,..,.,}
5. Template <class x> Int I; // error No other stmt can occur betw template stmt & GFD Void swap(x &a, x &b) { ,..,..,..,.,,..} A Function with two Generic types 1. More than one Generic datatype can be defined in the template statement by using a comma separated list. For Ex: 2. Template <class type1, class type2> void func(type1 m, type2 n) { cout<<m<< <<n; } void main() { func(10, I like C++); func(98.6, 19L); } 3. In the example, the placeholders type1, type2 are replaced with datatypes int and char*, double and long respectively.
1. Generic function overloads itself, but still can be explicitly overloaded. What is the difference?
2. Explicitly overloaded generic function overrides the generic function relative to that specific version. Template <class X> b = temp; void swap(X &a, X &b) cout<<Inside swap for int; { X temp; } temp = a; int main() a = b; { b = temp; int I = 10; j = 20; cout<<Inside template; double y = 10.1, z = 23.2; } swap(i, j); void swap(int &a, int &b) swap(y, z); { int temp; return 0; } temp = a; a = b;
1. The compiler does not generate the int version of generic swap function, bcoz the generic function is overridden by explicit overloading. 2. New-style syntax to denote explicit specialization template<> void swap<int> (int &a, int &b) { int temp; ---------; } 3. Template<> construct indicates specialization.
4. <int> indicates the type of data for which the specialization is being created.
Overloading a Function Template
1. To overload the template specification itself, simply create another version of the template that differs from others in its parameter list.
template <class x> void fun(x a) { cout<<This function accepts single parameter; }
template <class x, class y> void fun(x a, y b) { cout<< This function accepts two parameters; void main() { fun(10); fun(10, 20.5); }
Generic Function Restrictions: 1. Generic functions are similar to overloaded functions except..? 2. With overloaded functions different actions may be performed within the body of each function, But a generic function must perform the same general action for all versions. Applying Generic Functions 1. Generic functions can be applied to situations whenever one has a function that defines a generalizable algorithm 2. Sorting is one operation for which generic functions were designed. 3. Another function that is benefited on being made into a template is, the function that compacts the elements in an array.
Template <class X> Void bubble(X *items, int n) { int I, j; X temp; for(I = 1; I<n; I++) for(j = n-1; j>=I; j--) if (items[j-1] > items[j]) { temp = items[j-1]; items[j-1] = items[j]; items[j] = temp; } } void main() { int ia[5] = {7, 5, 4, 8, 6}; double da[5] = {4.2, 3.1, 2.7, 6.8, 5.3}; bubble(ia, 5); bubble(da, 5); // DISPLAY }
1. C++ allows automatic initialization of objects when they are created. This is performed through the use of a constructor function.
2. Constructor is a special function that is a member of the class and has the same name as that class. 3. The Constructor is automatically called whenever an object of its associated class is created. 4. A Constructor is declared and defined as follows class stack Stack :: stack() { int stk[size]; { int top; top = 0; public: cout<<\n Stack Initialized; stack(); } void push(int I); Stack s1; //creates object s1 int pop(); }; //& initializes top to zero
5. For local objects, the constructor is called each time the object declaration is encountered. 6. For global or static local objects, constructor is called once. 7. If a normal member function is defined for initialization then that function should be invoked for each object separately, which is inconvenient if there are large number of objects. Special Characteristics 1. They should be declared in the public section. 2. Invoked automatically when the objects are created. 3. Do not have return types therefore cannot return values. 4. They cannot be inherited. 8.they make implicit calls to the operator
5. They can have default arguments. 6. Constructors cannot be virtual. 7. Cannot refer to their addresses.
Destructors:
1. A destructor as the name implies is a complement of the constructor, used to destroy objects.
2. It will be invoked implicitly by the compiler upon exit from program o function or block 3. Local objects are destroyed when the block is left. Global objects are destroyed when the program terminates. 4. It is a member function whose name is same as the class name but is preceded by a tilde (~) operator. Ex : ~stack();
5. There are many reasons why a destructor may be needed. Ex: An object may need to deallocate memory that it had previously allocated, or close a file that it had opened. 6. A destructor never takes any argument nor does it return any value
7. It is a good practice to declare destructors in a program since it releases memory space for future use.
Illustration of Destructors:
Int count = 0; Class sample { public: sample() { count++; cout<<Object <<count; cout<< Created \n; } ~sample() { cout<<Object <<count; cout<< Destroyed \n; count--; } };
Void main() { sample s1, s2, s3; { sample s4; } { sample s5; } } Output Object 1 created Object 2 created Object 3 created Object 4 created Object 4 destroyed Object 4 created Object 4 destroyed Object 3 destroyed Object 2 destroyed Object 1 destroyed
1. Class is syntactically similar to a struct. The only difference between them is that by default all members are public in a struct and private in a class. void main() Struct emp class emp { { void getdata(); { int empno; s1.getdata(); void putdata(); char name[10]; s1.putdata(); private: public: } int empno; void getdata(); char name[10]; void putdata(); }s1; };
Why C++ contains two virtually equivalent keywords
1. C structures already provide a means of grouping data therefore it is just a small step to allow them to include member functions
2. It may be easier to port existing C programs to C++ bcoz structures and classes are related.
1. C++ unions may contain both member functions and variables. Union members are public by default.
2. A union in C++ retains its C feature that all data elements share the same location in memory. Void swapbyte :: swap() union swapbyte { unsigned char t; { void swap(); t = c[0]; void setbyte(unsigned short i); c[0] = c[1]; void show(); c[1] = t; private: } unsigned short u; Void main() unsigned char c[2] { swapbyte b; }; b.setbyte(49034); Void swapbyte :: show() b.swap(); { cout<<u; } b.show(); Void swapbyte :: setbyte(unsigned short i) } { u = i; }
4. It is best to use a class when we want a class, and a struct when we want a C-like structure.
Restrictions on C++ unions
Anonymous Unions
1. An anonymous union does not include a type name, and no objects of the can be declared.
2. So the variables are referred to directly without the dot operator syntax. Void main() l = 100000; { d = 123.2342; union cout<<l<< ; { cout<<d<< ; long l; } double d; }; 3. Anonymous union tells the compiler that its member variables are to share the same location. 4. The names of the members of an anonymous union must not conflict with other identifiers known within the same scope.
5. All restrictions on unions apply to anonymous unions in addition to the following: a) No member functions are allowed, only data. b) Cannot contain private or protected elements.
5. The function is defined elsewhere in the program like a normal C++ function (without using the keyword friend or the scope operator ::). class avg Float mean(avg s) { return float(s.a +s.b) / 2.0; } { int a , b; public: Void main() void setvalue() { avg a1; a1.setvalue(); { a = 25; b=40; } cout<< Average is : <<mean(a1); friend float mean(avg s); } }; Characteristics of Friend Functions 1. A Friend function is not in the scope of the class to which it has been declared as friend.
2. Therefore it cannot be called using the object of the class and must be invoked like a normal function.
3. It cannot access the member names directly and has to use an object name and dot operator with each member name.
2. Friend functions are used in situations where two classes like to share a particular function Ex: Two classes manager and scientist sharing a function income_tax().
3. FFs make the creation of some types of I/O functions easier. Member function of one class can be friend function of another class. 1. In such cases they are defined using the scope resolution operator.
Function friendly to two classes Class abc; //Forward declaration Class xyz; { int x; public: void setvalue() { x = 10; } friend void max(xyz, abc); }; Class abc { int a; public: void setvalue() { a = 20; } friend void max(xyz, abc); };
Void max(xyz m, abc n) { if(m.x >= n.a) cout<<m.x; else cout<<n.a; } Note:
Void main() { abc obj1; obj1.setvalue(); xyz obj2; obj2.setvalue(); max(obj2, obj1); }
1. Function max() has arguments from both xyz and abc. 2. When the function max() is declared as a friend in xyz for the first time, the compiler will not acknowledge the presence of abc unless its name is declared in the beginning called forward declaration.
1. When a function is defined inside a class declaration, it is automatically made into an inline function.
2. Therefore all restrictions that apply to inline functions also apply to functions defined inside the class declaration. 3. It is not necessary to precede its declaration with the inline keyword. 4. Constructor and destructor functions may also be inlined, either by default or explicitly. class sample { int a , b; public: void setvalue() { a = 25; b=40; } void show(); } Void sample :: show() { cout<<a = <<a<< <<b = <<b; }
Parameterized constructors
1. In practice it may be necessary to initialize the various data elements of different objects with different values when they are created.
2. This can be achieved by passing arguments to the constructor function when the objects are created. 3. Such Constructors that take arguments are called parameterized constructors. Sample :: sample(int x, int y) Class sample { int m, n; public: sample(int x, int y); void display(void) { cout<<\n m = <<m; cout<<\n n = <<n; } }; { m = x; n = y; } Void main() { sample s1(0,100); sample s2 = sample(25, 75); cout<<\n OBJECT 1 ; s1.display(); cout<<\n OBJECT 2 ; s2.display(); }
4. When a constructor has been parameterized, The object declaration statement such as sample s1 may not work. 5. Initial values are to be passed as arguments to the constructor function when the object is declared. This can be done in two ways: a) By calling the constructor explicitly b) By calling the constructor implicitly 6. Explicit Call: sample s1 = sample(0, 100);
1. Both data members and member functions of a class can be made static.
Static Data Members(SDM) 1. Preceding a data members declaration with the keyword static tells the compiler that only one copy of that variable will exist and that all objects of the class will share that variable. 2. A static data member is initialized to zero when the first object of its class is created. No other initialization is permitted. 3. Declaring a static data member is not defining it (means not allocating storage for it). Remember class is a logical construct that does not have physical reality. 4. Defining a static data member is done outside the class by redeclaring the static variable using the scope resolution operator . 5. SDM are normally used to maintain values common to entire class
Class item { static int count; int num; public: void getdata(int val) { num = val; count++; } void getcnt() { cout<<count : <<count; } };
Main() { item i1, i2; i1.getcnt(); i2.getcnt(); i1.getdata(100); i2.getdata(200); i1.getcnt(); i2.getcnt(); }
1. Bcoz there is only one copy of count shared by all the objects. The two output statements cause the value 2 to be displayed
2. While defining a static data member, some initial value can also be assigned to the variable Ex: int item :: count = 10;
1. One use of a static member variable is to provide access control to some shared resource used by all objects of a class.
2. For Ex: U might create several objects, each of which needs to write to a specific disk file, However only one object can write to the file at a time. In this case declare a static variable that indicates when the file is in use and when it is free. Each object then interrogates this variable before writing to the file.
3. Another use of SMV is to keep track of the number of objects of a particular class type that are in existence.
4. A public static data member can be accessed by outside function using the class name as follows class name :: data member
Class counter { public: static int count; counter() { count++; } ~counter() { count--; } }; int counter :: count; void fun() { counter temp; cout<<\nObjects in use: ; cout<<counter :: count; }
Void main() { counter o1; cout<<\nObjects in use: ; cout<<counter :: count; counter o2; cout<<\nObjects in use: ; cout<<counter :: count;
f();
cout<<\nObjects in use: ; cout<<counter :: count;
7. A static member function can be called using the class name as follows Class-name :: function-name;
8. Use is to preinitialize private static data before any object is created
Class static_type
Int static_type :: n;
{ static int n; Public: static void init(int x) { n = x; } void show() { cout<<n; } }; Scope Resolution Operator
Void main()
{ //static data bfore object creation static_type :: init(100);
static_type x; x.show();
}
1. In C++ several different classes can use the same function name, the compiler knows which function belongs to which class bcoz of scope resolution operator The :: operator links a class name with a member name in order to tell the compiler what class the member belongs to. Ex: void stack :: push(int i);
2. Allows access to global version of a variable. Int m = 10; Void main() { int m = 20; { int k = m; int m = 30; cout<<k = <<k<<\n; cout<<m = <<m<<\n; cout<<::m = <<::m<<\n; } cout<<m = <<m<<\n; cout<<::m = <<::m<<\n; }
Nested Classes
1. Defining one class within another creates a nested class. This is one way of Inheriting properties of one class into another.
2. That is a class can contain objects of other classes as its members.
class alpha{..};
class beta{..}; class gamma { alpha a; beta b;
};
3. Nested classes are rarely used because of C++s powerful inheritance mechanism.
Local Classes
2. When a class is declared within a function, it is known only to that function and unknown outside of it.
3. A local class has access to static local variables declared within the function or those declared as extern.
4. No static variables may be declared inside a local class.
5. Local class may access type names and enumerators defined by the enclosing function.
Passing Objects to Functions 1. Objects can be passed to functions just the same way as any other type of variable. 2. Although the passing of objects is straightforward some Unexpected events occur that relate to constructors and destructors.
Class myclass { int x; public: myclass(int n); ~myclass(); void set_x(int n) { x = n; } int get_x() { return x; } }; Myclass::myclass(int n) { x = n; cout<<Constructing <<x; }
myclass :: ~myclass()
{ cout<<Destroying <<x; }
Void fun(myclass ob) { ob.set_x(2); cout<< Local x: <<ob.get_x(); } Void main() { myclass o(1); fun(o); cout<< x in main: ; cout<<o.get_x(); }
1. According to the output, there is one call to constructor and two calls to the destructor. Why?
1. When an object is passed to a function, a copy of that object is made(and this copy becomes the parameter) 2. This means that a new object comes into existence. 3. When the function terminates, the copy of the argument is destroyed.
8. However if a class does not explicitly define a copy constructor, then C++ provides one by default.
9. The default copy constructor creates a bitwise(i.e identical) copy of the object
Copy Constructor
1. A copy constructor is used to declare and initialize an object from another object sample(sample & s)
For Ex: sample s2(s1) Declares the object s2 and at the same time initialize it to the values of s1 2. Another form: sample s2 = s1; 3. The statement s1 = s2; will not invoke the copy constructor.
Class code { int id; public: code(){ } code(int a) { id = a; } code(code & x) { id = x.id; } void display() { cout<<id; } };
Void main() { code a(100); code b(a); code c = a; code d; d = a; Cout<<a.display()<<b.display(); Cout<<c.display()<<d.display(); }
Class myclass { int x; public: void set_x(int n) { x = n; } int get_x() { return x; } }; myclass fun() { myclass m; m.set_x(2); return m; }
1. When an object is returned by a function, a temporary object is automatically created that holds the return value.
2. It is this object that is actually returned by the function. After the value has been returned, this object is destroyed
3. The destruction of this temporary object may cause unexpected side effects. 4. For Ex: if the object returned by the function has a destructor that frees dynamically allocated memory , that memory will be freed even though the object that is receiving the return value is still using it. 5. Ways to overcome this problem:
Object Assignment 1. An object can be assigned to another provided they are of same type.
2. This causes the data of the right side object to be copied into left side object
Void main() { myclass obj1, obj2; obj1.set_x(99); ob2 = ob1; cout<< X value of obj 2: ; cout<<obj2.get_x(); }
3. By default, all data from one object is assigned to the other by use of a bit-by-bit copy. 4. It is possible to overload the assignment operator and define some other assignment procedure.
If a class defines a parameterized constructor, an array declared of this class type must be initialized. Void main() Class c1 { c1 ob[3]; //error { int I; c1 ob[3] = {1, 2, 3}; //initializers public: for(int I=0; I<3; I++) c1(int j){ I = j; } cout<<ob[I].get_I(); int get_I(){ return I;} } };
There are two initialization syntaxes Shorthand form: c1 ob[3] = {1, 2, 3};
Class c1 { int h, I; public: c1(int j, int k){ h = j; I = k; } int get_I(){ return I;} int get_h(){ return h;} };
Void main() { c1 ob[2] = {c1(1, 2), c1(3, 4)}; for(int I = 0; I<2; I++) { cout<<ob[I].get_h()<< ; cout<<ob[I].get_I(); } }
Uninitialized arrays can be called by including a constructor that takes no parameters. Class c1 { int I; public: c1() { I = 0; } // called for non-initialized arrays c1(int j){ I = j; } // called for initialized arrays int get_I(){ return I;} };
Pointers to Objects 1. When accessing members of a class, given a pointer to an object, an arrow operator is used instead of dot operator. Class c1 Void main() { int I; { c1 ob(88), *p; public: p = &ob; c1(int j){ I = j; } cout<< p->get_I(); int get_I(){ return I; } } };
2. When a pointer is incremented, it points to the next element of its type. Class c1 { int I; public: c1() {I = 0; } c1(int j){ I = j; } int get_I(){ return I;} }; Void main() { c1 ob[3] = {1, 2, 3}; c1 *p; p = ob; //start addr assigned for(int I = 0; I<3; I++) { cout<<p.get_I()<<\n; p++; } }
3. The address of a public member of an object can be assigned to a pointer and then that member can be accessed by using the pointer. Void main() Class c1 { c1 ob(1); { public: int *p; int I; p = &ob.I; c1(int j){ I = j; } cout<< *p; }; } Type Checking C++ Pointers
1. One pointer can be assigned to another only if two pointer types are compatible.
2. For Ex: int *pi, *pj; float *pf; pi = pj // No type mismatch pi = pf // error, type mismatch
This Pointer
1. When a member function is called, it is automatically passed an implicit argument that is a pointer to the invoking object. This pointer is called this Pwr :: pwr(double base, int exp) Class pwr { b = base; e = exp; val = 1; { double b; if(e == 0) return; int e; For( ; e>0; e--) double val; val = val *b; public: } pwr(double base, int exp); Void main() double get_pwr() { pwr x(4.0 , 2), y(2.5 , 1); { return val; } cout<<x.get_pwr()<< ; }; cout<<y.get_pwr()<< ; }
2. Within a member function, the members of a class can be accessed directly, For Ex: b = base;
3. Since member pointers are not true pointers, the . and -> cannot be applied to them. 4. Special pointer-to-member operators .* and ->* are used to access a member of a class given a pointer to it.
Class c1 { public: c1(int i) { val = I;} int val; int double_val() { return val + val; } }; Void main() { int c1::*data; int (c1::*func)( );
c1 ob1(1), ob2(2);
data = &c1:: val; //get offset of val func = &c1:: double_val; //get offset.
cout<<ob1.*data;<< <<ob2.*data;
cout<<\n Doubled values:; cout<<(ob1.*func) ( )<< ; cout<<(ob2.*func) ( ); } // end of main
Points to Note
1. When declaring pointers to members, you must specify the class and use the scope resolution operator. (data and func of Example).
2. Must use .* operator when accessing a member of an object by using an object and ->* operator if a pointer to an object is used in accessing a member Class c1 c1 ob1(1), ob2(2); c1 *p1, *p2; { public: p1 = &ob1; p2 = &ob2; c1(int i) { val = I;} data = &c1:: val; //get offset of val int val; func = &c1:: double_val; //get offset. int double_val() cout<<p1->*data;<< <<p2->*data; { return val + val;} cout<<\n Doubled values:; }; cout<<(p1 ->*func) ( )<< ; Void main() cout<<(p2 ->*func) ( ); { int c1::*data; } // end of main int (c1::*func)( );
Pointers to members are different from pointers to specific instances of elements of an object int c1::*d; p = &o.val; // addr of specific val int *p; c1 o; d = &c1::val //offset of generic val Operator Overloading 1. The mechanism of giving additional meaning to an operator is known as operator overloading. Operator Overloading Restrictions 1. The following operators cannot be overloaded a) Class member access operators(. , .*) b) Scope resolution operator(::) c) Size operator(sizeof) d) conditional operator(?:) 2. The precedence of an operator cannot be altered.
4. Except for the function call operator( ( ) ), operator functions cannot have default arguments.
5. When an operator is overloaded its original meaning should not be lost. Ex: The operator + which has been overloaded to add two vectors can still be used to add two integers. 6. Except for the = operator , operator functions are inherited by a derived class Creating a member operator function 1. Operators are overloaded by creating operator functions, which defines an additional task to an operator. 2. The general form of an operator function is returntype classname :: operator #(arg-list) { function body // task defined }
3.
# is the placeholder representing the operator being overloaded, operator # is the function name.
4. Operator function must be either member function (non-static) or friend function. a) A friend function will have only 1 argument for unary operators and 2 for binary operators b) A member function has no arguments for unary operators and only 1 for binary operators.
Class complex
{ float x, y; public: complex() { } complex(float real, float img) { x = real; y = img; } complex operator +(complex); void show() { cout<< x << + ; cout<< y << j ; } };
{ complex temp; temp.x = x + c.x; temp.y = y + c.y; return temp; } Void main()
{ complex c1, c2, c3; c1 = complex(2.5, 3.5); c2 = complex(1.5, 2.5); c3 = c1 + c2;
Note: operator +() is a member function and receives only one complex type argument. 1. The function is expected to add two complex values and return a complex value but receives only one value as argument. Where does the other value come from? 2. C3 = C1 + C2 // invokes operator +() function Here C1 takes the responsibility of invoking the function C2 plays the role of an argument that is passed to the function 3. The above invocation is equivalent to C3 = C1.operator+(C2) 4. The data members of c1 are accessed directly and the data members of c2 are accessed using the dot operator. 5. Temp object creation can be avoided by replacing the entire function body by the following statement: return complex((x + c.x), (y + c.y))
6. On encountering such a statement the Compiler invokes an appropriate constructor, initializes an object with no name and returns the contents for copying into an object. 7. Such an object is called a temporary object and goes out of space as soon as the contents are assigned to another object.
8. Using temporary object, makes code shorter, more efficient and better to read.
9. Instead of statement c3.show(); It is possible to have a stmt like (c1 + c2) .show(); 10. In the above case c1 + c2 generates a temporary object that ceases to exist after the call to show() terminates.
Class sample Sample sample :: operator ++() { int val1, val2; { val1++; val2++; return *this; } public: Void main() sample() { } { sample s1(10, 20), s2(5, 30); sample(int a, int b) s1.show(); // displays 10 20 { val1 = a; s2.show(); // displays 5 30 val2 = b; } ++s1; void show() s1.show(); //displays 11 21 { cout<<val1<< ; s2 = ++s1; cout<<val2; } s1.show(); //displays 12 22 sample operator ++(); s2.show(); // displays 12 22 }; } Overloading the Shorthand Operators 1. Shorthand Operators such as +=, -= can also be overloaded. 2. They are binary operators and the operator function takes one arg. (Its Basically combining assignment with another type of operation)
Class sample { int val1, val2; public: sample() { } sample(int a, int b) { val1 = a; val2 = b; } void show() { cout<<val1<< ; cout<<val2; } sample operator+=(sample); };
Sample sample :: operator +=(sample s) { val1 = s. val1 + val1; val2 = s.val2 + val2; return *this; } Void main() { sample s1(10, 20), s2(5, 30); s1.show(); // displays 10 20 s2.show(); // displays 5 30 s1+=s2; s1.show(); //displays 15 50 }
Creating Prefix and Postfix Forms of the Increment and Decrement Operators
1. C++ allows creating prefix and postfix versions of the increment or decrement operators.
3. The general forms for the prefix and postfix ++ and -- operator functions. Pre-Increment Post-Increment
type operator++() type operator++(int x)
{ } { } 4. If the ++ follows its operand, the operator++(int x) is called and x has the value zero. 5. If the ++ precedes its operand, the operator++() is called.
6. Older versions of C++ does not support specifying separate prefix and postfix versions of ++ or --, the prefix form was used for both.
** In C++ if the = is not overloaded, a default assignment operation is created automatically for any class.
The default assignment is simply a member-by-member, bitwise copy
1. C++ allows overloading special operators like array subscripting ( [ ] ), function call operator (( )), ClassMember access operator ( ->).
2. The restrictions on overloading these operators are they must be non static member functions, and they cannot be friends. Overloading [ ] (It is considered as binary operator) 1. The general form of member operator[ ] () function is type class-name :: operator[ ](int i) 2. Technically, the parameter does not have to be of type int, but an operator[] ( ) function is typically used to provide array subscripting and as such, an integer value is generally used. 3. The expression O[3]; results in a call to operator[] ( ) function 4. O[3] is equivalent to O. operator[] (3 ).
Class atype { int a[3]; Void main() public: { atype(int i, int j, int k) atype ob(1, 2, 3); { a[0] = i; a[1] = j; a[2] = k; } cout << ob[1]; int operator [ ](int i) } { return a[i]; } }; [ ] can be used on both left and right sides of an assignment statement. To do so simply specify the return value of operator[]() as a reference. Class atype { int a[3]; public: atype(int i, int j, int k) { a[0] = i; a[1] = j; a[2] = k; } int &operator [ ](int i) { return a[i]; } }; Void main() { atype ob(1, 2, 3); cout << ob[1]; cout<< ; ob[1] = 25; cout<< ob[1]; }
1. In C++ It is possible to overrun or underrun an array boundary at runtime without generating a run-time error message. 2. The advantage of overloading the [ ] operator is that it allows a means of implementing safe array indexing in C++. 3. Create a class that contains the array and allow access to that array only through the overloaded [ ] operator then out-of-range index can be intercepted. Int& atype :: operator [ ](int i) Class atype { { int a[3]; if(i<0 || i>2) public: { cout<< Boundary Error\n; atype(int i, int j, int k) exit(1); { a[0] = i; a[1] = j; a[2] = k; } } int &operator [ ](int i); return a[i]; }; }
Void main() { atype ob(1, 2, 3); cout << ob[1]; // Displays 2 cout<< ; ob[1] = 25; // calls the operator funtion cout<< ob[1]; ob[3] = 44; // Generates run-time error, 3 out-of-range } 4. In the above program when the statement ob[3] = 44; executes, the boundary error is intercepted by operator [ ] ( ) and the program is terminated before any damage can be done.
3. The expression on the right side becomes the value of the total comma-separated expression.
4. Ex: x = (y = 3, y + 1); first assigns y the value 3 and then assigns x the value 4. 5. The parantheses are necessary bcoz the comma operator has a lower preedence than the assignment operator. 6. When comma operator is used on the right side of assignment statement, the value assigned is the value of the last expression of the comma-separated list. sample(int a, int b) Overloading comma operator { val1 = a; Class sample val2 = b; } { int val1, val2; void show(); public: sample operator +(sample op2); sample() { } Sample operator ,(sample op2); };
{ cout<<val1<< ; cout<<val2; } Void main() { sample s1(10, 20); sample s2(5, 30); Sample sample :: operator , (sample op2) sample s3(1, 1); { Temp.val1= op2.val1 ; s1.show(); s2.chow(); Temp.val2= op2.val2; s3.show(); cout<<\n; cout<<op2.val1<< << op2.val2; s1 = (s1, s2+s2, s3); return temp; } s1.show(); } Overloading << and >> (Input and Output operators)
1. The << and >> operators are overloaded in C++ to perform I/O operations on C++s built-in-types.
Sample sample :: operator + (sample op2) { sample temp; Temp.val1= op2.val1 + val1; Temp.val2= op2.val2 + val2; return temp; }
INHERITANCE
1. The mechanism of deriving a new class from an old one is called Inheritance.
2. The concept of Inheritance provides the idea of reusability. This is basically done by creating new classes, reusing the properties of the existing ones. 3. A class that is inherited is referred to as base class, and a class that inherits is called the derived class.
B Single Inheritance
C Multiple Inheritance
Hierarchical Inheritance
C Multilevel Inheritance
D Hybrid Inheritance
1. A derived class with only one base class is called single Inheritance.
2. Deriving a class from multiple base classes is called Multiple Inheritance.
3. The process of inheriting the properties of a class by more than one class is Hierarchical Inheritance
4. The mechanism of deriving a class from another derived class is Multilevel Inheritance.
Class base { int i, j; public: void set(int a, int b) { i = a; j = b; } void show() { cout<<i<< <<j; } };
Void main() { derived ob(3);
Class derived : public base { int k; public: derived(int x) { k = x; } void showk() { cout<<k<<\n; } };
ob.set(1 , 2); // accesses member of base ob.show(); // accesses member of base ob.showk(); // uses member of derived }
Class base { int i, j; public: void set(int a, int b) { i = a; j = b; } void show() { cout<<i<< <<j; } };
Void main() { derived ob(3);
Class derived : private base { int k; public: derived(int x) { k = x; } void showk() { cout<<k<<\n; } };
ob.set(1 , 2); // error, cannot access set() ob.show(); // error, cant access show() ob.showk(); // access member of derived }
Protected Members 1. When a member of a class is declared as protected, that member is not accessible by other nonmember elements of the program. 2. In other words access to a protected member is the same as access to a private member.
d) If the base class is inherited in public mode then all protected members of base class become protected in derived class
e) If the base class is inherited in private mode then all protected members of base class become private in derived class.
Class base { protected: int i, j; public: void set(int a, int b) { i = a; j = b; } void show() { cout<<i<< <<j; } }; Void main() { derived ob;
Class derived : public base { int k; public: void setk() // D can access i, j { k = i* j; } void showk() { cout<<k<<\n; } };
Class base { protected: int i, j; public: void set(int a, int b) { i = a; j = b; } void show() { cout<<i<< <<j; } }; Class derived2 : public derived1 { int m; public: void setm() { m = i - j; } void showm() { cout<<m<<\n; } };
Class derived1 : public base { int k; public: void setk() // can access i, j { k = i* j; } void showk() { cout<<k<<\n; } }; Void main() { derived1 ob1; derived2 ob2; ob1.set(2 , 3); ob2.set(3 , 4); ob1.show(); ob2.show(); ob1.setk(); ob2.setk(); ob.showk(); ob2.showk(); ob2.setm(); ob2.showm(); }
1. When a derived class is used as a base class for another derived class, any protected member of the initial base class that is inherited by the first derived class may also be inherited as protected again by a second derived class.(Base inherited by derived as public) 2. In the above example derived2 has access to i and j. 3. If base were inherited as private, then all members of base would become private members of derived 1, which means that they would not be accessible by derived2. 4. Example program for the above statement is left as an exercise. 5. The program in the previous slide can be quoted as an example for Multilevel Inheritance. 6. The most important point to note is: Private members of base class are not inherited by the derived class what ever be the access specifier or the mode of inheritance.
5. When the access specifier is public , all public members of the base class become public members of derived class, all protected members of base class become protected members of derived class. 6. When the access specifier is private , all public and protected members of the base class become private members of the derived class 7. When the access specifier is protected , all public and protected members of the base class become protected members of the derived class.
Base class Derived class Access Specifier visibility public private protected Private Not inherited Not inherited Not inherited Protected Protected Private Protected
Public
Public
Private
Protected
1. When a base class is inherited as protected, all public and protected members of base class become protected members of derived class. Class base void setk() { protected: { setk(10, 12); k = i*j; } int i, j; void showall() public: { cout<<k<< ; showij(); } void setij(int a, int b) }; { I =a; j = b; } Void main() void showij() { derived ob; { cout<<i<< <<j<< \n; ob.setij(2, 3); // illegal }; ob.setk(); //ok ob.showall(); //ok Class derived : protected base ob.showij(); //illegal { int k; } public:
1. As discussed earlier, a derived class inheriting two or more base classes is called multiple inheritance Class base1 Class base2 { protected: { protected: int x; int y; public: public: void showx() void showy() { cout<< x <<\n ; } { cout<< y <<\n ; } }; };
Class d1: public base1, public base2 { public:
Void main()
{ derived ob; ob.set(10, 20); ob.showx(); ob.showy(); }
1. It is possible for a base class, a derived class, or both to contain constructors and destructors.
2. Two things to be discussed relative to constructors and destructors when Inheritance is involved a) When base-classs and derived-classs constructors and destructors are called (order of execution) b) How parameters are passed to base class constructors. Execution of constructors and destructors w.r.t Inheritance When a derived class object is created, the base class constructor will be called first, followed by derived class constructor.
When a derived object is destroyed, its destructor is called first followed by the base class destructor
Put differently constructors are executed in their order of derivation and destructors are executed in reverse order of derivation.
Class base Output { Constructing base public: base() { cout<< constructing base\n ; } Constructing derived ~base() { cout<< Destructing base \n; } Destructing derived }; Destructing base Class derived : public base { public: derived() { cout<< Constructing Derived\n ; } ~derived() { cout<< Destructing derived \n; } }; Void main() { derived ob; }
Class base { public: base() { cout<< constructing base \n; } ~base() { cout<< Destructing base \n ; } }; Class derived1 : public base { public: derived1() { cout<< Constructing Derived1\n ; } ~derived1() { cout<< Destructing derived1 \n; } }; Class derived2 : public derived1 { public: derived2() { cout<< Constructing Derived2\n ; } ~derived2() { cout<< Destructing derived2 \n; } };
Void main() { derived2 ob; } Output Constructing base Constructing derived1 Constructing derived2
Destructing derived2
Destructing derived1 Destructing base In case of multiple Inheritance constructors are called in the order of derivation, left to right, as specified in deriveds Inheritance list and destructors in reverse order.
Class base1 { public: base1() { cout<< constructing base1\n ; } ~base1() { cout<< Destructing base1 \n; } }; Class base2 { public: base2() { cout<< constructing base2\n ; } ~base2() { cout<< Destructing base2 \n; } }; Class derived : public base1, public base2 { public: derived() { cout<< Constructing Derived\n ; } ~derived() { cout<< Destructing derived \n; } };
Void main() { derived ob; } Output Constructing base1 Constructing base2 Constructing derived Destructing derived Destructing base2 Destructing base1
Output
Constructing base2 Constructing base1
Constructing derived
Destructing derived Destructing base1
Destructing base2
1. The arguments to a base-class constructor are passed via arguments to the derived class constructor by using the Expanded derived-class constructor declaration.
2. The General form of it is: derived-constructor(arg-list) : base1(arg-list), .baseN(arg-list) { // Body of derived constructor } 3. Here base1 through baseN are the names of the base classes inherited by the derived class Class base { protected: int I; public: base(int x) { I = x; cout<<constructing base; ~base() { cout<<\n Destructing base; };
Class derived : public base { int j; public: derived(int x, int y) : base(y) { j = x; cout<<constructing derived\n; } ~derived() { cout<< Destructing derived \n; } void show() { cout<<i<< <<j<<\n; } }; Void main() { derived ob(3, 4) ob.show(); // displays 4 3 } The derived class constructor must declare both, the parameters that it requires as well as any required by the base class
Note: Even if a derived class constructor does not use any arguments, it will still need to declare one if the base class requires it. In this situation, the arguments passed to the derived class are simply passed along to the base. Class base1 Class base2 { protected: int i; { protected: int k; public: public: base1(int x) base2(int x) { i = x; cout<<constructing base1; {k=x; cout<<constructing b2; ~base1() ~base2() { cout<<\n Destructing base1; { cout<<\n Destructing b2; }; }; Class derived : public base1, public base2 { public: derived(int x, int y) : base1(x), base2(y) { cout<< Constructing Derived\n ; } ~derived() { cout<< Destructing Derived\n ; }
Void show() { cout<<I<< <<k<<\n;} }; // End of Derived class Void main() { derived ob(3, 4); ob.show(); } Granting Access 1. When a base class is inherited a private, All public and protected members of that class become private members of derived class. 2. Suppose that u want to restore one or more inherited members to their original access. 3. For Ex: U might want to grant certain public members of base class public status in the derived class even though the base class is inherited as Private 4. In C++ there are two ways to accomplish this a) By using a Using statement that supports namespaces b) Second, to employ an access declaration within the derived class
5. General form of Access Declaration base-class :: member ; 6. The access declaration is put under appropriate access heading in the derived class declaration. Note: No type declaration is allowed in access declaration Class derived : private base Class base { public: { int I; base :: j; // make j public again public: base :: seti; intr j, k; base :: I; // illegal cant elevate access void seti(int x) { I = x; } int a; void geti() { return I; } }; }; Void main() ob.seti(10); //legal { derived ob; ob.j = 20; //legal ob.k = 10; // illegal }
1. There is an ambiguity when multiple base classes are inherited Void main() Class base { d3 ob; { public: int I; }; ob.I = 10; // Which I ????? Class d1 : public base ob.j = 20; { public: int j; }; ob.k = 30; ob.sum = ob.I + ob.j + ob.k; Class d2 : public base cout<<ob.I<< ; { public: int k; }; cout<<ob.j<< <<ob.k<< ; Class d3 : public d1, public d2 cout<<ob.sum; { public: int sum; }; }
Note: There are two copies of base present in an object of type derived3 There are two ways to overcome this problem: 1) To use scope resolution operator and manually select one i
};
Class d1 : virtual public base { public: int j; }; Class d2 : virtual public base { public: int k; }; Class d3 : public d1, public d2 { public: int sum; };
Void main() { d3 ob; ob.I = 10; // unambiguous ob.j = 20; ob.k = 30; ob.sum = ob.I + ob.j + ob.k; cout<<ob.I<< ; cout<<ob.j<< <<ob.k<< ; cout<<ob.sum; }
1. D1 and D2 inherited base as virtual, any multiple inheritance involving them will cause only one copy of base to be present
2. D1 ob1; ob1.I = 88; //valid
Runtime Polymorphism
Function Overloading
Operator Overloading
Virtual Functions
CTP: Linking the function call to the appropriate function is done at compile time. Also called Early Binding or Static Binding or Static Linking RTP: Linking the function call to the appropriate function is done at Runtime. Also called Late Binding or Dynamic Binding
1. By using pointers to objects and Virtual Functions run-time polymorphism can be achieved 2. A pointer declared as a pointer to base class can also be made to point to derived class object. This is perfectly valid. 3. For Ex: D d; B *ptr; ptr = &b; B b; ptr = &d;
4. There is a problem with ptr in accessing public members of derived class D. Only those members inherited from B and not the members that originally belong to d can be accessed. Class B { public: int b; Class D : public B { public: int d; void show() { cout<<b=<<b<<\n; cout<<d=<<d<<\n; } };
void show()
{ cout<<b=<<b<<\n; } };
Void main()
{ B *bptr; B base;
bptr = &base; bptr->show(); D derived; bptr = &derived; bptr->show(); bptr->b = 200; bptr->b = 100; cout<< bptr points to base object\n;
cout<< bptr points to derived object\n; D *dptr; dptr = &derived; dptr->d = 300; cout<<dptr is derived type pointer\n; dptr->show(); }
1. A Virtual function is a member function that is declared within a base class and redefined by a derived class. 2. To create a virtual function, precede the functions declaration in the base class with the keyword Virtual.
Class base
{ public: virtual void vfunc()
{ public:
void vfunc() { cout<< This is d1s Vfunc;} };
{ base *p, b;
d1 do1; d2 do2; p = &b; //continued
p = &do1;
p->vfunc(); // access d1s vfunc p = &do2;
Output This is bases vfunc This is d1s vfunc This is d2s vfunc
Restrictions on Virtual Functions 1. Virtual Functions must be non-static members of the classes. 2. They cannot be Friends. 3. Constructor functions cannot be virtual, but destructor functions can
6. Base pointer can point to derived object but reverse is not true.
7. When a base pointer points to derived class, incrementing or decrementing it will not make it to point to the next object of Dclass.
8. If a virtual function is defined in the base class. It need not necessarily be redefined in the derived class.
9. A Virtual function in base class must be defined, even though it may not be used.
1. A reference is an implicit pointer. Thus a base class reference can be used to refer to an object of the base class or any object derived from that base.
2. The common situation in which a virtual function is invoked through a base class reference is when the reference is a function parameter. Class base Class d2: public base { public: { public: virtual void vfunc() void vfunc() { cout<< This is bases Vfunc;} { cout<<This is d2s Vfunc;} }; }; Class d1 : public base Void f(base &r) { public: { r.vfunc(); } void vfunc() Void main() { cout<< This is d1s Vfunc;} { base b; }; d1 do1; d2 do2; //contd
Class d2 : public d1 { public: void vfunc()//vfunc is still virtual { cout<< This is d2s Vfunc;} }; Void main() { base *p, b; d1 do1; d2 do2;
p = &b;
p = &do2;
p->vfunc(); // access d2s vfunc }
Output This is bases vfunc This is d1s vfunc This is d2s vfunc
1. As said earlier a virtual function does not have to be overridden. When a derived class fails to override, then when an object of that derived class accesses that function, the function defined by the base class is used. Class base { public: virtual void vfunc() { cout<< This is bases Vfunc; } };
Class d1 : public base { public: void vfunc() { cout<< This is d1s Vfunc; } };
Class d2 : public base { public: // vfunc not overriden }; Void main() { base *p, b; d1 do1; d2 do2; Output This is bases vfunc p = &b; This is d1s vfunc p->vfunc(); // access bases vfunc This is bases vfunc p = &do1; p->vfunc(); // access d1s vfunc p = &do2; p->vfunc(); // access bases vfunc } Because Inheritance is Hierarchical in C++. It makes sense that virtual functions are also hierarchical.
When a derived class fails to override a virtual function, the first redefinition found in reverse order of derivation is used. Void main() Class base { base *p, b; { public: virtual void vfunc() d1 do1; d2 do2; { cout<< This is bases vfunc; } p = &b; }; p->vfunc(); // access base vfunc p = &do1; Class d1 : public base p->vfunc(); // access d1s vfunc { public: p = &do2; void vfunc() p->vfunc(); // access d1s vfunc { cout<< This is d1s Vfunc; } }; } Output Class d2 : public d1 This is bases vfunc { public: This is d1s vfunc // vfunc not overriden by d2 This is d1s vfunc
1. A Pure Virtual Function is a Virtual function that has no definition within the base class.
2. The General form of declaring a Pure Virtual Function:
void show()
{ cout<<hex<<val<<\n; }
Class dectype : public number { public: void show() { cout<<val<<\n; } }; Void main() { dectype d; hextype h; octtype o; d.setval(20); d.show(); //displays 20 h.setval(20); h.show(); // displays 14 o.setval(20); o.show(); // displays 24 }
Class octtype : public number { public: void show() { cout<<oct <<val<<\n; } }; The Ex: illustrates how a base class may not be able to mraningfully define a virtual function When a Virtual Function is declared as Pure, all derived classes must override it. If a derived class fails to do this, a compile-time error will result
Abstract Classes
1. A class that contains atleast one pure virtual function is said to be abstract
2. An abstract class constitutes an incomplete type that is used as a foundation for derived classes. 3. Cannot create objects of an abstract class, can create pointers and references to an abstract class. 4. Class number in previous program is an example of an abstract class
Early Binding
Occurs when all information needed to call a function is known at compile time. Means object and function call are bound during compilation. Ex: Normal function calls, Function Overloading, Operator Overloading. The main advantage is efficiency (Faster Execution)
Late Binding
Refers to function calls that are not resolved until run time
The main adv is flexibility means LB allows creation of programs that can respond to events occurring while the program executes there by reducing code.
Slower Execution times
I/O Operations
1. C++ uses the concept of stream and stream classes to implement the I/O Operations. A stream acts as an interface between the program and I/O device.
C++ Streams
1. A stream is a sequence of bytes. It acts either as a source from which the input data can be obtained or as a destination to which output data can be sent. 2. The source stream that provides data to the program is called the input stream.
3. The destination stream that receives output from the program is called output stream. 4. The data in the input stream can come from keyboard or any other storage device. 5. The data in the output stream can go to the screen or any other storage device.
Input Device
Input stream
Program
Output Device Output stream Insertion into output stream
1. C++ contains several predefined streams that are automatically opened when a program begins its execution. 2. These include cin and cout. Cin represents the standard input stream. Cout represents the standard output stream.
ios
istream
streambuf
ostream
iostream
Istream_withassign
Iostream_withassign
Ostream_withassign
1. ios is the base class for istream and ostream which are inturn base classes for iostream. 2. The class ios is declared as Virtual base class so that only one copy of its members are inherited by the iostream
ios
Contains basic facilities that are used by other I/O classes Contains a pointer to a buffer object Inherits the properties of ios Declares input functions such as get(), getline() and read() Contains Overloaded Extraction operator>> Inherits the properties of ios Declares output functions such as put() and write() Contains overloaded insertion operator<< Inherits the properties of istream and ostream and thus contains all input output functions
istream
ostream
iostream
2. The function put() , a member of ostream class can be used to output a line of text, character by character. 3. For Ex. cout.put(x); // displays character x cout.put(ch); //displays the value of variable ch cout.put(68); // displays the character d
1. Reads and displays line of text . The general form is: cin . getline(line, size);
2. char name[20]; cin.getline(name, 20); 3. getline() function can read strings that contain white spaces also 4. After reading the string cin automatically adds the terminating null character. 5. cout.write(line , size); Ex: cout.write(name, size); 6. If the size is greater than the length of line, then write() function displays beyond the bounds of line. 7. It is possible to concatenate two strings using the write() function cout.write(string1, m). write(string2, n);
1. C++ supports a number of features that could be used for formatting the output. These features include
a) ios class functions and flags b) Manipulators c) User-Defined output functions Function Task ios format functions
Width()
Precision() Fill() Setf()
Unsetf()
To specify the required field size for displaying an output value To specify the number of digits to be displayed after the decimal point of a float value To specify a character that is used to fill the unused portion of a field To specify format flags that control the form of output To clear the flags specified
2. Manipulators are special functions that can be included in the I/O statements to format the parameters of a stream 3. To access these manipulators , the file iomanip.h should be included in the program
4. In addition to the manipulators supported by C++, User defined manipulators can be created
Manipulators Setw() Equivalent ios General Form function Width() Cout.width(w) // w is no. of columns
Setprecision()
Setfill() Setiosflags() Resetiosflags()
Precision()
Fill() Setf() Unsetf()
Cout.precision(d)// d is the no. of digits to the right of decimal point Cout.fill(ch) // ch is the character Cout.setf(arg1, arg2) Cout.unsetf(arg1, arg2)
Cout.width(5);
Cout<<543<<12;
5 4 3 1 2
Width function will specify the field width for only one item. After printing one item it will revert back to default If the specified field width is smaller than the size of the value to be printed, C++ expands the field to fit the value.
1 .
6 7
Cout<<1.6666;
The output is rounded to nearest cent Trailing zeros are truncated
Fill()
Cout.fill(*);
Cout.width(10); Cout<<3977;
* * * * * * 3 9 7 7
arg2 ( bit field) specifies the group to which the formatting flag belongs
Cout.setf(ios::left, ios::adjustfield); Cout.width(7)
H E L L O
Cout<<HELLO;
Bit-field (arg2)
ios :: adjustfield
ios :: internal
ios :: scientific ios :: fixed ios :: dec ios :: oct ios :: hex ios :: floatfield
ios :: basefield
Note: First argument should be one of the group members of the second argument
Cout.fill(*);
Cout.precision(3);
- * * * * * 1 . 2 4 3 e + 0 1
Cout.setf(ios :: internal, ios :: adjustfield); Cout.setf(ios :: scientific, ios :; floatfield); Cout.width(15); Cout<<-12.431276; Displaying Trailing zeroes and + sign
Certain flags that do not bit-fields are used as single arguments in setf() to achieve this.
ios :: showpoint - Displays trailing zeroes ios :: showpos - Print + before Positive numbers Cout.setf(ios :: showpoint); Cout.setf(ios :: showpos); Cout<<13.230000;
+ 1 3 .
2 3 0 0 0 0
Manipulators
Setprecision(int d)
Sets the floating point precision to d Setfill(int c) Sets the fill character to c Setiosflags(long f) Sets the format flag f Resetiosflags(long f) Clears the flag specified endl Insert new line
Cout<<setprecision(2);
Cout<<setfill(*);
Cout<<setiosflags(ios :: left)
Cout<<endl
User Defined Manipulators 1. The general form for user defined manipulators 2. Ostream & manipulator(ostream & output) { . return output; } 3. Ostream & unit(ostream & output) { output<< inches; return output; } 4. The statement cout<<36<<unit; //displays 36 inches