Creating Custom Insertion and Extraction Operators in C++

In previous DeveloperIQ articles, the use of operator overloading in C++ has been dealt with in great detail. Operator overloading is a mechanism through which Class instance, i.e. objects, can be treated like built-in types and normal operators can be applied to them as they are applied to inbuilt types. Insertion and extraction operators are yet another versatile examples of operator overloading. In C++, the output operation is called an insertion and << is called insertion operator. Similarly, the input operator is called extraction operator and the >> operator is called extraction operator. These operators, i.e. << and >> operators are bitwise left shift and right shift operators in C language. These operators have been overloaded in C++ to perform input and output operations as in the following example. Refer code 1.

Code 1

#include<iostream>
using namespace std;
int main()
{
int num;
cout<<”Please enter an integer:”;
cin>>num;
cout<<num;
return 0;
}


The above program when run, prompts to enter an integer, which we do through the standard input stream (keyboard), and then outputs the integer to the standard output stream, i.e. screen. The C++ I/O system is stream based. A stream is a logical device, which can either consume or produce data. Streams present programmers a consistent interface even if the devices may vary. For example, we can perform input and output operations to read from keyboard or from a file, and write to screen as well as files. This is where custom insertion and extraction operators come in handy.

The previous example is a typical illustration portraying the normal use of input and output operators. Let us now delve a little deeper into the insertion and extraction operators. We will first create extraction and insertion functions by overloading << and >> operators. The extraction and inserter functions have got the following forms, respectively.

istream &operator>>(istream &str,name_of_class &ob)
ostream &operator<<(ostream &str, name_of_class &ob)

The first parameter to the functions is a reference to stream and the second parameter is a class instance. We will first perform console-based input and output operations through these functions. Check out code 2.

#include<iostream>
using namespace std;
class person
{
private:
float height;
int age;
public:
person()
{
height=0;
age=0;
}
person(float h, int a)
{
height=h;
age=a;
}
friend ostream& operator<<(ostream &str, person &pers);
friend istream& operator>>(istream &str,person &pers);
};
istream &operator>>(istream &str, person &pers)
{
cout<<"Please enter height:";
str>>pers.height;
cout<<"Please enter age:";
str>>pers.age;
return str;
}
ostream &operator<<(ostream &str,person pers)
{
cout<<"The height is:";
str<<pers.height;
cout<<'/n';
cout<<"The age is:";
str<<pers.age;
return str;
}
int main()
{
person pers;
cin>>pers;
cout<<pers;
return 0;
}


On execution, the program asks for input height and age as it encounters cin>>pers in main() and outputs the data as it encounters cout<<pers. The insertion and extraction operators have been defined to insert and extract data for standard streams, i.e. console and keyboard. The insertion and extraction operators have been defined as friends. They cannot be member functions since as we know, in operator functions, the first parameter is an object that generates call to the operator function. So, if an operator function has to be member of the class, its first parameter must be an object but in inserter and extractor functions, the first parameter must be a stream.

The same insertion and extraction functions can be used for insertion and extraction of data for any stream. The stream may be a data file, a removable media or any other stream. Code 3 demonstrates a way to insert and extract data to and from data files. The main() function has to be changed. Minor modifications have to be done in inserter and extractor functions to delete input taking prompts and output prompts.

Code 3

#include<iostream.h>
#include<fstream.h>
#include<conio.h>
class person
{
private:
float height;
int age;
public:
person()
{
height=0;
age=0;
}
person(float h, int a)
{
height=h;
age=a;
}
friend ostream& operator<<(ostream &str, person &pers);
friend istream& operator>>(istream &str,person &pers);
};
istream& operator>>(istream& str, person& pers)

str>>pers.height>>pers.age;
return str;
}
ostream& operator<<(ostream& str,person& pers)
{
str.width(6);
str<<pers.height<<' ';
str.width(3);
str<<pers.age;
return str;
}
int main()
{
clrscr();
char answer;
person pers;
//Create a file to write to data file person.dat
ofstream outfile;
outfile.open("person.dat",ios::out|ios::ate|ios::binary);
if(!outfile)
{
cout<<"Unable to open file!";
return 1;
}
do
{
cout<<"Please enter height and age consecutively pressing Enter after entering height";
cin>>pers;
outfile<<pers;
outfile<<' ';
cout<<"Another entry(y/n)";
cin>>answer;
}
while(answer=='y');
outfile.close();
//Create a file to read data from data file and display on screen.
ifstream infile;
infile.open("person.dat",ios::in|ios::binary);
if(!infile)
{
cout<<"Unable to open file";
return 1;
}
cout<<"Height"<<" "<<"Age";
cout<<'\n';
while(infile)
{
infile>>pers;
if(infile.eof())
break;
cout<<pers;
cout<<endl;
}
infile.close();
getch();
return 0;
}


First we define an output file ‘outfile’, which is opened to write data to data file “person.dat”. For opening files, the constructor used is stream_object.open(“filename”, mode). The stream_object has been created by using ofstream outfile. The mode used is ios:out|ios:ate|ios:binary which indicates that the file is being opened for output in binary mode and data can be added at the end. Running the program prompts for input but the prompt is a part of main() method and not that of inserter function since it does not make much sense to take prompt from files. Also, if we use prompts in inserter functions, we will read these prompts while reading data from the datafile through infile>>pers. So, when we output data using outfile<<pers, we will get these prompts printed on screen with the putput data. The statement outfile<<pers writes data to person.dat. The program runs in a while loop as long as the user types ‘y’ for entering more data. Then to read data from data file person.dat, we create an input file infile. The stream infile reads data from person.dat and the statement cout<<pers displays the data on screen. This is possible since we have overloaded the insertion and extraction operators.

We can manipulate any stream by using overloaded inserter and extractor functions. For example, if we want to create the data file on 'A' drive, write data to the file in ‘A’ drive, we can proceed as follows:

outfile.open( “a:person.dat”,ios::out);
cin>>pers;
outfile<<pers;


This creates a data file person.dat on ‘A’ drive. For extracting data in data file person.dat on ‘A’ drive, we can proceed as follows:

infile.open(“a:person.dat”,ios::in);
infile>>pers;
cout<<pers;


Thus, we see that insertion and extraction of data can be performed very easily by overloading insertion and extraction operators for any stream. Readers may further explore other features on their own regarding inserter and extractor functions and data files operations. This article provides them with the basic knowledge to manipulate streams using inserter and extractor functions.



Added on June 13, 2007 Comment

Comments

Post a comment