- •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
40 |
Part I: Introduction to C++ Programming |
Performing Simple Binary Arithmetic
A binary operator is one that has two arguments. If you can say var1 op var2, op must be a binary operator. The most common binary operators are the simple operations you performed in grade school. The binary operators are flagged in Table 3-1.
Table 3-1 |
Mathematical Operators in Order of Precedence |
|
Precedence |
Operator |
Meaning |
1 |
+ (unary) |
Effectively does nothing |
|
|
|
1 |
- (unary) |
Returns the negative of its argument |
|
|
|
2 |
++ (unary) |
Increment |
|
|
|
2 |
-- (unary) |
Decrement |
|
|
|
3 |
* (binary) |
Multiplication |
|
|
|
3 |
/ (binary) |
Division |
|
|
|
3 |
% (binary) |
Modulo |
|
|
|
4 |
+ (binary) |
Addition |
|
|
|
4 |
- (binary) |
Subtraction |
|
|
|
5 |
=, *=,%=,+=,-= (special) |
Assignment types |
|
|
|
Multiplication, division, modulus, addition, and subtraction are the operators used to perform arithmetic. In practice, they work just like the familiar arith metic operations as well. For example, using the binary operator for division with a float variable looks like this:
float var = 133 / 12;
Each of the binary operators has the conventional meaning that you studied in grammar school — with one exception. You may not have encountered modulus in your studies.
The modulus operator (%) works much like division, except it produces the remainder after division instead of the quotient. For example, 4 goes into 15 three times with a remainder of 3. Expressed in C++ terms, 15 modulus 4 is 3.
int var = 15 % 4; // var is initialized to 3
Chapter 3: Performing Mathematical Operations |
41 |
Because programmers are always trying to impress nonprogrammers with the simplest things, C++ programmers define modulus as follows:
IntValue % IntDivisor
This expression is equal to
IntValue - (IntValue / IntDivisor) * IntDivisor
Try it out on this example:
15 % 4 is equal to 15 - (15/4) * 4 15 - 3 * 4
15 - 12
3
Modulus is not defined for floating-point variable because it depends on the round-off error inherent in integers. (I discuss round-off errors in Chapter 2.)
Decomposing Expressions
The most common type of statement in C++ is the expression. An expression is a C++ statement with a value. Every expression has a type (such as int, double, char, and so on). A statement involving any mathematical operator is an expression since all these operators return a value. For example, 1 + 2 is an expression whose value is 3 and type is int. (Remember that constants without decimal points are ints.)
Expressions can be complex or extremely simple. In fact, the statement 1 is an expression because it has a value (1) and a type (int). There are five expres sions in the following statement:
z = x * y + w;
The expressions are
x * y + w x * y
x y w
42 |
Part I: Introduction to C++ Programming |
An unusual aspect of C++ is that an expression is a complete statement. Thus, the following is a legal C++ statement:
1;
The type of the expression 1 is int.
Determining the Order of Operations
All operators perform some defined function. In addition, every operator has a precedence — a specified place in the order in which the expressions are evaluated. Consider, for example, how precedence affects solving the follow ing problem:
int var = 2 * 3 + 1;
If the addition is performed before the multiplication, the value of the expres sion is 2 times 4 or 8. If the multiplication is performed first, the value is 6 + 1 or 7.
The precedence of the operators determines who goes first. Table 3-1 shows that multiplication has higher precedence than addition, so the result is 7. (The concept of precedence is also present in arithmetic. C++ adheres to the common arithmetic precedence.)
So what happens when we use two operators of the same precedence in the same expression? Well, it looks like this:
int var = 8 / 4 / 2;
But is this 8 divided by 2 or 4, or is it 2 divided by 2 or 1? When operators of the same precedence appear in the same expression, they are evaluated from left to right (the same rule applied in arithmetic). Thus, the answer is 8 divided by 4, which is 2 divided by 2 (which is 1).
The expression
x / 100 + 32
divides x by 100 before adding 32. But what if the programmer wanted to divide x by 100 plus 32? The programmer can change the precedence by bundling expressions together in parentheses (shades of algebra!), as follows:
x/(100 + 32)
Chapter 3: Performing Mathematical Operations |
43 |
This expression has the same effect as dividing x by 132.
The original expression
x / 100 + 32
is identical to the expression
(x/100) + 32
In a given expression, C++ normally performs multiplication and division before addition or subtraction. Multiplication and division have higher precedence than addition and subtraction.
In summary: Precedence refers to the order in which operators are evaluated. An operator with higher precedence is executed first. You can override the precedence of an operator by using parentheses.
Performing Unary Operations
Arithmetic binary operators — those operators that take two arguments — are familiar to a lot of us from school days. You’ve probably been doing binary operations since the first grade in school. But consider the unary operators, which take a single argument (for example, –a). Many unary operations are not so well known.
The unary mathematical operators are plus, plus-plus, minus, and minus-minus (respectively, +, –, ++, and – –). Thus
int var1 = 10;
int var2 = -var1;
The latter expression uses the minus unary operator (–) to calculate the value negative 10.
The minus operator changes the sign of its argument. Positive numbers become negative and vice versa. The plus operator does not change the sign of its argument. It wouldn’t be weird to say the plus operator has no effect at all.
The ++ and the – – operators might be new to you. These operators (respec tively) add one to their arguments or subtract one from their arguments, so they’re known (also respectively) as the increment and decrement operators.
44 |
Part I: Introduction to C++ Programming |
Because they’re dependent upon numbers that can be counted, they’re lim ited to non-floating-point variables. For example, the value of var after exe cuting the following expression is 11.
int var = 10; // initalize var var++; // now increment it
// value of var is now 11
The increment and decrement operators are peculiar in that both come in two flavors: a prefix version and a postfix version (known as pre-increment and post-increment, respectively). Consider, for example, the increment operator (the decrement works in exactly the same way).
Suppose that the variable n has the value 5. Both ++n and n++ increment n to the value 6. The difference between the two is that the value of ++n in an expression is 6 while the value of n++ is 5. The following example illustrates this difference:
//declare three integer variables int n1, n2, n3;
//the value of both n1 and n2 is 6 n1 = 5;
n2 = ++n1;
//the value of n1 is 6 but the value of n3 is 5 n1 = 5;
n3 = n1++;
Thus n2 is given the value of n1 after n1 has been incremented (using the pre-increment operator), whereas n3 gets the value of n1 before it is incre mented using the post-increment operator.
Why define a separate increment operator?
The authors of C++ noted that programmers add 1 more than any other constant. To provide some convenience, a special add 1 instruction was added to the language.
In addition, most present-day computer proces sors have an increment instruction that is faster
than the addition instruction. Back when C++ was created, however — with microprocessors being what they were — saving a few instruc tions was a big deal.