Overloading IOStream Insertion and Extraction Operators: ,

 < Day Day Up > 



Overloading IOStream Insertion and Extraction Operators: <<, >>

The iostream insertion and extraction operators, << and >>, can be overloaded to properly handle user-defined data type objects. To illustrate iostream operator overloading let us revisit the Person class you have seen used in the past several chapters.

Up until now, to print information about a Person it was necessary to use an accessor function on a Person object in conjunction with iostream insertion as shown in the following lines of code:

Person P1('Richard', 'Warren', 'Miller'); cout<<P1.getFullName()<<endl;

In this example, a Person object named P1 is instantiated with first, middle, and last names. The getFullName() function is then called on the P1 object. The getFullName() returns a char* and the iostream insertion operator << knows how to handle char* types. But what would happen if you tried to insert only the P1 object into the output stream like so:

cout<<P1<<endl; //Ooops! Compiler error

You will get a compiler error because the insertion operator has not been programmed to handle Person objects. To change this situation you will need to overload the insertion operator and define what it means to insert a Person object into the output stream. In this section I will also show you how to overload both the insertion and extraction operators to both print Person objects to the screen, save them to a file on disk, and extract then from a file.

The modified Person class declaration appears in example 14.1.

Listing 14.1: modified person.h

start example
  1  #ifndef  __Person_H  2  #define  __Person_H  3  #include <iostream>  4  #include <fstream>  5  using namespace std;  6  7  class Person{  8      public:  9          Person(char*  _f_name = "John", char*  _m_name = "M",  10                 char* _l_name = "Doe", char _sex = 'M', int _age = 18); 11          ~Person(); 12          Person(Person&  person); 13          Person&operator=(Person& rhs); 14          void    setFirstName(char* f_name); 15          void    setMiddleName(char* m_name); 16          void    setLastName(char* l_name); 17          void    setAge(int age); 18          void    setSex(char sex); 19          char*   getFullName(); 20          char*   getFirstName(); 21          char*   getMiddleName(); 22          char*   getLastName(); 23          char    getSex(); 24          int     getAge(); 25          friend ofstream& operator<<(ofstream& out, Person& p); 26          friend ifstream& operator>>(ifstream& in, Person& p); 27          friend ostream& operator<<(ostream& out, Person& p); 28      private: 29          char* f_name; 30          char* l_name; 31          char* m_name; 32          char sex; 33          int      age; 34          char*    full_name; 35          bool     name_changed; 36  }; 37  #endif
end example

Lines 25, 26, and 27 contain the declarations for the overloaded insertion and extraction operators. Each of these functions are declared to be friends of the Person class. Declaring a non-member function to be a friend of a class gives the function special access to the class's private attributes. This may or may not be desirable in the context of your design. The alternative to granting friend status to non-member functions is to provide adequate class accessor and mutator functions through which the necessary class attributes can be manipulated.

Study each of the three overloaded operator functions appearing on lines 25, 26, and 27 of the Person class declaration. The first operator is an overloaded insertion for an ofstream. This version of the insertion operator will be invoked by the compiler when inserting into an output file stream object. The insertion operator appearing on line 27 is declared to operate on ostream objects. Knowing a little about the iostream inheritance hierarchy will help decipher the behavior of each of these operators. The ofstream and ifstream classes represent output file and input file streams respectively. The ostream class represents an output stream. The inheritance hierarchy is shown in figure 14-1.

click to expand
Figure 14-1: I/O Stream Class Hierarchy

Example 14.2 gives the source code for the implementation of the three versions of the overloaded stream operators. The code in example 14.2 was added to the person.cpp file but since the overloaded stream insertion and extraction operators are not member functions of the Person class they could appear in any implementation file. However, since they are overloaded to handle Person objects it makes sense to place them in the person.cpp file where they can be readily found.

Listing 14.2: overloaded stream operator implementation

start example
  1  /****************************************************  2        Overloaded IOStream Friend Functions  3  *****************************************************/  4  5  ofstream& operator<<(ofstream& out, Person& p){  6       out<<p.f_name<<"|"<<p.m_name<<"|"<<p.l_name<<"|"<<p.sex<<"|"<<p.age<<"|"<<endl;  7       return out;  8  }  9 10  ifstream& operator>>(ifstream& in, Person& p){ 11      char temp[31]; 12      temp[0] = '\0'; 13      in.getline(temp, 30, '|'); 14 15      if(temp[0]){ 16 17       delete[] p.f_name; 18       p.f_name = new char[strlen(temp)+1]; 19        strcpy(p.f_name, temp); 20 21        in.getline(temp, 30, '|');   22         delete[] p.m_name;   23         p.m_name = new char[strlen(temp)+1];   24         strcpy(p.m_name, temp);   25 26        in.getline(temp, 30, '|');   27         delete[] p.l_name;   28         p.l_name = new char[strlen(temp)+1];   29         strcpy(p.l_name, temp);   30 31         in.getline(temp, 30, '|');   32         p.sex = temp[0];   33 34         in.getline(temp, 30, '|');   35         p.age = atoi(temp);   36 37         p.name_changed = true;   38         return in;   39      }else return in;   40  } 41 42  ostream& operator<<(ostream& out, Person& p){   43    out<<p.f_name<<"  "<<p.m_name<<"  "<<p.l_name<<"  "<<p.sex<<"  "<<p.age<<endl;   44    return out;   45  }
end example

Referring to example 14.2 let us talk about the implementation of each overloaded operator. The definition of the first overloaded insertion operator begins on line 5. This version of the insertion operator is expecting an output file object. Since it has been declared as a friend function of the Person class the private attributes of Person objects can be directly accessed. The statement on line 6 does the hard work. It inserts various attributes of the Person object p into the ofstream object parameter out. The insertion of each Person attribute is followed by the insertion of a vertical bar character. The vertical bar will act as a delimiter between variable length fields in the file and will be used when the file is read by the overloaded extraction operator.

The overloaded extraction operator definition begins on line 10. It is overloaded to extract from an input file object into a Person object. A character array named temp in declared on line 11. This array will be used to temporarily hold the strings read from the input file before being copied into their corresponding Person object attribute arrays. On line 12 the first element in the temp array is set to the null character. On line 13 the fist series of characters are read from the input file up to 30 characters or until the vertical bar delimiter is read. Line 15 checks to see if the first character of the temp array has changed, if so, the rest of the information from the current line of the input file is read and the Person object attributes are set accordingly.

The second overloaded insertion operator definition begins on line 42. This version of the insertion operator differs from the first in that the designated Person object's attributes will be inserted into the stream without the vertical bar delimiters.

Example 14.3 gives the source code for the main() function used to test these overloaded stream operators.

Listing 14.3: main.cpp

start example
  1  #include <iostream>  2  using namespace std;    3  #include "person.h"  4  5  int main(){  6  7      /**********************variables****************/  8      int number = 0;  9      char fname[30]; 10      char mname[30]; 11      char lname[30]; 12      Person **people; 13 14      /******get number of people to enter ************/ 15      cout<<"Enter number of people: "; 16      cin>>number; 17      cout<<endl; 18 19      /***********create people array******************/ 20       people = new Person*[number];   21 22      /**********enter people names*********************/   23      for(int i=0; i<number; i++){   24 25       cout<<"Enter first name: ";   26       cin>>fname;   27       cout<<"Enter middle name: ";   28       cin>>mname;   29       cout<<"Enter last name: ";   30       cin>>lname;   31       cout<<endl<<endl;   32 33       people[i] = new Person(fname, mname, lname);   34      } 35 36      /**********create file to save people **********/   37      ofstream outfile("people.txt");   38 39      for(int i=0; i<number; i++)   40          outfile<<(*people[i]);   41 42      outfile.close();   43 44      /****open file and read people info***********/   45      ifstream infile("people.txt");   46 47 48      for(int i=0; i<number; i++)   49          infile>>(*people[i]);   50 51      infile.close();   52 53      /*********print people to screen***************/   54 55      for(int i=0; i<number; i++)   56          cout<<(*people[i]);   57 58      /***********delete pointers********************/   59      for(int i=0; i<number; i++)   60          delete people[i];   61 62      delete[] people;   63 64      return 0;   65  }
end example

Figure 14-2 shows the results of running example 14.3 and entering information for two people.


Figure 14-2: Results of Running Example 14.3

The program begins by prompting the user to enter the number of people they wish to enter. The people array is dynamically created on line 20 and on line 23 a for loop repeatedly prompts the user to enter the first, middle, and last name for each person. When all the Person objects have been created a file named people.txt is created and opened and each person object is inserted into the file. After the insertions the contents of the people.txt file looks like this:

Susan|Marie|Reeves|M|18| Coralie|Sylvia|Miller|M|18|

When the insertions are complete the output file is closed.

On line 45 an input file named people.txt is opened and its contents extracted into the two Person objects contained in the people array. (The people array is an array of pointers to people, hence the need to dereference each Person pointer to yield the person object.) Each person object is then inserted into the cout object yielding the output shown in figure 14-2. The main() function wraps up by deleting each Person pointer and the people array.



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net