Google
 

Monday, January 10, 2011

C++ Primer, Fourth Edition Notes Chapter 8. The IO Library

Section 8.1 An Object-Oriented Library

  1. Conceptually, neither the kind of device nor the character size affect the IO operations we want to perform.
  2. To manage the complexity, the library uses inheritance to define a set of object-oriented classes.
  3. The IO types are defined in three separate headers: iostream defines the types used to read and write to a console window, fstream defines the types used to read and write named files, and sstream defines the types used to read and write in-memory strings
  4. Each of the types in fstream and sstream is derived from a corresponding type defined in the iostream header

    Table 8.1. IO Library Types and Headers

    Header

    Type

    iostream

    istream reads from a stream

    ostream writes to a stream

    iostream reads and writes a stream; derived from istream and ostream,

    fstream

    ifstream, reads from a file; derived from istream

    ofstream writes to a file; derived from ostream

    fstream, reads and writes a file; derived from iostream

    sstream

    istringstream reads from a string; derived from istream

    ostringstream writes to a string; derived from ostream

    stringstream reads and writes a string; derived from iostream

    image
  5. Using inheritance for the IO types has another important implication: As we'll see in Chapter 15, when we have a function that takes a reference to a base-class type, we can pass an object of a derived type to that function
  6. International Character Support
    1. The stream classes described thus far read and write streams composed of type char. The library defines a corresponding set of types supporting the wchar_t type. Each class is distinguished from its char counterpart by a "w" prefix. Thus, the types wostream, wistream, and wiostream read and write wchar_t data to or from a console window. The file input and output classes are wifstream, wofstream, and wfstream. The wchar_t versions of string stream input and output are wistringstream, wostringstream, and wstringstream. The library also defines objects to read and write wide characters from the standard input and standard output. These objects are distinguished from the char counterparts by a "w" prefix: The wchar_t standard input object is named wcin; standard output is wcout; and standard error is wcerr
    2. Each of the IO headers defines both the char and wchar_t classes and standard input/output objects. The stream-based wchar_t classes and objects are defined in iostream, the wide character file stream types in fstream, and the wide character stringstreams in sstream.
  7. No Copy or Assign for IO Objects
    1. the library types do not allow allow copy or assignment:
    2. This requirement has two particularly important implications. As we'll see in Chapter 9, only element types that support copy can be stored in vectors or other container types. Because we cannot copy stream objects, we cannot have a vector (or other container) that holds stream objects
    3. The second implication is that we cannot have a parameter or return type that is one of the stream types. If we need to pass or return an IO object, it must be passed or returned as a pointer or reference
    4. Typically, we pass a stream as a nonconst reference because we pass an IO object intending to read from it or write to it. Reading or writing an IO object changes its state, so the reference must be nonconst.

Section 8.2 Condition States

  1. Keep in mind that the material we cover in this section and the next applies equally to plain streams, file streams, or string streams
  2. The IO library manages a set of condition state members that indicate whether a given IO object is in a usable state or has encountered a particular kind of error. The library also defines a set of functions and flags, listed in Table 8.2, that give us access to and let us manipulate the state of each stream

    Table 8.2. IO Library Condition State

    strm::iostate

    Name of the machine-dependent integral type, defined by each iostream class that is used to define the condition states.

    strm::badbit

    strm::iostate value used to indicate that a stream is corrupted.

    strm::failbit

    strm::iostate value used to indicate that an IO operation failed.

    strm::eofbit

    strm::iostate value used to indicate the a stream hit end-of-file.

    s.eof()

    true if eofbit in the stream s is set.

    s.fail()

    true if failbit in the stream s is set.

    s.bad()

    TRue if badbit in the stream s is set.

    s.good()

    true if the stream s is in a valid state.

    s.clear()

    Reset all condition values in the stream s to valid state.

    s.clear(flag)

    Set specified condition state(s) in s to valid. Type of flag is strm::iostate.

    s.setstate(flag)

    Add specified condition to s. Type of flag is strm::iostate.

    s.rdstate()

    Returns current condition of s as an strm::iostate value.

    a
  3. To be used for input or output, a stream must be in a non-error state. The easiest way to test whether a stream is okay is to test its truth value
    1.           if (cin)
      // ok to use cin, it is in a valid state

      while (cin >> word)
      // ok: read operation successful ...


  4. Condition States

    1. Each stream object contains a condition state member that is managed through the setstate and clear operations. This state member has type iostate, which is a machine-dependent integral type defined by each iostream class. 
    2. Each IO class also defines three const values of type iostate that represent particular bit patterns. These const values are used to indicate particular kinds of IO conditions. They can be used with the bitwise operators (Section 5.3, p. 154) to test or set multiple flags in one operation.
    3. The badbit indicates a system level failure, such as an unrecoverable read or write error. It is usually not possible to continue using a stream after such an error.
    4. The failbit is set after a recoverable error, such as reading a character when numeric data was expected. It is often possible to correct the problem that caused the failbit to be set.
    5. The eofbit is set when an end-of-file is encountered. Hitting end-of-file also sets the failbit
    6. If any of bad, fail, or eof are true, then testing the stream itself will indicate that the stream is in an error state. Similarly, the good operation returns TRue if none of the other conditions is true.
    7. The clear and setstate operations change the state of the condition member. The clear operations put the condition back in its valid state. They are called after we have remedied whatever problem occurred and we want to reset the stream to its valid state. The setstate operation turns on the specified condition to indicate that a problem occurred. setstate leaves the existing state variables unchanged except that it adds the additional indicated state(s).

  5. Interrogating and Controlling the State of a Stream

    1. Recall that the comma operator executes by evaluating each operand and returns its rightmost operand as its result.

  6. Accessing the Condition State

    1. The rdstate member function returns an iostate value that corresponds to the entire current condition state of the stream

  7. Dealing with Multiple States

    1. Alternatively, we could use the bitwise OR (Section 5.3, p. 154) operator to generate a value to pass two or more state bits in a single call. The bitwise OR generates an integral value using the bit patterns of its operands. For each bit in the result, the bit is 1 if the corresponding bit is 1 in either of its operands
      // sets both the badbit and the failbit
      is.setstate(ifstream::badbit | ifstream::failbit);


  8. tells the object is to turn on both the failbit and the badbit

Section 8.3 Managing the Output Buffer



  1. Each IO object manages a buffer, which is used to hold the data that the program reads and writes
  2. There are several conditions that cause the buffer to be flushedthat is, writtento the actual output device or file

    1. The program completes normally. All output buffers are emptied as part of the return from main.
    2. At some indeterminate time, the buffer can become full, in which case it will be flushed before writing the next value
    3. We can flush the buffer explicitly using a manipulator (Section 1.2.2, p. 7) such as endl
    4. We can use the unitbuf manipulator to set the stream's internal state to empty the buffer after each output operation
    5. We can tie the output stream to an input stream, in which case the output buffer is flushed whenever the associated input stream is read

  3. Flushing the Output Buffer

    1. Our programs have already used the endl manipulator, which writes a newline and flushes the buffer. There are two other similar manipulators. The first, flush, is used quite frequently. It flushes the stream but adds no characters to the output. The second, ends, is used much less often. It inserts a null character into the buffer and then flushes it
          cout << "hi!" << flush;      // flushes the buffer; adds no data
      cout << "hi!" << ends; // inserts a null, then flushes the buffer
      cout << "hi!" << endl; // inserts a newline, then flushes the buffer

  4. The unitbuf Manipulator

    1. If we want to flush every output, it is better to use the unitbuf manipulator. This manipulator flushes the stream after every write
    2. The nounitbuf manipulator restores the stream to use normal, system-managed buffer flushing









      Caution: Buffers Are Not Flushed if the Program Crashes


      Output buffers are not flushed if the program terminates abnormally. When attempting to debug a program that has crashed, we often use the last output to help isolate the region of program in which the bug might occur. If the crash is after a particular print statement, then we know that the crash happened after that point in the program.


      When debugging a program, it is essential to make sure that any output you think should have been written was actually flushed. Because the system does not automatically flush the buffers when the program crashes, it is likely that there is output that the program wrote but that has not shown up on the standard output. It is still sitting in an output buffer waiting to be printed.


      If you use the last output to help locate the bug, you need to be certain that all the output really did get printed. Making sure that all output operations include an explicit flush or call to endl is the best way to ensure that you are seeing all the output that the program actually processed.


      Countless hours of programmer time have been wasted tracking through code that appeared not to have executed when in fact the buffer simply had not been flushed. For this reason, we tend to use endl rather than \n when writing output. Using endl means we do not have to wonder whether output is pending when a program crashes.


  5. Tying Input and Output Streams Together

    1. When an input stream is tied to an output stream, any attempt to read the input stream will first flush the buffer associated output stream. The library ties cout to cin
    2. Interactive systems usually should be sure that their input and output streams are tied. Doing so means that we are guaranteed that any output, which might include prompts to the user, has been written before attempting to read
    3. The tie function can be called on either istream or an ostream. It takes a pointer to an ostream and ties the argument stream to the object on which tie was called. When a stream ties itself to an ostream, then any IO operation on the stream that called tie flushes the buffer associated with the argument it passed to tie
    4. An ostream object can be tied to only one istream object at a time. To break an existing tie, we pass in an argument of 0.
    // EXE-8.3.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    #include <iostream>
    #include <string>
    using namespace std;

    int main(int argc, _TCHAR* argv[])
    {

    cout << "hi!" << ends; // inserts a null, then flushes the buffer
    cout << "hi!" << flush; // flushes the buffer; adds no data
    cout << "hi!" << endl; // inserts a newline, then flushes the buffer

    cout << unitbuf << "first" << " second" << nounitbuf <<endl;
    cout << "first" << flush << " second" << flush;

    cout << endl;
    string tieTest;
    cin.tie(&cout); // illustration only: the library ties cin and cout for us
    ostream *old_tie = cin.tie();
    cin.tie(0); // break tie to cout, cout no longer flushed when cin is read
    cout << "test";
    cin >> tieTest ;
    cin.tie(&cerr); // ties cin and cerr, not necessarily a good idea!
    // ...
    cin.tie(0); // break tie between cin and cerr
    cin.tie(old_tie); // restablish normal tie between cin and cout


    int i;
    cin >> i;
    return 0;
    }

Section 8.4 File Input and Output



  1. The fstream header defines three types to support file IO:


    1. ifstream, derived from istream, reads from a file.



    2. ofstream, derived from ostream, writes to a file.



    3. fstream, derived from iostream, reads and writes the same file.


  2. In addition to the behavior that fstream types inherit, they also define two new operations of their ownopen and closealong with a constructor that takes the name of a file to open. These operations can be called on objects of fstream, ifstream, or ofstream but not on the other IO types
  3. 8.4.1. Using File Stream Objects

    1. When we want to read or write a file, we must define our own objects, and bind them to the desired files
    2. Supplying a file name as an initializer to an ifstream or ofstream object has the effect of opening the specified file
    3. Before we use an fstream object, we must also bind it to a file to read or write
    4. We bind an existing fstream object to the specified file by calling the open member. The open function does whatever system-specific operations are required to locate the given file and open it for reading or writing as appropriate









      Caution: File Names in C++


      For historical reasons, the IO library uses C-style character strings (Section 4.3, p. 130) rather than C++ strings to refer to file names. When we call open or use a file name as the initializer when creating an fstream object, the argument we pass is a C-style string, not a library string. Often our programs obtain file names by reading the standard input. As usual, it is a good idea to read into a string, not a C-style character array. Assuming that the name of the file we wish to use is in a string, we can use the c_str member (Section 4.3.2, p. 139) to obtain a C-style string.



    5. Checking Whether an Open Succeeded

      1. When we test a stream, the effect is to test whether the object is "okay" for input or output. If the open fails, then the state of the fstream object is that it is not ready for doing IO

    6. Rebinding a File Stream to a New File

      1. Once an fstream has been opened, it remains associated with the specified file. To associate the fstream with a different file, we must first close the existing file and then open a different file
      2. It is essential that we close a file stream before attempting to open a new file. The open function checks whether the stream is already open. If it is open, then it sets its internal state to indicate that a failure has happened. Subsequent attempts to use the file stream will fail

    7. Clearing the State of a File Stream

      1. Closing a stream does not change the internal state of the stream object
      2. If we reuse a file stream to read or write more than one file, we must clear the stream before using it to read from another file


  4. 8.4.2. File Modes

    1. Each fstream class defines a set of values that represent different modes in which the stream could be opened. Like the condition state flags, the file modes are integral constants that we use with the bitwise operators (Section 5.3, p. 154) to set one or more modes when we open a given file. The file stream constructors and open have a default argument (Section 7.4.1, p. 253) to set the file mode. The value of the default varies based on the type of the stream. Alternatively, we can supply the mode in which to open the file. Table 8.3 on the next page lists the file modes and their meanings






























      Table 8.3. File Modes

      in


      open for input


      out


      open output


      app


      seek to the end before every write


      ate


      seek to the end immediately after the open


      trunc


      truncate an existing stream when opening it


      binary


      do IO operations in binary mode

      The modes out, trunc, and app may be specifed only for files associated with an ofstream or an fstream; in may be specified only for files associated with either ifstream or fstream. Any file may be opened in ate or binary mode. The ate mode has an effect only at the open: Opening a file in ate mode puts the file at the end-of-file immediately after the open. A stream opened in binary mode processes the file as a sequence of bytes; it does no interpretation of the characters in the stream
    2. By default, files associated with an ifstream are opened in in mode, which is the mode that permits the file to be read. Files opened by an ofstream are opened in out mode, which permits the file to be written. A file opened in out mode is truncated: All data stored in the file is discarded
    3. In effect, specifying out mode for an ofstream is equivalent to specifying both out and trunc
    4. The only way to preserve the existing data in a file opened by an ofstream is to specify app mode explicitly
    5. Using the Same File for Input and Output

      1. An fstream object can both read and write its associated file. How an fstream uses its file depends on the mode specified when we open the file
      2. By default, an fstream is opened with both in and out set. A file opened with both in and out mode set is not truncated. If we open the file associated with an fstream with out mode, but not in mode specified, then the file is truncated. The file is also truncated if trunc is specified, regardless of whether in is specified

    6. Mode Is an Attribute of a File, Not a Stream

      1. Any time open is called, the file mode is set, either explicitly or implicitly. If a mode is not specified, the default value is used.

    7. Valid Combinations for Open Mode

      1. Not all open modes can be specified at once. Some are nonsensical, such as opening a file setting both in and TRunc. That would yield a stream we intend to read but that we have truncated so that there is no data to read






























        Table 8.4. File Mode Combinations

        out


        open for output; deletes existing data in the file


        out | app


        open for output; all writes at end of file


        out | trunc


        same as out


        in


        open for input


        in | out


        open for both input and output;
        positioned to read the beginning of the file


        in | out | trunc


        open for both input and output,
        deletes existing data in the file

        Any open mode combination may also include ate. The effect of adding ate to any of these modes changes only the initial position of the file. Adding ate to any of these mode combinations positions the file to the end before the first input or output operation is performed

  5. 8.4.3. A Program to Open and Check Input Files
// EXE-8.4.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, _TCHAR* argv[])
{

// construct an ifstream and bind it to the file named ifile
string filePath = "filestreamtesting.txt";
ifstream infile1(filePath);
// check that the open succeeded
if (!infile1) {
cerr << "error: unable to open input file: "
<< infile1 << endl;
//return -1;
}

// ofstream output file object to write file named ofile
ofstream outfile(filePath);
//char *pO = "try to write the file!";

outfile.write(filePath.c_str(),filePath.length());
outfile.flush();
outfile.close();

ifstream infile(filePath);
char *p = new char(0);
infile.read(p,filePath.length());

for (int i = 0 ; i < filePath.length(); ++i)
{
cout <<*(p+ i) ;
}
while( *p != 0)
{
cout << *p++;
}
delete p; // error to delete the p
p = 0;

infile.close();

//ifstream infile("in"); // opens file named "in" for reading
//infile.close(); // closes "in"
infile.open("no existing file.txt"); // opens file named "next" for reading
if(!infile)
{
cout << "To open file failed!" << endl;
}
infile.close();


if("Clearing the State of a File Stream")
{
ifstream input;
vector<string> files;
files.push_back(filePath);
files.push_back("ReadMe.txt");
vector<string>::const_iterator it = files.begin();
string s;
// for each file in the vector
void process(string s);
while (it != files.end()) {
input.open(it->c_str()); // open the file
// if the file is ok, read and "process" the input
if (!input)
break; // error: bail out!
while(input >> s) // do the work on this file
process(s);
input.close(); // close file when we're done with it
input.clear(); // reset state to ok
++it; // increment iterator to get next file
}
}

if("File Modes")
{
// output mode by default; truncates file named "file1"
ofstream outfile("file1");
// equivalent effect: "file1" is explicitly truncated
ofstream outfile2("file1", ofstream::out | ofstream::trunc);
// append mode; adds new data at end of existing file named "file2"
ofstream appfile("file2", ofstream::app);
}
if("Using the Same File for Input and Output")
{
// open for input and output
fstream inOut("copyOut", fstream::in | fstream::out);
}
if("Mode Is an Attribute of a File, Not a Stream")
{
ofstream outfile;
// output mode set to out, "scratchpad" truncated
outfile.open("scratchpad", ofstream::out);
outfile.close(); // close outfile so we can rebind it
// appends to file named "precious"
outfile.open("precious", ofstream::app);
outfile.close();
// output mode set by default, "out" truncated
outfile.open("out");
}

if("A Program to Open and Check Input Files")
{
// opens in binding it to the given file
ifstream& open_file(ifstream &in, const string &file);


// failed to build with the following code
// can't call the code like the following
//ifstream infile = open_file(infile, filePath);
ifstream &infile = open_file(infile, filePath);
}

int i;
cin >> i;

return 0;
}

void process(string s)
{
cout << s << endl;
}

// opens in binding it to the given file
ifstream& open_file(ifstream &in, const string &file)
{
in.close(); // close in case it was already open
in.clear(); // clear any existing errors
// if the open fails, the stream will be in an invalid state
in.open(file.c_str()); // open the file we were given
return in; // condition state is good if open succeeded
}

Section 8.5 String Streams



  1. The iostream library supports in-memory input/output, in which a stream is attached to a string within the program's memory. That string can be written to and read from using the iostream input and output operators. The library defines three kinds of string streams:


    • istringstream, derived from istream, reads from a string.



    • ostringstream, derived from ostream, writes to a string.



    • stringstream, derived from iostream, reads and writes a string


  2. In addition to the operations that the sstream types inherit, these types have a constructor that takes a string. The constructor copies the string argument into the stringstream object. The operations that read and write the stringstream read or write the string in the object. These classes also define a member named str to fetch or set the string value that the stringstream manipulates
  3. Note that although fstream and sstream share a common base class, they have no other interrelationship. In particular, we cannot use open and close on a stringstream, nor can we use str on an fstream
























    Table 8.5. stringstream-Specific Operations

    stringstream strm;


    Creates an unbound stringstream.


    stringstream strm(s);


    Creates a stringstream that holds a copy of the string s.


    strm.str()


    Returns a copy of the string that strm holds.


    strm.str(s)


    Copies the string s into strm. Returns void.


  4. Using a stringstream
  5. stringstreams Provide Conversions and/or Formatting

    1. One common use of stringstreams is when we want to obtain automatic formatting across multiple data types
    2. Reading an istringstream automatically converts from the character representation of a numeric value to its corresponding arithmetic value
    3. To read input_string, we must parse the string into its component parts. We want the numeric values; to get them we must read (and ignore) the labels that are interspersed with the data we want
    4. Because the input operator reads typed values, it is essential that the types of the objects into which we read be compatible with the types of the values read from the stringstream


// EXE-8.5.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
//here for using istringstream must include the sstream
#include <sstream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, _TCHAR* argv[])
{
if("Using a stringstream")
{
string line, word; // will hold a line and word from input, respectively
cout << "Please input a string (\\0 for break):" << endl;
while (getline(cin, line)) { // read a line from the input into line
// do per-line processing
if(line == "\\0")
break;

istringstream strIn(line); // bind to stream to the line we read
while (strIn >> word){ // read a word from line
// do per-word processing
cout << "PrintOut:" << word << endl;
}
}

}

if("stringstreams Provide Conversions and/or Formatting")
{
int val1 = 512, val2 = 1024;
ostringstream format_message;
// ok: converts values to a string representation
format_message << "val1: " << val1 << "\n"
<< "val2: " << val2 << "\n";

// str member obtains the string associated with a stringstream
istringstream input_istring(format_message.str());
string dump; // place to dump the labels from the formatted message
// extracts the stored ascii values, converting back to arithmetic types
input_istring >> dump >> val1 >> dump >> val2;
cout << val1 << " " << val2 << endl; // prints 512 1024


}

int i;
cin >> i;

return 0;
}

C++ Primer, Fourth Edition Notes Chapter 8 Exercise

Exercise 8.1:

Assuming os is an ofstream, what does the following program do?

    os << "Goodbye!" << endl;

What if os is an ostringstream? Whatif os is an ifstream?

Answer:

ofstream: write the “Goodbye” and endl to file

ifstream:ERRO

ostringstream: write the string to string stream

Exercise 8.2:

The following declaration is in error. Identify and correct the problem(s):

    ostream print(ostream os);

reason: the stream can be copied and only be referenced or use the pointer


Exercise 8.3:

Write a function that takes and returns an istream&. The function should read the stream until it hits end-of-file. The function should print what it reads to the standard output. Reset the stream so that it is valid and return the stream.

Exercise 8.4:

Test your function by calling it passing cin as an argument.

// Exercise 8.3.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;
ifstream &printOut( ifstream &in);
istream &print( istream &in);
int main(int argc, _TCHAR* argv[])
{
ifstream in("ReadMe.txt");

ifstream &inf = printOut(in);

if(inf)
{
cout << "Testing the stream input;" << endl;
string s;
//Issue here: already clear the stream, but the stream still can't be reused
while(inf >> s , !inf.eof())
{
cout << s << endl;
}
inf.close();
}

print(cin);
// test the cleared eofbit mark
if(cin.eof())
{
//why it always show it is not cleared here?
cout << "the eof mark is not cleared!" << endl;
}
else
cout << "the eof mark is cleared!" << endl;
int i(0);
cin >> i;
return 0;
}


ifstream &printOut( ifstream &in)
{
string s;
while(in >> s , !in.eof())
{
//print out all to standard output
cout << s << endl;
}
//reset the stream
in.clear( istream::eofbit);
//in.setstate( istream::eofbit);

return in;
}
istream &print( istream &in)
{
string s;
while(in >> s , !in.eof())
{
//print out all to standard output
cout << s << endl;
}
if(in.eof())
{
cout << "The end of file!" << endl;
}
// clear the eofbit mark
in.clear( istream::eofbit);

return in;
}

Exercise 8.5:

What causes the following while to terminate?

    while (cin >> i) /* . . . */

Answer: when the cin is not available, one of the bits are set: eofbit, badbit, failbit.


Exercise 8.6:

Because ifstream inherits from istream, we can pass an ifstream object to a function that takes a reference to an istream. Use the function you wrote for the first exercise in Section 8.2 (p. 291) to read a named file.

// Exercise 8.6.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;
ifstream &printOut( ifstream &in);
istream &print( istream &in);
int main(int argc, _TCHAR* argv[])
{
ifstream in("ReadMe.txt");

print( in);

int i(0);
cin >> i;
return 0;
}
istream &print( istream &in)
{
string s;
while(in >> s , !in.eof())
{
//print out all to standard output
cout << s << endl;
}
if(in.eof())
{
cout << "The end of file!" << endl;
}
// clear the eofbit mark
in.clear( istream::eofbit);

return in;
}

Exercise 8.7:

The two programs we wrote in this section used a break to exit the while loop if the open failed for any file in the vector. Rewrite these two loops to print a warning message if a file can't be opened and continue processing by getting the next file name from the vector.

// Exercise 8.7.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

void process(string s);

int main(int argc, _TCHAR* argv[])
{
vector<string> *files = new vector<string>();
string p[] = {
"Debug",
"Exercise 8.7.cpp",
"Exercise 8.7.vcxproj",
"Exercise 8.7.vcxproj.filters",
"Exercise 8.7.vcxproj.user",
"list.txt",
"ReadMe.txt",
"stdafx.cpp",
"stdafx.h",
"targetver.h"};

for (int i = 0; i < 10 ; ++i)
{
files->push_back( p[i] );
}
ifstream input;
vector<string>::const_iterator it = (*files).begin();
string s;
// for each file in the vector
while (it != (*files).end()) {
input.open(it->c_str()); // open the file
// if the file is ok, read and "process" the input
if (!input)
{
// error , print out message
cout << "To open the file:" << *it << " is failed!" << endl;
}
else
{
while(input >> s) // do the work on this file
process(s);
}
input.close(); // close file when we're done with it
input.clear(); // reset state to ok
++it; // increment iterator to get next file
}

return 0;
}

void process(string s)
{
cout << s << endl;
}

Exercise 8.8:

The programs in the previous exercise can be written without using a continue statement. Write the program with and without using a continue.

see Exercise 8.7

Exercise 8.9:

Write a function to open a file for input and read its contents into a vector of strings, storing each line as a separate element in the vector.

// Exercise 8.9.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <fstream>

using namespace std;

int getline(ifstream &in, string &s);

int main(int argc, _TCHAR* argv[])
{
ifstream in("ReadMe.txt");

vector<string> vect;
char carray[1000];
while(in.getline(carray,1000))
{
vect.push_back(carray);
}

// print out;
vector<string>::iterator it = vect.begin();
while(it != vect.end())
cout << *it++ << endl;

int i;
cin >> i;
return 0;
}

Exercise 8.10:

Rewrite the previous program to store each word in a separate element.

// Exercise 8.10.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, _TCHAR* argv[])
{
ifstream in("ReadMe.txt");

vector<string> vect;
string s;
while( in >> s , !in.eof())
{
vect.push_back(s);
}
// print out
vector<string>::iterator it = vect.begin();
while(it != vect.end())
cout << *it++ << endl;

int i;
cin >> i;

return 0;
}

Exercise 8.11:

In the open_file function, explain why we call clear before the call to open. What would happen if we neglected to make this call? What would happen if we called clear after the open?

Answer: Before open to neglect to call clear, it will fail to open the file if there any error bit was set. After the open to call clear, it will reset the state of the stream just opened.

Exercise 8.12:

In the open_file function, explain what the effect would be if the program failed to execute the close.

Answer: to open the new file will be failed.

Exercise 8.13:

Write a program similar to open_file that opens a file for output.

// Exercise 8.13.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>

using namespace std;
void open_file( ofstream &out, const string filename);
int main(int argc, _TCHAR* argv[])
{
ofstream out;
cout << "Please input the filename to save:" << endl;

string filename = "";
cin >> filename;
open_file(out, filename);
// print out content line by line
if(out)
{
cout << "Please input strings (\\n for break):" << endl;
string line;
while(cin >> line, !cin.eof())
{
if(line == "\\n")
break;
out << line << endl;
}
//out.flush();
// to save to file
out.close();
}

int i ;
cin >> i;

return 0;
}

void open_file( ofstream &out, const string filename)
{
// clear out state
out.clear();
out.close();
// open file
out.open(filename.c_str());
//return
return;
}

Exercise 8.14:

Use open_file and the program you wrote for the first exercise in Section 8.2 (p. 291) to open a given file and read its contents.

// Exercise 8.14.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <fstream>

using namespace std;
void open_file( fstream &out, const string filename);

int main(int argc, _TCHAR* argv[])
{
fstream fst;

string filename;
cout << "Please input the file to open:" << endl;
cin >> filename;

open_file(fst, filename);
if(fst)
{
char line[1000];

while(fst.getline(line,1000))
cout << line << endl;
}

int i;
cin >> i;
return 0;
}

void open_file( fstream &out, const string filename)
{
// clear out state
out.clear();
out.close();
// open file
out.open(filename.c_str());
//return
return;
}

Exercise 8.15:

Use the function you wrote for the first exercise in Section 8.2 (p. 291) to print the contents of an istringstream object.

// Exercise 8.15.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>
#include <sstream>

using namespace std;

void printOut(istream &in);
int main(int argc, _TCHAR* argv[])
{

cout << "Please input strings(\\n for break):" << endl;
string s;
string result;
while(cin >> s)
{
if( s == "\\n")
break;
result+=s;
}
// the result string changed after binding with stringstream will not reflashed to the stream
istringstream istring(result.c_str());
printOut(istring);
int i;
cin >> i;
return 0;
}

void printOut(istream &in)
{
string s;
while(in >> s)
{
cout << s << endl;
}
}

Exercise 8.16:

Write a program to store each line from a file in a vector<string>. Now use an istringstream to read each line from the vector a word at a time.

C++ Primer, Fourth Edition Notes Chapter 7. Exercise

Exercise 7.1:

What is the difference between a parameter and an argument?

Exercise 7.2:

Indicate which of the following functions are in error and why. Suggest how you might correct the problems.

     (a) int f() {
string s;
// ...
return s;
}
(b) f2(int i) { /* ... */ }
(c) int calc(int v1, int v1) /* ... */ }
(d) double square(double x) return x * x;

Exercise 7.3:

Write a program to take two int parameters and generate the result of raising the first parameter to the power of the second. Write a program to call your function passing it two ints. Verify the result.

Exercise 7.4:

Write a program to return the absolute value of its parameter.

// EXE-7.2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <cmath>

using namespace std;

int main(int argc, _TCHAR* argv[])
{
//Exercise 7.2: Indicate which of the following functions are in error and why. Suggest how you might correct the problems.

// (a) int f() {
// string s;
// // ...
// return s;
//}
// should return the int type value
//(b) f2(int i) { /* ... */ }
// should difine the return type
//(c) int calc(int v1, int v1) /* ... */ }
// should add the start block '{'
//(d) double square(double x) return x * x;
// should return the double type value

// Exercise 7.3:
// Write a program to take two int parameters and generate the result of raising the first parameter
// to the power of the second. Write a program to call your function passing it two ints. Verify the result.

long int power(int base,int exponent);
cout << "Please input two integers:\n";
int base(0), exponent(0);
cin >> base >> exponent;
cout << base << "'s power " << exponent << " is " << power(base,exponent) << endl;

int getArgument(int arg);
//Exercise 7.4: Write a program to return the absolute value of its parameter.
int param(0);
cin >> param;
cout << "The parameter is " << getArgument(param) << endl;

int i;
cin >> i;

return 0;
}

int getArgument(int arg)
{
return arg;
}
// How to implement the power efficient?
long int power(int base, int exponent)
{
long int result = 1;
while(exponent--!= 0)
{
result *= base;
}
return result;

//return exp(exponent * log((double)base));
}

Exercise 7.5:

Write a function that takes an int and a pointer to an int and returns the larger of the int value of the value to which the pointer points. What type should you use for the pointer?

// Exercise 7.5.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

int getLarger(int a, int *b);

int main(int argc, _TCHAR* argv[])
{
cout << "Please input two integers:\n";

int a(0), *b = new int();
cin >> a >> *b;
cout << "The larger is " << getLarger(a,b) << endl;

int i;
cin >> i;

return 0;
}
int getLarger(int a, int *b)
{
return (a > *b ? a : *b );
}

Exercise 7.6:

Write a function to swap the values pointed to by two pointers to int. Test the function by calling it and printing the swapped values.

// Exercise 7.6.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>

using namespace std;

int main(int argc, _TCHAR* argv[])
{
cout << "Please input two integers:\n";
int a(0), b(0);
cin >> a >> b;
cout << "Before swap:"
<< " a= " << a
<< " b= " << b << endl;
// here call the swap by swap(a, b); not swap(&a, &b);
swap(a, b);
cout << "After swap:"
<< " a= " << a
<< " b= " << b << endl;

int i;
cin >> i;

return 0;
}

void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}

Exercise 7.7:

Explain the difference in the following two parameter declarations:

     void f(T);
void f(T&);
       void f(T);
// pass parameter as non reference
void f(T&);
// pass parameter as reference

Exercise 7.8:

Give an example of when a parameter should be a reference type. Give an example of when a parameter should not be a reference.

// Exercise 7.8.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

void swap(int &a, int &b);
void print(int a, int b, string strMark);

int main(int argc, _TCHAR* argv[])
{
cout << "Please input two integers:\n";
int a(0),b(0);
cin >> a >> b;

print(a,b,"Before");
//Attention: call swap as swap(a,b);, not swap(&a,&b);
swap(a,b);
print(a,b,"After ");

int i;
cin >> i;

return 0;
}

// should use reference
void swap(int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
// print out
void print(int a, int b, string strMark)
{
cout << strMark << " Swap:"
<< " a=" << a
<< " b=" << b << endl;
}

Exercise 7.9:

Change the declaration of occurs in the parameter list of find_val (defined on page 234) to be a nonreference argument type and rerun the program. How does the behavior of the program change?

//The caller can’t get the occurs value from the callee.

Exercise 7.10:

The following program, although legal, is less useful than it might be. Identify and correct the limitation on this program:

     bool test(string& s) { return s.empty(); }

bool test(const string s) 
{
return s.empty();
}

Exercise 7.11:

When should reference parameters be const? What problems might arise if we make a parameter a plain reference when it could be a const reference?

// the parameter will not be changed by callee, then the parameter should be const


// if the parameter is reference which should be const, the function’s flexible will be limited.


Exercise 7.12:

When would you use a parameter that is a pointer? When would you use a parameter that is a reference? Explain the advantages and disadvantages of each.

To use as pointer, when the parameters will be changed and it can’t be copied.

To use as reference, when the parameters will be changed. Easy to control and use.

Exercise 7.13:

Write a program to calculate the sum of the elements in an array. Write the function three times, each one using a different approach to managing the array bounds.

// Exercise 7.13.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

int sumWithSize( int *array, int size);
int sumWithStartEndPointer(int *start, int *end);
int sumWithEndMark(int *array, int endMark);

int main(int argc, _TCHAR* argv[])
{
int *p = new int[100]();
for (int i = 0; i < 100 ; ++i)
{
p[i] = i % 10;
}
cout << "Sum is " << sumWithSize(p,100) << endl;
cout << "Sum is " << sumWithStartEndPointer(p,p + 100) << endl;
p[99] = -1;
cout << "Sum is " << sumWithEndMark(p,-1);

// release dinamic array memory by delete [] p, not delete p;
delete[] p;
//delete p;
p = 0;

int i ;
cin >> i;

return 0;
}

int sumWithSize( int *array, int size)
{
int result(0);
for (int i = 0; i < size; ++i)
{
result += *( array + i);
}
return result;
}
int sumWithStartEndPointer(int *start, int *end)
{
int result(0);
while( start != end)
{
result += *start++;
//start++
}
return result;
}

int sumWithEndMark(int *array, int endMark)
{
int result(0);
while(*array != endMark)
{
result += *array++;
}
return result;
}

Exercise 7.14:

Write a program to sum the elements in a vector<double>.

// Exercise 7.14.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;

long double sum(vector<double>::const_iterator begin,vector<double>::const_iterator end);
int main(int argc, _TCHAR* argv[])
{
vector<double> *pVect = new vector<double>();
double i(10000.0);
while(i != 0)
(*pVect).push_back(i--);
cout << "The sum is " << sum((*pVect).begin(),(*pVect).end()) << endl;

delete pVect;
pVect = 0;

cin >> i;
return 0;
}

long double sum(vector<double>::const_iterator begin,vector<double>::const_iterator end)
{
long double result(0);
while(begin != end)
{
result += *begin++;
}
return result;
}

Exercise 7.15:

Write a main function that takes two values as arguments and print their sum.

// Exercise 7.15.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
int a(0), b(0);
//convert char array to integers
a = atoi(argv[1]);
b = atoi(argv[2]);
cout << argv[0] << endl;
cout << "The sum is " << a + b << endl;

int i;
cin >> i;

return 0;
}

Exercise 7.16:

Write a program that could accept the options presented in this section. Print the values of the arguments passed to

// Exercise 7.16.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>
#include <vector>

using namespace std;

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

// print out all arguments;
// the argc is the count of the arguments
while( argc != 0)
{
cout << argc << " th arg is " << argv[--argc] << endl;
}
int i(0);
cin >> i;

return 0;
}

Exercise 7.17:

When is it valid to return a reference? A const reference?

To return a reference to non local variable is valid.

For const reference, has the same condition.

Exercise 7.18:

What potential run-time problem does the following function have?

     string &processText() {
string text;
while (cin >> text) { /* ... */ }
// ....
return text;
}

The application will throw “bad alloc exception”, for the text available is cleared when the processText exits.

image 

// Exercise 7.18.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

string &processText();
int main(int argc, _TCHAR* argv[])
{
string result = processText();

cout << result << endl;
int i;
cin >> i;
return 0;
}

string &processText()
{
string text;
while (!getline(cin,text)) {
}
return text;
}

Exercise 7.19:

Indicate whether the following program is legal. If so, explain what it does; if not, make it legal and then explain it:

     int &get(int *arry, int index) { return arry[index]; }
int main() {
int ia[10];
for (int i = 0; i != 10; ++i)
get(ia, i) = 0;
}

//set all array element as 0;

A natural way to solve this problem is recursively:

     // calculate val!, which is 1*2 *3 ... * val
int factorial(int val)
{
if (val > 1)
return factorial(val-1) * val;
return 1;
}
// calculate the valth factorial element value
// Exercise 7.19.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>

using namespace std;

int factorial(int val);
int &get(int *arry, int index) { return arry[index]; }
int main() {
int ia[10];
int i(10);
while(i!= 0)
{
cout << i << "th " << ia[--i] << endl;
}

for (int i = 0; i != 10; ++i)
get(ia, i) = 0;

int j(10);
while(j!= 0)
{
cout << j << "th " << ia[--j] << endl;
}

int f0(10);
cout << factorial(f0) << endl;

int k;
cin >> k;
return 0;
}

// calculate val!, which is 1*2 *3 ... * val
int factorial(int val)
{
if (val > 1)
return factorial(val-1) * val;
return 1;
}

Exercise 7.20:

Rewrite factorial as an iterative function.

 

Exercise 7.21:

What would happen if the stopping condition in factorial were:

     if (val != 0)

Exercise 7.22:

Write the prototypes for each of the following functions:



  1. A function named compare with two parameters that are references to a class named matrix and with a return value of type bool.



  2. A function named change_val that returns a vector<int> iterator and takes two parameters: one is an int and the other is an iterator for a vector<int>.


Hint: When you write these prototypes, use the name of the function as an indicator as to what the function does. How does this hint affect the types you use?

Exercise 7.23:

Given the following declarations, determine which calls are legal and which are illegal. For those that are illegal, explain why.

     double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
vector<int> vec(10);

(a) calc(23.4, 55.1);
(b) count("abcda", 'a');
(c) calc(66);
(d) sum(vec.begin(), vec.end(), 3.8);

 


Exercise 7.24:

Which, if any, of the following declarations are errors? Why?

     (a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char bckgrnd);

Exercise 7.25:

Given the following function declarations and calls, which, if any, of the calls are illegal? Why? Which, if any, are legal but unlikely to match the programmer's intent? Why?

     // declarations
char *init(int ht, int wd = 80, char bckgrnd = ' ');

(a) init();
(b) init(24,10);
(c) init(14, '*');

Exercise 7.26:

Write a version of make_plural with a default argument of 's'. Use that version to print singular and plural versions of the words "success" and "failure".

 


Exercise 7.27:

Explain the differences between a parameter, a local variable and a static local variable. Give an example of a program in which each might be useful.

Exercise 7.28:

Write a function that returns 0 when it is first called and then generates numbers in sequence each time it is called again.

 


Exercise 7.29:

Which one of the following declarations and definitions would you put in a header? In a program text file? Explain why.

     (a) inline bool eq(const BigInt&, const BigInt&) {...}
(b) void putValues(int *arr, int size);

Exercise 7.30:

Rewrite the is Shorter function from page 235 as an inline function.

 


Exercise 7.31:

Write your own version of the Sales_item class, adding two new public members to read and write Sales_item objects. These functions should operate similarly to the input and output operators used in Chapter 1. Transactions should look like the ones defined in that chapter as well. Use this class to read and write a set of transactions.

Exercise 7.32:

Write a header file to contain your version of the Sales_item class. Use ordinary C++ conventions to name the header and any associated file needed to hold non-inline functions defined outside the class.

Exercise 7.33:

Add a member that adds two Sales_items. Use the revised class to reimplement your solution to the average price problem presented in Chapter 1.

 


Exercise 7.34:

Define a set of overloaded functions named error that would match the following calls:

     int index, upperBound;
char selectVal;
// ...
error("Subscript out of bounds: ", index, upperBound);
error("Division by zero");
error("Invalid selection", selectVal);

Exercise 7.35:

Explain the effect of the second declaration in each one of the following sets of declarations. Indicate which, if any, are illegal.

     (a) int calc(int, int);
int calc(const int, const int);

(b) int get();
double get();

(c) int *reset(int *);
double *reset(double *);

Exercise 7.36:

What is a candidate function? What is a viable function?

Exercise 7.37:

Given the declarations for f, determine whether the following calls are legal. For each call list the viable functions, if any. If the call is illegal, indicate whether there is no match or why the call is ambiguous. If the call is legal, indicate which function is the best match.

     (a) f(2.56, 42);
(b) f(42);
(c) f(42, 0);
(d) f(2.56, 3.14);

 


Exercise 7.38:

Given the following declarations,

     void manip(int, int);
double dobj;

what is the rank (Section 7.8.4, p. 272) of each conversion in the following calls?

     (a) manip('a', 'z');    (b) manip(55.4, dobj);

Exercise 7.39:

Explain the effect of the second declaration in each one of the following sets of declarations. Indicate which, if any, are illegal.

     (a) int calc(int, int);
int calc(const int&, const int&);

(b) int calc(char*, char*);
int calc(const char*, const char*);

(c) int calc(char*, char*);
int calc(char* const, char* const);

Exercise 7.40:

Is the following function call legal? If not, why is the call in error?

     enum Stat { Fail, Pass };
void test(Stat);
test(0);