P. 1
ADS_Unit_II

ADS_Unit_II

|Views: 22|Likes:
Published by api-26548538
PDF Copy of ADS Unit II notes
PDF Copy of ADS Unit II notes

More info:

Published by: api-26548538 on Oct 18, 2008
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

05/09/2014

pdf

text

original

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 ). The copy constructor applies only to initializations.

UNIT II

3

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); 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; } // copy constructor

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. #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

Ex3:

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 decoupling is successful is in the way << and >> are overloaded in C++.

UNIT II

10

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; return temp; }
UNIT II

11

Restrictions for friend operator functions • • 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--; return op; } 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.

UNIT II

12

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; } NOTE: ob1 = ob2 + 10; ob3 = 10 + ob2; // both of these are valid

UNIT II

13

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

Ex1:

UNIT II

14

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 [] subscripting operator, then the out-of-range index can be intercepted.

UNIT II

15

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"); • Ex: When we overload the () operator, we define the parameters that we want to pass to that function. #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; } NOTE: ob1 = ob2 + ob1(10, 10); // can be used in expressions

UNIT II

16

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

UNIT II

18

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. Use an access-specifier for each base class inherited.

• •

UNIT II

20

Constructors, Destructors and Inheritance • When Constructors & Destructors functions are executed. o In single Inheritance o In multiple Inheritance Passing parameters to base-class constructors. #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 return 0; }

• Ex1:

UNIT II

21

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: public:

int k; 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; } NOTE: C++ allows dynamic initialization.

UNIT II

22

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; cout << ob.i << " "; // i ambiguous here, too

// also ambiguous, which i?

cout << ob.j << " " << ob.k << " "; cout << ob.sum; return 0; }

UNIT II

23

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; cout << ob.i << " "; // unambiguous // unambiguous

cout << ob.j << " " << ob.k << " "; cout << ob.sum; return 0; } NOTE: Even if the base is virtual, base is still present in objects of either type.

UNIT II

25

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” describe virtual function redefinition. is used to

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: public:

int val; 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); h.setval(20); o.setval(20); return 0; } d.show(); // displays 20 - decimal h.show(); // displays 14 -hexadecimal o.show(); // displays 24 - octal

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: 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; } double val1; double val2; // initial value // converted value

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 • • • 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

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.

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 • • Ex: We can overload the “template” specification. To do so, simply create another version of the template that differs from any others in parameter list.

#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 only specifying its declaration rather than duplicating its entire definition.

UNIT II

45

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->