- •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
320 Part V: Optional Features
4
5
6
Press any key to continue . . .
Reading Directly from a Stream
The inserter and extracter operators provide a convenient mechanism for reading formatted input. However, there are times when you just want to say, “give it to me, I don’t care what the format is.” There are two methods that are useful in this context. The function getline() returns a string of characters up until some terminator — the default is a newline. getline() strips off the terminator but makes no other attempt to reformat or otherwise interpret the input.
The member function read() is even more fundamental. This function reads the number of characters that you specify, or less if the program encounters an end-of-file. The function gcount() always returns the actual number of characters read.
The following program uses both getLine() and read() to open a file with random contents and spit them out to the display.
// FileInput - read blocks of data from a file #include <fstream>
#include <iostream> using namespace std;
ifstream* openFile(istream& input)
{
for(;;)
{
//open the file specified by the user char fileName[80];
cout << “Enter the name of a file” << endl;
//read input from the user in such a way
//that the input can’t overflow the buffer input.getline(fileName, 80);
//open file for reading; don’t create the file //if it isn’t there
ifstream* pFileStream = new ifstream(fileName); if (pFileStream->good())
{
return pFileStream;
}
cerr << “Couldn’t find “ << fileName << endl;
}
Chapter 24: Using Stream I/O 321
return 0;
}
int main(int nNumberofArgs, char* pszArgs[])
{
// get a file stream
ifstream* pFileStream = openFile(cin);
// read blocks of data 80 bytes at a time char buffer[80];
while (!pFileStream->eof() && pFileStream->good())
{
//read a block - 80 is the max but gcount() returns
//the actual number of bytes read
pFileStream->read(buffer, 80);
int noBytes = pFileStream->gcount();
// do something with the block for(int i = 0; i < noBytes; i++)
{
cout << buffer[i];
}
}
system(“PAUSE”); return 0;
}
The FileInput program first invokes openFile() to open a file. This version demonstrates two interesting aspects. First, the function reads from an istream object in the same way that it would read from cin. In fact, the main() function passes the cin object. However, a function that uses an arbitrary istream object can read from input files without modification.
The openFile() uses the getline() member function to read a string. One of the arguments to the function is the size of the buffer. getline() will not read beyond this point. Thus, getline(fileName, 80) reads up to the end of the line but not more than 80 characters and stores the result into the character buffer fileName.
Using the getline() function to read keyboard input is safer than using the extractor when reading into a simple character array — the extractor can read beyond the end of the array. The getline() function will not read more than the number of characters you specify.
The main() function reads 80 byte blocks from the file stream object returned from openFile(). The program checks the actual number of characters read using the gcount() function. The number returned from gcount() will never be more than the 80 bytes specified in the call to read() and will only be less when the program reaches the end-of-file. The program uses the conventional inserter to display the characters read.
322 Part V: Optional Features
The FileInput program simply outputs the contents of the file that you specify as shown in the following sample run:
Enter the name of a file MyName.txt
Stephen Davis is suave and handsome and definitely not balding prematurely Press any key to continue . . .
What’s Up with endl?
Most programs in this book terminate an output stream by inserting the object endl. However, some programs include a \n within the text to output a newline. What’s the deal?
The \n is, in fact, the newline character. The expression cout << “First line\nSecond line; outputs two lines. The endl object outputs a newline, but continues one step further.
Disks are slow devices. Writing to disk more often than necessary will slow your program down considerably. To avoid this, the fstream class collects up output into an internal buffer. The class writes the contents to disk when the buffer is full (this is known as flushing the buffer). The endl object auto matically flushes the output buffer. The member function flush() flushes the output buffer without tacking a newline onto the end.
Using the strstream Subclasses
The stream classes give the programmer mechanisms for easily breaking input among int, float, and char array variables (among others). A set of so-called “string stream” classes allow the program to “read” from an array of characters in memory. The classes istringstream and ostringstream are defined in the include file sstream.
The older versions of these are classes are istrstream and ostrstream defined in the include file strstream.
The string stream classes have the same semantics as the corresponding file based classes. This is demonstrated in the following StringStream program that parses account information from a file:
// StringStream - read and parse the contents of a file #include <fstream>
#include <sstream> #include <iostream>
Chapter 24: Using Stream I/O 323
using namespace std; |
|
// parseAccountInfo - read a passed buffer as if it were |
|
// |
an actual file - read the following |
// |
format: |
// |
name, account balance |
// |
return true if all worked well |
bool parseString(char* pString, char* pName, int arraySize, |
|
{ |
long& accountNum, double& balance) |
|
//associate an istrstream object with the input
//character string
istringstream inp(pString);
//read up to the comma separator inp.getline(pName, arraySize, ‘,’);
//now the account number
inp >> accountNum;
//and the balance inp >> balance;
//return the error status return !inp.fail();
}
int main(int nNumberofArgs, char* pszArgs[])
{
// get a file stream
ifstream* pFileStream = new ifstream(“Accounts.txt”); if (!pFileStream->good())
{
cout << “Can’t open Accounts.txt” << endl; return 0;
}
// read a line out of file, parse it and display results for(;;)
{
// add a divider
cout << “=============================” << endl;
//read a buffer char buffer[256];
pFileStream->getline(buffer, 256); if (pFileStream->fail())
{
break;
}
//parse the individual fields char name[80];
long accountNum;
324 Part V: Optional Features
double balance;
bool result = parseString(buffer, name, 80, accountNum, balance);
//output the result cout << buffer << “\n”; if (result == false)
{
cout << “Error parsing string\n”; continue;
}
cout << “name = “ << name << “,”
<<“account = “ << accountNum << “, “
<<“balance = “ << balance << endl;
//put the fields back together in a different
//order (inserting the ‘ends’ makes sure the
//buffer is null terminated
ostringstream out; out << name << “, “
<<balance << “ “
<<accountNum << ends;
//output the result - istringstream also works with
//the string class but I have been staying with
//character arrays until the discussion of the templates
string oString = out.str(); cout << oString << “\n” << endl;
}
system(“PAUSE”); return 0;
}
This program begins by opening a file called Accounts.txt containing account information in the format of: name, accountNumber, balance,\n. Assuming that the file was opened successfully, the program enters a loop, reading lines until the contents of the file are exhausted. The call to getline() reads up to the default newline terminator. The program passes the line just read to the function parseString().
parseString() associates an istringstream object with the character string. The program reads characters up to the ‘,’ (or the end of the string buffer) using the getline() member function. The program then uses the conventional extractors to read accountNum and balance. The reads from inp will have worked if inp.fail() returns a false.
After the call to parseString(), main() outputs the buffer read from the file followed by the parsed values. It then uses the ostringstream class to reconstruct a string object with the same data but a different format.