You are on page 1of 29

UNIT-4 OOP USING C++ Page 1 of 29

1 Operator Overloading: Introduction, operator keyword, overloaded operators,


unary operator overloading, operator return values, binary operators
overloading, overloading with friend function

1.1 Introduction
● The mechanism of giving special meanings to an operator for a data type is known
as operator overloading.
● It allows extending functionality of an existing operator to operate on user defined
data type.
● The operator such as +,-,+=,>,>> etc., are designed to operate only on standard
data types in structure oriented programming like C.
● The + operator can be used to perform the addition operation on integer, floating
point or mixed data types as indicated in the expression(a+b).
● In this expression, the data type of the operand a and b on which the + operator is
operating, is not mentioned explicitly.
● In such cases, the compiler implicitly selects suitable addition operation depending
on the data type of operands without any assistance from the programmer.
● In C++, such operator can also be overloaded explicitly to operate on operands of
user defined data types.
● For instance, the statement
C3=AddComplex(c1,c2);
Performs the addition of operand c1 and c2 belonging to the user defined data type
and assign the result to c3.
● In C++, by overloading operator, the above statement can be changed to an easily
readable form:
C3 = c1 + c2;
● Operator overloading, thus allows to provide additional meaning to operator such
as +,*,>=,+=, etc., when they are applied to user defined data types.
● It allows the user to program (develop solution to) the problems as perceived in the
real world.
● The concept of operator overloading can also be applied to data conversion. C++
offers automatic conversion of primitive data types.
● Operator overloading extends the semantics of an operator without changing its
syntax. The grammatical rules defined by C++ that governs its use such as the
UNIT-4 OOP USING C++ Page 2 of 29

number of operands, precedence, and associativity of the operator remain the


same for overloaded operator.
● Therefore, it should be remembered that the overloaded operator should not
change its original meaning. However, meaning can be changed, but it is advisable
to retain the predefined logical meaning.

1.2 Operator keyword


● The keyword operator provides the facility to overloaded the C++ operators. The
operator overloaded in a class is known as overloaded operator
function/Operator function.
● The syntax of the operator keyword is
ReturnType operator OperatorSymbol ( Arguments )
{
//Body of function
}
● Here, ReturnType is function return data type such as primitive, void or user
defined. OperatorSymbol is the C++ operator that can be overloaded. Arguments
are the arguments to the function which can be one or two arguments depend on
overloaded operator (unary or binary).
● Operator function must be either member function or friend function. A basic
difference between them is that a friend function will have only one argument for
unary operator and two for binary operators, while a member function has no
argument for unary operator and only one for binary operators.
● Example:

● Assume that array is a class and following are valid operator function.

● void operator + () { … } //Unary plus

● friend void operator +( array &t) { … } //Unary plus with friend, array is a class

● array operator + () {…} //Unary plus, that return an object of a class array

● void operator + (array &t1){…} //Binary plus

● friend void operator + (array &t1, array &t2){…} //Binary plus with friend

1.3.1 List of Overloaded Operators:

● The following are list of operators that can be overloaded.


Operator Category Operators
UNIT-4 OOP USING C++ Page 3 of 29

Arithmetic +, - , *, /, %
Bit-wise &, |, ~, ^
Logical &&, ||, !
Relational >, <, ==, !=, <=, >=
Assignment =
Arithmetic assignment +=, -=, *=, /=, %=, &=, |=, ^=
Shift <<, >>, <<=, >>=
Unary ++, --
Subscripting []
Function call ()
Dereferencing ->
Unary Sign prefix +, -
Memory allocate and Free new, delete

1.3.2 List of not overloaded Operators:

● The following are list of operators that cannot be overloaded.


Operator Category Operators
Member Access .
Scope Resolution ::
Conditional ?:
Pointer to Member *
Size of data type sizeof()

1.3.3 List of operators that cannot be overloaded with friend function:

Operator Category Operators


Assignment =
Function call ( )
Subscripting [ ]
Class member access ->

1.4 Unary operator overloading


● Overloading without explicit arguments to an operator function is known as unary
operator overloading. But, with the friend function, unary operators take one explicit
argument.
● The syntax of overloading the unary operator is as shown below:
UNIT-4 OOP USING C++ Page 4 of 29

ReturnType operator OperatorSymbol ( )


{
// Body of operator function
}
● Here, ReturnTpe is a primitive data type, void or user-defined data type, operator is
a keyword, OperatorSymbol is the operator that is to be overloaded.
● The unary operators are:
Operator Name Symbol
Unary Plus +
Unary Minus -
Increment ++
Decrement --

● Program: Consider an example of class Index that keeps track of the index value
without using overloaded concept.
#include<iostream.h>
#include<conio.h>

class Index
{
private:
int value;
public:
Index();
void print( );
void nextIndex( );
};
Index :: Index ( )
{
value = 0;
}
void Index :: print ( )
{
cout<<value;
}
void Index :: nextIndex ( )
{
value = value + 1;
}
void main ( )
UNIT-4 OOP USING C++ Page 5 of 29

{
Index i1;
clrscr ( );
cout<<”Value of i1.value=”;
i1.print();
i1.nextIndex( );
cout<<”After function nextIndex() call, Value of i1.value=”;
i1.print();
getch();
}
Output:
Value of i1.value=0
After function nextIndex() call, Value of i1.value=1
● In above program, the function nextIndex() increments the index value. Now, the
function nextIndex() is replaced by operator ++ function using operator overloading
concept. The above program can be rewritten using overloaded increment operator
as follow.

● In the both cases, the output of both the program is same.

● Program: Consider an example of class Index that keeps track of the index value
with using overloaded concept. (using increment operator)

#include<iostream.h>
#include<conio.h>
class Index
{
private:
int value;
public:
Index();
void print( );
void operator ++ ( );
};
Index :: Index ( )
{
value = 0;
}
void Index :: print ( )
{
cout<<value;
}
void Index :: operator ++ ( )
UNIT-4 OOP USING C++ Page 6 of 29

{
value = value + 1;
}
void main ( )
{
Index i1;
clrscr ( );
cout<<”Value of i1.value=”;
i1.print();
i1++; // It is equivalent to i1.operator++();
cout<<”After increment, Value of i1.value=”;
i1.print();
getch();
}
Output:
Value of i1.value=0
After increment, Value of i1.value=1
● In main(), the statement
o i1++ ;
● call the overloaded ++ operator member function defined in the class Index:
o void operator ++ ( )
● The unary increment/decrement operator can be prefix or postfix increment. It
means above program we may write ++i1; instead of i1++; In the both the
increment, output of the program is same.

1.5 Operator return values

● In above program, another way of writing operator++() function is that return data
type is name of a class instead of void.
[1]. Return data type is void [2]. Return data type is class itself

void operator ++() Index operator ++()


{ { Index temp;
value = value + 1; temp.value = value + 1;
return temp;
There is some changes in main() function:
} }
void main()
{
Index i1, i2;
clrscr ( );
cout<<”Value of i1.value=”;
UNIT-4 OOP USING C++ Page 7 of 29

i1.print();
i2 = i1++;
cout<<”After increment, Value of i2.value=”;
i2.print();
getch();
}
● In main() function, the statement
i2 = i1++;
● calls the overloaded operator function and assigns the return value to the object i2
of the class Index. The operator ++() function creates a new object of the class
Index called temp to be used as a return value; it can be assigned to another object
(i2).

1.6 Binary operator overloading


● Overloading with a single explicit argument to an operator function is known as
binary operator overloading. But, with the friend function, binary operators take two
explicit arguments.
● The syntax for overloading binary operator is shown below:

ReturnType operator OperatorSymbol ( Argument )


{
// Body of operator function
}
//Program on Binary operator overloading.
#include<iostream.h>
#include<conio.h>

class test
{
public:
int a;
test operator+(test &t);
};
test test :: operator+(test &t)
{
test temp;
temp.a = a + t.a;
return temp;
}
void main()
{
UNIT-4 OOP USING C++ Page 8 of 29

test t1,t2,t3;
clrscr();
cout<<"Enter a for object t1 = ";
cin>>t1.a;
cout<<"Enter a for object t2 = ";
cin>>t2.a;
t3 = t1 + t2;
cout<<endl<<"For t1 object Value of a = "<<t1.a;
cout<<endl<<"For t2 object Value of a = "<<t2.a;
cout<<endl<<"Value Sum of t1 and t2 object = "<<t3.a;
getch();
}
Output:
Enter a for object t1 = 2
Enter a for object t2 = 3
For t1 object value of a = 2
For t2 object value of a = 3
Value sum of t1 and t2 object = 5

1.7 Overloading with friend function


● The syntax of operator overloading with friend functions is
friend ReturnDataType operator OperatorSymbol ( Arguments )
{
// Body of function
}
● The friend function must be prefix with keyword friend inside the class.

● The friend function outside the body of a class is defined as a normal function
without prefix friend keyword.
● Friend functions can either be used with unary (One Argument) or binary operators
(Two Arguments).
● Friend function is not allowed to access members of a class (to which it is defined)
directly, but it can access all the members including the private members by using
object of that class.
● The difference between friend and member function is that, the friend function
requires the arguments to be explicitly passed to the function and processed them
explicitly whereas member function consider the first argument implicitly.

● Program on Unary Operator overloading using friend function:


#include<iostream.h>
#include<conio.h>
UNIT-4 OOP USING C++ Page 9 of 29

class Index
{
private:
int value;
public:
Index();
void print( );
friend void operator ++ (Index &t );
};
Index :: Index ( )
{
value = 0;
}
void Index :: print ( )
{
cout<<value;
}
void operator ++ (Index &t )
{
t.value = t.value + 1;
}
void main ( )
{
Index i1;
clrscr ( );
cout<<”Value of i1.value=”;
i1.print();
i1++; // It is equivalent to i1.operator++();
cout<<”After increment, Value of i1.value=”;
i1.print();
getch();
}
Output:
Value of i1.value=0
After increment, Value of i1.value=1
● //Program based on Binary operator overloading using friend function.
#include<iostream.h>
#include<conio.h>
class test
{
public:
int a;
friend test operator+(test &t1, test &t2);
};
UNIT-4 OOP USING C++ Page 10 of 29

test operator+(test &t1, test &t2)


{
test temp;
temp.a = t1.a + t2.a;
return temp;
}
void main()
{
test t1,t2,t3;
clrscr();
cout<<"Enter a for object t1 = ";
cin>>t1.a;
cout<<"Enter a for object t2 = ";
cin>>t2.a;
t3 = t1 + t2;
cout<<endl<<"For t1 object Value of a = "<<t1.a;
cout<<endl<<"For t2 object Value of a = "<<t2.a;
cout<<endl<<"Value Sum of t1 and t2 object = "<<t3.a;
getch();
}
Output:
Enter a for object t1 = 2
Enter a for object t2 = 3
For t1 object value of a = 2
For t2 object value of a = 3
Value sum of t1 and t2 object = 5

1.8 Rules for operator overloading:

● There are some rules for writing overloading operator. They are
1. Only existing operators can be overloaded. New operators cannot be created.
2. The overloaded operator must have at least one operand that is of user defined
type.
3. We can not change the basic meaning of an operator. That is to say, we can not
redefine the plus (+) operator to subtract one value from the other.

4. Overloaded operator follow the syntax rules of the original operators. They cannot
be overridden.
5. There are some operators that cannot be overloaded. (On Page No. 2)
6. We cannot use friend function to overloaded certain operators. However, member
function can be used them. (On Page No. 2)
7. Unary operator overloaded through a member function with no argument and those
which are overloaded through a friend function with one argument.
UNIT-4 OOP USING C++ Page 11 of 29

8. Binary operator overloaded through a member function with one argument and
those which are overloaded through a friend function with two arguments.

2 Usage of pointer in c++

2.1 Usage of pointer in C++

● Such variables that hold memory address are called pointers.

● A pointer is a variable that contains an address which is a location of another


variable in memory.
● A pointer is a data type that holds the address of a location in memory.

2.2 Declaring And Initialization of Pointers

GENERAL FORM :-
Data type *pt_names;

int *p; // Declaration

● This will declares the variables p as a pointer variable that points to an integer data
type.
Float *x;
● declares x as a pointer to a floating point variable.
P =&quantity;
● It means now p contain the address of quantity
Float a, b ;
int x, *p;
p= &a;
b= *p;
● This will result in erroneous output because we trying to assign the address of a
float variable to an integer pointer.
● The compiler will not detect such errors, care should be taken by the programmer.

int *ptr;
ptr= 5368;
● Assigning an absolute address to a pointer variable is invalid. A pointer variable
can be initialized in its declaration itself.
UNIT-4 OOP USING C++ Page 12 of 29

int x, *p = &x; // Valid


int *p = &x, x; // Invalid

2.3 Accessing variables using Pointers :-

void main( )
{
int x , y;
int *ptr ;
x =10;
ptr = &x;
y =*ptr;

printf(“ Value of x is %d \n”,x);


printf(“ %d is stored at addr %u \n” ,x, &x);
printf(“ %d is stored at addr %u \n” ,*&x, &x);
printf(“ %d is stored at addr %u \n” ,*ptr, ptr);
printf(“ %d is stored at addr %u \n” ,y, &*ptr);
printf(“ %d is stored at addr %u \n” ,ptr, &ptr);
printf(“ %d is stored at addr %u \n” ,y, &y);

*ptr = 25;
printf(“ \n Now X= %d \n “,x);
}

output:-
value of x is 10
10 is stored at addr 4104
10 is stored at addr 4104
10 is stored at addr 4104
10 is stored at addr 4104
4104 is stored at addr 4106
10 is stored at addr 4108
now x=25

2.4 Accessing variables using Pointers :-

● If p1 and p2 are properly declared and initialized pointers then, the following
statements are valid.
Y= *p1 * *p2;
Same as (*p1) * (*p2)
Sum=sum + *p1;
Z= 5*- *p2/*p1;
UNIT-4 OOP USING C++ Page 13 of 29

Same as (5 *(-(*p2)))/(*p1)
*p2 = *p2 + 10;

● ‘c’ and ‘c++’ allows us to add integers to or subtract integers from pointers, as well
as to subtract one pointer from another.
P1 + 4, p2 – 2 and p1- p2
● We may also short-hand operators with the pointers.
P1+ +;
- - p2;
sum + =*p2;

● Pointers can also be compared using the relational operators.


P1> p2, p1 = = p2, and p1 !=p2.
● We may not use pointers in division or multiplication.
P1/p2 or p1*p2 or p1/3
Void main( )
{
int a, b, *p1, 8p2, x, y, z;
a=12;
b=4;
p1=&a;
p2=&b;

x = *p1 * *p2 – 6;
y = 4* - *p2 / *p1 + 10;
printf(“Address of a= %u \n”,p1);
printf(“Address of b= %u \n”,p2);
printf(“ a= %d b = %d \n” ,a ,b);
printf(“ x= %d y =%d \n” ,x ,y);

*p2 = *p2 + 3 ;
*p1 = *p2 – 5;
z= *p1 * *p2- 6;

printf(“ a= %d b = %d ” ,a ,b);
printf(“ z= %d \n” ,z);
}

output:-
Address of a = 4020
Address of a = 4016
a = 12, b = 4
UNIT-4 OOP USING C++ Page 14 of 29

x = 42, y = 9
a = 2, b =7, z=8

2.5 Arrays of Pointers


Pointers may be arrayed like any other data type. The declaration for an int pointer
array of size 10 is
int *x[10];
To assign the address of an integer variable called var to the third element of the
pointer array, write
x[2] = &var;
To find the value of var, write
*x[2]
If you want to pass an array of pointers into a function, you can use the same
method that you use to pass other arrays—simply call the function with the array
name without any indexes. For example, a function that can receive array x looks
like this:

void display_array(int *q[])


{
int t;
for(t=0; t<10; t++)
printf("%d ", *q[t]);
}

Remember, q is not a pointer to integers, but rather a pointer to an array of pointers to


integers. Therefore you need to declare the parameter q as an array of integer pointers,
as just shown. You cannot declare q simply as an integer pointer because that is not what
it is.
Pointer arrays are often used to hold pointers to strings. You can create a function
that outputs an error message given its code number, as shown here:
void syntax_error(int num)
{
static char *err[] = {
"Cannot Open File\n",
"Read Error\n",
"Write Error\n",
"Media Failure\n"
};
cout<<err[num]
}

The array err holds pointers to each string. As you can see, printf( ) inside
syntax_error( ) is called with a character pointer that points to one of the various
error messages indexed by the error number passed to the function. For example,
if num is passed a 2, the message Write Error is displayed.

2.6 Pointers to Objects


Just as you can have pointers to other types of variables, you can have pointers to
UNIT-4 OOP USING C++ Page 15 of 29

objects. When accessing members of a class given a pointer to an object, use the arrow
(–>) operator instead of the dot operator. The next program illustrates how to access an
object given a pointer to it:

#include <iostream.h>

class cl {
int i;
public:
cl(int j) { i=j; }
int get_i() { return i; }
};

int main()
{
cl ob(88), *p;
p = &ob; // get address of ob
cout << p->get_i(); // use -> to call get_i()
return 0;
}

As you know, when a pointer is incremented, it points to the next element of its type.
For example, an integer pointer will point to the next integer. In general, all pointer
arithmetic is relative to the base type of the pointer. (That is, it is relative to the type of
data that the pointer is declared as pointing to.) The same is true of pointers to objects.

For example, this program uses a pointer to access all three elements of array ob after
being assigned ob's starting address:

#include <iostream.h>
class cl {
int i;
public:
cl() { i=0; }
cl(int j) { i=j; }
int get_i() { return i; }
};
int main()
{
cl ob[3] = {1, 2, 3};

cl *p;
int i;
p = ob; // get start of array
for(i=0; i<3; i++) {
cout << p->get_i() << "\n";
p++; // point to next object
}
return 0;
}
UNIT-4 OOP USING C++ Page 16 of 29

You can assign the address of a public member of an object to a pointer and then access
that member by using the pointer. For example, this is a valid C++ program
that displays the number 1 on the screen:

#include <iostream.h>
class cl {
public:
int i;
cl(int j) { i=j; }
};
int main()
{
cl ob(1);
int *p;
p = &ob.i; // get address of ob.i
cout << *p; // access ob.i via p
return 0;
}

Because p is pointing to an integer, it is declared as an integer pointer. It is irrelevant


that i is a member of ob in this situation.

2.7.1 Dynamic Allocation Operators


C++ provides two dynamic allocation operators: new and delete. These operators are
used to allocate and free memory at run time. Dynamic allocation is an important part of
almost all real-world programs. C++ also supports dynamic memory allocation functions,
called malloc( ) and free( ). These are included for the sake of compatibility with C.
However, for C++ code, you should use the new and delete operators because they have
several advantages.
The new operator allocates memory and returns a pointer to the start of it. The delete
operator frees memory previously allocated using new. The general forms of
new and delete are shown here:

p_var = new type;


delete p_var;

Here, p_var is a pointer variable that receives a pointer to memory that is large enough to
hold an item of type type.Since the heap is finite, it can become exhausted. If there is
insufficient available memory to fill an allocation request, then new will fail and a
bad_alloc exception will be generated. This exception is defined in the header <new>.
Your program should handle this exception and take appropriate action if a failure occurs.
Finally, it was decided that a new failure will generate an exception by default, but that a
null pointer could be returned instead, as an option. Thus, new has been implemented
differently, at different times, by compiler manufacturers.

#include <iostream.h>

void main()
{
int *p;
p = new int; // allocate space for an int
*p = 100;
UNIT-4 OOP USING C++ Page 17 of 29

cout << "At " << p << " ";


cout << "is the value " << *p << "\n";
}

This program assigns to p an address in the heap that is large enough to hold an
integer. It then assigns that memory the value 100 and displays the contents of the
memory on the screen.
The delete operator must be used only with a valid pointer previously allocated by
using new. Using any other type of pointer with delete is undefined and will almost
certainly cause serious problems, such as a system crash. Although new and delete
perform functions similar to malloc( ) and free( ), they have several advantages.
● First, new automatically allocates enough memory to hold an object of the specified
type. You do not need to use the sizeof operator. Because the size is computed
automatically, it eliminates any possibility for error in this regard.
● Second, new automatically returns a pointer of the specified type. You don't need
to use an explicit type cast as you do when allocating memory by using malloc( ).
Finally, both new and delete can be overloaded, allowing you to create customized
allocation systems.

You can initialize allocated memory to some known value by putting an initializer
after the type name in the new statement. Here is the general form of new when an
initialization is included:
p_var = new var_type (initializer);
Of course, the type of the initializer must be compatible with the type of data for which
memory is being allocated.
This program gives the allocated integer an initial value of 87:
#include <iostream.h>

int main()
{
int *p;
p = new int (87); // initialize to 87
cout << "At " << p << " ";
cout << "is the value " << *p << "\n";
delete p;
return 0;
}

Allocating Arrays
You can allocate arrays using new by using this general form:
p_var = new array_type [size];
Here, size specifies the number of elements in the array. To free an array, use this form of
delete:
delete [ ] p_var;
Here, the [ ] informs delete that an array is being released.
For example, the next program allocates a 10-element integer array.
#include <iostream.h>
int main()
{
int *p, i;
UNIT-4 OOP USING C++ Page 18 of 29

p = new int [10]; // allocate 10 integer array


for(i=0; i<10; i++ )
p[i] = i;
for(i=0; i<10; i++)
cout << p[i] << " ";
delete [] p; // release the array
return 0;
}
Notice the delete statement. As just mentioned, when an array allocated by new
is released, delete must be made aware that an array is being freed by using the [ ].
One restriction applies to allocating arrays: They may not be given initial values.
That is, you may not specify an initializer when allocating arrays.

2.7.2 Allocating Objects


You can allocate objects dynamically by using new. When you do this, an object is
created and a pointer is returned to it. The dynamically created object acts just like
any other object. When it is created, its constructor (if it has one) is called. When the
object is freed, its destructor is executed.

Here is a short program that creates a class called balance that links a person's
name with his or her account balance. Inside main( ), an object of type balance is
created dynamically.
#include <iostream.h>
#include <cstring.h>

class balance {
double cur_bal;
char name[80];
public:
void set(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};

int main()
{
balance *p;
char s[80];
double n;
p = new balance;
p->set(12387.87, "Ralph Wilson");
p->get_bal(n, s);
cout << s << "'s balance is: " << n;
cout << "\n";
delete p;
return 0;
UNIT-4 OOP USING C++ Page 19 of 29

Because p contains a pointer to an object, the arrow operator is used to access members
of the object. As stated, dynamically allocated objects may have constructors and
destructors. Also, the constructors can be parameterized. Examine this version of the
previous program:
#include <iostream.h>
#include <cstring.h>
class balance {
double cur_bal;
char name[80];
public:
balance(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
~balance() {
cout << "Destructing ";
cout << name << "\n";
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};
int main()
{
balance *p;
char s[80];
double n;
// this version uses an initializer
p = new balance (12387.87, "Ralph Wilson");
p->get_bal(n, s);
cout << s << "'s balance is: " << n;
cout << "\n";
delete p;
return 0;
}
Notice that the parameters to the object's constructor are specified after the type name,
just as in other sorts of initializations.
You can allocate arrays of objects, but there is one catch. Since no array allocated by new
can have an initializer, you must make sure that if the class contains constructors, one will
be parameterless. If you don't, the C++ compiler will not find a matching constructor when
you attempt to allocate the array and will not compile your program. In this version of the
preceding program, an array of balance objects is allocated, and the parameterless
constructor is called.
#include <iostream.h>
#include <cstring.h>
class balance {
double cur_bal;
char name[80];
public:
UNIT-4 OOP USING C++ Page 20 of 29

balance(double n, char *s) {


cur_bal = n;
strcpy(name, s);
}
balance() {} // parameterless constructor
~balance() {
cout << "Destructing ";
cout << name << "\n";
}
void set(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};
int main()
{
balance *p;
char s[80];
double n;
int i;
p = new balance [3]; // allocate entire array
// note use of dot, not arrow operators
p[0].set(12387.87, "Ralph Wilson");
p[1].set(144.00, "A. C. Conners");
p[2].set(-11.23, "I. M. Overdrawn");
for(i=0; i<3; i++) {
p[i].get_bal(n, s);
cout << s << "'s balance is: " << n;
cout << "\n";
}
delete [] p;
return 0;
}

The output from this program is shown here.


Ralph Wilson's balance is: 12387.9
A. C. Conners's balance is: 144
I. M. Overdrawn's balance is: -11.23
Destructing I. M. Overdrawn
Destructing A. C. Conners
Destructing Ralph Wilson

One reason that you need to use the delete [ ] form when deleting an array of
dynamically allocated objects is so that the destructor can be called for each object
in the array.
UNIT-4 OOP USING C++ Page 21 of 29

3 File Handling in C++


3.1 Introduction
▪ A file just a collection of related data that treated by C++ as a series of bytes.
▪ By the way, streams move in one way and in serial fashion from receiver and sender.
▪ Other than files on disk, devices such as magnetic tapes, primary or secondary
storage devices, printers and networks that carrying data are also treated as files.
▪ File I/O is one of the interesting topics in C/C++ because of the wide applications. We
need to write to disk when we do software installation, writing windows registry, read,
write, delete, update file contents, sending and receiving data through networks
(sockets) etc.
▪ The include files needed in order to use the disk file I/O class objects are:
<iostream.h> and <fstream.h>. iostream.h contains standard input output objects
such as cin, cout, cerr and clog (refer to C++ Formatted I/O).
▪ Furthermore same as in C File I/O, all the program examples don’t consider the file
access permissions. Normally, file access permissions are combined with the user
permissions and rights as security features of the Operating Systems.
▪ For C++, all file objects belong to the one of the following classes:

Class Brief description


IfstreamObjects belong to this class are associated with files opened for input
purposes.
Ofstream Objects belong to this class are associated with files opened for output
purposes.
Fstream Objects belong to this class are associated with files opened for input and
output purposes.
[Table: File input/output classes]
▪ These classes are defined in <fstream> header file.


Data files are attached with files objects using the open() member function of the file
object.
▪ The open() prototype is:
void open(const char *name, int mode, int access);
▪ Where:
1. name is the filename.
2. Open file modes (flags) are used to indicate what to do to the file (e.g. open,
close) and the data (e.g. read, write). The flags can be logically ORed. Mode
must be one of the following:
3. File protection [Table: File open modes]
Mode Description
ios::app Append data to the end of the output file.
ios::ate Go to the end of the file when open.
ios::in Open for input, with open() member function of the ifstream variable.
ios::out Open for output, with open() member function of the ofstream
variable.
ios::binary Binary file, if not present, the file is opened as an ASCII file as
UNIT-4 OOP USING C++ Page 22 of 29

default.
ios::trunc Discard contents of existing file when opening for write.
ios::nocreate Fail if the file does not exist. For output file only, opening an input file
always fails if there is no fail.
ios::noreplace Do not overwrite existing file. If a file exists, cause the opening file to
fail.
4. File protection access determines how the file can be accessed. It is Operating
System dependent and there are others attributes that are implementation
extensions. For DOS example, it must be one of the following:

Attributes Description
0 Normal file or Archive
1 Read-only file
2 Hidden file
4 System file
[Table: File types]
▪ To declare the input file stream i.e. for reading we would declare like this:
ifstream theinputfile;
▪ To declare the output file stream i.e. for writing we would declare like this:
ofstream theoutputfile;
▪ Then, to connect to a stream, let say opening file testfileio.dat for reading, the file
which located in the same folder as the executable program we would write like
this:
theinputfile.open("testfileio.dat ");
▪ Or with the path we could write:
theinputfile.open("c:\\testfileio.dat ");
▪ Next, opening a file for reading and binary type modes, we could write:
theinputfile.open("testfileio.dat ", ios::in | ios::binary);
▪ Another example, opening a file for reading, with normal type access and go to the
end of the file, we could write like this:
theinputfile.open("testfileio.dat ", ios::in | ios::ate, 0);
▪ For writing, to connect to a stream, let say opening file testfileio.dat for writing, the
file which located in the same folder as the running program. Previous content of
the testfileio.dat will be overwritten, we could write like this:
theoutputfile.open("testfileio.dat");
▪ Or with the path:
theoutputfile.open("c:\\testfileio.dat ");
▪ Then, opening a file for writing and appending at the end of the file modes, we
could write like this:
theoutputfile.open("testfileio.dat", ios::out | ios::app);
▪ Or opening for writing, check the existing of the file with normal access modes, we
could write like this:
theoutputfile.open("testfileio.dat", ios::out | ios::nocreate, 0);
▪ After we have completed the file processing, we have to close or disconnect the
stream to free up the resources to be used by other processes or programs. Using
the close() member function, for input stream we would write like this:
UNIT-4 OOP USING C++ Page 23 of 29

theinputfile.close();
▪ And for output stream:
theoutputfile.close();
UNIT-4 OOP USING C++ Page 24 of 29

3.2 Checking state flags


During the opening for reading or writing, we should provide error handling routines
to make sure the file operations have completed successfully otherwise some error
message should be displayed or error handling routines been executed.
In addition to good(), which checks whether the stream is ready for input/output
operations, other member functions exist to check for specific states of a stream (all of
them return a bool value)
Some functions depending upon it:
● bad() : Returns true if a reading or writing operation fails. For example in the case
that we try to write to a file that is not open for writing or if the device where we try
to write has no space left.
● fail() : Returns true in the same cases as bad(), but also in the case that a format
error happens, like when an alphabetical character is extracted when we are trying
to read an integer number.
● eof() : Returns true if a file open for reading has reached the end.

● good() : It is the most generic state flag: it returns false in the same cases in which
calling any of the previous functions would return true.

For example we can use fail() member function:


#include <iostream.h>
#include <fstream.h>

void main(void)
{
ifstream inputfile;
inputfile.open("testfileio.dat");
if(inputfile.fail())
{
cout<<"The file could not be opened!\n";
exit(1); // 0 – normal exit, non zero – some error
}
...
}
Or bad() with cerr.
if(inputfile.bad())
{
cerr<<"Unable to open testfileio.dat\n";
exit(1); // 0 – normal exit, non zero – some error
}

In order to reset the state flags checked by any of these member functions we have just
seen we can use the member function clear(), which takes no parameters.

3.2.1 tellg() and tellp()


These two member functions have no parameters and return a value of the member type
pos_type, which is an integer data type representing the current position of the get stream
pointer (in the case of tellg) or the put stream pointer (in the case of tellp).
UNIT-4 OOP USING C++ Page 25 of 29

3.2.2 seekg() and seekp()


These functions allow us to change the position of the get and put stream pointers. Both
functions are overloaded with two different prototypes. The first prototype is:

seekg ( position );
seekp ( position );

Using this prototype the stream pointer is changed to the absolute position position
(counting from the beginning of the file). The type for this parameter is the same as the
one returned by functions tellg and tellp: the member type pos_type, which is an integer
value.

The other prototype for these functions is:

seekg ( offset, direction );


seekp ( offset, direction );

Using this prototype, the position of the get or put pointer is set to an offset value relative
to some specific point determined by the parameter direction. offset is of the member type
off_type, which is also an integer type. And direction is of type seekdir, which is an
enumerated type (enum) that determines the point from where offset is counted from, and
that can take any of the following values:
ios::b
offset counted from the beginning of the stream
eg
ios::cu offset counted from the current position of the stream
r pointer
ios::e
offset counted from the end of the stream
nd

The following example uses the member functions we have just seen to obtain the size of
a file:
obtaining file size
#include <iostream.h>
#include <fstream.h>

int main () {
long begin,end;
ifstream myfile ("example.txt");
Ouput:
begin = myfile.tellg();
size is: 40
myfile.seekg (0, ios::end);
bytes.
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\
n";
return 0;
}

3.3 Reading and Writing Text Files


It is very easy to read from or write to a text file. Simply use the << and >>
operators
UNIT-4 OOP USING C++ Page 26 of 29

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 <iostream.h>
#include <fstream.h>

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;
}
The following program reads the inventory file created by the previous program
and displays its contents on the screen:

#include <iostream.h>
#include <fstream.h>

int main()
{
ifstream in("INVNTRY"); // input
if(!in) {
cout << "Cannot open INVENTORY file.\n";
return 1;
}
char item[20];
float cost;
in >> item >> cost;
cout << item << " " << cost << "\n";
in >> item >> cost;
cout << item << " " << cost << "\n";
in >> item >> cost;
cout << item << " " << cost << "\n";
in.close();
return 0;
}
All information is stored in the file in the same format as it would be displayed on
the screen while use the >> and << operator for reading from and writing to files.
Following is another example of disk I/O. This program reads strings entered at the
keyboard and writes them to disk. The program stops when the user enters
an exclamation point. To use the program, specify the name of the output file on the
command line.
#include <iostream.h>
#include <fstream.h>
UNIT-4 OOP USING C++ Page 27 of 29

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


{
if(argc!=2) {
cout << "Usage: output <filename>\n";
return 1;
}
ofstream out(argv[1]); // output, normal file
if(!out) {
cout << "Cannot open output file.\n";
return 1;
}
char str[80];
cout << "Write strings to disk. Enter ! to stop.\n";
do {
cout << ": ";
cin >> str;
out << str << endl;
} while (*str != '!');
out.close();
return 0;
}
When reading text files using the >> operator, keep in mind that certain character
translations will occur. For example, white-space characters are omitted. If you want to
prevent any character translations, you must open a file for binary access and use the
functions discussed in the next section.
When inputting, if end-of-file is encountered, the stream linked to that file will
evaluate as false.

3.4 Binary files


In binary files, to input and output data with the extraction and insertion operators
(<< and >>) and functions like getline is not efficient, since we do not need to format any
data, and data may not use the separation codes used by text files to separate elements
(like space, newline, etc...).
File streams include two member functions specifically designed to input and output
binary data sequentially: write and read. The first one (write) is a member function of
ostream inherited by ofstream. And read is a member function of istream that is inherited
by ifstream. Objects of class fstream have both members. Their prototypes are:
write ( memory_block, size );
read ( memory_block, size );
Where memory_block is of type "pointer to char" (char*), and represents the
address of an array of bytes where the read data elements are stored or from where the
data elements to be written are taken. The size parameter is an integer value that
specifies the number of characters to be read or written from/to the memory block.
#include<iostream.h> the complete file content is
#include<fstream.h> copied to another file
void main()
{
char *buffer;
long size;
ifstream infile("d:\\file.txt",ios::binary|ios::ate);
ofstream outfile("d:\\file1.txt",ios::binary);
size=infile.tellg();
UNIT-4 OOP USING C++ Page 28 of 29

buffer = new char[size];


infile.seekg(0);
infile.read(buffer,size);
infile.close()
outfile.write(buffer,size);
outfile.close();
delete[] buffer;
}

Here we first define a pointer string and the integer type size variable to store the sizew of
the file. Then with the help of ifstream we open the file file.txt in binary read mode and
place the cursore at the end of file with the help of ios::binary|ios:ate

Then we open the another file in binary in which we want to place the data in output mode
defined as ios::binary
size=infile.tellg();
buffer = new char[size];
with tellg() functiojn we get the size of the file and define the buffer variable of that size.

infile.seekg(0);
infile.read(buffer,size);
infile.close();
As in the opened input fil, the cursor at the end of file, now we have to manually move it
beginning of file, then read the data and store it to buffer and then close the file.

outfile.write(buffer,size);
outfile.close();
Now, we write the data in the another file that is open in the output format, and then close
that file.

At last we free the memory of the buffer because it is no more use.

Buffers and Synchronization


When we operate with file streams, these are associated to an internal buffer of type
streambuf. This buffer is a memory block that acts as an intermediary between the stream
and the physical file. For example, with an ofstream, each time the member function put
(which writes a single character) is called, the character is not written directly to the
physical file with which the stream is associated. Instead of that, the character is inserted
in that stream's intermediate buffer.
When the buffer is flushed, all the data contained in it is written to the physical medium (if
it is an output stream) or simply freed (if it is an input stream). This process is called
synchronization and takes place under any of the following circumstances:

● When the file is closed: before closing a file all buffers that have not yet been
flushed are synchronized and all pending data is written or read to the physical
medium.
● When the buffer is full: Buffers have a certain size. When the buffer is full it is
automatically synchronized.
● Explicitly, with manipulators: When certain manipulators are used on streams,
an explicit synchronization takes place. These manipulators are: flush and endl.
UNIT-4 OOP USING C++ Page 29 of 29

● Explicitly, with member function sync(): Calling stream's member function


sync(), which takes no parameters, causes an immediate synchronization. This
function returns an int value equal to -1 if the stream has no associated buffer or in
case of failure. Otherwise (if the stream buffer was successfully synchronized) it
returns 0.

You might also like