hometutionsite.files.wordpress.com€¦  · web viewunit 2. operator overloading: creating a...

76
Unit 2 Operator Overloading: Creating a member operator function: A member operator function takes this general form: ret-type class-name::operator#(arg-list) { // operations } Often, operator functions return an object of the class they operate on, but ret-type can be any valid type. The # is a placeholder. When you create an operator function, substitute the operator for the #. For example, if you are overloading the / operator, use operator/. When you are overloading a unary operator, arg-list will be empty. When you are overloading binary operators, arg-list will contain one parameter. (The reasons for this seemingly unusual situation will be made clear in a moment.) Examine this program carefully, paying special attention to the definition of operator+() : #include <iostream> using namespace std; class loc { int longitude, latitude; public: loc() {} loc(int lg, int lt) { longitude = lg; latitude = lt; } void show() { cout << longitude << " "; cout << latitude << "\n";

Upload: others

Post on 27-Jan-2021

8 views

Category:

Documents


0 download

TRANSCRIPT

Unit 2

Operator Overloading:

Creating a member operator function:

A member operator function takes this general form:

ret-type class-name::operator#(arg-list)

{

// operations

}

Often, operator functions return an object of the class they operate on, but ret-type can be any valid type. The # is a placeholder. When you create an operator function, substitute the operator for the #. For example, if you are overloading the / operator, use

operator/.

When you are overloading a unary operator, arg-list will be empty. When you are overloading binary operators, arg-list will contain one parameter. (The reasons for this seemingly unusual situation will be made clear in a moment.)

Examine this program carefully, paying special attention to the definition of operator+() :

#include

using namespace std;

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 = op2.longitude + longitude;

temp.latitude = op2.latitude + 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;

}

Creating Prefix and Postfix Forms of the Increment and Decrement Operators:

In the preceding program, only the prefix form of the increment operator was overloaded. However, Standard C++ allows you to explicitly create separate prefix and postfix versions of increment or decrement operators.

To accomplish this, you must define two versions of the operator++() function. One is defined as shown in the foregoing program. The other is declared like this:

loc operator++(int x);

If the ++ precedes its operand, the operator++() function is called. If the ++ follows its

operand, the operator++(int x) is called and x has the value zero.

The preceding example can be generalized. Here are the general forms for the

prefix and postfix ++ and – – operator functions.

// Prefix increment

type operator++( ) {

// body of prefix operator

}

// Postfix increment

type operator++(int x) {

// body of postfix operator

}

// Prefix decrement

type operator– –( ) {

// body of prefix operator

}

// Postfix decrement

type operator– –(int x) {

// body of postfix operator

}

Overloading the Shorthand Operators:

You can overload any of C++'s "shorthand" operators, such as +=, –=, and the like. For

example, this function overloads += relative to loc:

loc loc::operator+=(loc op2)

{

longitude = op2.longitude + longitude;

latitude = op2.latitude + latitude;

return *this;

}

When overloading one of these operators, keep in mind that you are simply

combining an assignment with another type of operation.

Operator Overloading Restrictions:

There are some restrictions that apply to operator overloading. You cannot alter the precedence of an operator. You cannot change the number of operands that an operator takes. (You can choose to ignore an operand, however.)

Except for the function call operator (described later), operator functions cannot have default arguments. Finally, these operators cannot be overloaded:

As stated, technically you are free to perform any activity inside an operator function. For example, if you want to overload the + operator in such a way that it writes I like C++ 10 times to a disk file, you can do so. However, when you stray significantly from the normal meaning of an operator, you run the risk of dangerously destructuring your program.

When someone reading your program sees a statement like Ob1+Ob2, he or she expects something resembling addition to be taking place—not a disk access, for example. Therefore, before decoupling an overloaded

operator from its normal meaning, be sure that you have sufficient reason to do so. Except for the = operator, operator functions are inherited by any derived class.

However, a derived class is free to overload any operator (including those overloaded by the base class) it chooses relative to itself.

Overloading new and delete:

It is possible to overload new and delete. You might choose to do this if you want to use some special allocation method. For example, you may want allocation routines that automatically begin using a disk file as virtual memory when the heap has been exhausted.

Whatever the reason, it is a very simple matter to overload these operators. The skeletons for the functions that overload new and delete are shown here:

// Allocate an object.

void *operator new(size_t size)

{

/* Perform allocation. Throw bad_alloc on failure.

Constructor called automatically. */

return pointer_to_memory;

}

// Delete an object.

void operator delete(void *p)

{

/* Free memory pointed to by p.

Destructor called automatically. */

}

Overloading Some Special Operators:

C++ defines array subscripting, function calling, and class member access as operations. The operators that perform these functions are the [ ], ( ), and –>, respectively.

These rather exotic operators may be overloaded in C++, opening up some very interesting uses. One important restriction applies to overloading these three operators: They must be nonstatic member functions.

They cannot be friends. Overloading [ ] In C++, the [ ] is considered a binary operator when you are overloading it. Therefore, the general form of a member operator[ ]() function is as shown here:

type class-name::operator[](int i)

{

// . . .

}

Technically, the parameter does not have to be of type int, but an operator[ ]() function

is typically used to provide array subscripting, and as such, an integer value is generally used The overloaded operator[ ]() function returns the value of the array as indexed by the value of its parameter.

#include

using namespace std;

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;

}

Overloading ( ) When you overload the ( ) function call operator, you are not, per se, creating a new way to call a function. Rather, you are creating an operator function that can be passed

an arbitrary number of parameters. Let's begin with an example.

Given the overloaded operator function declaration

double operator()(int a, float f, char *s);

and an object O of its class, then the statement

O(10, 23.34, "hi");

translates into this call to the operator() function.

O.operator()(10, 23.34, "hi");

#include

using namespace std;

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()(int i, int j);

};

// Overload ( ) for loc.

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

{

longitude = i;

latitude = j;

return *this;

}

// Overload + for loc.

loc loc::operator+(loc op2)

{

loc temp;

temp.longitude = op2.longitude + longitude;

temp.latitude = op2.latitude + latitude;

return temp;

}

int main()

{

loc ob1(10, 20), ob2(1, 1);

ob1.show();

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

ob1.show();

ob1 = ob2 + ob1(10, 10); // can be used in expressions

ob1.show();

return 0;

}

Overloading –> The –> pointer operator, also called the class member access operator, is considered a unary operator when overloading. Its general usage is shown here:

object->element;

Here, object is the object that activates the call. The operator–>() function must return a

pointer to an object of the class that operator–>() operates upon. The element must be

some member accessible within the object.

The following program illustrates overloading the –> by showing the equivalence

between ob.i and ob–>i when operator–>() returns the this pointer:

#include

using namespace std;

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;

}

UNIT III:Inheritance

Base-class access control:

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

· A derived class always can access the members of base class

· Where as a base class never access the members of derived class.

· The main purpose of inheritance is reusability

Class inheritance uses this general form:

class derived-class-name : access base-class-name

{

// body of class

};

· The base-class access specifier must be either public, private, or protected.

· If no access specifier is present, the access specifier is private by default in the derived class.

· When the access specifier for a 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.

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

Here is an example:

#include

#include

class ABC

{

Public://protected:(same output)

char name[15];

int age;

};

class abc:public ABC

{

float height;

float weight;

public:

void getdata()

{

cout<<"\n Enter name and age :";

cin>>name>>age;

cout<<"\n Enter height and weight :";

cin>>height>>weight;

}

void show()

{

cout<<"\n name :"<

}

};

void main()

{

clrscr();

abc s;

s.getdata();

s.show();

getch();

}

Output:

Here is an example: Base class is private

#include

#include

class ABC

{

Private:

char name[15];

int age;

};

class abc:public ABC

{

float height;

float weight;

public:

void getdata()

{

cout<<"\n Enter name and age :";

cin>>name>>age;

cout<<"\n Enter height and weight :";

cin>>height>>weight;

}

void show()

{

cout<<"\n name :"<

}

};

void main()

{

clrscr();

abc s;

s.getdata();

s.show();

getch();

}

Output:

name is not accessible

age is not accessible

Inheritance and protected members:

· The protected keyword is included in C++ to provide greater flexibility in the inheritance mechanism.

· When a member of a class is declared as protected, that member is not accessible by other, nonmember elements of the program.

· However, protected members behave differently. If the base class is inherited as public, then the base class' protected members become protected members of the derived class and are,therefore, accessible by the derived class. By using protected

Ex:program

#include

#include

class ABC

{

Protected:

char name[15];

int age;

};

class abc:private ABC

{

float height;

float weight;

public:

void getdata()

{

cout<<"\n Enter name and age :";

cin>>name>>age;

cout<<"\n Enter height and weight :";

cin>>height>>weight;

}

void show()

{

cout<<"\n name :"<

}

};

void main()

{

clrscr();

abc s;

s.getdata();

s.show();

getch();

}

Output:

Inheriting multiple base classes:

· It is possible for a derived class to inherit two or more base classes.

· One derived class with several base classes is called as multiple inheritance.

· In multiple inheritance there is only one derived class that is derived from several base classes

(XYZ)

(Here x,y base classes and z is derived class)

Simple program:

#include

#include

class A

{

public:

int a;

};

class B

{

protected:

int b;

};

class C

{

protected:

int c;

};

class D

{

protected:

int d;

};

class E:public A,B,C,D

{

int e;

public:

void getdata()

{

cout<<"\n Enter values of a,b,c,d,e:::";

cin>>a>>b>>c>>d>>e;

}

void srikanth()

{

cout<<"\n a="<

}

~base()

{

cout << "Destructing base\n";

}

};

class derived: public base

{

public:

derived()

{

cout << "Constructing derived\n";

}

~derived()

{

cout << "Destructing derived\n";

}

};

int main()

{

derived ob;

return 0;

}

As the comment in main() indicates, this program simply constructs and then

destroys an object called ob that is of class derived. When executed,

this program displays:

Constructing base

Constructing derived

Destructing derived

Destructing base

For example,

#include

using namespace std;

class base

{

public:

base()

{

cout << "Constructing base\n";

}

~base()

{

cout << "Destructing base\n";

}

};

class derived1 : public base

{

public:

derived1()

{

cout << "Constructing derived1\n";

}

~derived1()

{

cout << "Destructing derived1\n";

}

};

class derived2: public derived1

{

public:

derived2()

{

cout << "Constructing derived2\n";

}

~derived2()

{

cout << "Destructing derived2\n";

}

};

int main()

{

derived2 ob;

// construct and destruct ob

return 0;

}

displays this output:

Constructing base

Constructing derived1

Constructing derived2

Destructing derived2

Destructing derived1

Destructing base

passing parameters to base class constructors:

· constructor requires one or more parameters

· you simply use the standard parameterized constructor However, how do you pass arguments to a constructor in a base class?

· The answer is to use an expanded form of the derived class's constructor declaration that passes along arguments to one or more base-class constructors.

· The general form of this expanded derived-class constructor declaration is shown here:

derived-constructor(arg-list) : base1(arg-list),

base2(arg-list),

// ...

baseN(arg-list)

{

// body of derived constructor

}

Here, base1 through baseN are the names of the base classes inherited by the derived class.

#include

using namespace std;

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(j)

{

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;

}

Granting Access

· When a base class is inherited as private, all public and protected members of that class become private members of the derived class.

· For example, you might want to grant certain public members of the base class public status in the derived class even though the base class is inherited as private.

· In Standard C++, you have two ways to accomplish this. First, you can use a using statement, which is the preferred way. The using statement is designed primarily to support namespaces

An access declaration takes this general form:

base-class::member;

The access declaration is put under the appropriate access heading in the derived class' declaration. Notice that no type declaration is required (or, indeed, allowed) in an access declaration.

class base

{

public:

int j; // public in base

};

// Inherit base as private.

class derived: private base

{

public:

// here is access declaration

base::j; // make j public again

.

.

.

};

Because base is inherited as private by derived, the public member j is made a private

member of derived. However, by including

base::j;

#include

using namespace std;

class base {

int i; // private to base

public:

int j, k;

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

int geti() { return i; }

};

// Inherit base as private.

class derived: private base {

public:

base::j; // make j public again - but not k

base::seti; // make seti() public

base::geti; // make geti() public

int a; // public

};

int main()

{

derived ob;

ob.j = 20; // legal because j is made public in derived

ob.a = 40; // legal because a is public in derived

ob.seti(10);

cout << ob.geti() << " " << ob.j << " " << ob.a;

return 0;

}

virtual base classes

C++  - What is a virtual base class?

When two or more objects are derived from a common base class, we can prevent multiple copies of the base class being present in an object derived from those objects by declaring the base class as virtual when it is being inherited. Such a base class is known as virtual base class. This can be achieved by preceding the base class’ name with the word virtual.

Consider following example:

class A {      public:           int i; };

class B : virtual public A {        public:                 int j; };

class C: virtual public A {         public:              int k; };

class D: public B, public C { public:       int sum; };

int main() {          D ob;          ob.i = 10; //unambiguous since only one copy of i is inherited.          ob.j = 20;          ob.k = 30;          ob.sum = ob.i + ob.j + ob.k;          cout << “Value of i is : ”<< ob.i<<”\n”;          cout << “Value of j is : ”<< ob.j<<”\n”; cout << “Value of k is :”<< ob.k<<”\n”;          cout << “Sum is : ”<< ob.sum <<”\n”; 

return 0; }

Also read

What is virtual function? Explain with an example.

A virtual function is a member function that is declared within a base class and redefined by a derived class. To create virtual function, precede the function’s declaration in the base class with the keyword virtual..............

Consider following program code:

Class A {         int a;         public:         A()         {                 a = 1;         }         virtual void show()         {                     cout <

Class B: public A {          int b;          public:          B()          {                  b = 2;          }         virtual void show()          {                    cout <

int main() {            A *pA;            B oB;            pA = &oB;            pA->show();            return 0; }

Output is 2 since pA points to object of B and show() is virtual in base class A.

calling a virtual function through a base class reference:

This code refuses to print the appropriate messages to the console when I run it. Using pointers instead of references seems to work (-> instead of .).

#include

using namespace std;

class instrument {public: virtual void play(){}};

class drum : public instrument {public: void play() { cout << "dum, dum" << endl; }};

class piano : public instrument {public: void play(){ cout << "pling" << endl; }};

int main (){ instrument i; piano p; drum d;

instrument &sri = i; sri.play(); // -

sri= p; sri.play(); // pling

sri= d; sri.play(); // dum, dum}

The Virtual Attribute Is Inherited

When a virtual function is inherited, its virtual nature is also inherited. This means that when a derived class that has inherited a virtual function is itself used as a base class for another derived class, the virtual function can still be overridden. Put differently, no matter how many times a virtual function is inherited, it remains virtual.

For example, consider this program:

#include using namespace std;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"; }}; /* derived2 inherits virtual function vfunc() from derived1. */class derived2 : public derived1 {public: // vfunc() is still virtual void vfunc() { cout << "This is derived2's vfunc().\n"; }}; int main(){ base *p, b; derived1 d1; derived2 d2; // point to base p = &b; p->vfunc(); // access base's vfunc() // point to derived1 p = &d1; p->vfunc(); // access derived1's vfunc() // point to derived2 p = &d2; p->vfunc(); // access derived2's vfunc() return 0;}

virtual functions are hierarchical:

when a function is declared as virtual by a base class, it may be overridden by a derived class. However, the function does not have to be overridden. 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 is used.

For example, consider this program in which derived2 does not override vfunc() :

Virtual Functions Are Hierarchical

#include using namespace std;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:// vfunc() not overridden by derived2, base's is used};int main(){ base *p, b; derived1 d1; derived2 d2; // point to base p = &b; p->vfunc(); // access base's vfunc() // point to derived1 p = &d1; p->vfunc(); // access derived1's vfunc() // point to derived2 p = &d2; p->vfunc(); // use base's vfunc() return 0;}

pure virtual function

A pure virtual function  is a function that has the notation "= 0" in the declaration of that function.

C++ supports the pure virtual function. A pure virtual function is a virtual function that has no definition within the base class. To declare a pure virtual function, use this general form:

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

When a virtual function is made pure, any derived class must provide its own

definition. If the derived class fails to override the pure virtual function, a compile-time error will result.

Simple Example of a pure virtual function in C++class SomeClass {public: virtual void pure_virtual() = 0; // a pure virtual function // note that there is no function body };

#include

using namespace std;

class number {

protected:

int val;

public:

void setval(int i) { val = i; }

// show() is a pure virtual function

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;

Abstract classes

· A class that contains at least one pure virtual function is said to be abstract.

· Because an abstract class contains one or more functions for which there is no definition (that is, apure virtual function),

· No objects of an abstract class may be created. Instead, an abstract class constitutes an incomplete type that is used as a foundation for derived classes.

· Although you cannot create objects of an abstract class, you can create pointers and references to an abstract class.

· This allows abstract classes to support run-time polymorphism, which relies upon base-class pointers and references to select the proper virtual function.

Early vs late binding

Early binding refers to assignment of values to variables during design time whereas late binding refers to assignment of values to variables during run time.

early binding and late binding:

· Early binding refers to events that occur at compile time. In essence, early binding occurs when all information needed to call a function is known at compile time.

· (Put differently, early binding means that an object and a function call are bound during compilation.) Examples of early binding include normal function calls

· (including tandard library functions), overloaded function calls, and overloaded operators. The main advantage to early binding is efficiency. Because all information necessary to call a function is determined at compile time, these types of function calls are very fast.

· The opposite of early binding is late binding. As it relates to C++, late binding refers to function calls that are not resolved until run time.

· Virtual functions are used to achieve late binding. As you know, when access is via a base pointer or reference, the virtual function actually called is determined by the type of object pointed to by the pointer.

· Because in most cases this cannot be determined at compile time, the object

and the function are not linked until run time. late binding can make for some what slower execution times.

Templates:

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.

· A generic function is created using the keyword template. The normal meaning of the word "template" accurately reflects its use in C++.

· It 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.

· The general form of a template function definition is

template ret-type func-name(parameter list)

{

// body of function

}

Here, Ttype is a placeholder name for a data type used by the function. This name may be used within the function definition. the keyword class to specify a generic type in a template declaration is traditional, you may also use the keyword typename.

// Function template example.

#include

using namespace std;

// This is a function template.

template 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;

}

Let's look closely at this program. The line:

template void swapargs(X &a, X &b)

tells the compiler two things: that a template is being created and that a generic

definition is beginning. Here, X is a generic type that is used as a placeholder. After the template portion, the function swapargs() is declared, using X as the data type of the values that will be swapped. In main() , the swapargs() function is called using three different types of data: ints, doubles, and chars. Because swapargs() is a generic function, the compiler automatically creates three versions of swapargs() : one that will exchange integer values, one that will exchange floating-point values, and one that will swap characters.

Function Template

· Function template is also known as generic function.

· The main advantage of function overloading is that you can use same name of function for performing similar tasks in different parts of a program.

· If you want to accommodate more data types, you will require additional versions of function. This is the main disadvantage of function overloading.

· However, C++ compilers have a facility called template which will help you create only one version of function. This version will accommodate different data types of C++.

· A function which works for all data types of C++ is called a generic function or a function template.

· Syntax is:template type func_name(type arg1, ...);type is any name of your choice, which is a placeholder name for a data type used by the function. The name X is replaced by actual data type when the compiler creates a specific version of the function.

Example#include#includetemplate X big(X a, X b){return ((a>b)?a:b);}void main(){cout<<”\n Bigger of two is: ”<

The output look like:

Bigger of two is: 21Bigger of two is: 16.54Bigger of two is: L

A function with two generic types:

You can define more than one generic data type in the template statement by using a comma-separated list. For example, this program creates a template function that has two generic types.

#include

using namespace std;

template

void myfunc(type1 x, type2 y)

{

cout << x << ' ' << y << '\n';

}

int main()

{

myfunc(10, "I like C++");

myfunc(98.6, 19L);

return 0;

}

In this example, the placeholder types type1 and type2 are replaced by the compiler with the data types int and char *, and double and long, respectively, when the compiler generates the specific instances of myfunc() within main() .

overloading generic functions:

Even though a generic function overloads itself as needed, you can explicitly overload one, too. This is formally called explicit specialization. If you overload a generic function, that overloaded function overrides (or "hides") the generic function relative to that specific version. For example, consider the following revised version of the argumentswapping example shown earlier.

// Overriding a template function.

#include

using namespace std;

template 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 ints.

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';

cout << "Original i, j: " << i << ' ' << j << '\n';

cout << "Original x, y: " << x << ' ' << y << '\n';

cout << "Original a, b: " << a << ' ' << b << '\n';

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;

}

This program displays the following output.

Original i, j: 10 20

Original x, y: 10.1 23.3

Original a, b: x z

Inside swapargs int specialization.

Inside template swapargs.

Inside template swapargs.

Swapped i, j: 20 10

Swapped x, y: 23.3 10.1

Swapped a, b: z x

Overloading a Function Template

In addition to creating explicit, overloaded versions of a generic function, you can also overload the template specification itself. To do so, simply create another version of the template that differs from any others in its parameter list. For example:

// Overload a function template declaration.

#include

using namespace std;

// First version of f() template.

template void f(X a)

{

cout << "Inside f(X a)\n";

}

// Second version of f() template.

template 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;

}

Here, the template for f() is overloaded to accept either one or two parameters.

Generic Function Restrictions

Generic functions are similar to overloaded functions except that they are more restrictive. When functions are overloaded, you may have different actions performed within the body of each function. But a generic function must perform the same general action for all versions only the type of data can differ. Consider the overload functions in the following example program. These functions could not be replaced by a generic function because they do not do the same thing.

#include

#include

using namespace std;

void myfunc(int i)

{

cout << "value is: " << i << "\n";

}

void myfunc(double d)

{

double intpart;

double fracpart;

fracpart = modf(d, &intpart);

cout << "Fractional part: " << fracpart;

cout << "\n";

cout << "Integer part: " << intpart;

}

int main()

{

myfunc(1);

myfunc(12.2);

return 0;

}

Generic classes

An Example with Two Generic Data Types

A template class can have more than one generic data type. Simply declare all the data types required by the class in a comma-separated list within the template specification. For example, the following short example creates a class that uses two generic data types.

/* This example uses two generic data types in a

class definition.

*/

#include

using namespace std;

template 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 ob1(10, 0.23);

myclass ob2('X', "Templates add power.")

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

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

return 0;

}

This program produces the following output:

10 0.23

X Templates add power.

The program declares two types of objects. ob1 uses int and double data. ob2 uses a character and a character pointer. For both cases, the compiler automatically generates the appropriate data and functions to accommodate the way the objects are created.

UNIT – IV

Handling

Exception handling fundamentals:

Programmers commit several errors at the time of development of an application. These errors are also called ‘bugs’ and the process of removing them is called ‘debugging’. Let us take a look at different types of errors that are possible in a program.

There are three types of Errors

· Compile-time errors: compile-time errors are errors which occurred at the time of compilation of a program. Compile time errors are syntactical errors found in the code, for example, forgetting a semicolon at the end of the statement. These errors are shown by the compiler at the time of compilation

· Run-time Errors: runtime errors are errors which are occurred during the program execution, for example if you want to store the result of division of a number by zero into an int varibale will cause a run time error, because an int variable can’t store an infinite value.

· Logical errors: logical errors are not detectd either by Java compiler or by JVM. If a Logical error occurs the program will not terminate in the middle of execution but the output will be wrong.

What are the five keywords which are used to manage exception handling?

1. try

2. catch

3. finally

4. throw

5. throws

· The try block: Try block is used to place (put) the statements where (in which statements) there is a chance to occur runtime errors. Every try block must precede either catch block or finally block i.e. after a try block you must write either catch block or finally block. One try block may contain another try block such type of try block are called nested try blocks

Note: for a try block we can write any number of catch blocks but we can write only one finally block

· The catch block: It is used to catch the exception thrown by the Java Runtime Environment, catch block must follow either a try block or a catch block, we can write any number of catch blocks for a try block.

· The finally block: It is used to write(place) compulsorily executable statements, like close the streams, close the database connections etc. JavaRuntimeEnvironment always executes this block either exception handled or not or occurred or not.

· The throw keyword: This key word is used to throw the exceptions manually. Moreover throw keyword can also be used to pass a custom message to the exception handling module.

· The throws keyword: throws clause is used to throw any exception out of a method

Exceptions that can be thrown by the monitored code are caught by a catch

statement, which immediately follows the try statement in which the exception was thrown. The general form of try and catch are shown here.

try {

// try block

}

catch (type1 arg) {

// catch block

}

catch (type2 arg) {

// catch block

}

catch (type3 arg) {

// catch block

}

..

.

catch (typeN arg) {

// catch block

}

The general form of the throw statement is shown here:

throw exception;

Here is a simple example that shows the way C++ exception handling operates.

// A simple exception handling example.

#include

using namespace std;

int main()

{

cout << "Start\n";

try { // start a try block

cout << "Inside try block\n";

throw 100; // throw an error

cout << "This will not execute";

}

catch (int i) { // catch an error

cout << "Caught an exception -- value is: ";

cout << i << "\n";

}

cout << "End";

return 0;

}

This program displays the following output:

Start

Inside try block

Caught an exception -- value is: 100

End

Catching Class Types:

· An exception can be of any type, including class types that you create.

Actually, in real-world programs, most exceptions will be class types rather than built-in types.

· Perhaps the most common reason that you will want to define a class type for an exception is to create an object that describes the error that occurred.

· This information can be used by the exception handler to help it process the error.

· The following example demonstrates this.

// Catching class type exceptions.

#include

#include

using namespace std;

class MyException {

public:

char str_what[80];

int what;

MyException() { *str_what = 0; what = 0; }

MyException(char *s, int e) {

strcpy(str_what, s);

what = e;

}

};

int main()

{

int i;

try {

cout << "Enter a positive number: ";

cin >> i;

if(i<0)

throw MyException("Not Positive", i);

}

catch (MyException e) { // catch an error

cout << e.str_what << ": ";

cout << e.what << "\n";

}

return 0;

}

Here is a sample run:

Enter a positive number: -4

Not Positive: -4

Using Multiple catch Statements

As stated, you can have more than one catch associated with a try. In fact, it is common to do so. However, each catch must catch a different type of exception.

For example this program catches both integers and strings.

#include

using namespace std;

// Different types of exceptions can be caught.

void Xhandler(int test)

{

try{

if(test) throw test;

else throw "Value is zero";

}

catch(int i) {

cout << "Caught Exception #: " << i << '\n';

}

catch(const char *str) {

cout << "Caught a string: ";

cout << str << '\n';

}

}

int main()

{

cout << "Start\n";

Xhandler(1);

Xhandler(2);

Xhandler(0);

Xhandler(3);

cout << "End";

return 0;

}

This program produces the following output:

Start

Caught Exception #: 1

Caught Exception #: 2

Caught a string: Value is zero

Caught Exception #: 3

End

Handling Derived-Class Exceptions:

You need to be careful how you order your catch statements when trying to catch

exception types that involve base and derived classes because a catch clause for a base class will also match any class derived from that base. Thus, if you want to catch exceptions of both a base class type and a derived class type, put the derived class first in the catch sequence. If you don't do this, the base class catch will also catch all derived classes.

For example, consider the following program.

// Catching derived classes.

#include

using namespace std;

class B {

};

class D: public B {

};

int main()

{

D derived;

try {

throw derived;

}

catch(B b) {

cout << "Caught a base class.\n";

}

catch(D d) {

cout << "This won't execute.\n";

}

return 0;

}

Here, because derived is an object that has B as a base class, it will be caught by the first catch clause and the second clause will never execute. Some compilers will flag this condition with a warning message. Others may issue an error. Either way, to fix this condition, reverse the order of the catch clauses.

Catching All Exceptions

In some circumstances you will want an exception handler to catch all exceptions

instead of just a certain type. This is easy to accomplish. Simply use this form of catch.

catch(...) {

// process all exceptions

}

Here, the ellipsis matches any type of data. The following program illustrates catch(...).

// This example catches all exceptions.

#include

using namespace std;

void Xhandler(int test)

{

try{

if(test==0) throw test; // throw int

if(test==1) throw 'a'; // throw char

if(test==2) throw 123.23; // throw double

}

catch(...) { // catch all exceptions

cout << "Caught One!\n";

}

}

int main()

{

cout << "Start\n";

Xhandler(0);

Xhandler(1);

Xhandler(2);

cout << "End";

return 0;

}

This program displays the following output.

Start

Caught One!

Caught One!

Caught One!

End

As you can see, all three throws were caught using the one catch statement.

One very good use for catch(...) is as the last catch of a cluster of catches. In this

capacity it provides a useful default or "catch all" statement.

Restricting Exceptions:

You can restrict the type of exceptions that a function can throw outside of itself. In fact, you can also prevent a function from throwing any exceptions whatsoever. To accomplish these restrictions, you must add a throw clause to a function definition. The general form of this is shown here:

ret-type func-name(arg-list) throw(type-list)

{

// ...

}

Attempting to throw an exception that is not supported by a function will cause the

standard library function unexpected() to be called. By default, this causes abort() to be called, which causes abnormal program termination

// Restricting function throw types.

#include

using namespace std;

// This function can only throw ints, chars, and doubles.

void Xhandler(int test) throw(int, char, double)

{

if(test==0) throw test; // throw int

if(test==1) throw 'a'; // throw char

if(test==2) throw 123.23; // throw double

}

int main()

{

cout << "start\n";

try{

Xhandler(0); // also, try passing 1 and 2 to Xhandler()

}

catch(int i) {

cout << "Caught an integer\n";

}

catch(char c) {

cout << "Caught char\n";

}

catch(double d) {

cout << "Caught double\n";

}

cout << "end";

return 0;

}

Rethrowing an Exception:

If you wish to rethrow an expression from within an exception handler, you may do so by calling throw, by itself, with no exception. This causes the current exception to be passed on to an outer try/catch sequence. The most likely reason for doing so is to allow multiple handlers access to the exception When you rethrow an exception, it will not be recaught by the same catch statement. It will propagate outward to the next catch statement.

The following program illustrates rethrowing an exception, in this case a char * exception.

// Example of "rethrowing" an exception.

#include

using namespace std;

void Xhandler()

{

try {

throw "hello"; // throw a char *

}

catch(const char *) { // catch a char *

cout << "Caught char * inside Xhandler\n";

throw ; // rethrow char * out of function

}

}

Note

int main()

{

cout << "Start\n";

try{

Xhandler();

}

catch(const char *) {

cout << "Caught char * inside main\n";

}

cout << "End";

return 0;

}

This program displays this output:

Start

Caught char * inside Xhandler

Caught char * inside main

End

The uncaught_exception( ) Function:

The C++ exception handling subsystem supplies one other function that you may find useful: uncaught_exception() . Its prototype is shown here:

bool uncaught_exception( );

This function returns true if an exception has been thrown but not yet caught. Once caught, the function returns false.

C++ I/O system basics:

The C++ Stream Classes:

4

As mentioned, Standard C++ provides support for its I/O system in . In

this header, a rather complicated set of class hierarchies is defined that supports I/O operations. The I/O classes begin with a system of template classes. Standard C++ creates two specializations of the I/O template classes: one for 8-bit characters and another for wide characters. The class hierarchy that you will most commonly be working with is derived from basic_ios. This is a high-level I/O class that provides formatting, error checking, and status information related to stream I/O. (A base class for basic_ios is called ios_base, which defines several nontemplate traits used by basic_ios.) basic_ios is used as a base for several derived classes, including basic_istream, basic_ostream, and basic_iostream. These classes are used to create streams capable of input, output, and input/output, respectively.

Standard C++ also defines these four additional streams: win, wout, werr, and

wlog. These are wide-character versions of the standard streams. Wide characters are of type wchar_t and are generally 16-bit quantities. Wide characters are used to hold the large character sets associated with some human languages

Formatting Using the ios Members:

Each stream has associated with it a set of format flags that control the way

information is formatted. The ios class declares a bitmask enumeration called fmtflags in which the following values are defined. (Technically, these values are defined within ios_base, which, as explained earlier, is a base class for ios.)

These values are used to set or clear the format flags. If you are using an older

compiler, it may not define the fmtflags enumeration type. In this case, the format flags will be encoded into a long integer. When the skipws flag is set, leading white-space characters (spaces, tabs, and newlines) are discarded when performing input on a stream. When skipws is cleared, white-space characters are not discarded. When the left flag is set, output is left justified. When right is set, output is right justified. Setting the oct flag causes output to be displayed in octal. Setting the hex flag causes output to be displayed in hexadecimal. To return output to decimal, set the dec flag. Setting showpos causes a leading plus sign to be displayed before positive values. Setting showpoint causes a decimal point and trailing zeros to be displayed for all floating-point output—whether needed or not.

By setting the scientific flag, floating-point numeric values are displayed using

scientific notation. When fixed is set, floating-point values are displayed using normal notation. Since it is common to refer to the oct, dec, and hex fields, they can be collectively referred to as basefield. Similarly, the left, right, and internal fields can be referred to as adjustfield. Finally, the scientific and fixed fields can be referenced as floatfield.

Setting the Format Flags:

To set a flag, use the setf() function. This function is a member of ios. Its most common form is shown here:

fmtflags setf(fmtflags flags);

This function returns the previous settings of the format flags and turns on those flags specified by flags. For example, to turn on the showpos flag, you can use this statement: stream.setf(ios::showpos);

Here, stream is the stream you wish to affect. Notice the use of ios:: to qualify showpos. Since showpos is an enumerated constant defined by the ios class, it must be qualified by ios when it is used. The following program displays the value 100 with the showpos and showpoint flags turned on.

#include

using namespace std;

int main()

{

cout.setf(ios::showpoint);

cout.setf(ios::showpos);

cout << 100.0; // displays +100.0

return 0;

}

It is important to understand that setf() is a member function of the ios class and

affects streams created by that class. Therefore, any call to setf() is done relative to a specific stream. There is no concept of calling setf() by itself. Put differently, there is no concept in C++ of global format status. Each stream maintains its own format status information individually. Although there is nothing technically wrong with the preceding program, there is a more efficient way to write it. Instead of making multiple calls to setf() , you can simply OR together the values of the flags you want set. For example, this single call accomplishes the same thing:

// You can OR together two or more flags, cout.setf(ios::showpoint | ios::showpos);

Clearing Format Flags complement of setf() is unsetf() . This member function of ios is used to clear one or more format flags. Its general form is

void unsetf(fmtflags flags);

The flags specified by flags are cleared. (All other flags are unaffected.) The previous flag settings are returned. The following program illustrates unsetf() . It first sets both the uppercase and scientific flags. It then outputs 100.12 in scientific notation. In this case, the "E" used in the scientific notation is in uppercase. Next, it clears the uppercase flag and again outputs 100.12 in scientific notation, using a lowercase "e."

#include

using namespace std;

int main()

{

cout.setf(ios::uppercase | ios::scientific);

cout << 100.12; // displays 1.0012E+02

cout.unsetf(ios::uppercase); // clear uppercase

cout << " \n" << 100.12; // displays 1.0012e+02

return 0;

}

Using width( ), precision( ), and fill( ):

In addition to the formatting flags, there are three member functions defined by ios

that set these format parameters: the field width, the precision, and the fill character. The functions that do these things are width() , precision() , and fill() , respectively. Each is examined in turn. By default, when a value is output, it occupies only as much space as the number of characters it takes to display it. However, you can specify a minimum field width by using the width() function.

Its prototype is shown here: streamsize width(streamsize w);

Here, w becomes the field width When outputting floating-point values, you can determine the number of digits to be displayed after the decimal point by using the precision() function. Its prototype is shown here:

streamsize precision(streamsize p)

Here, the precision is set to p, and the old value is returned. The default precision is 6. In some implementations, the precision must be set before each floating-point output. By default, when a field needs to be filled, it is filled with spaces. You can specify the fill character by using the fill() function. Its prototype is char fill(char ch);

After a call to fill() , ch becomes the new fill character, and the old one is returned.

Here is a program that illustrates these functions:

#include

using namespace std;

int main()

{

cout.precision(4) ;

cout.width(10);

cout << 10.12345 << "\n"; // displays 10.12

cout.fill('*');

cout.width(10);

cout << 10.12345 << "\n"; // displays *****10.12

// field width applies to strings, too

cout.width(10);

cout << "Hi!" << "\n"; // displays *******Hi!

cout.width(10);

cout.setf(ios::left); // left justify

cout << 10.12345; // displays 10.12*****

return 0;

}

This program's output is shown here:

10.12

*****10.12

*******Hi!

10.12*****

There are overloaded forms of width() , precision() , and fill() that obtain but do

not change the current setting. These forms are shown here:

char fill( );

streamsize width( );

streamsize precision( );

Using Manipulators to Format I/O:

The second way you can alter the format parameters of a stream is through the use of special functions called manipulators that can be included in an I/O expression.

many of the I/O manipulators parallel member functions of the ios class. Many of the manipulators were added recently to C++ and will not be supported by older

compilers.

Here is an example that uses some manipulators:

#include

#include

using namespace std;

int main()

{

cout << hex << 100 << endl;

cout << setfill('?') << setw(10) << 2343.0;

return 0;

}

This displays

64

??????2343

Here is an example that uses some manipulators:

#include

#include

using namespace std;

int main()

{

cout << hex << 100 << endl;

cout << setfill('?') << setw(10) << 2343.0;

return 0;

}

This displays

64

??????2343

One of the more interesting manipulators is boolapha. It allows true and false

values to be input and output using the words "true" and "false" rather than numbers.

For example

#include

using namespace std;

int main()

{

bool b;

b = true;

cout << b << " " << boolalpha << b << endl;

cout << "Enter a Boolean value: ";

cin >> boolalpha >> b;

cout << "Here is what you entered: " << b;

return 0;

}

Here is a sample run.

1 true

Enter a Boolean value: false

Here is what you entered: false

C++ File I/O:

Opening and closing a file:

In C++, you open a file by linking it to a stream. Before you can open a file, you must first obtain a stream. There are three types of streams: input, output, and input/output. To create an input stream, you must declare the stream to be of class ifstream. To create an output stream, you must declare it as class ofstream. Streams that will be performing both input and output operations must be declared as class fstream. For example, this fragment creates one input stream, one output stream, and one stream capable of both input and output:

ifstream in; // input

ofstream out; // output

fstream io; // input and output

Once you have created a stream, one way to associate it with a file is by using

open() . This function is a member of each of the three stream classes. The prototype for each is shown here:

void ifstream::open(const char *filename, ios::openmode mode = ios::in);

void ofstream::open(const char *filename, ios::openmode mode = ios::out | ios::trunc);

void fstream::open(const char *filename, ios::openmode mode = ios::in | ios::out);

openmode, which is an enumeration defined by ios (through its base class ios_base).

ios::app

ios::ate

ios::binary

ios::in

ios::out

ios::trunk

The following fragment opens a normal output file.

ofstream out;

out.open("test", ios::out);

Reading and Writing Text Files:

It is very easy to read from or write to a text file. Simply use the << and >> operators the same way you do when performing console I/O, except that instead of using cin and cout, substitute a stream that is linked to a file. For example, this program creates a short inventory file that contains each item's name and its cost:

#include

#include

using namespace std;

int main()

{

ofstream out("INVNTRY"); // output, normal file

if(!out) {

cout << "Cannot open INVENTORY file.\n";

return 1;

}

out << "Radios " << 39.95 << endl;

out << "Toasters " << 19.95 << endl;

out << "Mixers " << 24.80 << endl;

out.close();

return 0;

}

In a way, reading and writing files by using >> and << is like using the C-based

functions fprintf() and fscanf() functions. All information is stored in the file in the same format as it would be displayed on the screen.

Unformatted and Binary I/O:

While reading and writing formatted text files is very easy, it is not always the most efficient way to handle files. Also, there will be times when you need to store

unformatted (raw) binary data, not text. The functions that allow you to do this are

described here. When performing binary operations on a file, be sure to open it using the ios::binary mode specifier. Although the unformatted file functions will work on files opened for text mode, some character translations may occur. Character translations negate the purpose of binary file operations.

Characters vs. Bytes

Before beginning our examination of unformatted I/O, it is important to clarify an

important concept. For many years, I/O in C and C++ was thought of as byte oriented. This is because a char is equivalent to a byte and the only types of streams available were char streams.

put( ) and get( ):

One way that you may read and write unformatted data is by using the member

functions get() and put() . These functions operate on characters. That is, get() will

read a character and put() will write a character. Of course, if you have opened the file for binary operations and are operating on a char (rather than a wchar_t stream), then these functions read and write bytes of data. The get() function has many forms, but the most commonly used version is shown

here along with put() :

istream &get(char &ch);

ostream &put(char ch);

#include

#include

using namespace std;

int main(int argc, char *argv[])

{

char ch;

if(argc!=2) {

cout << "Usage: PR \n";

return 1;

}

ifstream in(argv[1], ios::in | ios::binary);

if(!in) {

cout << "Cannot open file.";

return 1;

}

while(in) { // in will be false when eof is reached

in.get(ch);

if(in) cout << ch;

}

return 0;

}

read( ) and write( ):

Another way to read and write blocks of binary data is to use C++'s read() and write() functions. Their prototypes are

istream &read(char *buf, streamsize num);

ostream &write(const char *buf, streamsize num);

The read() function reads num characters from the invoking stream and puts them in the buffer pointed to by buf. The write() function writes num characters to the invoking stream from the buffer pointed to by buf

#include

#include

#include

using namespace std;

struct status {

char name[80];

double balance;

unsigned long account_num;

};

int main()

{

struct status acc;

strcpy(acc.name, "Ralph Trantor");

acc.balance = 1123.23;

acc.account_num = 34235678;

// write data

ofstream outbal("balance", ios::out | ios::binary);

if(!outbal) {

cout << "Cannot open file.\n";

return 1;

}

outbal.write((char *) &acc, sizeof(struct status));

outbal.close();

// now, read back;

ifstream inbal("balance", ios::in | ios::binary);

if(!inbal) {

cout << "Cannot open file.\n";

return 1;

}

inbal.read((char *) &acc, sizeof(struct status));

cout << acc.name << endl;

cout << "Account # " << acc.account_num;

cout.precision(2);

cout.setf(ios::fixed);

cout << endl << "Balance: $" << acc.balance;

inbal.close();

return 0;

}

getline( ):

Another function that performs input is getline() . It is a member of each input stream class. Its prototypes are shown here:

istream &getline(char *buf, streamsize num);

istream &getline(char *buf, streamsize num, char delim);

The first form reads characters into the array pointed to by buf until either num−1

characters have been read, a newline character has been found, or the end of the file has been encountered. The array pointed to by buf will be null terminated by getline() .

// Read and display a text file line by line.

#include

#include

using namespace std;

int main(int argc, char *argv[])

{

if(argc!=2) {

cout << "Usage: Display \n";

return 1;

}

ifstream in(argv[1]); // input

if(!in) {

cout << "Cannot open input file.\n";

return 1;

}

char str[255];

while(in) {

in.getline(str, 255); // delim defaults to '\n'

if(in) cout << str << endl;

}

in.close();

return 0;

}

Detecting EOF:

You can detect when the end of the file is reached by using the member function eof(), which has this prototype: bool eof( ); It returns true when the end of the file has been reached; otherwise it returns false. The following program uses eof() to display the contents of a file in both hexadecimal and ASCII Random Access In C++'s I/O system, you perform random access by using the seekg() and seekp()

functions. Their most common forms are

istream &seekg(off_type offset, seekdir origin);

ostream &seekp(off_type offset, seekdir origin);

Here, off_type is an integer type defined by ios that is capable of containing the largest valid value that offset can have. seekdir is an enumeration defined by ios that determines how the seek will take place. The seekg() function moves the associated file's current get pointer offset number of characters from the specified origin, which must be one of these three values:

ios::beg Beginning-of-file

ios::cur Current location

ios::end End-of-file

#include

#include

#include

using namespace std;

int main(int argc, char *argv[])

{

if(argc!=4) {

cout << “Usage: CHANGE \n”;

return 1;

}

fstream out(argv[1], ios::in | ios::out | ios::binary);

if(!out) {

cout << “Cannot open file.”;

return 1;

}

out.seekp(atoi(argv[2]), ios::beg);

out.put(*argv[3]);

out.close();

return 0;

}

Obtaining the Current File Position:

You can determine the current position of each file pointer by using these functions:

pos_type tellg( );

pos_type tellp( );

Here, pos_type is a type defined by ios that is capable of holding the largest value that either function can return. You can use the values returned by tellg() and tellp() as arguments to the following forms of seekg() and seekp() , respectively.

istream &seekg(pos_type pos);

ostream &seekp(pos_type pos);

These functions allow you to save the current file location, perform other file

operations, and then reset the file location to its previously saved location.

Introducing the standard Template Library:

Containers:

Containers are objects that hold other objects, and there are several different types. For example, the vector class defines a dynamic array, deque creates a double-ended queue, and list provides a linear list. These containers are called sequence containers because in STL terminology, a sequence is a linear list. In addition to the basic containers, the STL also defines associative containers, which allow efficient retrieval of values based on keys. For example, a map provides access to values with unique keys. Thus, a map stores a key/value pair and allows a value to be retrieved given its key. Each container class defines a set of functions that may be applied to the container. For example, a list container includes functions that insert, delete, and merge elements. A stack includes functions that push and pop values.

Algorithms:

Algorithms act on containers. They provide the means by which you will manipulate the contents of containers. Their capabilities include initialization, sorting, searching, and transforming the contents of containers. Many algorithms operate on a range of elements within a container

Iterators:

Iterators are objects that are, more or less, pointers. They give you the ability to cycle through the contents of a container in much the same way that you would use a pointer to cycle through an array. There are five types of iterators:

.