By Ray Lischner
Book Price: $39.95 USD
£28.50 GBP
PDF Price: $31.99
Cover | Table of Contents | Colophon
#include and conditional preprocessing directives
to produce what the standard calls a translation
unit. Translation units are important because they have no
dependencies on other files. Nonetheless, programmers still speak in
terms of source files, even if they actually mean translation units,
so this book uses the phrase source
file because it is familiar to most readers. The
term "translation" encompasses
compilation and interpretation, although most C++ translators are
compilers. This section discusses how C++ reads and compiles
(translates) source files (translation units).\u1234#include and conditional preprocessing directives
to produce what the standard calls a translation
unit. Translation units are important because they have no
dependencies on other files. Nonetheless, programmers still speak in
terms of source files, even if they actually mean translation units,
so this book uses the phrase source
file because it is familiar to most readers. The
term "translation" encompasses
compilation and interpretation, although most C++ translators are
compilers. This section discusses how C++ reads and compiles
(translates) source files (translation units).\u1234) and
must not be at the end of a file. It can be used in a character or
string literal, or to continue a preprocessor directive or one-line
comment on multiple lines. A non-empty file must end with a newline.<
header
>
as a single token (for #include directives); the
compiler does not.a...z,
A...Z, and underscore, but the
standard permits letters in other languages.
/* and end with */.
These comments do not nest. For example:/* this is a comment /* still a comment */ int not_in_a_comment;
//, extending to the
end of the line. For example:const int max_widget = 42; // Largest size of a widget
/* and */ comment,
// characters have no special meaning. Within a
// comment, /* and
*/ have no special meaning. Thus, you can
"nest" one kind of comment within
the other kind. For example:/* Comment out a block of code: const int max_widget = 42; // Largest size of a widget */ ///* Inhibit the start of a block comment const int max_widget = 10; // Testing smaller widget limit //*/
str/*comment*/ing describes two separate tokens,
str and ing.
a ... z
A ... Z
0 ... 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '\u
and, or, and
not to be easier to read and understand than
&&, ||, and
!.|
Alternative token
|
Primary token
|
|---|---|
<%
|
{
|
%>
|
}
|
<: |
[
|
:>
|
]
|
%: |
#
|
%:%: |
##
|
and
|
&&
|
???- represent the two
characters ?~. Note that trigraphs are expanded
anywhere they appear, including within string literals and character
literals, in comments, and in preprocessor directives.|
Trigraph
|
Replacement
|
|---|---|
??=
|
#
|
??/
|
\
|
??' |
^
|
??(
|
[
|
??)
|
]
|
??!
|
#include file) for
every name it uses.#include file) for
every name it uses.for loop is a scope, in which the
variable x is declared as an
int; the if statement creates a
nested scope, in which another declaration of x
hides the outer x. The reference to
x at the end of main is
invalid: no x is in scope at that point.#include <iostream>
#include <ostream>
int main( )
{
for (int i = 0; i < 100; ++i)
{
int x = 42;
if (x < i)
{
double x = 3.14;
std::cout << x; // Prints 3.14
}
std::cout << x; // Prints 42
}
std::cout << x; // Error: no x declared in this scope
}
func(int) is global, and
func(double) is defined in namespace
N. Inside call_func, the
compiler looks up the name func by searching first
in the local scope (that is, the function body), then in namespace
N, where it finds func(double).
Name lookup stops at that point because the compiler found a match.
Therefore, func(3) converts 3
to type double and calls
func(double). The main function
brings all the overloaded func functions into its
scope (with using declarations, which are
described at the end of this chapter), so name lookup can find the
best match, which is func(int).void func(int i)
{
std::cout << "int: " << i << '\n';
}
namespace N {
void func(double d)
{
std::cout << "double: " << std::showpoint << d << '\n';
}
void call_func( )
{
// Even though func(int) is a better match, the compiler finds
// N::func(double) first.
func(3);
}
}
int main( )
{
N::call_func( ); // Prints "double: 3.000000"
using N::func;
using ::func;
// Now all overloaded func( )s are at the same scope level.
func(4); // Prints "int: 4"
}static declarations have internal linkage, as do
const declarations that are not also
extern. Data members of anonymous unions have
internal linkage. Names in an unnamed namespace have internal
linkage.extern specifier have
external linkage, as do entities declared at namespace scope that do
not have internal linkage.extern have no linkage.
C++". The only other standard language linkage is
"C". All other language linkages and the
properties associated with different language linkages are
implementation-defined.bigint—you can do so, and programmers will
be able to use bigint objects the same way they
use int objects.typedef, which is a synonym for an
existing type. Note that while the name typedef
seems to be a shorthand for "type
definition," it is actually a type declaration. (See
Section 2.5.4 later in
this chapter.)unsigned
long
int), you can mix the keywords in any order, but
the order shown in the following list is the conventional order. If a
type specifier requires multiple words, one of which is
int, the int can be omitted. If
a type is signed, the signed
keyword can be omitted (except in the case of
signed
char).bool
true and false.
char
char. (If a narrow
character literal contains multiple characters, the type is
int.) Unlike the other integral types, a plain
char is not necessarily equivalent to
signed
const and
volatile qualifiers, and the
object's type, in any order.auto. For declarations
at namespace scope, the default is usually an object with static
lifetime and external linkage. C++ has no explicit storage class for
such a declaration. (See Section 2.6.4 later in this chapter and
Section 2.4 earlier in this chapter
for more information.) If you use a storage class specifier, you must
choose only one of the following:auto
auto specifier is the default for function
parameters and local variables, which are the only kinds of
declarations for which it can be used, so it is rarely used
explicitly.extern
fraction,
which represents built-up fractions in an equation; the arithmetic
package has a class called fraction, for computing
with exact rational numbers; and the layout package has a class
called fraction for laying out fractional regions
of a page. Without namespaces, all three names would collide,
and you would not be able to use more than one of the three packages
in a single program.layout::fraction,
eqn::fraction, and
math::fraction.namespace keyword followed by an optional
identifier (the namespace name) and zero or more declarations in
curly braces. Namespace declarations can be discontiguous, even in
separate source files or headers. The namespace scope is the
accumulation of all definitions of the same namespace that the
compiler has seen at the time it looks up a given name in the
namespace. Namespaces can be nested. Example 2-15
shows a sample namespace definition.// The initial declaration
namespace numeric {
class rational { ... }
template<typename charT, typename traits>
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& out, const rational& r);
}
. . .
// This is a second definition. It adds an operator to the namespace.
namespace numeric {
rational operator+(const rational&, const rational&);
}
// The definition of operator+ can appear inside or outside the namespace
// definition. If it is outside, the name must be qualified with the scope
// operator.
numeric::rational numeric::operator+(const rational& r1,
const rational& r2)
{
. . .
}
int main( )
{
using numeric::rational;
rational a, b;
std::cout << a + b << '\n';
}#define rvalue 42 int lvalue; lvalue = rvalue;
#define rvalue 42 int lvalue; lvalue = rvalue;
const, in which case it cannot be the target of an
assignment. (C has since evolved and now has const
lvalues.)&) operator also requires an lvalue operand,
as do the increment (++) and decrement
(--) operators. Other operators require rvalues.
The rules are not as strict for user-defined operators. Any object,
including an rvalue, can be used to call member functions, including
overloaded bool,
char, signed
char, unsigned
char, int,
short, long,
unsigned
int,
unsigned
short,
unsigned
long,
float, double, or
long
double. Some operations
are permitted only on arithmetic types, pointer types, enumerations,
class types, or some combination of types. The description of each
operator tells you which types the operator supports.int if the type
int can represent all of the values of the source
type; otherwise, it is converted to unsigned
int. A "small"
value is an integral bit-field (see Chapter 6)
whose size is smaller than an int, or a value with
one of the following types: char,
signed
char,
unsigned
char,
short
int,
unsigned
shortcase labels.
Null pointer constants are a special case of integral constants.case labels, bit-field sizes, static member
initializers, and value template arguments. The compiler must be able
to evaluate the expression at compile time, so you can use only
literals, enumerators, const objects that have
constant initializers, integral or enumerated template parameters,
sizeof expressions, and constant addresses. The
address of a static lvalue object is a constant address, as is the
address of a function. A string literal, being a static array of
characters, is also a constant address.const data member can be
initialized in the class definition if the initializer is a constant
integral or enumerated expression. The member can then be used as a
constant expression elsewhere in the class definition. For example:template<typename T, size_t size>
class array {
public:
static const size_t SIZE = size;
...
private:
T data[SIZE];
};
0 can be a
null pointer constant
. A null pointer constant can be
converted to a null pointer value. The
colloquial term null pointer almost always means null pointer value.
volatile
objects, it is important that you know
exactly when it is safe to access those objects. That time is after a
sequence point. Also, any expression that modifies a scalar object
more than once between sequence points, or that examines a scalar
object's value after modifying it, yields undefined
behavior. This rule often bites the unwary programmer who uses the
increment and decrement operators. For example:int i = 0;
i = ++i - ++i; // Error: undefined behavior
printf("%d,%d", ++i, ++i); // Error: undefined behavior
i = 3, ++i, i++; // OK: i == 5
!a), binary operators such as addition
(a+b), and even a ternary operator
(a?b:c). Unlike many other languages, an array
subscript is also an operator (a[b]), and a
function call is an n-ary operator (e.g.,
a(b, c, d)).a( )
+
b( )
*
c( ), the multiplication has higher precedence,
but a( ) might be called first.)x
/
y
/
z is equivalent to
(x
/
y)
/
z. Other operators group
right to left, as in x
=
y
=
z, which
is equivalent to x
=
(y
=
z). The
order of grouping is called the operator's
associativity.*ptr++ is read as *(ptr++)
because the postfix ++ operator has higher
precedence than the unary * operator.|
Group
|
Associativity
|
|---|
expr ;
;
42; // Valid but pointless cout << 42; // More typical x = y * z; // Remember that assignment is an expression ; // Null statement
while ( test( ) ) int x = init( );
while ( test( ) ) {
int x = init( );
}
x is
limited to the body of the while loop.
expr ;
;
42; // Valid but pointless cout << 42; // More typical x = y * z; // Remember that assignment is an expression ; // Null statement
while ( test( ) ) int x = init( );
while ( test( ) ) {
int x = init( );
}
x is
limited to the body of the while loop.using declaration or
using directive. You can declare a function, but
not define a function, although there is rarely any reason to declare
a function locally. You cannot define a namespace or declare a
template.