CONTENTS
Section 1.1 Writing a Simple C++ Program 2
Section 1.2 A First Look at Input/Output 5
Section 1.3 A Word About Comments 10
Section 1.4 Control Structures 11
Section 1.5 Introducing Classes 20
Section 1.6 The C++ Program 25
This chapter introduces most of the basic elements of C++: built-in, library, and class types; variables; expressions; statements; and functions. Along the way, we’ll briefly explain how to compile and execute a program.
Having read this chapter and worked through the exercises, the reader should be able to write, compile, and execute simple programs. Subsequent chapters will explain in more detail the topics introduced here.
Learning a new programming language requires writing programs. In this chapter, we’ll write a program to solve a simple problem that represents a common data-processing task: A bookstore keeps a file of transactions, each of which records the sale of a given book. Each transaction contains an ISBN (International Standard Book Number, a unique identifier assigned to most books published throughout the world), the number of copies sold, and the price at which each copy was sold. Each transaction looks like
0-201-70353-X 4 24.99
where the first element is the ISBN, the second is the number of books sold, and the last is the sales price. Periodically the bookstore owner reads this file and computes the number of copies of each title sold, the total revenue from that book, and the average sales price. We want to supply a program to do these computations.
Before we can write this program we need to know some basic features of C++. At a minimum we’ll need to know how to write, compile, and execute a simple program. What must this program do? Although we have not yet designed our solution, we know that the program must
• Define variables
• Do input and output
• Define a data structure to hold the data we’re managing
• Test whether two records have the same ISBN
• Write a loop that will process every record in the transaction file
We’ll start by reviewing these parts of C++ and then write a solution to our bookstore problem.
Every C++ program contains one or more functions, one of which must be named main
. A function consists of a sequence of statements that perform the work of the function. The operating system executes a program by calling the function named main
. That function executes its constituent statements and returns a value to the operating system.
Here is a simple version of main
does nothing but return a value:
int main()
{
return 0;
}
The operating system uses the value returned by main
to determine whether the program succeeded or failed. A return value of 0 indicates success.
The main
function is special in various ways, the most important of which are that the function must exist in every C++ program and it is the (only) function that the operating system explicitly calls.
We define main
the same way we define other functions. A function definition specifies four elements: the return type, the function name, a (possibly empty) parameter list enclosed in parentheses, and the function body. The main
function may have only a restricted set of parameters. As defined here, the parameter list is empty; Section 7.2.6 (p. 243) will cover the other parameters that can be defined for main
.
The main
function is required to have a return type of int
, which is the type that represents integers. The int
type is a built-in type, which means that the type is defined by the language.
The final part of a function definition, the function body, is a block of statements starting with an open curly brace and ending with a close curly:
{
return 0;
}
The only statement in our program is a return
, which is a statement that terminates a function.
Note the semicolon at the end of the return
statement. Semicolons mark the end of most statements in C++. They are easy to overlook, but when forgotten can lead to mysterious compiler error messages.
When the return
includes a value such as 0
, that value is the return value of the function. The value returned must have the same type as the return type of the function or be a type that can be converted to that type. In the case of main
the return type must be int
, and the value 0
is an int
.
On most systems, the return value from main
is a status indicator. A return value of 0
indicates the successful completion of main
. Any other return value has a meaning that is defined by the operating system. Usually a nonzero return indicates that an error occurred. Each operating system has its own way of telling the user what main
returned.
Having written the program, we need to compile it. How you compile a program depends on your operating system and compiler. For details on how your particular compiler works, you’ll need to check the reference manual or ask a knowledgeable colleague.
Many PC-based compilers are run from an integrated development environment (IDE) that bundles the compiler with associated build and analysis tools. These environments can be a great asset in developing complex programs but require a fair bit of time to learn how to use effectively. Most of these environments include a point-and-click interface that allows the programmer to write a program and use various menus to compile and execute the program. Learning how to use such environments is well beyond the scope of this book.
Most compilers, including those that come with an IDE, provide a command-line interface. Unless you are already familiar with using your compiler’s IDE, it can be easier to start by using the simpler, command-line interface. Using the command-line interface lets you avoid the overhead of learning the IDE before learning the language.
Whether we are using a command-line interface or an IDE, most compilers expect that the program we want to compile will be stored in a file. Program files are referred to as source files. On most systems, a source file has a name that consists of two parts: a file name—for example, prog1
—and a file suffix. By convention, the suffix indicates that the file is a program. The suffix often also indicates what language the program is written in and selects which compiler to run. The system that we used to compile the examples in this book treats a file with a suffix of .cc
as a C++ program and so we stored this program as
prog1.cc
The suffix for C++ program files depends on which compiler you’re running. Other conventions include
prog1.cxx
prog1.cpp
prog1.cp
prog1.C
If we are using a command-line interface, we will typically compile a program in a console window (such as a shell window on a UNIX system or a Command Prompt window on Windows). Assuming that our main
program is in a file named prog1.cc
, we might compile it by using a command such as:
$ CC prog1.cc
where CC
names the compiler and $
represents the system prompt. The output of the compiler is an executable file that we invoke by naming it. On our system, the compiler generates the executable in a file named a.exe
. UNIX compilers tend to put their executables in a file named a.out
. To run an executable we supply that name at the command-line prompt:
$ a.exe
executes the program we compiled. On UNIX systems you sometimes must also specify which directory the file is in, even if it is in the current directory. In such cases, we would write
$ ./a.exe
The “.
” followed by a slash indicates that the file is in the current directory.
The value returned from main
is accessed in a system-dependent manner. On both UNIX and Windows systems, after executing the program, you must issue an appropriate echo
command. On UNIX systems, we obtain the status by writing
$ echo $?
To see the status on a Windows system, we write
C:directory> echo %ERRORLEVEL%
C++ does not directly define any statements to do input or output (IO). Instead, IO is provided by the standard library. The IO library provides an extensive set of facilities. However, for many purposes, including the examples in this book, one needs to know only a few basic concepts and operations.
Most of the examples in this book use the iostream
library, which handles formatted input and output. Fundamental to the iostream
library are two types named istream
and ostream
, which represent input and output streams, respectively. A stream is a sequence of characters intended to be read from or written to an IO device of some kind. The term “stream” is intended to suggest that the characters are generated, or consumed, sequentially over time.
The library defines four IO objects. To handle input, we use an object of type istream
named cin
(pronounced “see-in”). This object is also referred to as the standard input. For output, we use an ostream
object named cout
(pronounced “see-out”). It is often referred to as the standard output. The library also defines two other ostream
objects, named cerr
and clog
(pronounced “see-err” and “see-log,” respectively). The cerr
object, referred to as the standard error, is typically used to generate warning and error messages to users of our programs. The clog
object is used for general information about the execution of the program.
Ordinarily, the system associates each of these objects with the window in which the program is executed. So, when we read from cin
, data is read from the window in which the program is executing, and when we write to cout, cerr
, or clog
, the output is written to the same window. Most operating systems give us a way of redirecting the input or output streams when we run a program. Using redirection we can associate these streams with files of our choosing.
So far, we have seen how to compile and execute a simple program, although that program did no work. In our overall problem, we’ll have several records that refer to the same ISBN. We’ll need to consolidate those records into a single total, implying that we’ll need to know how to add the quantities of books sold.
To see how to solve part of that problem, let’s start by looking at how we might add two numbers. Using the IO library, we can extend our main
program to ask the user to give us two numbers and then print their sum:
#include <iostream>
int main()
{
std::cout << "Enter two numbers:" << std::endl;
int v1, v2;
std::cin >> v1 >> v2;
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
return 0;
}
This program starts by printing
Enter two numbers:
on the user’s screen and then waits for input from the user. If the user enters
3 7
followed by a newline, then the program produces the following output:
The sum of 3 and 7 is 10
The first line of our program is a preprocessor directive:
#include <iostream>
which tells the compiler that we want to use the iostream
library. The name inside angle brackets is a header. Every program that uses a library facility must include its associated header. The #include
directive must be written on a single line—the name of the header and the #include
must appear on the same line. In general, #include
directives should appear outside any function. Typically, all the #include
directives for a program appear at the beginning of the file.
The first statement in the body of main
executes an expression. In C++ an expression is composed of one or more operands and (usually) an operator. The expressions in this statement use the output operator (the <<
operator) to print the prompt on the standard output:
std::cout << "Enter two numbers:" << std::endl;
This statement uses the output operator twice. Each instance of the output operator takes two operands: The left-hand operand must be an ostream
object; the right-hand operand is a value to print. The operator writes its right-hand operand to the ostream
that is its left-hand operand.
In C++ every expression produces a result, which typically is the value generated by applying an operator to its operands. In the case of the output operator, the result is the value of its left-hand operand. That is, the value returned by an output operation is the output stream itself.
The fact that the operator returns its left-hand operand allows us to chain together output requests. The statement that prints our prompt is equivalent to
(std::cout << "Enter two numbers:") << std::endl;
Because (std::cout << "Enter two numbers:")
returns its left operand, std::cout
, this statement is equivalent to
std::cout << "Enter two numbers:";
std::cout << std::endl;
endl
is a special value, called a manipulator, that when written to an output stream has the effect of writing a newline to the output and flushing the buffer associated with that device. By flushing the buffer, we ensure that the user will see the output written to the stream immediately.
Programmers often insert print statements during debugging. Such statements should always flush the stream. Forgetting to do so may cause output to be left in the buffer if the program crashes, leading to incorrect inferences about where the program crashed.
Careful readers will note that this program uses std::cout
and std::endl
rather than just cout
and endl
. The prefix std::
indicates that the names cout
and endl
are defined inside the namespace named std
. Namespaces allow programmers to avoid inadvertent collisions with the same names defined by a library. Because the names that the standard library defines are defined in a namespace, we can use the same names for our own purposes.
One side effect of the library’s use of a namespace is that when we use a name from the library, we must say explicitly that we want to use the name from the std
namespace. Writing std::cout
uses the scope operator (the ::
operator) to say that we want to use the name cout
that is defined in the namespace std
. We’ll see in Section 3.1 (p. 78) a way that programs often use to avoid this verbose syntax.
Having written our prompt, we next want to read what the user writes. We start by defining two variables named v1
and v2
to hold the input:
int v1, v2;
We define these variables as type int
, which is the built-in type representing integral values. These variables are uninitialized, meaning that we gave them no initial value. Our first use of these variables will be to read a value into them, so the fact that they have no initial value is okay.
The next statement
std::cin >> v1 >> v2;
reads the input. The input operator (the >>
operator) behaves analogously to the output operator. It takes an istream
as its left-hand operand and an object as its right-hand operand. It reads from its istream
operand and stores the value it read in its right-hand operand. Like the output operator, the input operator returns its left-hand operand as its result. Because the operator returns its left-hand operand, we can combine a sequence of input requests into a single statement. In other words, this input operation is equivalent to
std::cin >> v1;
std::cin >> v2;
The effect of our input operation is to read two values from the standard input, storing the first in v1
and the second in v2
.
What remains is to print our result:
std::cout << "The sum of " << v1 << " and " << v2
<< " is " << v1 + v2 << std::endl;
This statement, although it is longer than the statement that printed the prompt, is conceptually no different. It prints each of its operands to the standard output. What is interesting is that the operands are not all the same kinds of values. Some operands are string literals, such as
"The sum of "
and others are various int
values, such as v1, v2
, and the result of evaluating the arithmetic expression:
v1 + v2
The iostream
library defines versions of the input and output operators that accept all of the built-in types.
When writing a C++ program, in most places that a space appears we could instead use a newline. One exception to this rule is that spaces inside a string literal cannot be replaced by a newline. Another exception is that spaces are not allowed inside preprocessor directives.
Before our programs get much more complicated, we should see how C++ handles comments. Comments help the human readers of our programs. They are typically used to summarize an algorithm, identify the purpose of a variable, or clarify an otherwise obscure segment of code. Comments do not increase the size of the executable program. The compiler ignores all comments.
In this book, we italicize comments to make them stand out from the normal program text. In actual programs, whether comment text is distinguished from the text used for program code depends on the sophistication of the programming environment.
There are two kinds of comments in C++: single-line and paired. A single-line comment starts with a double slash (//
). Everything to the right of the slashes on the current line is a comment and ignored by the compiler.
The other delimiter, the comment pair (/* */
), is inherited from the C language. Such comments begin with a /*
and end with the next */
. The compiler treats everything that falls between the /*
and */
as part of the comment:
A comment pair can be placed anywhere a tab, space, or newline is permitted. Comment pairs can span multiple lines of a program but are not required to do so. When a comment pair does span multiple lines, it is often a good idea to indicate visually that the inner lines are part of a multi-line comment. Our style is to begin each line in the comment with an asterisk, thus indicating that the entire range is part of a multi-line comment.
Programs typically contain a mixture of both comment forms. Comment pairs generally are used for multi-line explanations, whereas double slash comments tend to be used for half-line and single-line remarks.
Too many comments intermixed with the program code can obscure the code. It is usually best to place a comment block above the code it explains.
Comments should be kept up to date as the code itself changes. Programmers expect comments to remain accurate and so believe them, even when other forms of system documentation are known to be out of date. An incorrect comment is worse than no comment at all because it may mislead a subsequent reader.
A comment that begins with /*
always ends with the next */
. As a result, one comment pair cannot occur within another. The compiler error message(s) that result from this kind of program mistake can be mysterious and confusing. As an example, compile the following program on your system:
When commenting out a large section of a program, it can seem easiest to put a comment pair around a region that you want to omit temporarily. The trouble is that if that code already has a comment pair, then the newly inserted comment will terminate prematurely. A better way to temporarily ignore a section of code is to use your editor to insert single-line comment at the beginning of each line of code you want to ignore. That way, you need not worry about whether the code you are commenting out already contains a comment pair.
Statements execute sequentially: The first statement in a function is executed first, followed by the second, and so on. Of course, few programs—including the one we’ll need to write to solve our bookstore problem—can be written using only sequential execution. Instead, programming languages provide various control structures that allow for more complicated execution paths. This section will take a brief look at some of the control structures provided by C++. Chapter 6 covers statements in detail.
while
StatementA while
statement provides for iterative execution. We could use a while
to write a program to sum the numbers from 1
through 10
inclusive as follows:
This program when compiled and executed will print:
Sum of 1 to 10 inclusive is 55
As before, we begin by including the iostream
header and define a main
function. Inside main
we define two int
variables: sum
, which will hold our summation, and val
, which will represent each of the values from 1 through 10. We give sum
an initial value of zero and start val
off with the value one.
The important part is the while
statement. A while
has the form
while (condition) while_body_statement;
A while
executes by (repeatedly) testing the condition and executing the associated while_body_statement until the condition is false.
A condition is an expression that is evaluated so that its result can be tested. If the resulting value is nonzero, then the condition is true; if the value is zero then the condition is false.
If the condition is true (the expression evaluates to a value other than zero) then while_body_statement is executed. After executing while_body_statement, the condition is tested again. If condition remains true, then the while_body_statement is again executed. The while
continues, alternatively testing the condition and executing while_body_statement until the condition is false.
In this program, the while
statement is:
The condition in the while
uses the less-than-or-equal operator (the <=
operator) to compare the current value of val
and 10
. As long as val
is less than or equal to 10, we execute the body of the while
. In this case, the body of the while
is a block containing two statements:
A block is a sequence of statements enclosed by curly braces. In C++, a block may be used wherever a statement is expected. The first statement in the block uses the compound assignment operator, (the += operator). This operator adds its right-hand operand to its left-hand operand. It has the same effect as writing an addition and an assignment:
sum = sum + val; // assign sum + val to sum
Thus, the first statement adds the value of val
to the current value of sum
and stores the result back into sum
.
The next statement
++val; // add 1 to val
uses the prefix increment operator (the ++
operator). The increment operator adds one to its operand. Writing ++val
is the same as writing val = val + 1
.
After executing the while
body we again execute the condition in the while
. If the (now incremented) value of val
is still less than or equal to 10, then the body of the while
is executed again. The loop continues, testing the condition and executing the body, until val
is no longer less than or equal to 10.
Once val
is greater than 10, we fall out of the while
loop and execute the statement following the while
. In this case, that statement prints our output, followed by the return
, which completes our main
program.
for
StatementIn our while
loop, we used the variable val
to control how many times we iterated through the loop. On each pass through the while
, the value of val
was tested and then in the body the value of val
was incremented.
The use of a variable like val
to control a loop happens so often that the language defines a second control structure, called a for
statement, that abbreviates the code that manages the loop variable. We could rewrite the program to sum the numbers from 1 through 10 using a for
loop as follows:
#include <iostream>
int main()
{
int sum = 0;
// sum values from 1 up to 10 inclusive
for (int val = 1; val <= 10; ++val)
sum += val; // equivalent to sum = sum + val
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
return 0;
}
Prior to the for
loop, we define sum
, which we set to zero. The variable val
is used only inside the iteration and is defined as part of the for
statement itself. The for
statement
for (int val = 1; val <= 10; ++val)
sum += val; // equivalent to sum = sum + val
has two parts: the for
header and the for
body. The header controls how often the body is executed. The header itself consists of three parts: an init-statement, a condition, and an expression. In this case, the init-statement
int val = 1;
defines an int
object named val
and gives it an initial value of one. The initstatement is performed only once, on entry to the for
. The condition
val <= 10
which compares the current value in val
to 10, is tested each time through the loop. As long as val
is less than or equal to 10, we execute the for
body. Only after executing the body is the expression executed. In this for
, the expression uses the prefix increment operator, which as we know adds one to the value of val
. After executing the expression, the for
retests the condition. If the new value of val
is still less than or equal to 10
, then the for
loop body is executed and val
is incremented again. Execution continues until the condition fails.
In this loop, the for
body performs the summation
sum += val; // equivalent to sum = sum + val
The body uses the compound assignment operator to add the current value of val
to sum
, storing the result back into sum
.
To recap, the overall execution flow of this for
is:
val
and initialize it to 1
.val
is less than or equal to 10
.val
is less than or equal to 10, execute the for
body, which adds val
to sum
. If val
is not less than or equal to 10, then break out of the loop and continue execution with the first statement following the for
body.val
.When we exit the for
loop, the variable val
is no longer accessible. It is not possible to use val
after this loop terminates. However, not all compilers enforce this requirement.
In pre-Standard C++ names defined in a for
header were accessible outside the for
itself. This change in the language definition can surprise people accustomed to using an older compiler when they instead use a compiler that adheres to the standard.
if
StatementA logical extension of summing the values between 1 and 10 is to sum the values between two numbers our user supplies. We might use the numbers directly in our for
loop, using the first input as the lower bound for the range and the second as the upper bound. However, if the user gives us the higher number first, that strategy would fail: Our program would exit the for
loop immediately. Instead, we should adjust the range so that the larger number is the upper bound and the smaller is the lower. To do so, we need a way to see which number is larger.
Like most languages, C++ provides an if
statement that supports conditional execution. We can use an if
to write our revised sum program:
If we compile and execute this program and give it as input the numbers 7 and 3, then the output of our program will be
Sum of 3 to 7 inclusive is 25
Most of the code in this program should already be familiar from our earlier examples. The program starts by writing a prompt to the user and defines four int
variables. It then reads from the standard input into v1
and v2
. The only new code is the if
statement
// use smaller number as lower bound for summation
// and larger number as upper bound
int lower, upper;
if (v1 <= v2) {
lower = v1;
upper = v2;
} else {
lower = v2;
upper = v1;
}
The effect of this code is to set upper
and lower
appropriately. The if
condition tests whether v1
is less than or equal to v2
. If so, we perform the block that immediately follows the condition. This block contains two statements, each of which does an assignment. The first statement assigns v1
to lower
and the second assigns v2
to upper
.
If the condition is false—that is, if v1
is larger than v2
—then we execute the statement following the else
. Again, this statement is a block consisting of two assignments. We assign v2
to lower
and v1
to upper
.
Another change we might make to our summation program on page 12 would be to allow the user to specify a set of numbers to sum. In this case we can’t know how many numbers we’ll be asked to add. Instead, we want to keep reading numbers until the program reaches the end of the input. When the input is finished, the program writes the total to the standard output:
If we give this program the input
3 4 5 6
then our output will be
Sum is: 18
As usual, we begin by including the necessary headers. The first line inside main
defines two int
variables, named sum
and value
. We’lluse value
to hold each number we read, which we do inside the condition in the while
:
while (std::cin >> value)
What happens here is that to evaluate the condition, the input operation
std::cin >> value
is executed, which has the effect of reading the next number from the standard input, storing what was read in value
. The input operator (Section 1.2.2, p. 8) returns its left operand. The condition tests that result, meaning it tests std::cin
.
When we use an istream
as a condition, the effect is to test the state of the stream. If the stream is valid—that is, if it is still possible to read another input— then the test succeeds. An istream
becomes invalid when we hit end-of-file or encounter an invalid input, such as reading a value that is not an integer. An istream
that is in an invalid state will cause the condition to fail.
Until we do encounter end-of-file (or some other input error), the test will succeed and we’ll execute the body of the while
. That body is a single statement that uses the compound assignment operator. This operator adds its right-hand operand into the left hand operand.
Once the test fails, the while
terminates and we fall through and execute the statement following the while
. That statement prints sum
followed by endl
, which prints a newline and flushes the buffer associated with cout
. Finally, we execute the return
, which as usual returns zero to indicate success.
The only remaining feature we need to understand before solving our bookstore problem is how to write a data structure to represent our transaction data. In C++ we define our own data structure by defining a class. The class mechanism is one of the most important features in C++. In fact, a primary focus of the design of C++ is to make it possible to define class types that behave as naturally as the built-in types themselves. The library types that we’ve seen already, such as istream
and ostream
, are all defined as classes—that is, they are not strictly speaking part of the language.
Complete understanding of the class mechanism requires mastering a lot of information. Fortunately, it is possible to use a class that someone else has written without knowing how to define a class ourselves. In this section, we’ll describe a simple class that we can use in solving our bookstore problem. We’ll implement this class in the subsequent chapters as we learn more about types, expressions, statements, and functions—all of which are used in defining classes.
To use a class we need to know three things:
For our bookstore problem, we’ll assume that the class is named Sales_item
and that it is defined in a header named Sales_item.h
.
Sales_item
ClassThe purpose of the Sales_item
class is to store an ISBN and keep track of the number of copies sold, the revenue, and average sales price for that book. How these data are stored or computed is not our concern. To use a class, we need not know anything about how it is implemented. Instead, what we need to know is what operations the class provides.
As we’ve seen, when we use library facilities such as IO, we must include the associated headers. Similarly, for our own classes, we must make the definitions associated with the class available to the compiler. We do so in much the same way. Typically, we put the class definition into a file. Any program that wants to use our class must include that file.
Conventionally, class types are stored in a file with a name that, like the name of a program source file, has two parts: a file name and a file suffix. Usually the file name is the same as the class defined in the header. The suffix usually is .h
, but some programmers use .H, .hpp
, or .hxx
. Compilers usually aren’t picky about header file names, but IDEs sometimes are. We’ll assume that our class is defined in a file named Sales_item.h
.
Sales_item
ObjectsEvery class defines a type. The type name is the same as the name of the class. Hence, our Sales_item
class defines a type named Sales_item
. As with the built-in types, we can define a variable of a class type. When we write
Sales_item item;
we are saying that item
is an object of type Sales_item
. We often contract the phrase “an object of type Sales_item”
to“aSales_ item
object” or even more simply to “a Sales_item.”
In addition to being able to define variables of type Sales_item
, we can perform the following operations on Sales_item
objects:
• Use the addition operator, +
, to add two Sales_item
s
• Use the input operator, <<
to read a Sales_item
object,
• Use the output operator, >>
to write a Sales_item
object
• Use the assignment operator, =
, to assign one Sales_item
object to another
• Call the same_isbn
function to determine if two Sales_item
s refer to the same book
Sales_item
sNow that we know the operations that the class provides, we can write some simple programs to use this class. For example, the following program reads data from the standard input, uses that data to build a Sales_item
object, and writes that Sales_item
object back onto the standard output:
If the input to this program is
0-201-70353-X 4 24.99
then the output will be
0-201-70353-X 4 99.96 24.99
Our input said that we sold four copies of the book at $24.99 each, and the output indicates that the total sold was four, the total revenue was $99.96, and the average price per book was $24.99.
This program starts with two #include
directives, one of which uses a new form. The iostream
header is defined by the standard library; the Sales_item
header is not. Sales_item
is a type that we ourselves have defined. When we use our own headers, we use quotation marks (" "
) to surround the header name.
Headers for the standard library are enclosed in angle brackets (< >
). Nonstandard headers are enclosed in double quotes (" "
).
Inside main
we start by defining an object, named book
, which we’ll use to hold the data that we read from the standard input. The next statement reads into that object, and the third statement prints it to the standard output followed as usual by printing endl
to flush the buffer.
Sales_item
sA slightly more interesting example adds two Sales_item
objects:
If we give this program the following input
0-201-78345-X 3 20.00
0-201-78345-X 2 25.00
our output is
0-201-78345-X 5 110 22
This program starts by including the Sales_item
and iostream
headers. Next we define two Sales_item
objects to hold the two transactions that we wish to sum. The output expression does the addition and prints the result. We know from the list of operations on page 21 that adding two Sales_item
s together creates a new object whose ISBN is that of its operands and whose number sold and revenue reflect the sum of the corresponding values in its operands. We also know that the items we add must represent the same ISBN.
It’s worth noting how similar this program looks to the one on page 6: We read two inputs and write their sum. What makes it interesting is that instead of reading and printing the sum of two integers, we’re reading and printing the sum of two Sales_item
objects. Moreover, the whole idea of “sum” is different. In the case of int
s we are generating a conventional sum—the result of adding two numeric values. In the case of Sales_item
objects we use a conceptually new meaning for sum—the result of adding the components of two Sales_item
objects.
Unfortunately, there is a problem with the program that adds Sales_item
s. What should happen if the input referred to two different ISBNs? It doesn’t make sense to add the data for two different ISBNs together. To solve this problem, we’ll first check whether the Sales_item
operands refer to the same ISBNs:
The difference between this program and the previous one is the if
test and its associated else
branch. Before explaining the if
condition, we know that what this program does depends on the condition in the if
. If the test succeeds, then we write the same output as the previous program and return 0
indicating success. If the test fails, we execute the block following the else
, which prints a message and returns an error indicator.
The if
condition
// first check that item1 and item2 represent the same book
if (item1.same_isbn(item2)) {
calls a member function of the Sales_item
object named item1
. A member function is a function that is defined by a class. Member functions are sometimes referred to as the methods of the class.
Member functions are defined once for the class but are treated as members of each object. We refer to these operations as member functions because they (usually) operate on a specific object. In this sense, they are members of the object, even though a single definition is shared by all objects of the same type.
When we call a member function, we (usually) specify the object on which the function will operate. This syntax uses the dot operator (the “.” operator):
item1.same_isbn
means “the same_isbn
member of the object named item1
.” The dot operator fetches its right-hand operand from its left. The dot operator applies only to objects of class type: The left-hand operand must be an object of class type; the right-hand operand must name a member of that type.
Unlike most other operators, the right operand of the dot (“.
”) operator is not an object or value; it is the name of a member.
When we use a member function as the right-hand operand of the dot operator, we usually do so to call that function. We execute a member function in much the same way as we do any function: To call a function, we follow the function name by the call operator (the “()
” operator). The call operator is a pair of parentheses that encloses a (possibly empty) list of arguments that we pass to the function.
The same_isbn
function takes a single argument, and that argument is another Sales_item
object. The call
item1.same_isbn(item2)
passes item2
as an argument to the function named same_isbn
that is a member of the object named item1
. This function compares the ISBN part of its argument, item2
, to the ISBN in item1
, the object on which same_isbn
is called. Thus, the effect is to test whether the two objects refer to the same ISBN.
If the objects refer to the same ISBN, we execute the statement following the if
, which prints the result of adding the two Sales_item
objects together. Otherwise, if they refer to different ISBNs, we execute the else
branch, which is a block of statements. The block prints an appropriate error message and exits the program, returning -1
. Recall that the return from main
is treated as a status indicator. In this case, we return a nonzero value to indicate that the program failed to produce the expected result.
Now we are ready to solve our original bookstore problem: We need to read a file of sales transactions and produce a report that shows for each book the total revenue, average sales price, and the number of copies sold.
We’ll assume that all of the transactions for a given ISBN appear together. Our program will combine the data for each ISBN in a Sales_item
object named total
. Each transaction we read from the standard input will be stored in a second Sales_item
object named trans
. Each time we read a new transaction we’ll compare it to the Sales_item
object in total
. If the objects refer to the same ISBN, we’ll update total
. Otherwise we’ll print the value in total
and reset it using the transaction we just read.
This program is the most complicated one we’ve seen so far, but it uses only facilities that we have already encountered. As usual, we begin by including the headers that we use: iostream
from the library and Sales_item.h
, which is our own header.
Inside main
we define the objects we need: total
, which we’ll use to sum the data for a given ISBN, and trans
, which will hold our transactions as we read them. We start by reading a transaction into total
and testing whether the read was successful. If the read fails, then there are no records and we fall through to the outermost else
branch, which prints a message to warn the user that there was no input.
Assuming we have successfully read a record, we execute the code in the if
branch. The first statement is a while
that will loop through all the remaining records. Just as we did in the program on page 18, our while
condition reads a value from the standard input and then tests that valid data was actually read. In this case, we read a Sales_item
object into trans
. As long as the read succeeds, we execute the body of the while
.
The body of the while
is a single if
statement. We test whether the ISBNs are equal, and if so we add the two objects and store the result in total
. If the ISBNs are not equal, we print the value stored in total
and reset total
by assigning trans
to it. After execution of the if
, we return to the condition in the while
, reading the next transaction and so on until we run out of records.
Once the while
completes, we still must write the data associated with the last ISBN. When the while
terminates, total
contains the data for the last ISBN in the file, but we had no chance to print it. We do so in the last statement of the block that concludes the outermost if
statement.
This chapter introduced enough of C++ to let the reader compile and execute simple C++ programs. We saw how to define a main
function, which is the function that is executed first in any C++ program. We also saw how to define variables, how to do input and output, and how to write if, for
, and while
statements. The chapter closed by introducing the most fundamental facility in C++: the class. In this chapter we saw how to create and use objects of a given class
. Later chapters show how to define our own classes.
A value passed to a function when it is called.
Sequence of statements enclosed in curly braces.
A region of storage used to hold data. IO facilities often store input (or output) in a buffer and read or write the buffer independently of actions in the program. Output buffers usually must be explicitly flushed to force the buffer to be written. By default, reading cin flushes cout
; cout
is also flushed when the program ends normally.
A type, such as int
, defined by the language.
ostream
object tied to the standard error, which is often the same stream as the standard output. By default, writes to cerr
are not buffered. Usually used for error messages or other output that is not part of the normal logic of the program.
istream
object used to read from the standard input.
C++ mechanism for defining our own data structures. The class is one of the most fundamental features in C++. Library types, such as istream
and ostream
, are classes.
A type defined by a class. The name of the type is the class name.
ostream
object tied to the standard error. By default, writes to clog
are buffered. Usually used to report information about program execution to a log file.
Program text that is ignored by the compiler. C++ has two kinds of comments: single-line and paired. Single-line comments start with a //. Everything from the // to the end of the line is a comment. Paired comments begin with a /*
and include all text up to the next */
.
An expression that is evaluated as true or false. An arithmetic expression that evaluates to zero is false; any other value yields true.
ostream
object used to write to the standard output. Ordinarily used to write the output of a program.
Curly braces delimit blocks. An open curly ({) starts a block; a close curly (}) ends one.
A logical grouping of data and operations on that data.
The process of getting a program to execute properly.
System-specific marker in a file that indicates that there is no more input in the file.
The smallest unit of computation. An expression consists of one or more operands and usually an operator. Expressions are evaluated to produce a result. For example, assuming i
and j
are int
s, then i + j
is an arithmetic addition expression and yields the sum of the two int
values. Expressions are covered in more detail in Chapter 5.
Control statement that provides iterative execution. Often used to step through a data structure or to repeat a calculation a fixed number of times.
A named unit of computation.
Statement block that defines the actions performed by a function.
Name by which a function is known and can be called.
A mechanism whereby the definitions of a class or other names may be made available to multiple programs. A header is included in a program through a #include
directive.
Conditional execution based on the value of a specified condition. If the condition is true, the if
body is executed. If not, control flows to the statement following the else
if there is one or to the statement following the if
if there is no else
.
Library type providing stream-oriented input and output.
Library type providing stream-oriented input.
A type, such as istream
, defined by the standard library.
Function called by the operating system when executing a C++ program. Each program must have one and only one function named main.
Object, such as std::endl
, that when read or written “manipulates” the stream itself. Section A.3.1 (p. 825) covers manipulators in more detail.
Operation defined by a class. Member functions ordinarily are called to operate on a specific object.
Synonym for member function.
Mechanism for putting names defined by a library into a single place. Namespaces help avoid inadvertent name clashes. The names defined by the C++ library are in the namespace std
.
Library type providing stream-oriented output.
Part of the definition of a function. Possibly empty list that specifies what arguments can be used to call the function.
An instruction to the C++ preprocessor. #include
is a preprocessor directive. Preprocessor directives must appear on a single line. We’ll learn more about the preprocessor in Section 2.9.2.
Type of the value returned by a function.
Term used to describe a file that contains a C++ program.
An output stream intended for use for error reporting. Ordinarily, on a windowing operating system, the standard output and the standard error are tied to the window in which the program is executed.
The input stream that ordinarily is associated by the operating system with the window in which the program executes.
Collection of types and functions that every C++ compiler must support. The library provides a rich set of capabilities including the types that support IO. C++ programmers tend to talk about “the library,” meaning the entire standard library or about particular parts of the library by referring to a library type. For example, programmers also refer to the “iostream
library,” meaning the part of the standard library defined by the iostream
classes.
The output stream that ordinarily is associated by the operating system with the window in which the program executes.
The smallest independent unit in a C++ program. It is analogous to a sentence in a natural language. Statements in C++ generally end in semicolons.
Name of the namespace used by the standard library. std::cout
indicates that we’re using the name cout
defined in the std
namespace.
Sequence of characters enclosed in double quotes.
Variable that has no initial value specified. There are no uninitialized variables of class type. Variables of class type for which no initial value is specified are initialized as specified by the class definition. You must give a value to an uninitialized variable before attempting to use the variable’s value. Uninitialized variables can be a rich source of bugs.
A named object.
An iterative control statement that executes the statement that is the while
body as long as a specified condition is true. The body is executed zero or more times, depending on the truth value of the condition.
The call operator: A pair of parentheses “()” following a function name. The operator causes a function to be invoked. Arguments to the function may be passed inside the parentheses.
Increment operator. Adds one to the operand; ++i
is equivalent to i = i+ 1
.
A compound assignment operator. Adds right-hand operand to the left and stores the result back into the left-hand operand; a += b
is equivalent to a =a + b
.
Dot operator. Takes two operands: the left-hand operand is an object and the right is the name of a member of that object. The operator fetches that member from the named object.
Scope operator. We’ll see more about scope in Chapter 2. Among other uses, the scope operator is used to access names in a namespace. For example, std::cout
says to use the name cout
from the namespace std
.
Assigns the value of the right-hand operand to the object denoted by the left-hand operand.
Output operator. Writes the right-hand operand to the output stream indicated by the left-hand operand: cout << "hi"
writes hi
to the standard output. Output operations can be chained together: cout << "hi << "bye"
writes hibye
.
Input operator. Reads from the input stream specified by the left-hand operand into the right-hand operand: cin >> i
reads the next value on the standard input into i
. Input operations can be chained together: cin >> i >> j
reads first into i
and then into j
.
The equality operator. Tests whether the left-hand operand is equal to the right-hand.
The inequality operator. Tests whether the left-hand operand is not equal to the right-hand.
The less-than-or-equal operator. Tests whether the left-hand operand is less than or equal to the right-hand.
The less-than operator. Tests whether the left-hand operand is less than the right-hand.
Greater-than-or-equal operator. Tests whether the left-hand operand is greater than or equal to the right-hand.
Greater-than operator. Tests whether the left-hand operand is greater than the right-hand.