2

I'll go straight to the point:

I have a text file that contains a list of book names. I know how to read a text file, for example:

string word;
file >> word;

However, say I have a class called "word". Would it be possible to do:

word myWord;
file >> myWord;

If yes, how would such object have to be declared ? Would it read to the first public string it contains ?

Thank you for your time !

  • 8
    [operator overloading](http://stackoverflow.com/questions/4421706/operator-overloading) – chris Sep 09 '13 at 16:30

2 Answers2

3

The simple answer is yes, you can do file >> myWord;, as long as you've defined the >> operator for your class:

std::ostream&
operator>>( std::ostream& source, Word& word )
{
    //  ...
    return source;
}

Several considerations:

  • This operator cannot be a member; it must be a free function. (If it were a member, your class would be the left hand argument.) If it needs to access private data, you might have to make it a friend.

  • Don't forget about error checking, and the conventions of istream for errors. If you encounter input you cannot parse into your class, you need to set std::ios::failbit. (Of course, a lot of the time, you'll be delegating to previously defined >>, and if they fail, std::ios::failbit will already have been set.)

EDIT:

As for what you should do inside the newly overloaded operator, there are several possibilities (which can, with care, be mixed):

  • You can invoke existing >>, for example, for strings or built-in types. This is by far the easiest, but it supposes that your input can easily be parsed uniquely in terms of existing >>, which is rarely the case.

  • You can also use unformatted input, like istream::get(), this is most often used in parallel with the previous solution, to input things like separators, or other syntactical elements of your format.

  • Or you can do revert to reading the bytes directly from the streambuf, and parse them. This is appropriate if you have some totally new type, for example. If you go this route, do not forget to set eofbit if you read an end of file, even if you can successfully parse otherwise. If you do this, you'll also have to create a sentry object at the top of your >>, and only proceed if it is good. (It is only in such operator>> that std::ios::good makes sense. You must never try to read a character from the streambuf if std::ios::good() returns false, and you must set std::ios::eofbit anytime you read an EOF (which will cause all future calls to std::ios::good() to return false). This is so essential that I tend to use a small wrapper object for it, and read through it.

In all cases, you may have to play around with the formatting information: as a simple example, if you don't want to allow white space within your input, but you are still using >>, you should set nows (and restore it at the end). Thus, most such >> operators will start by saving the formatting status, and restore it at the end. (This is usually done by means of an IOSave class, which you should have in your toolkit anyway.) And again, if something in the input format is incorrect, you should set failbit.

As a simple example, consider a >> for a simple Complex class:

std::istream&
operator>>( std::istream& source, Complex& dest )
{
    IOSave state( source );
    //      Skip leading whitespace, depending on formatting options.
    if ( (source.flags() & std::ios_base::skipws) != 0 ) {
        source >> std::ws;
    }
    source.unsetf( std::ios_base::skipws );
    std::streamsize totalWidth
        = std::max( source.width() - 3, std::streamsize(0) ); ;
    std::streamsize imagWidth = totalWidth / 2;
    std::streamsize realWidth = totalWidth - imagWidth;
    if ( source.get() != '(' ) {
        source.unget();
        source.setstate( std::ios::failbit );
    }
    double real = 0.0;
    source >> std::setw( realWidth ) >> real;
    std::numpunct<char> const& np
        = std::use_facet<std::numpunct<char>>( source.getloc() );
    if ( std::get() != (np.decimal_point() != ',' ? ',' : ';') ) {
        source.unget();
        source.setstate( std::ios::failbit );
    }
    double imag = 0.0;
    source >> std::setw( imagWidth ) >> imag;
    if ( std::peek() != ')' ) {
        source.unget();
        source.setstate( std::ios::failbit );
    }
    if ( source ) {
        dest = Complex( real, imag );
    }
    return source;
}

This is an extremely simplistic example. A real Complex class would, for example, also accept input in the form a+ib. But it should give you an idea of the sort of things you have to consider when writing such an operator.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I have trouble understanding what would be the code required inside the redefinition of the operator. Say I have a private string called "theWord" in my class Word. Would I just have to type, in the redefinition of the operator, `file >> theWord` ? – Dwight Shrout Sep 09 '13 at 21:46
  • @Martin It's not redefinition, it's overloading. (The original definitions continue to exist.) I've edited the answer to add some of the considerations you need to keep in mind when writing your own `operator>>`, and added a simple example. – James Kanze Sep 10 '13 at 09:26
1

Of course not. you have to overload >> operator for your class word. You do that either as member function or a non member function. To overload as non-member you may write:

fstream& operator >> (fstream& arg1,word& arg2)
{
}

if this function needs to access private members of word you may use:

class word{
    // somewhere in your class add this line
    friend fstream& operator >> (fstream&,word&);
}
deeiip
  • 3,319
  • 2
  • 22
  • 33