UNIT II OBJECT ORIENTED PROGRAMMING CONCEPTS 9

2.COPY CONSTRUCTOR

  1. A Copy constructor is used to declare and initialize an object from another object.

`s (s &o);

  1. There are two ways to invoke the copy constructor

s o(o1) -define the object o and at the same time initialize it to the values of o1

s o = o1- define the object o and at the same time initialize it to the values of o1

  1. .Copy constructor is one of the more important forms of an overloaded constructor .
  2. The purpose of the copy constructor is to initialize a new object with data copied from another object of the same class.
  3. For example to invoke a copy constructor the programmer writes: Exforsys e3(e2); or Exforsys e3=e2; For Example:

#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(); }

9. In the above the copy constructor takes one argument an object of type Exforsys which is passed by reference.

10.The output of the above program is Example of Copy Constructor e3=50.

Some important points about constructors:

  1. A constructor takes the same name as the class name.
  2. The programmer cannot declare a constructor as virtual or static, nor can the programmer declare a constructor as const, volatile, or const volatile.
  3. No return type is specified for a constructor.
  4. The constructor must be defined in the public. The constructor must be a public member.
  5. Overloading of constructors is possible.

3. POLYMORPHISM

  1. The wordpolymorphismmeans having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
  1. C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.

Consider the following example where a base class has been derived by other two classes:

Program:
#include<iostream>
usingnamespace std;
classShape{
protected:
int width, height;
public:
Shape(int a=0,int b=0)
{
width = a;
height = b; }
int area()
{
cout "Parent class area :"endl;
return0;
}};
classRectangle:publicShape{
public:
Rectangle(int a=0,int b=0):Shape(a, b){}
int area ()
{
cout "Rectangle class area :"endl;
return(width * height);
}};
classTriangle:publicShape{
public:
Triangle(int a=0,int b=0):Shape(a, b){}
int area ()
{
cout "Triangle class area :"endl;
return(width * height /2);
}};
// Main function for the program
int main()
{
Shape*shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
shape =rec;
// call rectangle area.
shape->area();
// store the address of Triangle
shape =tri;
// call triangle area.
shape->area();return0;}
  1. When the above code is compiled and executed, it produces the following result:

Parentclass area

Parentclass area

  1. The reason for the incorrect output is that the call of the function area() is being set once by the compiler as the version defined in the base class.
  2. This is calledstatic resolutionof the function call, orstatic linkage- the function call is fixed before the program is executed.
  3. This is also sometimes calledearly bindingbecause the area() function is set during the compilation of the program.
  4. But now, let's make a slight modification in our program and precede the declaration of area() in the Shape class with the keywordvirtualso that it looks like this:

classShape{
protected:
int width, height;
public:
Shape(int a=0,int b=0)
{
width = a;
height = b;
}
virtualint area()
{
cout "Parent class area :"endl;
return0;
}
};
  1. After this slight modification, when the previous example code is compiled and executed, it produces the following result:
  2. Rectangleclass area
  3. Triangleclass area
  4. This time, the compiler looks at the contents of the pointer instead of it's type. Hence, since addresses of objects of tri and rec classes are stored in *shape the respective area() function is called.
  5. As you can see, each of the child classes has a separate implementation for the function area(). This is howpolymorphismis generally used. You have different classes with a function of the same name, and even the same parameters, but with different implementations.

4.COMPILE TIME AND RUN TIME POLYMORPHISMS

Virtual functions provides runtime polymorphism, and what templates offer is called compiletime polymorphism or parametric polymorphism.

4.1 Compile time Polymorphism:

  1. One function having same name but with different function body.
    For every function call compiler binds the call to one function definition at compile time.
  2. This decision of binding among several functions is taken by considering formal arguments of the function, their data type and their sequence.

4.1.(a) Example of compile time polymorphism:

Example 1:Example of compile time polymorphism; static time binding

void f(int i){cout<"int";}
void f(char c){cout<"char";}
int main()
{
f(10);
return 0;
}
Output: int

4.2 Run time polymorphism:

When you have a function with same name, equal number of arguments and same data type in same sequence in base class as well derived class and a function call of form: base_class_type_ptr->member_function(args); will always called base class member function.

  1. The keywordvirtualon a member function in base class indicates to the compiler to delay the binding till run time.
    Every class with atleast one virtual function has avtablethat helps in binding at run time.
  2. Looking at the content of base class type pointer it will correctly call the member function of one of possible derived / base class member function.

4.2 (a)Example of run time polymorphism:

Program:
class Base
{
public:
virtualvoid display(int i)
{ cout<"Base::"<i; }
};
class Derv: public Base
{
public:
void display(int j)
{ cout<"Derv::"<j; }
};
int main()
{
Base *ptr=new Derv;
ptr->display(10);
return 0;}
Output: Derv::10

5.FUNCTION OVERLOADING

1.In C++ programming, two functions can have same identifier(name) if either number of arguments or type of arguments passed to functions are different.

These types of functions having similar name are called overloaded functions.

/* Example of function overloading

int test() { }
int test(int a){ }
int test(double a){ }
int test(int a, double b){ }

All 4 functions mentioned above are overloaded function. It should be noticed that, the return type of all 4 functions is same,i.e,int.

/*Calling overloaded function test() with different argument/s.*/

1

Program:
#include<iostream>
usingnamespace std;
void test(int);
void test(float);
void test(int,float);
int main(){
int a =5;
float b =5.5;
test(a);
test(b);
test(a, b);
return0; }
void test(intvar){
cout"Integer number: "varendl;
}
void test(floatvar){
cout"Float number: "varendl;
}
void test(int var1,float var2){
cout"Integer number: "var1;
cout" And float number:"var2; }
Output
Integer number: 5
Float number: 5.5
Integer number: 5 And float number: 5.5

1

5.In above example, functiontest()is called with integer argument at first. Then, functiontest()is called with floating point argument and finally it is called using two arguments of typeintandfloat.

6.Although the return type of all these functions is same, that is,void, it's not mandatory to have same return type for all overloaded functions. This can be demonstrated by example below.

6.OPERATORS OVERLOADING

The mechanism of giving special meaning to an operator is known as Operator overloading.

All the operators can be overloaded except the following

1.Class member access operator (., .*)

2.Scope resolution operator(::)

3.Size operator (sizeof)

4.Conditional operator(?: )

6.1 Rules for overloading operators

  1. Only existing operators can be overloaded.
  2. Must have at least one operand that is of user defined data-type.
  3. Not able to change basic meaning of an operator
  4. Some operators that cannot be overloaded
  5. Some operators cannot be overloaded by friend functions. However ,can be overloaded by member functions

6.2Declaring operator overloading:

Syntax:

Return type operator op(arg list)

Return type- Type of value returned by the specified operation

operator - Keyword that precedes the op

op- the operator being overloaded

arg list- the arguments passed to the function

Example:

void operator +();

The operator ‘+’ is overloaded

6.3 Defining operator overloading:

Syntax:

Returntype classname:: operator op(arg list)

{

Function body

}

1

Example

void operator+()

{

cout<“operator”;

}

In c++ operators are of two types

1.Unary operator

2.Binary operator

6.4The process of overloading involves the following steps

1.Create a class that defines the data type that is to be used in the overloading function

2.Declare the operator overloading function. It may be either a member function or

a friend function

3.Define the operator overloading function

For unary operators:

  1. Member function takes Zero arguments
  2. Friend function takes one argument

For Binary operators:

Member function takes one argument

Friend function takes two arguments

Example:

Unary operator overloading= op o;

Binary operator overloading= o op o1;

Where op is an operator

1

Overloading unary operators using Member function

#include<iostream.h>
class Minus
{
private:
int a, b, c ;
public:
Minus(int A, int B, int C)
{
a = A;
b = B;
c = C; }
void display(void);
//********Declaration of the operator function**********
void operator - ( );
};
void Minus :: display(void)
{
cout < "t a = " < a < endl ;
cout < "t b = " < b < endl ;
cout < "t c = " < c < endl ;
}
//*Definition of the operator function***********
void Minus :: operator - ( )
{
a = -a ;
b = -b ;
c = -c ;
}
//***Main Function Definition*
void main(void)
{
Minus M(5, 10, -15) ;
cout < "n Before activating operator - ( )n" ;
M.display( ) ;
-M ;
cout < "n After activating operator - ( )n" ;
M.display( ) ;
}
Output
Before activating operator - ( )
a = 5
b = 10
c = -15
After activating operator - ( )
a = -5
b = -10
c = 15

Overloading unary operators using Friend Function

#include<iostream.h>
class Minus
{
private:
int a, b, c ;
public:
Minus(int A, int B, int C)
{
a = A;
b = B;
c = C; }
void display(void);
//********Declaration of the operator function**********
friend void operator - (Minus &m );
};
void Minus :: display(void)
{
cout < "t a = " < a < endl ;
cout < "t b = " < b < endl ;
cout < "t c = " < c < endl ;}
//*Definition of the operator function***********
void operator - ( )
{
a = -a ;
b = -b ;
c = -c ;}
//***Main Function Definition*
void main(void)
{
Minus M(5, 10, -15) ;
cout < "n Before activating operator - ( )n" ;
M.display( ) ;
-M ;
cout < "n After activating operator - ( )n" ;
M.display( ) ;}
Output
Before activating operator - ( )
a = 5 b = 10 c = -15
After activating operator - ( )
a = -5 b = -10 c = 15

Overloading Binary operators using Member function

#include<iostream.h>
class Minus
{
private:
int a, b, c ;
public:
Minus(int A, int B, int C)
{
a = A;
b = B;
c = C; }
void display(void);
//********Declaration of the operator function**********
Minus operator -(Minus m );
};
void Minus :: display(void)
{
cout < "t a = " < a < endl ;
cout < "t b = " < b < endl ;
cout < "t c = " < c < endl ;}
//*Definition of the operator function***********
Minus Minus :: operator - ( Minus m)
{
Minus m1;
m1.a = a-m.a ;
m1.b = b-m.b ;
m1.c = c-m.c ;
return m1; }
//***Main Function Definition*
void main(void)
{
Minus M(5, 10, 15) ;
Minus M1(3,2,5);
Minus M2=M-M1 ;
cout < "n After activating operator - ( )n" ;
M2.display( ) ; }
Output
After activating operator - ( ) , a = 2 b = 8 c = 10

Overloading Binary operators using Friend function

#include<iostream.h>
class Minus
{
private:
int a, b, c ;
public:
Minus(int A, int B, int C)
{
a = A;
b = B;
c = C; }
void display(void);
//********Declaration of the operator function**********
friend Minus operator -(Minus m,Minus m1 );
};
void Minus :: display(void)
{
cout < "t a = " < a < endl ;
cout < "t b = " < b < endl ;
cout < "t c = " < c < endl ;}
//*Definition of the operator function***********
Minus operator - ( Minus m,Minus m1)
{
Minus m1;
m1.a = a-m.a ;
m1.b = b-m.b ;
m1.c = c-m.c ;
return m1; }
//***Main Function Definition*
void main(void)
{
Minus M(5, 10, 15) ;
Minus M1(3,2,5);
Minus M2=M-M1 ;
cout < "n After activating operator - ( )n" ;
M2.display( ) ; }
Output
After activating operator - ( ) , a = 2, b = 8, c = 10,

1

The following Operators cannot be overloaded

1.Size of operator(sizeof)

Scope resolution operator(::)

Conditional operator(?:)

Class member access operator(. , .* , ->* )

Pointer to member declarator(::*)

The following Operators cannot be overloaded when a friend function is used

Assignment operator(=)

Function call operator (( ))

Subscripting operator([ ])

Class member access operator(->)

7.DYNAMIC MEMORY ALLOCATION

  1. Thus far. all local variables that we have used are automatic variables.This

term tells us that local variables are created when they are reached

in the function and that they are destroyed when they are no longer in scope

(e.g., when the function returns).

  1. Sometimes, objects need to be created in adifferent way. This different way is called dynamic memory allocation.

7.1The newOperator

1.Objects can be created dynamically by calling new. The new operator dynamically allocates memory and returns a pointer to the newly created Object.

2.The following program illustrates the issues involved in dynamic memory allocation. However, this example is a poor use of dynamic memory; an automaticstring should be used instead.

We use it here only to illustrate dynamicmemory allocation in a simple context.

Program:
#include <iostream>
#include <string>
using namespace std;
int main( )
{
string *P;
P = new string( "hello" ) ;
cout < "The string is: " < *P < endl;
cout < "Its length is: " c< (*P).length( ) < endl;
*P += " world";
cout < "Now the string is " c< *P< endl;
delete P;
return 0;
}

3.In this diagram line 9 creates a new string object dynamically. Note

that strptr is a pointer to a string, so the string itself is accessed by

*strPtr, as shown on lines 10-13. The parentheses are needed at line 11

because of precedence rules

7.2 Garbage Collection and delete

  1. In some languages, when an object is no longer referenced, it is subject to

automatic garbage collection.

  1. When an object allocated by new is no longer referenced, the delete operator must be applied to the object (through a pointer).
  2. Otherwise, the memory that it consumes is lost (until the program terminates), which is known as a memory leak. Unfortunately,Memory leaks are common occurrences in many C++ programs.
  3. One important rule is not to use new when an automatic variable can beused instead.
  4. An automatic variable is automatically cleaned up (hence itsname). You should never use delete on an object that was not created bynew; if you do, run-time havoc is likely to result.

7.3Stale Pointers, Double Deletion, and More

  1. One reason that programmers can get in trouble when using pointers is that

one object may have several pointers pointing at it. Consider the following

code:

  1. string *s = new string( "hello" ) ; / / s points at new string
  2. string *t = s; / / t points there, too
  3. delete t; / / The object is gone
  1. Prior to the call to delete, we have one dynamically allocated object that has two pointerspointing to it.
  2. A stale pointer is a After the call to delete, the values of s and t (i.e., where they arepointer value pointing) are unchanged. However, as illustrated in Figure 1.1 1, they areno longer refers to a valid object. now stale.
  3. A stale pointer is a pointer whose value no longer refers to a valid object.

Dereferencing s and t can lead to unpredictable results.

Figure 1.1 1 Stale pointers: Because of the call to delete t, pointers s and t

are now pointing at an object that no longer exists; a call to delete s would now be an illegal double deletion.

  1. A second problem is the so-called double-delete. A double-deleteoccurs when we attempt to call delete on the same object more than once.

It would occur if we now made the call

delete s;/ / Oops -- double delete

because s is stale and the object that it points to is no longer valid. Trouble

in the form of a run-time error is likely to result.

  1. These are the perils of dynamic memory allocation. We must be certain

never to call delete more than once on an object-and then only after we

no longer need it.

  1. If we don't call delete at all, we get a memory leak. And if we have a pointer variable and intend to call delete, we must be certain that the object being pointed at was created by a call to new.
  2. When we have functions calling functions calling other functions, keeping track of everything is not so easy.
  3. Finally, pointers can go stale even if no dynamic allocation is performed.Consider the code in Figure 1.12.
  4. For no good reason (except to illustrate the error), we have the function

stupid return a pointer to a string.

  1. If stupid calls new to create astring, then the caller will be responsible for calling delete.
  2. Rather thanburdening the caller, we mistakenly decided to have stupid use an automaticstring, and return its address. The program compiles but may or maynot work; it contains an error.
  3. .The problem is that the value that stupidreturns is a pointer. But the pointer is pointing at s, which no longer existsbecause it is an automatic variable and stupid has returned. When returningpointers, be sure that you have something to point to and that the somethingexists after the return has been completed.

string *stupid( )

string s = "stupid";

return &s;

int main( )

{

cout < *stupid( ) < endl;

return 0;

}

Figure 1.12 A stale pointer: the pointee, s, does not exist after stupid returns.

8.NESTED CLASSES

  1. Classes can be defined inside other classes. Classes that are defined inside other classes are callednested classes.
  2. Nested classes are used in situations where the nested class has a close conceptual relationship to its surrounding class.
  3. A class can be nested in every part of the surrounding class: in thepublic, protectedorprivatesection.
  4. Such a nested class can be considered a memberof the surrounding class. The normal access and rules in classes apply to nested classes.
  5. If a class is nested in thepublicsection of a class, it isvisible outside the surrounding class.
  6. If it is nested in theprotectedsection it is visible in subclasses, derived from the surrounding class, if it is nested in theprivatesection, it is only visible for the members of the surrounding class.
  7. The surrounding class has no special privileges towards the nested class. The nested class has full control over the accessibility of its members by the surrounding class.

For example, consider the following class definition:

class Surround
{
public:
class FirstWithin
{
int d_variable;
public:
FirstWithin();
int var() const;};
private:
class SecondWithin
{
int d_variable;
public:
SecondWithin();
int var() const;};};
inline int Surround::FirstWithin::var() const
{
return d_variable;}
inline int Surround::SecondWithin::var() const
{
return d_variable;}

Here access to the members is defined as follows:

9.The classFirstWithinis visible outside and insideSurround. The classFirstWithinthus has global visibility.

10.FirstWithin's constructor and its member functionvarare also globally visible.The data memberd_variableis only visible to the members of the classFirstWithin. Neither the members ofSurroundnor the members ofSecondWithincan directly accessFirstWithin::d_variable.