- •Table of Contents
- •Introduction
- •What Is C++?
- •Conventions Used in This Book
- •How This Book Is Organized
- •Part I: Introduction to C++ Programming
- •Part III: Introduction to Classes
- •Part IV: Inheritance
- •Part V: Optional Features
- •Part VI: The Part of Tens
- •Icons Used in This Book
- •Where to Go from Here
- •Grasping C++ Concepts
- •How do I program?
- •Installing Dev-C++
- •Setting the options
- •Creating Your First C++ Program
- •Entering the C++ code
- •Building your program
- •Executing Your Program
- •Dev-C++ is not Windows
- •Dev-C++ help
- •Reviewing the Annotated Program
- •Examining the framework for all C++ programs
- •Clarifying source code with comments
- •Basing programs on C++ statements
- •Writing declarations
- •Generating output
- •Calculating Expressions
- •Storing the results of expression
- •Declaring Variables
- •Declaring Different Types of Variables
- •Reviewing the limitations of integers in C++
- •Solving the truncation problem
- •Looking at the limits of floating-point numbers
- •Declaring Variable Types
- •Types of constants
- •Special characters
- •Are These Calculations Really Logical?
- •Mixed Mode Expressions
- •Performing Simple Binary Arithmetic
- •Decomposing Expressions
- •Determining the Order of Operations
- •Performing Unary Operations
- •Using Assignment Operators
- •Why Mess with Logical Operations?
- •Using the Simple Logical Operators
- •Storing logical values
- •Using logical int variables
- •Be careful performing logical operations on floating-point variables
- •Expressing Binary Numbers
- •The decimal number system
- •Other number systems
- •The binary number system
- •Performing Bitwise Logical Operations
- •The single bit operators
- •Using the bitwise operators
- •A simple test
- •Do something logical with logical calculations
- •Controlling Program Flow with the Branch Commands
- •Executing Loops in a Program
- •Looping while a condition is true
- •Using the for loop
- •Avoiding the dreaded infinite loop
- •Applying special loop controls
- •Nesting Control Commands
- •Switching to a Different Subject?
- •Writing and Using a Function
- •Divide and conquer
- •Understanding the Details of Functions
- •Understanding simple functions
- •Understanding functions with arguments
- •Overloading Function Names
- •Defining Function Prototypes
- •Variable Storage Types
- •Including Include Files
- •Considering the Need for Arrays
- •Using an array
- •Initializing an array
- •Accessing too far into an array
- •Using arrays
- •Defining and using arrays of arrays
- •Using Arrays of Characters
- •Creating an array of characters
- •Creating a string of characters
- •Manipulating Strings with Character
- •String-ing Along Variables
- •Variable Size
- •Address Operators
- •Using Pointer Variables
- •Comparing pointers and houses
- •Using different types of pointers
- •Passing Pointers to Functions
- •Passing by value
- •Passing pointer values
- •Passing by reference
- •Limiting scope
- •Examining the scope problem
- •Providing a solution using the heap
- •Defining Operations on Pointer Variables
- •Re-examining arrays in light of pointer variables
- •Applying operators to the address of an array
- •Expanding pointer operations to a string
- •Justifying pointer-based string manipulation
- •Applying operators to pointer types other than char
- •Contrasting a pointer with an array
- •Declaring and Using Arrays of Pointers
- •Utilizing arrays of character strings
- •Identifying Types of Errors
- •Choosing the WRITE Technique for the Problem
- •Catching bug #1
- •Catching bug #2
- •Calling for the Debugger
- •Defining the debugger
- •Finding commonalities among us
- •Running a test program
- •Single-stepping through a program
- •Abstracting Microwave Ovens
- •Preparing functional nachos
- •Preparing object-oriented nachos
- •Classifying Microwave Ovens
- •Why Classify?
- •Introducing the Class
- •The Format of a Class
- •Accessing the Members of a Class
- •Activating Our Objects
- •Simulating real-world objects
- •Why bother with member functions?
- •Adding a Member Function
- •Creating a member function
- •Naming class members
- •Calling a Member Function
- •Accessing a member function
- •Accessing other members from a member function
- •Defining a Member Function in the Class
- •Keeping a Member Function After Class
- •Overloading Member Functions
- •Defining Arrays of and Pointers to Simple Things
- •Declaring Arrays of Objects
- •Declaring Pointers to Objects
- •Dereferencing an object pointer
- •Pointing toward arrow pointers
- •Passing Objects to Functions
- •Calling a function with an object value
- •Calling a function with an object pointer
- •Calling a function by using the reference operator
- •Returning to the Heap
- •Comparing Pointers to References
- •Linking Up with Linked Lists
- •Performing other operations on a linked list
- •Hooking up with a LinkedListData program
- •A Ray of Hope: A List of Containers Linked to the C++ Library
- •Protecting Members
- •Why you need protected members
- •Discovering how protected members work
- •Protecting the internal state of the class
- •Using a class with a limited interface
- •Creating Objects
- •Using Constructors
- •Why you need constructors
- •Making constructors work
- •Dissecting a Destructor
- •Why you need the destructor
- •Working with destructors
- •Outfitting Constructors with Arguments
- •Justifying constructors
- •Using a constructor
- •Defaulting Default Constructors
- •Constructing Class Members
- •Constructing a complex data member
- •Constructing a constant data member
- •Constructing the Order of Construction
- •Local objects construct in order
- •Static objects construct only once
- •Global objects construct in no particular order
- •Members construct in the order in which they are declared
- •Destructors destruct in the reverse order of the constructors
- •Copying an Object
- •Why you need the copy constructor
- •Using the copy constructor
- •The Automatic Copy Constructor
- •Creating Shallow Copies versus Deep Copies
- •Avoiding temporaries, permanently
- •Defining a Static Member
- •Why you need static members
- •Using static members
- •Referencing static data members
- •Uses for static data members
- •Declaring Static Member Functions
- •What Is This About, Anyway?
- •Do I Need My Inheritance?
- •How Does a Class Inherit?
- •Using a subclass
- •Constructing a subclass
- •Destructing a subclass
- •Having a HAS_A Relationship
- •Why You Need Polymorphism
- •How Polymorphism Works
- •When Is a Virtual Function Not?
- •Considering Virtual Considerations
- •Factoring
- •Implementing Abstract Classes
- •Describing the abstract class concept
- •Making an honest class out of an abstract class
- •Passing abstract classes
- •Factoring C++ Source Code
- •Defining a namespace
- •Implementing Student
- •Implementing an application
- •Project file
- •Creating a project file under Dev-C++
- •Comparing Operators with Functions
- •Inserting a New Operator
- •Overloading the Assignment Operator
- •Protecting the Escape Hatch
- •How Stream I/O Works
- •The fstream Subclasses
- •Reading Directly from a Stream
- •Using the strstream Subclasses
- •Manipulating Manipulators
- •Justifying a New Error Mechanism?
- •Examining the Exception Mechanism
- •What Kinds of Things Can I Throw?
- •Adding Virtual Inheritance
- •Voicing a Contrary Opinion
- •Generalizing a Function into a Template
- •Template Classes
- •Do I Really Need Template Classes?
- •Tips for Using Templates
- •The string Container
- •The list Containers
- •Iterators
- •Using Maps
- •Enabling All Warnings and Error Messages
- •Insisting on Clean Compiles
- •Limiting the Visibility
- •Avoid Overloading Operators
- •Heap Handling
- •Using Exceptions to Handle Errors
- •Avoiding Multiple Inheritance
- •Customize Editor Settings to Your Taste
- •Highlight Matching Braces/Parentheses
- •Enable Exception Handling
- •Include Debugging Information (Sometimes)
- •Create a Project File
- •Customize the Help Menu
- •Reset Breakpoints after Editing the File
- •Avoid Illegal Filenames
- •Include #include Files in Your Project
- •Executing the Profiler
- •System Requirements
- •Using the CD with Microsoft Windows
- •Using the CD with Linux
- •Development tools
- •Program source code
- •Index
|
|
|
Chapter 24: Using Stream I/O 315 |
|
|
|
|
||
|
|
|
|
|
|
Table 24-1 |
Standard Stream I/O Objects |
|
|
|
Object |
Class |
Purpose |
|
|
cin |
istream |
Standard input |
|
|
|
|
|
|
|
cout |
ostream |
Standard output |
|
|
|
|
|
|
|
cerr |
ostream |
Standard error output |
|
|
|
|
|
|
|
clog |
ostream |
Standard printer output |
|
|
|
|
|
|
The fstream Subclasses
The subclasses ofstream, ifstream, and fstream are defined in the include file fstream.h to perform stream input and output to a disk file. These three classes offer a large number of member functions. A complete list is provided with your compiler documentation, but let me get you started.
Class ofstream, which is used to perform file output, has several construc tors, the most useful of which is the following:
ofstream::ofstream(char *pszFileName, int mode = ios::out,
int prot = filebuff::openprot);
The first argument is a pointer to the name of the file to open. The second and third arguments specify how the file will be opened. The legal values for mode are listed in Table 24-2, and those for prot are in Table 24-3. These values are bit fields that are ORed together (the classes ios and filebuff are both parent classes of ostream). (See Chapter 4 for an explanation of the ORing of bit fields.)
The expression ios::out refers to a static data member of the class ios.
Table 24-2 |
Constants Defined in ios to Control |
|
How Files Are Opened |
Flag |
Meaning |
ios::app |
Append to the end of the line. Generate an error if |
|
the file doesn’t already exist. |
ios::ate |
Append to the end of the file, if it exists. |
|
|
ios::in |
Open file for input (implied for istream). |
|
|
(continued)
316 Part V: Optional Features
Table 24-2 (continued)
Flag |
Meaning |
ios::out |
Open file for output (implied for ostream). |
|
|
ios::trunc |
Truncate file if it exists (default). |
|
|
ios::noreplace |
If file does exist, return error. |
|
|
ios::binary |
Open file in binary mode (alternative is text mode). |
|
|
Table 24-3 |
Values for prot in the ofstream Constructor |
|
Flag |
|
Meaning |
filebuf::openprot |
Compatibility sharing mode |
|
|
|
|
filebuf::sh_none |
|
Exclusive; no sharing |
|
|
|
filebuf::sh_read |
|
Read sharing allowed |
|
|
|
filebuf::sh_write |
Write sharing allowed |
|
|
|
|
For example, the following StreamOutput program opens the file MyName.txt and then writes some important and absolutely true information to that file:
// StreamOutput - simple output to a file #include <fstream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
ofstream my(“MyName.txt”);
my << “Stephen Davis is suave and handsome\n”
<<“and definitely not balding prematurely”
<<endl;
system(“PAUSE”); return 0;
}
The constructor ofstream::ofstream(char*) expects only a filename and provides defaults for the other file modes. If the file MyName.txt already exists, it is truncated; otherwise, MyName.txt is created. In addition, the file is opened in compatibility sharing mode.
Referring to Table 24-2, if I wanted to open the file in binary mode and append to the end of the file if the file already exists, I would create the
Chapter 24: Using Stream I/O 317
ostream object as follows. (In binary mode, newlines are not converted to carriage returns and line feeds on output, nor are carriage returns and line feeds converted back to newlines on input.)
void fn()
{
//open the binary file BINFILE for writing; if it //exists, append to end of whatever’s already there
ofstream bfile(“BINFILE”, ios::binary | ios::ate); //...continue on as before...
}
The stream objects maintain state information about the I/O process. The member function bad() returns a TRUE if something “bad” happens. That neb ulous term means that the file couldn’t be opened, some internal object was messed up, or things are just generally hosed. A lesser error fail() indicates that either something bad() happened or the last read failed — for example, if you try to read an int and all the program can find is a character that rates a fail() but not a bad(). The member function good() returns TRUE if both bad() and fail() are FALSE. The member function clear() zeros out the error flag to give you another chance. The following program adds basic error checking to the StreamOutput program:
// StreamOutputWithErrorChecking - simple output to a file #include <fstream>
#include <iostream> using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
const static char fileName[] = “MyName.txt”; ofstream my(fileName);
if (my.bad()) //if the open didn’t work...
{
cerr << “Error opening file “
<<fileName
<<endl;
return 0; |
//...output error and quit |
}
my << “Stephen Davis is suave and handsome\n”
<<“and definitely not balding prematurely”
<<endl;
if (my.bad())
{
cerr << “Error writing to file “
<<fileName
<<endl;
}
system(“PAUSE”); return 0;
}
318 Part V: Optional Features
All attempts to output to an ofstream object that has an error have no effect if my.bad() is true.
This last paragraph is meant quite literally — no output is possible as long as the internal error state is non-zero. The program won’t even try until you call clear() to clear the error flags.
The destructor for class ofstream automatically closes the file. In the pre ceding example, the file was closed when the function exited.
Class ifstream works much the same way for input, as the following exam ple demonstrates:
// StreamInput - simple input from a file using fstream #include <fstream>
#include <iostream> using namespace std;
ifstream* openFile()
{
ifstream* pFileStream = 0; for(;;)
{
// open the file specified by the user char fileName[80];
cout << “Enter the name of a file with integers” << endl;
cin >> fileName;
//open file for reading; don’t create the file //if it isn’t there
pFileStream = new ifstream(fileName); if (pFileStream->good())
{
break;
}
cerr << “Couldn’t open “ << fileName << endl; delete pFileStream;
}
return pFileStream;
}
int main(int nNumberofArgs, char* pszArgs[])
{
// get a file stream
ifstream* pFileStream = openFile();
// stop when no more data in file while (!pFileStream->eof())
{
// read a value int nValue = 0;
Chapter 24: Using Stream I/O 319
(*pFileStream) >> nValue;
//stop if the file read failed (probably because
//we ran upon something that’s not an int or
//because we found a new line with nothing after it) if (pFileStream->fail())
{
break;
}
//output the value just read
cout << nValue << endl;
}
system(“PAUSE”); return 0;
}
The function openFile() prompts the user for the name of a file to open. The function creates an ifstream() object with the specified name. Creating an ifstream object automatically opens the file for input. If the file is opened properly, the function returns a pointer to the ifstream object to use for reading. Otherwise, the program deletes the object and tries again. The only way to get out of the loop is to enter a valid filename or abort the program.
Don’t forget to delete the pFileStream object if the open fails. These are the sneaky ways that memory leaks creep in.
The program reads integer values from the object pointed at by pFileStream until either fail() or the program reaches the End-Of-File as indicated by the member function eof(). An attempt to read an ifstream object that has the error flag set, indicating a previous error, returns immediately without reading anything.
Let me warn you one more time: Not only is nothing returned from reading an input stream that has an error, but also the buffer comes back unchanged. This program can easily come to the false conclusion that it has just read the same value it previously read. Furthermore, eof() will never return a true on an input stream that has an error.
The output from this program appears as follows (I added boldface to my input):
Enter the name of a file with integers chicken
Couldn’t open chicken
Enter the name of a file with integers
integers.txt
1
2
3