Menu

Saturday, 4 July 2015

MORE ON FUNCTIONS & CLASSES

8.1 Virtual Functions:

Polymorphism refers to the property by which objects belonging to different classes are able to respond to the same message, but in different forms. An essential requirement of polymorphism is therefore the ability to refer to, objects without any regard to their classes. This necessitates the use of a single pointer variable to refer to the objects of different classes. Here, we use the pointer to base class to refer to all the derived objects. But, we just discovered that a base pointer, even when it is made to contain the address of a derived class, always executes the function in the base class. The compiler simply ignores the contents of the pointer and chooses the member function that matches the type of the pointer. How do we then achieve polymorphism? It is achieved using what is known as 'virtual' functions.

Virtual means existing in effect but not reality. A virtual function then, is one that does not really exist but nevertheless appears real to some parts of a program. Virtual functions provide a way for a program to decide: when it is running, what function to call. Ordinarily such decisions are made at compile time. Virtual functions take possible greater flexibility in performing the same kind of action on different kinds of objects. In particular, they allow the use of functions called from an array of type pointer-to-base that actually holds pointers to a variety of derived types. Typically a function is declared virtual in the base class, and other functions with the same name are declared in derived classes. A pure virtual function has no body in the base class.

When we use the same function name in both the base and derived class the friction in base class is declared as virtual using the keyword virtual preceding its normal declaration. When a function is made virtual, C++ determines which function to use at run time based on the type of object pointed to by the base pointer rather than the type of the pointer, Thus by making the base pointer to point to different objects, we can execute different versions of the virtual function. Program below illustrates this point.

# include<iostream.h>
 class Base
{
 public:
void display ( ) { cout < < "\n Display base "; }
virtual void show ( ) { cout < < " \n show base"; }
};
class Derived: public Base
{
public:
void display( ) { cout << “\n Display derived"; }
void show ( )   {  cout << " \n show derived" ; }
 } ;
void main( )
{ 
Base B;           
Derived D;     
Base *bptr;                  //Declarations
cout < < "\n     bptr points to Base \n" ;
bptr = &B;
bptr -> display( ) ;                               //calls Base version
bptr -> show( ) ;                                  //calls Base version
cout << "\n\n bptr points to Derived\n";
bptr = &D;
bptr -> display( );                                //calls Base version
bptr -> show( ) ;                                  //calls Derived version
}
Note that when bptr is made to point to the object D, the statement 

bptr -> display( );

calls only the function associated with the Base (i.e. Base :: display( )) whereas the statement

bptr -> show-6;

calls the Derived version of show( ). This is because the function display( ) has not been made virtual in the Base class. One important point is that, we must access virtual functions through the use of a pointer declared as a pointer to the base class. We can use the object name with the dot operator the same way as any other member function to call the virtual functions, but runtime polymorphism is achieved only when a virtual function is accessed through a pointer to the base class. Consider the program below a function display( ) is used in all the classes to display the class contents. Notice that the function display( ) has been declared virtual in the base class. In the main program we create a heterogeneous list of pointers of type media as shown below:

media *lst[2] = {&book1 , &tape1};

The base pointers list[0] and list[l] are initialized with the addresses of objects book1 and tape1 respectively.

# include <iostream.h>
# include <string.h> 
class media
{          protected:
char title[50];
float price; 
public:
media(char *s, float a)
{
strcpy(title, s);
price = a;
            }
virtual void display( ) {          }                                  //empty virtual function;
};
class book: public media
{
int pages;
public:
book(char *5, float a, int pj ):media(s,a)
{          pages = p;        }
void display ( ); 
};
class tape: public media
{          float time;
public:
tape(char * s, float a, float t) :media(s, a)
{          time = t;           }
void display( ) ; 
};
void book :: display( )
{
cout << "\n Title: " << title;
cout << "\n Pages: " << pages;
cout << "\n Price: “ << price;
}
void tape: : display( )
{
cout << "\n Title: " << title;
cout << "\n play time: " << time << "mins";
cout << "\n price: “ << price;
}
void main( )
{          char * title;
float price, time;
int pages;
cout << "\n ENTER BOOK DETAILS\n";
cout << " Title:";         cin >> title;
cout << " Price: ";        cin >> price;
cout << " Pages: ";      cin >> pages;
book bookl ( title, price, pages) ;
cout << "\n ENTER TAPE DETAILS\n";  
cout << “ Title: ";        cin >> title;
cout << " Price: ";        cin >> price;
cout << " Play time (mins): "; cin >> time;
tape tapel(title, price, time) ;
media* list [2] ;
list[0] = &bookl;
list[l] = &tapel;
cout << "\n MEDIA DETAILS";
cout << "\n BOOK ";
list[0] -> display( );                 //display book details
cout << "\n TAPE ";
list[l] -> display( ) ;                 //display tape details
}

8.2 Rules for Virtual Functions:


When virtual functions are created for implementing late binding, we should observe some basic rules that satisfy the compiler requirements:

  1. The virtual functions must be members of some class. 
  2. They cannot be static members.
  3. They are accessed by using object pointers.
  4. A virtual function can be a friend of another class.
5.      A virtual function in a base class must be defined, even though it may not be     used.
  1. The prototypes of the base class version of a virtual function and all the           derived class versions must be identical. If two functions with the same name           have different prototype, C++ considers them as overloaded functions, and the           virtual function mechanism is ignored.
  2. We cannot have virtual constructors/ but we can have virtual destructors.
  3. While a base pointer can point to any type of the derived object, the reverse is           not true. That is, we cannot use a pointer to a derived class to access an object           of the base type.
  4. When a base pointer points to a derived class, incrementing or decrementing it           will not make it to point to the next object of the derived class. It is incremented or decremented only relative to its base type. Therefore, we should not use this method to move the pointer to the next object.
  5. If a virtual function is defined in the base class, it need not be necessarily         redefined in the derived class. In such cases, calls will invoke the base function.

8.3 Virtual Member Functions Accessed With Pointers:

We can make use of pointers to access the virtual member function. The program below illustrates this.

# include<iostream.h>
class base                     //base class
{
public:
virtual void show( 
            {         
cout<<”\n base”;        
}
};
class der1: public base //derived class 1
{
public:
            void show( )
            {          cout<< “\n Derv1 “;    }
};
class der2: public base //derived class 2
{
public:
            void show( )
            {          cout<< “\n Derv2 “;    }
};
void main( )
{
            Derv1  dv1;                 //object of derived class 1
            Derv2  dv2;                 //object of derived class 2
            ptr =&dv1;                  // put address of dv1 in pointer
            ptr->show( );               //execute show ( )
            ptr =&dv2;                  // put address of dv2 in pointer
            ptr->show( );               //execute show ( )
}
The member functions of the derived classes are executed and not the base class. The function call
            ptr -> show( );

executes different functions, depending on the contents of ptr. The rule is that the compiler selects the function based on the contents of the pointer ptr.
The compiler has no problem with the expression
            ptr -> show( )

It always compiles a call to the show( ) function in the base class. But in the program above the compiler doesn't know what class the contents of ptr may contain. It could be the address of an object of the Derv1 class or of the Derv2 class. Which version of
draw( ) does the compiler call? In fact the compiler doesn't know what to do, so it arranges for the decision to be deferred until the program is running. At run time, when it is known what class is pointed to by ptr, the appropriate version of draw will be called. This is called late binding or dynamic binding. (Choosing functions in the normal way, during compilation, is called early, or static, binding.) Late binding requires some overhead but provides increased power and flexibility.

8.4 Pure Virtual Functions:

A pure virtual function is a virtual function with no body. It is a normal practice to declare a function virtual inside the base class and redefine it in the derived classes. The function inside the base class is seldom used for performing any task, It only serves as a placeholder, For example, we have not defined any object of class media and therefore the function display( ) in the base class has been defined ‘empty'. Such functions are called "do- nothing" functions,
A 'do-nothing" function may be defined as follows:
virtual void display( ) = 0;

Such functions are called pure virtual functions. A pure virtual function is a function declared in a base class that has no definition relative to the base class. In such cases, the compiler requires each derived class to either define the function or redeclare it as a pure virtual function, Remember that a class containing pure virtual functions cannot be used to declare any objects of its own. Such classes are called abstract base classes. The main objective of an abstract base class is to provide some traits, to the derived classes and to create a base pointer required for achieving runtime polymorphism.
 // Program to demonstrate pure virtual function
# include <iostream.h>
class Base       
{
public:
virtual void show( ) = 0;                     // pure virtual function
} ;
class Derv1 : public Base                                // derived class 1
{
public:
void show( )
{
cout << "\nDerv1";
}
 };
class Derv2 : public Base                                //derived class 2
{
public :
void show( )
{ cout << "\nDerv2"; }
 };
void main( )
{
Base* list[2];                                                   II list of pointers to base class
Derv1 dv1;                                                      II object of derived class 1
Derv2 dv2;                                                      II object of derived class 2
list[0] = &dv1;                                                II put address of dv1 in list
list[1] = &dv2;                                                // put address of dv2 in list
list[0]->show( );                                              II execute show( ) in both objects
list[1]->show( );
}
Now the virtual function is declared as

virtual  void show( ) = 0;                    II pure virtual function

The equals sign here has nothing to do with assignment; the value 0 is not assigned to anything. The = 0 syntax is simply how we tell the compiler that a function will be pure that is, have no body. You might wonder, if we can remove the body of the virtual show ( ) function in the base class, why we can't remove the function altogether. That would be even cleaner, but it doesn't work. Without a function show() in the base class statements like

list[0]->show();

would not be valid, because the pointers in the list[ ] array must point to members of class Base. The addresses of the member functions are stored-in an array of pointers, and accessed using array elements. This works in just the same way as using a single pointer.


8.5 Abstract classes:

An abstract class is one that is not used to create object. It is designed only to act as a base class (to be inherited by other classes). It is a design concept in program development and provides a base upon which other classes may be built. It is often defined as one that will not be used to create any objects, but exists only to act as a base class of other classes.

8.6 Virtual Base Classes:

Virtual base classes allow an object derived from multiple bases that themselves share a common base to inherit just one object of that shared base class.

Consider the situation with a base class, Parent; two derived classes Child1 and Child2; and a fourth class, Grandchild, derived from both Child1and Child2.In this arrangement a problem can arise if a member function in the Grandchild class wants to access data or functions in the Parent class.

//ambiguous reference to base class
class Parent
{
protected:
int basedata;
};
class Child:public parent
{ };
class Child2 : public Parent
{ };
class Grandchild: public Child, public Child2
{
public:
int getdata( )
{ return basedata; }                 // ERROR: ambiguous
);

A compiler error occurs when the getdata ( ) member function in Grandchild attempts to access base data in Parent. Because the Child1 and Child2 classes are derived from Parent, each inherits a copy of Parent; this copy is called a subobject. Each of the two subobjects contains its own copy of Parent’s data, including basedata. Now, when Grandchild refers to basedata, which of the two copies will it access? The situation is ambiguous, and that's what the compiler reports. The duplication of inherited members due to these multiple paths can be avoided by making the common base class as virtual base class. To eliminate the ambiguity in the above program, we make Child1 and Child2 into virtual base classes.

class Parent
{
protected:
int basedata;
}; 
class Child1 : virtual public Parent                 // shares copy or Parent
{ }; 
class Child2 : virtual public Parent                 // shares copy of Parent
{ } ;
class Grandchild: public Child, public Child2
{
public:
int getdata( )
{ return basedata; }                 // OK: only one copy of Parent
 };

The use of the keyword virtual in these two classes causes them to share a single common subobject of their base class Parent. Now, since there is only one, copy of basedata, there is no ambiguity when it is referred to in Grandchild.

8.7 Friend:

The private members cannot be accessed from outside the class. That is, a non-member function cannot have an access to the private data of a class. However, there could be a situation where we would like two classes to share a particular function. In such situations, C++ allows the common function to be made friendly with both the classes, thereby allowing the function to have access to the private data of these classes. Such a function need not be a member of any of these classes.

A friend function can access a class's private data, even though it is not a member function of the class. This is useful when one function must have access to two or more unrelated classes and when an overloaded operator must use, on its left side, a value of a class other than the one of which it is a member. friends are also used to facilitate functional notation.

As you’ve seen, C++ controls access to the private portions of a class object.  Usually public class methods serve as the only access, but sometimes this restriction is too rigid to fit particular programming problems. In such cases, C++ provides another form of access, the friend. Friends come in three varieties:
·         Friend functions
·         Friend class
·         Friend member functions

By making a function a friend to a class, you allow the function the same access privileges that a member function of the class has. To make an outside function "friendly" to a class, we have to simply declare this function as a friend of the class as shown below:

            class ABC
                        {          ………….
                        public:
                                    ………….
                                    friend void xyz(void); // declaration
                        };
The keyword friend should precede the function declaration. The function is defined elsewhere in the program like a normal C++ function. The function definition does not use either the keyword friend or the scope operator(::). The functions that are declared with the keyword friend are known as friend functions. A function can be declared as a friend in any number of classes. A friend function, although not a member function, has full access rights to the private members of the class.

A friend function possesses certain special characteristics:
  • It is not in the scope of the class to which it has been declared as friend.
  • Since it is not in the scope of the class, it cannot be called using the object of that class. It can be invoked like a normal function without the help of any object.
  • Unlike member functions, it cannot access the member names directly and has to use an object name and dot membership operator with each member name. (e.g.A.x). .
  • It can be declared either in the public or the private part of a class without affecting its meaning.
  • Usually, it has the objects as arguments.

//Program below illustrates the use of a friend function. ,
# include <iostream.h>
class sample
{
int a;
int b;
public:
void setvalue( ) {a = 25; b = 40; }
friend float mean(sample s);               //FRIEND declared
} ;
 float mean(sample s) 
{          return float(s.a + s.b)/2.0;        }
void main( )
{
sample X; //object X
X.setvalue( ) ;
cout << "Mean value = “ << mean(X) << "\n";
}

Note that the friend function accesses the class variables a and b by using the dot operator and the object passed to it. The function call mean(X) passes the object X by value to the friend function. Member functions of one class can be friend functions of another class. In such cases, they are defined using the scope resolution operator as shown below:
class X
{          …………
            …………
int fun1( );                   //member function of X
                        …………
};
class Y
{         
            …………
friend int X :: fun1( );                         II fun1( ) of X is friend of y
                                    …………
};
The function funl( ) is a member of class X and a friend of class Y.

We can also declare all the member functions of one class as the friend functions of another class. In such cases, the class is called a friend class. This can be specified as follows:
class Z
{
            …………
friend class X; Il all member functions of X are I friends to Z
};

Program below demonstrates how friend functions work as a bridge between the classes. Note that the function max( ) has arguments from both XYZ and ABC. 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 as

class ABC;

This is known as 'forward' declaration.

# include <iostream.h>
class ABC:                  // Forward declaration
class XYZ
{
int x;
public:
void setvalue(int i)
{          x = i;    }
 friend void max(XYZ, ABC) ;
};
class ABC
{
int a;
public:
void setvalue(int i) {   a = i;    }
friend void max(XYZ, ABC) ;
} ;
 void max(XYZ m, ABC n )     // Definition of friend 
{
if(m.x >= n.a)
cout << m.x;
else
cout << n.a; 
}
main( )
{
ABC abc;
abc.setvalue(10);
XYZ xyz;
xyz.setvalue(20);
max(xyz, abc) ; 
}
As pointed out, a friend function can be called by reference. In this case, local copies of the objects are not made. Instead, a pointer to the address of the object is passed and the called function directly works on the actual object used in the call. This method can be used to alter the values of the private members of a class. Remember, altering the values of private members is against the basic principles of data hiding. It should be used only when absolutely necessary. Program below shows how to use a common friend function to exchange the private values of two classes. The function is called by reference.

# include <iostream.h>
class class_2;
class class_1
{          int valuel;
 public:
void indata(int a)
{          valuel = a;        }
void display(void)
{          cout << valuel << "\n";            }
friend void exchange(class_l &, class_2 &) ;
};
class class_2
{          int value2;
public:
void indata(int a) {     value2 = a;       }
void display(void) {    cout << value2 << "\n";           }
friend void exchange(class-l &, class-2 &) ;
} ;
void exchange(class_l & x, class_2 & y)
{
int temp = x.value1;
x.value1 = y.value2;
y.value2 = temp;
}
void main( )
{
class_1 C1;
class_2 C2;
C1.indata(100);
C2.indata(200) ;
cout << "Values before exchange" '<< "\n";
C1.display( ) ;
C2.display( ) ;
exchange(C1, C2) ;                 //SWAPPING
cout << "Values after exchange" <<”\n";
C1.display( );
C2.display( );
}
The objects x and y are aliases of C1 and C2 respectively. The statements
int temp = x.value1
x.value1 = y.value2;
y.value2 = temp;

directly modify the values of value1 and value2 declared in class_1 and class_2.

friend class: The member function of a class can all be made friends at the same time when you make the entire class a friend. For example consider the program below:

# include <iostream.h>
class alpha
{
private:
            int data;
public:
            alpha( )
            { data = 99 }
            friend class beta;         // beta is a friend class
};
class beta
{
public:
            void func1(alpha a)     {          cout << “\n data= “<< a.data; }
            void func2(alpha a)     {          cout << “\n data= “<< a.data; }
            void func3(alpha a)     {          cout << “\n data= “<< a.data; }
};
void main( )
{
            alpha a;
            beta b;
            b.func1(a);
            b.func2(a);
            b.func3(a);
}

In class alpha the entire class beta is proclaimed a friend. Now all the member function of beta can access the private data of alpha. Note that in the friend declaration we specify that beta is a class using the class keyword:
                       
            friend class beta;




8.9 this Pointer:

The this pointer is predefined in member functions to point to the object of which the function is a member. The this pointer is useful in returning the object of which the function is a member. The member functions of every object have access to a sort of magic pointer named this, which points to the object itself.  Thus any member function can find out the address of the object of which it is a member.

# include<iostream.h>
class where
{
private:
            char charray[10];
public:
            void reveal( )
                        { cout<<”\n My object’s address is”<<this;   }
};
void main( )
{
where w1,w2, w3;                  // make three objects
w1.reveal( )
w2.reveal( )
w3.reveal( )
}
The main( ) program in this example creates three objects of type where.  It then asks each object to print its address, using the reveal( ) member function.  This function prints out the value of this pointer.


Exercise:

  1. Create a class Distance and an overloaded * operator so that two Distance class can be multiplied together. Make it a friend function. You will need a one-argument constructor to convert floating-point values into Distance value. Write a program to test this operator.

  1. Implement the virtual function using the classes Employee (base class), Adminstaff, Factorystaff (both are derived from base class) and Staff (derived from subclasses). Write a program to display the staff details.