- •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 7: Storing Sequences in Arrays 103
The choice of ‘\0’ as the terminating character was not random. Remember that zero is the only numeric value that converts to false; all other values translate to true. This means that the for loop could (and usually is) writ ten as:
for(int i = 0; stringArray[i]; i++)
This whole business of null terminated character strings is so ingrained into the C++ language psyche that C++ uses a string of characters surrounded by double quotes to be an array of characters automatically terminated with a ‘\0’ character. The following are identical declarations:
char szMyName[] = “Stephen”; char szAlsoMyName[] =
{‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’, ‘\0’};
The naming convention used here is exactly that, a convention. C++ does not care. The prefix sz stands for zero-terminated string.
The string Stephen is eight characters long and not seven — the null charac ter after the n is assumed. The string “” is one character long consisting of just the null character.
Manipulating Strings with Character
The C++ programmer is often required to manipulate strings. C++ provides a number of standard string-manipulation functions to make the job easier. A few of these functions are listed in Table 7-1.
Table 7-1 |
String-Handling Functions |
|
Name |
|
Operation |
int strlen(string) |
|
Returns the number of characters |
|
|
in a string. |
void strcpy(target, source, n) |
Copies the source string into a |
|
|
|
target array. |
|
|
|
void strcat(target, source, n) |
Concatenates the source string |
|
|
|
onto the end of the target string. |
|
|
|
void strncpy(target, source, n) |
Copies a string up to n characters |
|
|
|
from the source string into a target |
|
|
array. |
|
|
|
(continued)
104 Part II: Becoming a Functional C++ Programmer
Table 7-1 (continued)
Name |
Operation |
void strncat(target, source, n) |
Concatenates the source string |
|
onto the end of the target string or |
|
‘n’ characters, whichever comes |
|
first. |
|
|
int strnstr(string, pattern, n) |
Finds the first occurrence of one |
|
pattern string in another. |
int strncmp(source1, source2, n) |
Compares the first n characters in |
|
two strings. Returns a zero if the |
|
two strings match exactly. |
|
|
int strnicmp(source1, source2) |
Compares up to n characters in |
|
two strings without regard to |
|
case. |
|
|
You need to add the statement #include <strings.h> to the beginning of any program that uses a str... function.
The current ANSI C++ standard suggests that you avoid the str...() func tions. ANSI C++ says that these functions are deprecated, meaning that ANSI will leave them alone for now, but don’t be surprised if they go away some day. That’s why strings.h uses the older standard of ending all include files with a “.h”. The ANSI standard suggests that you use the string type as defined in the next section. However, you will see a large number of programs that continue to use these functions.
The following Concatenate program inputs two strings from the keyboard and concatenates them into a single string.
// Concatenate - concatenate two strings // with a “ - “ in the middle #include <cstdio>
#include <cstdlib> #include <iostream> using namespace std;
//the following include file is deprecated;
//however, it is required for the str functions #include <strings.h>
int main(int nNumberofArgs, char* pszArgs[])
{
// read first string...
char szString1[256];
cout << “Enter string #1:”; cin >> szString1;
Chapter 7: Storing Sequences in Arrays 105
//safer alternative
//cin.getline(szString1, 128);
//...now the second string...
char szString2[128];
cout << “Enter string #2:”; cin >> szString2;
//safer alternative
//cin.getline(szString1, 128);
//accumulate both strings into a single buffer char szString[260];
//copy the first string into the buffer...
strncpy(szString, szString1, 128);
//...concatenate a “ - “ onto the first...
strncat(szString, “ - “, 4);
//...now add the second string...
strncat(szString, szString2, 128);
//...and display the result
cout << “\n” << szString << endl;
//wait until user is ready before terminating program
//to allow the user to see the program results system(“PAUSE”);
return 0;
}
The Concatenate program reads two character strings and appends them together with a “ - “ in the middle.
The arguments to the str...() functions appear backward to any reason able individual (you might consider this an acid test for “reasonable”). For example, the function strncat(target, source, count) tacks the second string source onto the end of the first argument target.
An example output from the program appears as follows:
Enter string #1:Chester
Enter string #2:Dog
Chester - Dog
Press any key to continue . . .
The program begins by reading a string from the keyboard. cin >> szString1 stops when any type of whitespace is encountered. Characters up to the first whitespace are read, the whitespace character is tossed, and
106 Part II: Becoming a Functional C++ Programmer
the remaining characters are left in the input hopper for the next cin>> statement. Thus, if I were to enter “the Dog”, szString2 would be filled with “the”, and the word “Dog” would be left in the input buffer.
The cin >> extractor knows nothing about the length of the string. cin is perfectly willing to read thousands of characters and stuff them into szString1, even though it is declared 256 characters long. This causes a dangerous overflow condition that hackers can (and will) exploit to put a virus in your program.
C++ provides work-arounds for many of the string overflow problems. For example, the function getline() inputs a line of text; however, this function accepts the length of the string as one of its arguments:
cin.getline(string, lengthOfTheString);
(Ignore the strange looking cin. format for now.)
The strncpy() and strncat() functions accept the length of the target buffer as one of their arguments. The call strncpy(szString, szString1, 128) says “copy the characters in szString1 into szString until you copy a null character or until you’ve copied 128 characters, whichever comes first.” The call specifically does not mean copy 128 characters every time.
There are both “counting” and “noncounting” versions of most of the str...() functions. The noncounting versions don’t require the maximum number of characters to process as an argument. You can use these when you don’t know the buffer size, but be aware that they are perfectly happy to write beyond the end of the target string.
String-ing Along Variables
ANSI C++ includes a type string designed to make it easier to manipulate strings of text.
I use the term string to refer to an array of characters terminated by a null and string type to refer to the type string. The string type includes opera tions for copying, concatenating, capitalizing, knotting, and simple magic tricks. string avoids the overrun problems inherent with null terminated strings. The functions that manipulate string objects are defined in the include file <string>.
The string type based StringConcatenate program appears as follows:
Chapter 7: Storing Sequences in Arrays 107
// StringConcatenate - concatenate two string type // variables with a “ - “ in the middle #include <cstdio>
#include <cstdlib> #include <iostream> #include <string> using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
//read first string...
string string1;
cout << “Enter string #1:”; cin >> string1;
//...now the second string...
string string2;
cout << “Enter string #2:”; cin >> string2;
//accumulate both strings into a single buffer string buffer;
string divider = “ - “;
buffer = string1 + divider + string2;
//...and display the result
cout << “\n” << buffer << endl;
//wait until user is ready before terminating program
//to allow the user to see the program results system(“PAUSE”);
return 0;
}
This concatenate function defines two variables, string1 and string2 (clever, no?). A string type variable is not defined of any specified length — it can grow and shrink to fit the number of characters it contains (up to avail able memory, of course, or the cows come home, whichever is first). Not only do you not have to guess how big to make a target character array, but some nefarious user can’t crash your program by inputting too many characters. The StringConcatenate program manipulates the string type variables as it would any other.
Notice that some operations have to be understood in a slightly different way from their arithmetic equivalent. For example, to add two string type vari ables together means to concatenate them. In addition, notice how C++ can convert a null terminated character string into a string type variable with out being told to.
108 Part II: Becoming a Functional C++ Programmer
The string type is not intrinsic to C++ like int or float, meaning that its operations are not built into the syntax of the language. Operations on string type variables are defined in the string include file. The string class is discussed further in Chapter 27. I mention string here to demon strate that it is often easier to use than manipulating null terminated charac ter arrays yourself.