- •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
48 |
Part I: Introduction to C++ Programming |
decisions. (By the same token, that same property makes a computer look really stupid when the program makes the wrong decision.) Making deci sions, right or wrong, requires the use of logical operators.
Using the Simple Logical Operators
The simple logical operators, shown in Table 4-1, evaluate to true or false.
Table 4-1 |
Simple Operators Representing Daily Logic |
Operator Meaning
==Equality; true if the left-hand argument has the same value as the right
!= |
Inequality; opposite of equality |
>, < |
Greater than, less than; true if the left-hand argument is greater |
|
than or less than the right-hand argument |
|
|
>=, <= |
Greater than or equal to, less than or equal to; true if either > or |
== is true, OR either < or == is true
&&AND; true if both the left-and right-hand arguments are true
|| |
OR; true if either the left-or the right-hand argument is true |
! |
NOT; true if its argument is false |
|
|
The first six entries in Table 4-1 are comparison operators. The equality opera tor is used to compare two numbers. For example, the following is true if the value of n is 0, and is false otherwise:
n == 0;
Looks can be deceiving. Don’t confuse the equality operator (==) with the assignment operator (=). Not only is this a common mistake, but it’s a mis take that the C++ compiler generally cannot catch — that makes it more than twice as bad.
n = 0; // programmer meant to say n == 0
The greater-than (>) and less-than (<) operators are similarly common in everyday life. The following expression logical comparison is true:
Chapter 4: Performing Logical Operations |
49 |
int n1 = 1; int n2 = 2; n1 < n2;
It’s easy to forget which operator is “greater than” and which is “less than.” Just remember that the operator is true if the arrow points to the smaller of the two.
You may think that n1 is greater than or less than n2; however, this ignores the possibility that n1 and n2 are equal. The greater-than-or-equal-to opera tor (<=) and the less-than-or-equal-to operator (>=) include that bit of mathe matical nuance. They are similar to the less-than and greater-than operators, with one major exception: They include equality; the other operators don’t.
The && (AND) and || (OR) can combine with the other logic operators, like this:
//true if n2 is greater than n1 but n2 smaller than n3
//(this is the most common way determining that n2 is in
//the range of n1 to n3, exclusive)
(n1 < n2) && (n2 < n3);
Storing logical values
The result of a logical operation can be assigned to a variable of type bool:
int n1 = 1; int n2 = 2;
bool b;
b = (n1 == n2);
This expression highlights the difference between the assignment operator = and the comparison operator ==. The expression says, “Compare the vari ables n1 and n2. Store the results of this comparison in the variable b.”
The assignment operators are about as low down on the precedence totem pole as you can get. The equality operator is executed before the assignment. The parentheses are not required — so the following is an equally valid form of logical confusion:
b = n1 == n2; // compare n1 with n2; generate a true if n1
//if n1 has the same value as n2, false if not
//store the result, true or false, in b
Whoa. Better look at that again. Note the difference between the operators.
50 |
Part I: Introduction to C++ Programming |
The following program demonstrates the use of a bool variable:
//BoolTest - compare variables input from the
//keyboard and store the results off
//into a logical variable
#include <cstdio> #include <cstdlib> #include <iostream> using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
//set output format for bool variables
//to true and false instead
//of 1 and 0 cout.setf(cout.boolalpha);
//initialize two arguments
int nArg1;
cout << “Input value 1: “; cin >> nArg1;
int nArg2;
cout << “Input value 2: “; cin >> nArg2;
bool b; |
|
b = nArg1 == nArg2; |
|
cout << “The statement, “ << nArg1 |
|
<< “ equals “ |
<< nArg2 |
<< “ is “ |
<< b |
<<endl;
//wait until user is ready before terminating program
//to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first line cout.setf() makes sure that our bool variable b is output as “true” or “false”. The next section explains why this is necessary.
The program inputs two values from the keyboard and displays the result of the equality comparison:
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is true
Press any key to continue . . .
Chapter 4: Performing Logical Operations |
51 |
The special value endl inserts a newline. The difference between the value endl and the character ‘\n’ as described in Chapter 2 is subtle and explained in Chapter 24.
Using logical int variables
C++ hasn’t always had a bool type variable. Back in the old days (before that guy on TV kept walking around saying “Can you hear me now?”), C++ used int variables to store logical values. A value of 0 was considered false and all other values true. By the same token, a logical operator generated a 0 for false and a 1 for true. (Thus, 0 was false while 10 > 5 returned a 1.)
C++ retains a high degree of compatibility between bool and int in order to support the older programs that still have that quirk. You get completely different output from the BitTest program if you remove the line cout. setf(cout.boolalpha):
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is 1
Press any key to continue . . .
Variables of type int and bool can be mixed in expressions as well. For exam ple, Dev-C++ allows the following bizarre statement without batting an eyelid:
int n;
n = nArg1 == nArg2;
Continue to use type bool to hold logical values despite this wart that modern C++ inherits from its forefathers. Other compilers may not be as forgiving.
Be careful performing logical operations on floating-point variables
Real numbers are those numbers that can have a fractional part. Because of this, real numbers cannot be counting numbers. That is, you can say the first (1st), second (2nd), third, fourth, and so on because the relationship of 1, 2, and 3 are known exactly. It does not make sense to speak of the 4.5th number in a sequence. (This brings to mind the number between the fourth and fifth, but it has no real meaning.)
Similarly the C++ type float, which is the C++ representation, is not a count ing number. Even worse (unlike a real number), a floating-point number can’t have an infinite number of digits beyond the decimal point if a computer is
52 |
Part I: Introduction to C++ Programming |
going to make any use of it. Because of this limitation, be careful when you use comparison operators on floating-point numbers. Consider the following example:
float f1 = 10.0; float f2 = f1 / 3;
f1 == (f2 * 3.0); // are these two equal?
The comparison in the preceding example is not necessarily true. A floatingpoint variable cannot hold an unlimited number of significant digits. Thus, f2 is not equal to the number we’d call “three-and-a-third,” but rather to 3.3333..., stopping after some number of decimal places.
A float variable supports about 6 digits of accuracy while a double sup ports 13 digits. I say “about” because the computer is likely to generate a number like 3.3333347 due to vagaries in floating point calculations.
Now, in pure math, the number of threes after the decimal point is infinite — but no computer built can handle infinity. So, after multiplying 3.3333 by 3, you get 9.9999 instead of the 10 you’d get if you multiplied “three-and-a-third” — in effect, a round-off error. Such small differences may be unnoticeable to a person, but not to the computer. Equality means exactly that — exact equality.
Modern processors are very sophisticated in performing such calculations. The processor may, in fact, accommodate the round-off error, but from inside C++, you can’t predict exactly what any given processor will do.
Problems can arise even in a straightforward calculation, such as the following:
float f1 |
= |
10.0; |
|
float |
f2 |
= |
100 % 30; |
f1 == |
f2; |
|
// are these two equal? |
Theoretically, f1 and f2 should be equal (after you apply that percentlike operator that Chapter 3 identifies as modulus). There doesn’t appear to be any problem with round off. So far. But you can’t be sure — you have no idea how the computer that eventually runs your program is going to represent floating-point numbers internally. To flatly claim that there’s no round-off error lurking here makes unwarranted assumptions about CPU internals.
The safer comparison is as follows:
float f1 = 10.0; float f2 = f1 / 3;
float f3 = f2 * 3.0;
(f1 - f3) < 0.0001 && (f3 - f1) < 0.0001;