- •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 325
The result from a sample execution appears as follows:
=============================
Chester, 12345 56.60
name = Chester,account = 12345, balance = 56.6 Chester, 56.6 12345
=============================
Arthur, 34567 67.50
name = Arthur,account = 34567, balance = 67.5 Arthur, 67.5 34567
=============================
Trudie, 56x78 78.90 Error parsing string
=============================
Valerie, 78901 89.10
name = Valerie,account = 78901, balance = 89.1 Valerie, 89.1 78901
=============================
Press any key to continue . . .
Reflect a second before continuing. Notice how the program was able to resynch itself after the error in the input file. Notice, also, the simplicity of the heart of the program, the parseString() function. Consider what this function would look like without the benefit of the istringstream class.
Manipulating Manipulators
You can use stream I/O to output numbers and character strings by using default formats. Usually the defaults are fine, but sometimes they don’t cut it.
For example, I was less than tickled when the total from the result of a financial calculation from a recent program appeared as 249.600006 rather than 249.6 (or, better yet, 249.60). There must be a way to bend the defaults to my desires. True to form, C++ provides not one but two ways to control the format of output.
Depending on the default settings of your compiler, you may get 249.6 as your output. Nevertheless, you really want 249.60.
First, you can control the format by invoking a series of member functions on the stream object. For example, the number of significant digits to display is set by using the function precision() as follows:
326 Part V: Optional Features
#include <iostream.h>
void fn(float interest, float dollarAmount)
{
cout << “Dollar amount = “; cout.precision(2);
cout << dollarAmount; cout.precision(4); cout << interest
<< “\n”;
}
In this example, the function precision() sets the precision to 2 immedi ately before outputting the value dollarAmount. This gives you a number such as 249.60, the type of result you want. It then sets the precision to 4 before outputting the interest.
A second approach uses what are called manipulators. (Sounds like someone behind the scenes of the New York Stock Exchange, doesn’t it? Well, manipula tors are every bit as sneaky.) Manipulators are objects defined in the include file iomanip.h to have the same effect as the member function calls. (You must include iomanip.h to have access to the manipulators.) The only advantage to manipulators is that the program can insert them directly into the stream rather than resort to a separate function call.
If you rewrite the preceding example to use manipulators, the program appears as follows:
#include <iostream.h> #include <iomanip.h>
void fn(float interest, float dollarAmount)
{
cout << “Dollar amount = “
<<setprecision(2) << dollarAmount
<<setprecision(4) << interest
<<“\n”;
}
The most common manipulators and their corresponding meanings are shown in Table 24-4.
Table 24-4 |
Common Manipulators and Stream |
|
|
Format Control Functions |
|
Manipulator |
Member Function |
Description |
dec |
flags(10) |
Set radix to 10 |
|
|
|
hex |
flags(16) |
Set radix to 16 |
|
|
|
|
|
|
Chapter 24: Using Stream I/O 327 |
|
|
|
|
||
|
|
|
|
|
|
Manipulator |
Member Function |
Description |
|
|
oct |
flags(8) |
Set radix to 8 |
|
|
|
|
|
|
|
setfill(c) |
fill(c) |
Set the fill character to c |
|
|
|
|
|
|
|
setprecision(c) |
precision(c) |
Set display precision to c |
|
|
|
|
|
|
|
setw(n) |
width(n) |
Set width of field to n characters* |
|
|
|
|
|
|
*This returns to its default value after the next field is output.
Watch out for the width parameter (width() function and setw() manipula tor). Most parameters retain their value until they are specifically reset by a subsequent call, but the width parameter does not. The width parameter is reset to its default value as soon as the next output is performed. For exam ple, you might expect the following to produce two eight-character integers:
#include |
<iostream.h> |
|
#include <iomanip.h> |
|
|
void fn() |
|
|
{ |
|
|
cout << setw(8) |
// width is 8... |
|
|
<< 10 |
//...for the 10, but... |
|
<< 20 |
//...default for the 20 |
} |
<< “\n”; |
|
|
|
|
|
|
|
What you get, however, is an eight-character integer followed by a twocharacter integer. To get two eight-character output fields, the following is necessary:
#include <iostream.h> #include <iomanip.h> void fn()
{
cout << |
setw(8) |
// set the width... |
<< |
10 |
|
<< |
setw(8) |
//...now reset it |
<<20
<<“\n”;
}
Thus, if you have several objects to output and the default width is not good enough, you must include a setw() call for each object.
Which way is better, manipulators or member function calls? Member func tions provide a bit more control because there are more of them. In addition, the member functions always return the previous setting so you know how to restore it (if you want to). Finally, a query version of each member function exists to enable you to just ask what the current setting is without changing it, as shown in the following example:
328 Part V: Optional Features
#include <iostream.h> void fn(float value)
{
int previousPrecision;
//...doing stuff here...
//you can ask what the current precision is: previousPrecision = cout.precision();
//or you can save the old value when you change it previousPrecision = cout.precision(2);
cout << value;
//now restore the precision to previous value cout.precision(previousPrecision);
//...do more neat stuff...
}
Even with all these features, the manipulators are more common than member function calls, probably because they look neat. Use whatever you prefer, but be prepared to see both in other peoples’ code.
Chapter 25
Handling Errors — Exceptions
In This Chapter
Introducing an exceptional way of handling program errors
Finding what’s wrong with good ol’ error returns
Examining throwing and catching exceptions
Packing more heat into that throw
Iknow that it’s hard to accept, but occasionally functions don’t work prop erly — not even mine. The traditional means of reporting failure is to return
some indication to the caller. C++ includes a new, improved mechanism for cap turing and handling errors called exceptions. An exception is “a case in which
a rule or principle does not apply.” Exception is also defined as an objection to something. Either definition works: An exception is an unexpected (and presumably objectionable) condition that occurs during the execution of the program.
The exception mechanism is based on the keywords try, catch, and throw (that’s right, more variable names that you can’t use). In outline, it works like this: A function trys to get through a piece of code. If the code detects a prob lem, it throws an error indication that the calling function must catch.
The following code snippet demonstrates how that works in 1s and 0s:
//
//FactorialException - demonstrate exceptions using
// |
a factorial function |
// |
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <iostream> |
|
using namespace std; |
|
// factorial - compute factorial int factorial(int n)
{
//you can’t handle negative values of n;
//better check for that condition first if (n < 0)
330 Part V: Optional Features
{
throw string(“Argument for factorial negative”);
}
// go ahead and calculate factorial int accum = 1;
while(n > 0)
{
accum *= n; n--;
}
return accum;
}
int main(int nNumberofArgs, char* pszArgs[])
{
try
{
// this will work
cout << “Factorial of 3 is “ << factorial(3) << endl;
// this will generate an exception
cout << “Factorial of -1 is “ << factorial(-1) << endl;
// control will never get here
cout << “Factorial of 5 is “ << factorial(5) << endl;
}
// control passes here catch(string error)
{
cout << “Error occurred: “ << error << endl;
}
catch(...)
{
cout << “Default catch “ << endl;
}
//wait until user is ready before terminating program
//to allow the user to see the program results system(“PAUSE”);
return 0;
}
main() starts out by creating a block outfitted with the try keyword. Within this block, it can proceed on the way it would if the block were not present. In this case, main() attempts to calculate the factorial of a negative number. Not to be hoodwinked, the clever factorial() function detects the bogus request and throws an error indication using the throw keyword. Control passes to the catch phrase, which immediately follows the closing brace of the try block. The second call to factorial() is not performed.