Professional C++


By Nicholas A. Solter

John Wiley & Sons

ISBN: 0-7645-7484-1


Chapter One

A Crash Course in C++

The goal of this chapter is to cover briefly the most important parts of C++ so that you have a base of knowledge before embarking on the rest of the book. This chapter is not a comprehensive lesson in the C++ programming language. The very basic points (like what a program is and the difference between = and ==) are not covered. The very esoteric points (remember what a union is? how about the volatile keyword?) are also omitted. Certain parts of the C language that are less relevant in C++ are also left out, as are parts of C++ that get in-depth coverage in later chapters.

This chapter aims to cover the parts of C++ that programmers encounter on a daily basis. If you've been away from C++ for a while and you've forgotten the syntax for a for loop, you'll find that in this chapter. If you're fairly new to C++ and you don't understand what a reference variable is, you'll learn that here as well.

If you already have significant experience with C++, skim this chapter to make sure that there aren't any fundamental parts of the language on which you need to brush up. If you're new to C++, take the time to read this chapter carefully and make sure that you understand the examples. If you need additional introductory information, consult the titles listed in Appendix B.

The Basics of C++

The C++ language is often viewed as a "better C" or a "superset of C." Many of the annoyances or rough edges of the C language were addressed when C++ was designed. Because C++ is based on C, much of the syntax you'll see in this section will look familiar to you if are an experienced C programmer. The two languages certainly have their differences, though. As evidence, The C++ Programming Language by C++ creator Bjarne Stroustrup weighs in at 911 pages, while Kernighan and Ritchie's The C Programming Language is a scant 274 pages. So if you're a C programmer, be on the lookout for new or unfamiliar syntax!

The Obligatory Hello, World

In all its glory, the following code is the simplest C++ program you're likely to encounter.

// helloworld.cpp

#include

int main(int argc, char** argv)

{ std::cout << "Hello, World!" << std::endl;

return 0;

}

This code, as you might expect, prints the message Hello, World! on the screen. It is a simple program and unlikely to win any awards, but it does exhibit several important concepts about the format of a C++ program.

Comments

The first line of the program is a comment, a message that exists for the programmer only and is ignored by the compiler. In C++, there are two ways to delineate a comment. In the preceding example, two slashes indicate that whatever follows on that line is a comment.

// helloworld.cpp

The same behavior (this is to say, none) would be achieved by using a C-style comment, which is also valid in C++. C-style comments start with /* and end with */. In this fashion, C-style comments are capable of spanning multiple lines. The code below shows a C-style comment in action (or, more appropriately, inaction).

/* this is a multiline * C-style comment. The * compiler will ignore * it. */

Comments are covered in detail in Chapter 7.

Preprocessor Directives

Building a C++ program is a three-step process. First, the code is run through a preprocessor, which recognizes metainformation about the code. Next, the code is compiled, or translated into machine-readable object files. Finally, the individual object files are linked together into a single application. Directives that are aimed at the preprocessor start with the # character, as in the line #include in the previous example. In this case, an include directive tells the preprocessor to take everything from the iostream header file and make it available to the current file. The most common use of header files is to declare functions that will be defined elsewhere. Remember, a declaration tells the compiler how a function is called. A definition contains the actual code for the function. The iostream header declares the input and output mechanisms provided by C++. If the program did not include it, it would be unable to perform its only task of outputting text.

The main function

main() is, of course, where the program starts. An int is returned from main(), indicating the result status of the program. main() takes two parameters: argc gives the number of arguments passed to the program, and argv contains those arguments. Note that the first argument is always the name of the program itself.

I/O Streams

If you're new to C++ and coming from a C background, you're probably wondering what std::cout is and what has been done with trusty old printf(). While printf() can still be used in C++, a much better input/output facility is provided by the streams library.

I/O streams are covered in depth in Chapter 14, but the basics of output are very simple. Think of an output stream as a laundry chute for data. Anything you toss into it will be output appropriately. std::cout is the chute corresponding to the user console, or standard out. There are other chutes, including std::cerr, which outputs to the error console. The << operator tosses data down the chute. In the preceding example, a quoted string of text is sent to standard out. Output streams allow multiple data of varying types to be sent down the stream sequentially on a single line of code. The following code outputs text, followed by a number, followed by more text.

std::cout << "There are " << 219 << " ways I love you." << std::endl;

std::endl represents an end of line character. When the output stream encounters std::endl, it will output everything that has been sent down the chute so far and move to the next line. An alternate way of representing the end of a line is by using the '\n' character. The \n character is an escape character, which refers to a new-line character. Escape characters can be used within any quoted string of text. The list below shows the most common escape characters.

\n new line

\r carriage return

\t tab

\\ the backslash character

\" quotation mark

Streams can also be used to accept input from the user. The simplest way to do this is to use the >> operator with an input stream. The std::cin input stream accepts keyboard input from the user. User input can be tricky because you can never know what kind of data the user will enter. See Chapter 14 for a full explanation of how to use input streams.

Namespaces

Namespaces address the problem of naming conflicts between different pieces of code. For example, you might be writing some code that has a function called foo(). One day, you decide to start using a third-party library, which also has a foo() function. The compiler has no way of knowing which version of foo() you are referring to within your code. You can't change the library's function name, and it would be a big pain to change your own.

Namespaces come to the rescue in such scenarios because you can define the context in which names are defined. To place code in a namespace, simply enclose it within a namespace block:

// namespaces.h

namespace mycode {

void foo();

}

The implementation of a method or function can also be handled in a namespace:

// namespaces.cpp

#include #include "namespaces.h"

namespace mycode {

void foo() { std::cout << "foo() called in the mycode namespace" << std::endl; } }

By placing your version of foo() in the namespace "mycode," it is isolated from the foo() function provided by the third-party library. To call the namespace-enabled version of foo(), prepend the namespace onto the function name as follows.

mycode::foo(); // Calls the "foo" function in the "mycode" namespace

Any code that falls within a "mycode" namespace block can call other code within the same namespace without explicitly prepending the namespace. This implicit namespace is useful in making the code more precise and readable. You can also avoid prepending of namespaces with the using directive. This directive tells the compiler that the subsequent code is making use of names in the specified namespace. The namespace is thus implied for the code that follows:

// usingnamespaces.cpp

#include "namespaces.h"

using namespace mycode;

int main(int argc, char** argv)

{ foo(); // Implies mycode::foo(); }

A single source file can contain multiple using directives, but beware of overusing this shortcut. In the extreme case, if you declare that you're using every namespace known to humanity, you're effectively eliminating namespaces entirely! Name conflicts will again result if you are using two namespaces that contain the same names. It is also important to know in which namespace your code is operating so that you don't end up accidentally calling the wrong version of a function.

You've seen the namespace syntax before - we used it in the Hello, World program. cout and endl are actually names defined in the std namespace. We could have rewritten Hello, World with the using directive as shown here:

// helloworld.cpp

#include

using namespace std;

int main(int argc, char** argv) { cout << "Hello, World!" << endl;

return 0; }

The using directive can also be used to refer to a particular item within a namespace. For example, if the only part of the std namespace that you intend to use is cout, you can refer to it as follows:

using std::cout;

Subsequent code can refer to cout without prepending the namespace, but other items in the std namespace will still need to be explicit:

using std::cout;

cout << "Hello, World!" << std::endl;

Variables

In C++, variables can be declared just about anywhere in your code and can be used anywhere in the current block below the line where they are declared. In practice, your engineering group should decide whether variables will be declared at the start of each function or on an as-needed basis. Variables can be declared without being given a value. These undeclared variables generally end up with a semirandom value based on whatever is in memory at the time and are the source of countless bugs. Variables in C++ can alternatively be assigned an initial value when they are declared. The code that follows shows both flavors of variable declaration, both using ints, which represent integer values.

// hellovariables.cpp

#include

using namespace std;

int main(int argc, char** argv) { int uninitializedInt; int initializedInt = 7;

cout << uninitializedInt << " is a random value" << endl; cout << initializedInt << " was assigned an initial value" << endl;

return (0); }

When run, this code will output a random value from memory for the first line and the number 7 for the second. This code also shows how variables can be used with output streams.

The table that follows shows the most common variable types used in C++.

Variables can be converted to other types by casting them. For example, an int can be cast to a bool. C++ provides three ways of explicitly changing the type of a variable. The first method is a holdover from C, but is still the most commonly used. The second method seems more natural at first but is rarely seen. The third method is the most verbose, but often considered the cleanest.

bool someBool = (bool)someInt; // method 1

bool someBool = bool(someInt); // method 2

bool someBool = static_cast(someInt); // method 3

The result will be false if the integer was 0 and true otherwise. In some contexts, variables can be automatically cast, or coerced. For example, a short can be automatically converted into a long because a long represents the same type of data with additional precision.

long someLong = someShort; // no explicit cast needed

When automatically casting variables, you need to be aware of the potential loss of data. For example, casting a float to an int throws away information (the fractional part of the number). Many compilers will issue a warning if you assign a float to an int without an explicit cast. If you are certain that the left-hand-side type is fully compatible with the right-hand side type, it's okay to cast implicitly.

Operators

What good is a variable if you don't have a way to change it? The table below shows the most common operators used in C++ and sample code that makes use of them. Note that operators in C++ can be binary (operate on two variables), unary (operate on a single variable), or even ternary (operate on three variables). There is only one ternary operator in C++ and it is covered in the next section, "Conditionals."

The following program shows the most common variable types and operators in action. If you're unsure about how variables and operators work, try to figure out what the output of this program will be, and then run it to confirm your answer.

// typetest.cpp

#include

using namespace std;

int main(int argc, char** argv) { int someInteger = 256; short someShort; long someLong; float someFloat; double someDouble;

someInteger++; someInteger *= 2; someShort = (short)someInteger; someLong = someShort * 10000; someFloat = someLong + 0.785; someDouble = (double)someFloat / 100000;

cout << someDouble << endl; }

The C++ compiler has a recipe for the order in which expressions are evaluated. If you have a complicated line of code with many operators, the order of execution may not be obvious. For that reason, it's probably better to break up a complicated statement into several smaller statements or explicitly group expressions using parentheses. For example, the following line of code is confusing unless you happen to know the C++ operator precedence table by heart:

int i = 34 + 8 * 2 + 21 / 7 % 2; Adding parentheses makes it clear which operations are happening first:

int i = 34 + (8 * 2) + ( (21 / 7) % 2 );

Breaking up the statement into separate lines makes it even clearer:

int i = 8 * 2; int j = 21 / 7; j %= 2; i = 34 + i + j;

For those of you playing along at home, all three approaches are equivalent and end up with i equal to 51. If you assumed that C++ evaluated expressions from left to right, your answer would have been 1. In fact, C++ evaluates /, *, and % first (in left to right order), followed by addition and subtraction, then bitwise operators. Parenthesis let you explicitly tell the compiler that a certain operation should be evaluated separately.

Types

In C++, you can use the basic types (int, bool, etc.) to build more complex types of your own design. Once you are an experienced C++ programmer, you will rarely use the following techniques, which are features brought in from C, because classes are far more powerful. Still, it is important to know about the two most common ways of building types so that you will recognize the syntax.

Enumerated Types

An integer really represents a value within a sequence - the sequence of numbers. Enumerated types let you define your own sequences so that you can declare variables with values in that sequence. For example, in a chess program, you could represent each piece as an int, with constants for the piece types, as shown in the following code. The integers representing the types are marked const to indicate that they can never change.

const int kPieceTypeKing = 0; const int kPieceTypeQueen = 1; const int kPieceTypeRook = 2; const int kPieceTypePawn = 3; //etc.

int myPiece = kPieceTypeKing;

This representation is fine, but it can become dangerous. Since the piece is just an int, what would happen if another programmer added code to increment the value of the piece? By adding one, a king becomes a queen, which really makes no sense. Worse still, someone could come in and give a piece a value of -1, which has no corresponding constant.

(Continues...)



Excerpted from Professional C++ by Nicholas A. Solter Excerpted by permission.
All rights reserved. No part of this excerpt may be reproduced or reprinted without permission in writing from the publisher.
Excerpts are provided by Dial-A-Book Inc. solely for the personal use of visitors to this web site.