100% found this document useful (1 vote)
104 views46 pages

ADS Unit II

Function overloading allows functions to have the same name but different parameters. Constructors can be overloaded to provide flexibility in object initialization. The copy constructor is especially important as it prevents shallow copying by default. Default function arguments allow omitting parameters in function calls by specifying default values. Overloading and default arguments provide convenience and flexibility in C++ programs.

Uploaded by

api-26548538
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
104 views46 pages

ADS Unit II

Function overloading allows functions to have the same name but different parameters. Constructors can be overloaded to provide flexibility in object initialization. The copy constructor is especially important as it prevents shallow copying by default. Default function arguments allow omitting parameters in function calls by specifying default values. Overloading and default arguments provide convenience and flexibility in C++ programs.

Uploaded by

api-26548538
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 46

FUNCTION OVERLOADING

• Provides support for compile-time polymorphism.

• Adds flexibility and convenience.

• Most commonly overloaded functions are constructors.

o Most important form of an overloaded constructor is the


copy constructor.

Function Overloading

• Is the process of using the same name for 2 or more functions.

• Each redefinition of the function must use either different


types of parameters or a different number of parameters.

• 2 functions differing in only their return types cannot be


overloaded.

• Also 2 functions whose only difference is that one takes


reference parameter and the other call-by-value parameter cannot
be overloaded.

Overloading Constructors

3 main reasons why constructors are overloaded:

1. To Gain Flexibility:

• By providing a constructor for each way that a user of


the class may plausibly want to construct an object, the
flexibility of the class is increased.

2. Allowing Both Initialized & UnInitialized Objects:

• Important if one wants to be able to create dynamic


arrays of object of the class, since it is not possible
to initialize a dynamically allocated array.
• The default constructor is used to construct the
uninitialized array and the dynamically created array.
• The parameterized constructor is called to create the
objects for the initialized array version.

UNIT II 1
Ex1: #include <iostream.h>

class powers
{
int x;
public:
// overload constructor two ways
powers() { x = 0; } // no initializer
powers(int n) { x = n; } // initializer

int getx() { return x; }


void setx(int i) { x = i; }
};

int main()
{
powers ofTwo[] = {1, 2, 4, 8, 16}; // initialized
powers ofThree[5]; // uninitialized
powers *p;
int i;

cout << "Powers of two: ";


for(i=0; i<5; i++) // show powers of two
cout << ofTwo[i].getx() << " ";
cout << "\n\n";

for(i=1; i<6; i++) // set powers of three


ofThree[i-1].setx(i*i*i);
cout << "Powers of three: "; // show powers of three
for(i=0; i<5; i++)
cout << ofThree[i].getx() << " ";
cout << "\n\n";

p = new powers[5]; // dynamically allocate an array


for(i=0; i<5; i++)
p[i].setx(ofTwo[i].getx());
cout << "Powers of two: ";
for(i=0; i<5; i++) // show powers of two
cout << p[i].getx() << " ";
cout << "\n\n";

delete [] p;

return 0;

UNIT II 2
3. Copy Constructors:

Helps prevent problems that might occur


• When one object is used to initialize another.
o Myclass B = A;
o By default, when one object is used to initialize
another, C++ performs bit-wise copy.
o This should not be done, since the new object shares
the allocated memory as well.
o Hence a destructor frees the same memory twice once
for both objects.
• When a copy of an object is made when it is passed as an
argument to a function.
• When a temporary object is created as a return value from
a function.

 C++ allows creation of copy constructor, which the compiler uses


when one object initializes another.
 When a copy constructor exists, the default, bit wise copy is
bypassed.
 General for :
classname(const classname &ob)
{ // body
}
 It is permissible for a copy constructor to have additional
parameters as long as they have default arguments defined for
them.
 In all cases, the first parameter must be a reference to the
object doing the initializing.

NOTE:

 C++ defines 2 distinct types of situations in which the value of


one object is given to another.
1. Assignment

2. Initialization, which can occur in 3 ways –

a. One object explicitly initializes another


(declaration).
b. A copy of an object is made to be passed to a
function.
c. A temporary object is generated (a return value ).

UNIT II 3
 The copy constructor applies only to initializations.
Ex2: #include <iostream.h>
class array
{ int *p;
int size;
public:
array(int sz)
{ p = new int[sz];
size = sz;
}
~array() { delete [] p; }

array(const array &a); // copy constructor

void put(int i, int j)


{ if(i>=0 && i<size)
p[i] = j;
}
int get(int i)
{ return p[i];
}
};

array::array(const array &a) // Copy Constructor


{ int i;

p = new int[a.size];
for(i=0; i<a.size; i++)
p[i] = a.p[i];
}

int main()
{ array num(10);
int i;

for(i=0; i<10; i++)


num.put(i, i);
for(i=9; i>=0; i--)
cout << num.get(i);
cout << "\n";

// create another array and initialize with num


array x(num); // invokes copy constructor

for(i=0; i<10; i++)


cout << x.get(i);
return 0;

UNIT II 4
}
Default Function Arguments

• C++ allows a function to assign a parameter a default value when


no argument corresponding to that parameter is specified in a
call to that function.

• Quite frequently a function contains more parameters than are


required for its most common usage. When the default arguments
apply, one needs to specify only the arguments that are
meaningful to the exact situations, not all those needed by the
general case.

• Many of C++ I/O functions make use of default arguments for just
this reason.

• The default argument values must be specified only once, and


this must be the first time the function is declared within the
file.

Ex3: #include <iostream.h>

/* Default indent to -1. This value tells the


function to reuse the previous value. */
void iputs(char *str, int indent = -1);

int main()
{
iputs("Hello there", 10);
iputs("This will be indented 10 spaces by default");
iputs("This will be indented 5 spaces", 5);
iputs("This is not indented", 0);

return 0;
}

void iputs(char *str, int indent)


{ static i = 0; // holds previous indent value

if(indent >= 0)
i = indent;
else // reuse old indent value
indent = i;

for( ; indent; indent--)


cout << " ";
cout << str << "\n";

UNIT II 5
}
• All parameters that take default values must appear to the right
of those that do not.

• Default parameters can also be used in an object’s constructor.


There are 2 advantages –

o They prevent one from having to provide an overloaded


constructor that takes no parameter.

o Defaulting common initial values is more convenient than


specifying them each time an object is declared.

Ex4:
#include <iostream.h>

class cube
{
int x, y, z;
public:
cube(int i=0, int j=0, int k=0)
{
x=i;
y=j;
z=k;
}

int volume()
{
return x*y*z;
}
};

int main()
{
cube a(2,3,4), b;

cout << a.volume() << endl;

cout << b.volume();

return 0;
}

• Default arguments can be used as a shorthand form of function


overloading.

UNIT II 6
OPERATOR OVERLOADING

• In C++, most operators can be overloaded so that they perform


special operations relative to classes that user creates.

• When an operator is overloaded, none of its original meanings


are lost. Instead, the type of objects it can be applied to is
expanded.

• It allows the full integration of new class types into the


programming environment.

• Operator overloading also forms the basics of C++ approach to


I/O.

• Operators are overloaded by creating operator functions.


• An operator function defines the operations that the overloaded
operator will perform relative to the class upon which it will
work.
• Keyword – operator

• Operator functions can be either members or nonmembers of a


class.
• Nonmember operator functions are almost always friend functions
of the class.

CREATING A MEMBER OPERATOR FUNCTION

• General form :
Ret-type classname :: operator#(arg-list)
{
// operations
}
• When overloading a unary operator, argument list will be
empty.
• When overloading a binary operator, argument list will have 1
parameter.

• When binary operators are overloaded, it is the object on the


left that generates the call to the operator function.

• An operator function can return any type and the type returned
depends solely upon the specific application.

UNIT II 7
Ex1: overloading +
#include <iostream.h>

class loc
{
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt)
{
longitude = lg;
latitude = lt;
}

void show()
{
cout << longitude << " ";
cout << latitude << "\n";
}

loc operator+(loc op2);


};

// Overload + for loc.

loc loc::operator+(loc op2)


{
loc temp;

temp.longitude = longitude + op2.longitude ;


temp.latitude = latitude + op2.latitude ;

return temp;
}

int main()
{
loc ob1(10, 20), ob2( 5, 30);

ob1.show(); // displays 10 20
ob2.show(); // displays 5 30

ob1 = ob1 + ob2;


ob1.show(); // displays 15 50

return 0;

UNIT II 8
}
NOTE:

The reason the binary operator function takes only one parameter is
that the operand on the left side of the operator is passed
implicitly to the function through the this pointer. The operand on
the right is passed in the parameter.

Ex2: overloading –

loc loc::operator-(loc op2)


{
loc temp;

temp.longitude = longitude - op2.longitude;


temp.latitude = latitude - op2.latitude;

return temp;
}

Ex3: overloading =

// Overload asignment for loc.

loc loc::operator=(loc op2)


{
longitude = op2.longitude;
latitude = op2.latitude;

return *this; // i.e., return object that generated call


}

• Operator = alters the value of an operand (the calling operand).

• In C++, if the = is not overloaded, a default assignment


operation is created for any class that is defined.

• The default assignment is simply a member-by-member bit wise


copy.

• By overloading = one can define explicitly what the assignment


does relative to a class.

ob1 = ob2 = ob3; // multiple assignment

UNIT II 9
Ex4: overloading ++ (prefix form)

• operator++() takes no parameter, since ++ is a unary operator.

• Its only operand is passed implicitly using this pointer.

• operator ++ alters the value of the operand.

loc loc::operator++()
{
longitude++;
latitude++;

return *this;
}

Ex5: overloading shorthand operators

Shorthand operators like += , -= , etc.. can be overloaded

loc loc::operator+=(loc op2)


{
longitude = op2.longitude + longitude;
latitude = op2.latitude + latitude;

return *this;
}

Operator Overloading Restrictions

• The precedence of an operator cannot be altered.


• The number of operands an operator takes cannot be changed,
however the operand can be ignored.
• Except for function call operator, operator functions cannot
have default arguments.
• The operators that cannot be overloaded are - . , :: , .* , ?
• Except for = operator, operator functions are inherited by any
derived class.

NOTE:
Before decoupling an overloaded operator from its normal meaning, be
sure to have a sufficient reason to do so. One good example where

UNIT II 10
decoupling is successful is in the way << and >> are overloaded in
C++.
OPERATOR OVERLOADING USING A FRIEND FUNCTION

• Since a friend function is not a member of the class, it does


not have a this pointer.

• Hence an overloaded friend operator function is passed the


operands explicitly.

• This means that a friend function that overloads a unary


operator has 1 parameter, a binary operator has 2 parameters.

Ex1: Overloading + using a friend

#include <iostream.h>

class loc
{
int longitude, latitude;
public:
loc() {} // needed to construct temporaries
loc(int lg, int lt)
{
longitude = lg;
latitude = lt;
}

void show()
{
cout << longitude << " ";
cout << latitude << "\n";
}

friend loc operator+(loc op1, loc op2);


};

// Now, + is overloaded using friend function.

loc operator+(loc op1, loc op2)


{
loc temp;

temp.longitude = op1.longitude + op2.longitude;


temp.latitude = op1.latitude + op2.latitude;

UNIT II 11
return temp;
}
Restrictions for friend operator functions

• The operators that can be overloaded using friend functions –


= , () , [] , ->
• When overloading the increment or decrement operators, we need
to use a reference parameter when using a friend function.
Ex:
#include <iostream.h>

class loc
{
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt)
{
longitude = lg;
latitude = lt;
}

void show()
{
cout << longitude << " ";
cout << latitude << "\n";
}

friend loc operator++(loc &op);


friend loc operator--(loc &op);
};

// Now a friend; use a reference parameter.


loc operator++(loc &op)
{
op.longitude++;
op.latitude++;

return op;
}

// Make op-- a friend; use reference.


loc operator--(loc &op)
{
op.longitude--;
op.latitude--;

UNIT II 12
return op;
}
Friend operator functions add flexibility
• Assume a class that defines operator+() function that adds an
object of the class to integer. Then,
o Ob + 100; is valid
o 100 + Ob; is invalid
• To allow both versions, simply overload + using a friend
function and overload this function twice – one version for each
situation.
Ex:
#include <iostream.h>
class loc
{
int longitude, latitude;
public: loc() {}
loc(int lg, int lt)
{
longitude = lg;
latitude = lt;
}
void show()
{
cout << longitude << " ";
cout << latitude << "\n";
}
friend loc operator+(loc op1, int op2);
friend loc operator+(int op1, loc op2);
};

// + is overloaded for loc + int.


loc operator+(loc op1, int op2)
{
loc temp;
temp.longitude = op1.longitude + op2;
temp.latitude = op1.latitude + op2;
return temp;
}

// + is overloaded for int + loc.


loc operator+(int op1, loc op2)
{
loc temp;
temp.longitude = op1 + op2.longitude;
temp.latitude = op1 + op2.latitude;
return temp;
}

UNIT II 13
NOTE: ob1 = ob2 + 10; ob3 = 10 + ob2; // both of these are valid
OVERLOADING SPECIAL OPERATORS

Overloading [] , () , and ->

Restriction –
• They must be nonstatic member functions.
• They cannot be friends.

1. Overloading []
• In C++ the [] is considered as binary operator when
overloading it.
• General syntax : type classname :: operator[] (int i)
{ // function body
}

• Given an object called O, the expression


O[3]
Translates to operator[] function as
O.operator[](3)

Ex1:
#include <iostream.h>
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];
}
};

int main()
{
atype ob(1, 2, 3);

cout << ob[1]; // displays 2

UNIT II 14
return 0;
}
The operator[] can be designed in such a way that [] can be used both
on left & right sides of an assignment statement.

Ex2:
#include <iostream.h>

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];
}

};

int main()
{
atype ob(1, 2, 3);

cout << ob[1]; // displays 2


cout << " ";

ob[1] = 25; // [] on left of =

cout << ob[1]; // now displays 25

return 0;
}

NOTE:

One advantage of being able to overload the [] operator is that it


allows a means of implementing safe array indexing in C++.

• If a class is created that contains an array, and allow


access to that array only through the overloaded []

UNIT II 15
subscripting operator, then the out-of-range index can be
intercepted.
2. Overloading ()
• When we overload the () function call operator, we are not
creating a new way to call a function. Rather, we are
creating an operator function that can be passed an
arbitrary number of parameters.
Ex:
double operator()(int a, float f, char *s);
An object O of this class, then the statement
O(10, 23.34, "hi");
Translates into the call as -
O.operator()(10, 23.34, "hi");

• When we overload the () operator, we define the parameters


that we want to pass to that function.

Ex: #include <iostream.h>


class loc
{ int longitude, latitude;
public:
loc() {}
loc(int lg, int lt)
{ longitude = lg;
latitude = lt;
}
void show()
{ cout << longitude << " ";
cout << latitude << "\n";
}
loc operator()(int i, int j);
};

loc loc::operator()(int i, int j)


{ longitude = i;
latitude = j;
return *this;
}
int main()
{
loc ob1(10, 20), ob2(1, 1);
ob1.show();

ob1(7, 8); // can be executed by itself


ob1.show();
return 0;
}

UNIT II 16
NOTE: ob1 = ob2 + ob1(10, 10); // can be used in expressions
3. Overloading ->

• The -> pointer operator, also called the class member access
operator, is considered a unary operator when overloading.

• General usage - object->element

• The operator->() function must return a pointer to an object


of the class the operator->() operates on.

• The ‘element’ must be some member accessible within the


object.

Ex:

#include <iostream.h>

class myclass
{
public:
int i;

myclass *operator->()
{
return this;
}
};

int main()
{
myclass ob;

ob->i = 10; // same as ob.i

cout << ob.i << " " << ob->i;

return 0;
}

NOTE:

An operator->() function must be a member of the class upon which it


works.

UNIT II 17
OVERLOADING THE COMMA OPERATOR
• Comma is a binary operator.
• If the overloaded comma is to perform in a fashion similar to
its normal operation, then the values of all operands, except
the rightmost, has to be discarded.
Ex:
#include <iostream.h>
class loc
{
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt)
{ longitude = lg;
latitude = lt;
}
void show()
{ cout << longitude << " ";
cout << latitude << "\n";
}
loc operator+(loc op2);
loc operator,(loc op2);
};

loc loc::operator,(loc op2)


{ loc temp;

temp.longitude = op2.longitude;
temp.latitude = op2.latitude;
cout << op2.longitude <<" "<< op2.latitude << "\n";
return temp;
}

int main()
{ loc ob1(10, 20), ob2( 5, 30), ob3(1, 1);
ob1.show(); ob2.show(); ob3.show(); cout << "\n";

ob1 = (ob1, ob2+ob2, ob3);


ob1.show(); // displays 1 1, the value of ob3

return 0;
}

UNIT II 18
NOTE: The left-hand operand is passed via this and its value is
discarded by the operator,() function. The value of the right-hand
operand is returned by the function.
INHERITANCE

• Using Inheritance a general class that defines traits common to


a set of related items can be generated.

• This class may then be inherited by other, more specific


classes, each adding only those things that are unique to the
inhering class.

• A class that is inherits another is referred to as a base class.

• The class that does the inheriting is called derived class.

Base Class Access Control

• When a class inherits another, the members of the base class


become members of the derived class.

class derived-class : access base-class


{ // body of the class
};

• The access status of the base-class members inside the derived


class is determined by access.

• The base-class access specifier must be either

o Public
o private (default) or
o protected.

• When the access specifier for the base class is public, all
public members of the base become public members of the derived
class and all protected members of the base become protected
members of the derived class.

• When the base class is inherited by using the private access


specifier, all public and protected members of the base class
become private members of the derived class.

• In all cases, the base’s private members remain private to the


base and are not accessible by members of the derived class.

UNIT II 19
Inheritance and Protected members

• When a member of a class is defined as protected, that member is


not accessible by other, nonmember elements of the program.

• Access to a protected member is same as a private member, except


that, when a protected member is inherited, it differs
substantially from a private member.

• If the base class is inherited as public, then the base class


protected members become protected members of the derived class,
& are therefore accessible by derived class.

• By using protected we can create class members that are private


to their class but that can still be inherited and accessed by a
derived class.

• 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 (as public) by the 1st derived class may also be
inherited as protected by a 2nd derived class.

• If, however, base were inherited as private, then all members of


the base would become private members of the 1st derived class,
which means that they would not be accessible by the 2nd derived
class.

Protected Base Class Inheritance

• It is possible to inherit a base class as protected.

• When this is done, all public & protected members of the base
class become protected members of the derived class.

Inheriting Multiple base classes

• It is possible for a derived class to inherit 2 or more base


classes.

• To do so, use a comma separated list.

UNIT II 20
• Use an access-specifier for each base class inherited.
Constructors, Destructors and Inheritance

• When Constructors & Destructors functions are executed.


o In single Inheritance
o In multiple Inheritance

• Passing parameters to base-class constructors.

Ex1:
#include <iostream.h>

class base
{
protected:
int i;
public:
base(int x)
{ i=x;
cout << "Constructing base\n";
}
~base()
{
cout << "Destructing base\n";
}
};

class derived: public base


{
int j;
public:
// derived uses x; y is passed along to base.
derived(int x, int y): base(y)
{ j=x;
cout << "Constructing derived\n";
}
~derived()
{ cout << "Destructing derived\n"; }
void show()
{ cout << i << " " << j << "\n"; }
};

int main()
{
derived ob(3, 4);
ob.show(); // displays 4 3

UNIT II 21
return 0;
}
Ex2: #include <iostream.h>
class base1
{
protected: int i;
public:
base1(int x)
{ i=x;
cout << "Constructing base1\n";
}
~base1()
{ cout << "Destructing base1\n"; }
};

class base2
{
protected: int k;
public:
base2(int x)
{ k=x;
cout << "Constructing base2\n";
}
~base2()
{ cout << "Destructing base1\n"; }
};

class derived: public base1, public base2


{
int j;
public:
derived(int x, int y, int z): base1(y), base2(z)
{ j=x;
cout << "Constructing derived\n";
}
~derived()
{ cout << "Destructing derived\n"; }
void show()
{ cout << i << " " << j << " " << k << "\n"; }
};

int main()
{
derived ob(3, 4, 5);
ob.show(); // displays 4 3 5
return 0;
}

UNIT II 22
NOTE: C++ allows dynamic initialization.
VIRTUAL BASE CLASSES

An element of ambiguity can be introduced into a program when


multiple base classes are inherited.

Ex: // This program contains an error and will not compile.


#include <iostream.h>

class base
{
public: int i;
};
class derived1 : public base
{
public: int j;
};
class derived2 : public base
{
public: int k;
};

/* derived3 inherits both derived1 and derived2.


This means that there are two copies of base
in derived3! */

class derived3 : public derived1, public derived2


{
public: int sum;
};

int main()
{
derived3 ob;

ob.i = 10; // this is ambiguous, which i???


ob.j = 20;
ob.k = 30;

ob.sum = ob.i + ob.j + ob.k; // i ambiguous here, too

cout << ob.i << " "; // also ambiguous, which i?

cout << ob.j << " " << ob.k << " ";
cout << ob.sum;

UNIT II 23
return 0;
}
There are 2 ways to solve this problem :

1. Apply the scope resolution operator to i & select one i.

Ex:
#include <iostream.h>

class base
{
public: int i;
};

class derived1 : public base


{
public: int j;
};

class derived2 : public base


{
public: int k;
};

class derived3 : public derived1, public derived2


{
public: int sum;
};

int main()
{
derived3 ob;

ob.derived1::i = 10; // scope resolved, use derived1's i


ob.j = 20;
ob.k = 30;

ob.sum = ob.derived1::i + ob.j + ob.k;

cout << ob.derived1::i << " ";

cout << ob.j << " " << ob.k << " ";
cout << ob.sum;

return 0;
}

UNIT II 24
2. If only one copy of the base is actually required, then we need
to prevent 2 copies from being included in derived3.This
solution is achieved using virtual-base classes.
Declare the base class as virtual when it is inherited.
Ex:
#include <iostream.h>

class base
{
public: int i;
};
class derived1 : virtual public base
{
public: int j;
};
class derived2 : virtual public base
{
public: int k;
};

/* derived3 inherits both derived1 and derived2.


This time, there is only one copy of base class. */

class derived3 : public derived1, public derived2


{
public: int sum;
};

int main()
{
derived3 ob;

ob.i = 10; // now unambiguous


ob.j = 20;
ob.k = 30;

ob.sum = ob.i + ob.j + ob.k; // unambiguous


cout << ob.i << " "; // unambiguous

cout << ob.j << " " << ob.k << " ";
cout << ob.sum;

return 0;
}
NOTE:

UNIT II 25
Even if the base is virtual, base is still present in objects of
either type.
VIRTUAL FUNCTIONS AND POLYMORPHISM

• Polymorphism is supported by C++ both at compile time & run time.

o Compile time polymorphism is achieved by overloading


functions and operators.

o Run time polymorphism is achieved by using inheritance &


virtual functions.

VIRTUAL FUNCTIONS

• A virtual function is a member function that is declared within


a base class & redefined by a derived class, to fit its own
needs.

• In essence, virtual functions implement the “one interface,


multiple methods” philosophy that underlies polymorphism.

• When accessed normally, virtual functions behave just like any


other type of class member functions.

• What makes virtual functions important & capable of supporting


run-time polymorphism is how they behave when accessed via a
pointer.

• A base-class pointer can be used to point to an object af any


class derived from that base.

• When a base pointer points to a derived object that contains a


virtual function , C++ determines which version of that function
to call based upon “the type of object pointed to” by the
pointer. This determination is made at “run-rime”. This process
forms the basis for “run-time polymorphism”.

• When different objects are pointed to, different versions of the


virtual function are executed.

UNIT II 26
Ex: #include <iostream.h>

class base
{
public:
virtual void vfunc()
{
cout << "This is base's vfunc().\n";
}
};

class derived1 : public base


{
public:
void vfunc()
{
cout << "This is derived1's vfunc().\n";
}
};

class derived2 : public base


{
public:
void vfunc()
{
cout << "This is derived2's vfunc().\n";
}
};

int main()
{
base *p, b;

derived1 d1;
derived2 d2;

p = &b; // point to base


p->vfunc(); // access base's vfunc()

p = &d1; // point to derived1


p->vfunc(); // access derived1's vfunc()

p = &d2; // point to derived2


p->vfunc(); // access derived2's vfunc()

return 0;

UNIT II 27
}

The term overloading is not applied to virtual function redefinition


because several differences exist :

1. The prototype for a redefined virtual function must match


exactly the prototype specified in the base class.

2. Virtual functions must be nonstatic members of the classes of


which they are part.

3. They cannot be friends.

4. Constructor functions cannot be virtual, but destructor


functions can.

Because of the above reasons, the term “overriding” is used to


describe virtual function redefinition.

Calling a Virtual function through a Base Class reference :

• The polymorphic nature of a virtual function is also available


when called through a base class reference.

• A reference is an implicit pointer.

• A base class reference can be used to refer to an object of the


base class or any object derived from that base.

• The most common situation in which a virtual function is invoked


through a base class reference is when the reference is a
function parameter.

UNIT II 28
Ex:
#include <iostream.h>

class base
{
public:
virtual void vfunc()
{
cout << "This is base's vfunc().\n";
}
};
class derived1 : public base
{
public:
void vfunc()
{
cout << "This is derived1's vfunc().\n";
}
};
class derived2 : public base
{
public:
void vfunc()
{
cout << "This is derived2's vfunc().\n";
}
};

// Use a base class reference parameter.


void f(base &r)
{
r.vfunc();
}

int main()
{
base b;
derived1 d1;
derived2 d2;

f(b); // pass a base object to f()


f(d1); // pass a derived1 object to f()
f(d2); // pass a derived2 object to f()

return 0;
}

UNIT II 29
The virtual Attribute is inherited

• When a virtual function is inherited, its virtual nature is


also inherited.

• Put differently, no matter how many times a virtual function


is inherited, it remains virtual.

Virtual functions are Hierarchical

• When a derived class fails to override a virtual function ,


then when an object of that derived class accesses that
function, the function defined by the base class used.

• This means that when a derived class fails to override a


virtual function, the first redefinition found in the reverse
order of derivation is used.

Pure Virtual Functions

• In many situations there can be no meaningful definition of a


virtual function within a base class.

o A base class may not be able to define an object


sufficiently to allow a base-class virtual function to be
created.
o One wants to ensure that all derived classes override a
virtual function.

• To handle these 2 cases, C++ supports pure virtual function.

• A pure virtual function is a virtual function that has no


definition within the base-class.

• General form :

virtual type function-name(parameter list) = 0;

UNIT II 30
Ex: #include <iostream.h>

class number
{
protected: int val;

public: void setval(int i)


{ val = i; }

virtual void show() = 0;


};
class hextype : public number
{
public: void show()
{
cout << hex << val << "\n";
}
};
class dectype : public number
{
public: void show()
{
cout << val << "\n";
}
};
class octtype : public number
{
public: void show()
{
cout << oct << val << "\n";
}
};

int main()
{
dectype d;
hextype h;
octtype o;

d.setval(20); d.show(); // displays 20 - decimal

h.setval(20); h.show(); // displays 14 -hexadecimal

o.setval(20); o.show(); // displays 24 - octal

return 0;

UNIT II 31
}
Abstract classes

• A class that contains at least one pure virtual function is


said to be abstract.

• Since an abstract class has one or more functions for which


there is no definition, no objects of an abstract class may
be created.

• Pointers & references can be created for abstract class. This


allows abstract classes to support run-time polymorphism.

Early VS Late Binding

• Early binding refers to events that occur at compile time.

• Examples include normal function calls, overloaded function


calls, & overloaded operators.

• The main advantage of early binding is efficiency.

• Late binding refers to function calls that are not resolved


until runtime.

• Virtual functions are used to achieve late binding.

• The main advantage to late binding is flexibility.

• Since a function call is not resolved until run time, late


binding can make execution slower.

Using Virtual Functions

• To create a class hierarchy that moves from general to specific.

• An important use of abstract classes & virtual functions is in


class libraries. One can create a generic, extensible class
library that will be used by other programmers.

UNIT II 32
#include <iostream.h>

class convert
{ protected: double val1; // initial value
double val2; // converted value
public:
convert(double i)
{ val1 = i;
}
double getconv()
{ return val2; }
double getinit()
{ return val1; }

virtual void compute() = 0;


};
class l_to_g : public convert // Liters to gallons.
{
public: l_to_g(double i) : convert(i) { }
void compute()
{ val2 = val1 / 3.7854;
}
};
class f_to_c : public convert // Fahrenheit to Celsius
{
public: f_to_c(double i) : convert(i) { }
void compute()
{ val2 = (val1-32) / 1.8;
}
};
int main()
{ convert *p; // pointer to base class
l_to_g lgob(4);
f_to_c fcob(70);

p = &lgob; // use virtual function mechanism


cout << p->getinit() << " liters is ";
p->compute();
cout << p->getconv() << " gallons\n"; // l_to_g

p = &fcob;
cout << p->getinit() << " in Fahrenheit is ";
p->compute();
cout << p->getconv() << " Celsius\n"; // f_to_c

return 0;

UNIT II 33
}
TEMPLATES

Using templates it is possible to create generic functions and


classes.

GENERIC FUNCTIONS

• A generic function defines a general set of operations that will


be applied to various types of data.

• The type of data that the function will operate upon is passed
to it as a parameter.

• Through a generic function , a single general procedure can be


applied to a wide range of data.

• In essence, when we create a generic function we are creating a


function that can automatically overload itself.

• A generic function is created using the keyword “template”.

• “template” is used to create a template ( or framework) that


describes what a function will do, leaving it to the compiler to
fill in the details as needed.

o General Form:

template<class Ttype>
Ret-type function-name(parameter list)
{
// body
}

o Ttype is a place holder name for a data type used by the


function. This name may be used within the function
definition.

o It is only a placeholder that the compiler will


automatically replace with an actual data type when it
creates a specific version of the function.

o We can use keyword “typename” instead of “class”.

UNIT II 34
Ex:
#include <iostream.h>

// This is a function template.


template <class X>
void swapargs(X &a, X &b)
{
X temp;

temp = a;
a = b;
b = temp;
}

int main()
{
int i=10, j=20;
double x=10.1, y=23.3;
char a='x', b='z';

cout << "Original i, j: " << i << ' ' << j << '\n';
cout << "Original x, y: " << x << ' ' << y << '\n';
cout << "Original a, b: " << a << ' ' << b << '\n';

swapargs(i, j); // swap integers

swapargs(x, y); // swap floats

swapargs(a, b); // swap chars

cout << “Swapped I, j: “ << I << ‘ ‘ << j << ‘\n’;


cout << “Swapped x, y: “ << x << ‘ ‘ << y << ‘\n’;
cout << “Swapped a, b: “ << a << ‘ ‘ << b << ‘\n’;

return 0;
}

UNIT II 35
• A generic function is also called a template function.
• When the compiler creates a specific version of this function,
it is said to have created a specialization. This is also called
a generated function.
• The act of generating a function is referred to as instantiating
it.
• Put differently, a generated function is a specific instance of
a template function.

More than one generic data type can be defined in the template
statement by using a comma-seperated list.

Ex:

#include <iostream.h>

template <class type1, class type2>


void myfunc(type1 x, type2 y)
{
cout << x << ' ' << y << '\n';
}

int main()
{
myfunc(10, "I like C++");

myfunc(98.6, 19L);

return 0;
}

Note:

When we create a template function, we are in essence, allowing the


compiler to generate as many different versions of that function as
are necessary for handling the various ways that the program calls
the function.

UNIT II 36
Explicitly Overloading a Generic Function

• Even though a generic function overloads itself as needed, you


can explicitly overload one, too.
• This is formally called explicit specialization.
• If we overload a generic function, that overloaded function
overrides(or”hides”) the generic function relative to that
specific version.
Ex:
#include <iostream.h>

template <class X>


void swapargs(X &a, X &b)
{
X temp;

temp = a;
a = b;
b = temp;
cout << "Inside template swapargs.\n";
}

// This overrides the generic version of swapargs() for int.


void swapargs(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "Inside swapargs int specialization.\n";
}
int main()
{
int i=10, j=20;
double x=10.1, y=23.3;
char a='x', b='z';

swapargs(i, j); // calls explicitly overloaded swapargs()


swapargs(x, y); // calls generic swapargs()
swapargs(a, b); // calls generic swapargs()

cout << "Swapped i, j: " << i << ' ' << j << '\n';
cout << "Swapped x, y: " << x << ' ' << y << '\n';
cout << "Swapped a, b: " << a << ' ' << b << '\n';

return 0;

UNIT II 37
}
new style-syntax to explicitly overload a template function

template< >
void swapargs<int>(int &a,int &b)
{ // function body
}

The new style syntax uses the template<> construct to indicate


specialization. The type of data for which the specialization is
being created is placed inside the angle brackets following the
function name.

Overloading a Function Template

• We can overload the “template” specification.


• To do so, simply create another version of the template that
differs from any others in parameter list.

Ex:

#include <iostream.h>

// First version of f() template.


template <class X>
void f(X a)
{
cout << "Inside f(X a)\n";
}

// Second version of f() template.


template <class X, class Y>
void f(X a, Y b)
{
cout << "Inside f(X a, Y b)\n";
}

int main()
{
f(10); // calls f(X)
f(10, 20); // calls f(X, Y)

return 0;
}

UNIT II 38
Using Standard Parameters with Template Functions

• We can mix standard parameters with generic type parameters in a


template function.

• These non-generic parameters work just like they do with any


other function.

Ex:

template< class X >


void show(X data, int num)
{
cout<<data<<” “<<num;
}

int main()
{
show(10,10);
show(15.5,10);
show(“Templates”,10);

return 0;
}

Restriction on Generic function

A generic function must perform the same general action for all
versions – only the type of data can differ.

Applying Generic Functions

• Whenever we have a function that defines a generalizable


algorithm, we can make it into a template function.

• Once we have done so,we may use it with any type of data without
having to recode it.

UNIT II 39
Ex:// A generic bubble sort
#include <iostream.h>

template <class X>


void bubble( X *items, int count) //no of items in n
{
int a, b;
X t;
for(a=0; a<count-1; a++)
for(b=0; b<count-a-1; b--)
if(items[b] > items[b+1])
{
t = items[b];
items[b] = items[b+1];
items[b+1] = t;
}
}

int main()
{
int iarray[7] = {7, 5, 4, 3, 9, 8, 6};
double darray[5] = {4.3, 2.5, -0.9, 100.2, 3.0};
int i;

cout << "Here is unsorted integer array: ";


for(i=0; i<7; i++)
cout << iarray[i] << ' ';

cout << "Here is unsorted double array: ";


for(i=0; i<5; i++)
cout << darray[i] << ' ';

bubble(iarray, 7);
bubble(darray, 5);

cout << "Here is sorted integer array: ";


for(i=0; i<7; i++)
cout << iarray[i] << ' ';

cout << "Here is sorted double array: ";


for(i=0; i<5; i++)
cout << darray[i] << ' ';

return 0;
}

UNIT II 40
GENERIC CLASSES

• Generic classes creates a class that defines all the


algorithms used by that class; however, the actual type of the
data being manipulated will be specified as a parameter when
objects of that class are created.

• Generic classes are useful when a class uses logic that can be
generalized.

• The compiler will automatically generate the correct type of


object, based upon the type that is specified when the object
is created.

o General form :

template<class Ttype>
class class-name
{
//body
}

o Ttype is a place holder typename,which will be specified


when a class is instantiated.

o We can define more than one generic data type using a


comma-seperated list.

• Once a generic class is created ,a specific instance of that


class is created using the general form

o class-name<type> ob;

o Here, type is the type name of the data that the class
will be operating upon.

• Member functions of a generic class are themselves


automatically generic. Hence keyword “template” need not be
used explicitly to specify them as such.

UNIT II 41
• A template class can have more than one generic data type.

• Declare all the data types required by the class in a comma


separated list within the template specification.

Ex:
/* This example uses two generic data types in a
class definition.*/

#include <iostream.h>

template <class Type1, class Type2>


class myclass
{
Type1 i;

Type2 j;

public:
myclass(Type1 a, Type2 b)
{ i = a; j = b;
}
void show()
{ cout << i << ' ' << j << '\n';
}
};

int main()
{
myclass<int, double> ob1(10, 0.23);

myclass<char, char *> ob2('X', "Templates add power.");

ob1.show(); // show int, double

ob2.show(); // show char, char *

return 0;
}

UNIT II 42
Applying Template Classes: A generic safe array example.

#include <iostream.h>
#include <cstdlib.h>
const int SIZE = 10;

template <class AType>


class atype
{
AType a[SIZE];
public:
atype()
{ register int i;
for(i=0; i<SIZE; i++)
a[i] = i;
}
AType &operator[](int i);
};

template <class AType>


AType atype<AType>::operator[](int i)
{
if(i<0 || i> SIZE-1)
{ cout << "\nIndex value of ";
cout << i << " is out-of-bounds.\n";
exit(1);
}
return a[i];
}
int main()
{
atype<int> intob; // integer array
atype<double> doubleob; // double array

int i;

cout << "Integer array: ";


for(i=0; i<SIZE; i++) intob[i] = i;
for(i=0; i<SIZE; i++) cout << intob[i] << " ";

cout << "Double array: ";


for(i=0; i<SIZE; i++) doubleob[i] = (double) i/3;
for(i=0; i<SIZE; i++) cout << doubleob[i] << " ";

intob[12] = 100; // generates runtime error


return 0;

UNIT II 43
}
Using Non-Type Arguments with Generic Classes

• Non-type parameters are restricted to integers, pointers or


references.
• Other types such as float are not allowed.

• The arguments that are passed to a non-type parameter must


consist of either an integer constant, or a pointer or reference
to a global function or object.

• Thus , non-type parameters should themselves be thought of as


constants, since their values cannot be changed.

Using Default Arguments with Template Classes

• A template class can have a default argument associated with a


generic type.

• template <class X=int> class myclass { //

• Here, the type int will be used if no other type is specified


when an object of type myclass is instantiated.

• It is also permissible for non-type arguments to take default


arguments.

Explicit Class Specification

• As with template functions , we can create an explicit


specialization of a generic class.

• To do so, use template<> construct.

• Explicit class specialization expands the utility of generic


classes because it lets the programmer easily handle one or two
special cases while allowing all others to be automatically
processed by the compiler.

UNIT II 44
Ex: // Demonstrate class specialization.
#include <iostream.h>

template <class T>


class myclass
{
T x;
public: myclass(T a)
{ cout << "Inside generic myclass\n";
x = a;
}
T getx() { return x; }
};

template <>
class myclass<int>
{
int x;
public: myclass(int a)
{ cout << "In myclass<int> specialization\n";
x = a * a;
}
int getx() { return x; }
};
int main()
{
myclass<double> d(10.1);
cout << "double: " << d.getx() << "\n\n";

myclass<int> i(5);
cout << "int: " << i.getx() << "\n";

return 0;
}

The typename and export Keywords:


• typename keyword can be used in place of keyword class in a
template declaration.
template <typename X>
• It can also be used to tell the compiler that a name used in a
template declaration is a typename rather than an object name.
typename X::Name someObject;

• The export keyword can precede a template declaration. It allows


other files to use a template declared in a different file by

UNIT II 45
only specifying its declaration rather than duplicating its
entire definition.

UNIT II 46

You might also like