Don't hesitate to send in feedback: send an e-mail if you like the C++ Annotations; if you think that important material was omitted; if you find errors or typos in the text or the code examples; or if you just feel like e-mailing. Send your e-mail to Frank B. Brokken.Please state the document version you're referring to, as found in the title (in this document: 8.3.1) and please state chapter and paragraph name or number you're referring to.
All received mail is processed conscientiously, and received suggestions for improvements will usually have been processed by the time a new version of the Annotations is released. Except for the incidental case I will normally not acknowledge the receipt of suggestions for improvements. Please don't interpret this as me not appreciating your efforts.
This document offers an introduction to the C++ programming language. It is a guide for C/C++ programming courses, yearly presented by Frank at the University of Groningen. This document is not a complete C/C++ handbook, as much of the C-background of C++ is not covered. Other sources should be referred to for that (e.g., the Dutch book De programmeertaal C, Brokken and Kubat, University of Groningen, 1996) or the on-line book suggested to me by George Danchev (danchev at spnet dot net).
The reader should be forwarned that extensive knowledge of the C programming language is actually assumed. The C++ Annotations continue where topics of the C programming language end, such as pointers, basic flow control and the construction of functions.
The version number of the C++ Annotations (currently 8.3.1) is updated when the contents of the document change. The first number is the major number, and will probably not be changed for some time: it indicates a major rewriting. The middle number is increased when new information is added to the document. The last number only indicates small changes; it is increased when, e.g., series of typos are corrected.
This document is published by the Computing Center, University of Groningen, the Netherlands under the GNU General Public License.
The C++ Annotations were typeset using the yodl formatting system.
All correspondence concerning suggestions, additions, improvements or changes to this document should be directed to the author:
Frank B. Brokken
Center of Information Technology,
University of Groningen
Nettelbosje 1,
P.O. Box 11044,
9700 CA Groningen
The Netherlands
(email: f.b.brokken@rug.nl)
In this chapter an overview of C++'s defining features is presented. A few extensions to C are reviewed and the concepts of object based and object oriented programming (OOP) are briefly introduced.
g++
compiler version 4.4 is available. With elements
of the C++-0x standard requiring versions beyond 4.4 the required versions
are explicitly mentioned, if already known. All suggestions sent in by various
readers have also been processed, their help to improve the quality of the
C++ Annotations is greatly appreciated: thanks!
shared_ptrs
(section
18.4.5) and about sharing arrays of objects (18.4.6).
endl
by '\n'
, making virtual functions private,
etc., etc. The sections labeled C++0x
were improved and sections showing
C++0x
now also mention the g++
version in which the new feature will
be made available, using ?
if this is as yet unknown. No version is shown
if the feature is already available in g++
4.3 (or in one of its
subreleases, like 4.3.3). I received a host of suggestions from Francesco
Poli (thanks, Francesco (and several others), for all the effort you've put
into sending me those corrections).
http://en.wikipedia.org/wiki/C++0x
) becoming (partially)
available in the Gnu g++
compiler (http://gcc.gnu.org/projects/cxx0x.html).
Not all new elements of the new standard (informally called the C++0x
standard) are available right now, and new subreleases of the C++
Annotations will appear once more elements become implemented in the g++
compiler. In section 2.2.3 the way to activate the new standard is
shown, and new sections covering elements of the new standard show C++0x
in their section-titles.
Furthermore, two new chapters were added: the STL chapter is now split in two. The STL chapter now covers the STL except for the Generic Algorithms which are now discussed in a separate chapter. Name spaces, originally covered by the introductory chapter are now also covered in a separate chapter.
operator[]()
, section
23.8.3 discusses in the context of the Bisonc++ parser generator how to
use polymorphism instead of a union to define different types of semantic
values. As usual, several typos were repaired and various other improvements
were made.
type_info::before()
member (cf. section 14.5.2). Furthermore, several typographical
corrections were made.
new[]
can be initialized by non-default
constructors. In addition to all this, Elwin Dijck (e dot dijck at gmail dot
com), one of the students of the 2006-2007 edition of the C++ course, did
a magnificent job by converting all images to vector graphics (in the
process prompting me to start using vector graphics as well :-). Thanks, Elwin
for a job well done!
unsigned
into size_t
where appropriate,
and explicitly mentioned int
-derived types like int16_t
. In-class
member function definitions were moved out of (below) their class definitions
as inline
defined members. A paragraphs about implementing pure virtual
member functions was added. Various bugs and compilation errors were fixed.
template
keyword to distinguish types nested
under class templates from template members. Furthermore,
Sergio Bacchi
s dot bacchi at gmail dot com
did an impressive job when translating the
Annotations into Portuguese. His translation (which may lag a distribution or
two behind the latest verstion of the Annotations) may also be retrieved
from the contributions/
subdirectory in the
c++-annotations_X.Y.Z.tar.gz
archive atstatic const
data members; and the final chapter
(23) covers configurable class templates using local context
structs (replacing the previous ForEach, UnaryPredicate
and
BinaryPredicate
classes). Furthermore, the final section (covering a
C++ parser generator) now uses bisonc++, rather than the old
(and somewhat outdated) bison++ program.
.pdf
files are from now on
distributed with the C++ Annotations. Also, some sections were slightly
adapted.
try
-blocks (section 9.10);
calling conventions of static and global functions (section
11.2.1) and virtual constructors (section 14.11). The
chapter on templates was completely rewritten and split into two separate
chapters: chapter 20 discusses the syntax and use of template
functions; chapter 21 discusses template classes. Various
concrete examples were modified; new examples were included as well (chapter
23).
compare()
function in
chapter 5 contained an error, which was repaired.
iterator
types. This topic was further elaborated in chapter 23, where the
section about the construction of a reverse iterator (section
23.7) was completely rewritten. In the same chapter, a
universal text to anything convertor is discussed (section 23.5).
Also, LaTeX
, PostScript
and PDF
versions fitting the
US-letter
paper size are now available as
cplusplus
us versions: cplusplusus.latex,
cplusplusus.ps
and cplusplus.pdf
. The A4-paper size is of course
kept, and remains to be available in the cplusplus.latex, cplusplus.ps
and
cpluspl.pdf
files.
mutable
keyword (section 7.8), and after thoroughly changing the
discussion of the Fork()
abstract base class (section 23.3). All
examples should now be up-to-date with respect to the use of the std
namespace.
__gnu_cxx
. This namespace should be used
when using these containers. However, this may break compilations of sources
with g++
, version 3.0. In that case, a compilation can be performed
conditionally to the 3.2 and the 3.0 compiler version, defining __gnu_cxx
for the 3.2 version. Alternatively, the dirty trick
#define __gnu_cxx stdcan be placed just before header files in which the
__gnu_cxx
namespace is used. This might eventually result in name-collisions, and it's a
dirty trick by any standards, so please don't tell anybody I wrote this down.
fork()
system call in chapter 23. Under the
ANSI/ISO
standard many of the previously available extensions (like
procbuf
, and
vform
) applied to streams were discontinued. Starting with version
5.1.1. ways of constructing these facilities under the ANSI/ISO standard are
discussed in the C++ Annotations. I consider the involved subject
sufficiently complex to warrant the upgrade to a new subversion.
g++
compiler version 3.00, a more
strict implementation of the ANSI/ISO C++ standard became
available. This resulted in version 5.1.0 of the Annotations, appearing
shortly after version 5.0.0. In version 5.1.0 chapter 6 was
modified and several cosmetic changes took place (e.g., removing class
from template type parameter lists, see chapter 20). Intermediate
versions (like 5.0.0a, 5.0.0b) were not further documented, but were mere
intermediate releases while approaching version 5.1.0. Code
examples will gradually be adapted to the new release of the compiler.
In the meantime the reader should be prepared to insertusing namespace std;
in many code examples, just beyond the#include
preprocessor directives as a temporary measure to make the example accepted by the compiler.
stringstream
class, replacing the strstream
class is now
covered too (sections 6.4.3 and 6.5.3). Actually,
the chapter on input and output was completely rewritten. Furthermore, the
operators new
and delete
are now discussed in chapter 8,
where they fit better than in a chapter on classes, where they previously were
discussed. Chapters were moved, split and reordered, so that subjects could
generally be introduced without forward references. Finally, the html
,
PostScript and pdf versions of the C++ Annotations now contain an
index (sigh of relief ?) All in, considering the volume and nature of the
modifications, it seemed right to upgrade to a full major version. So here it
is.
Considering the volume of the Annotations, I'm sure there will be typos found every now and then. Please do not hesitate to send me mail containing any mistakes you find or corrections you would like to suggest.
4.4.1b
the pagesize in the LaTeX file was defined to
be din A4
. In countries where other pagesizes are standard the default
pagesize might be a better choice. In that case, remove the
a4paper,twoside
option from cplusplus.tex
(or cplusplus.yo
if you
have yodl
installed), and reconstruct the Annotations from the
TeX-file or Yodl
-files.
The Annotations mailing lists was stopped at release 4.4.1d
. From this
point on only minor modifications were expected, which are not anymore
generally announced.
At some point, I considered version 4.4.1
to be the final version of the
C++ Annotations. However, a section on special I/O functions was added to
cover unformatted I/O, and the section about the string
datatype had its
layout improved and was, due to its volume, given a chapter of its own
(chapter 5). All this eventually resulted in version 4.4.2
.
Version 4.4.1
again contains new material, and reflects the
ANSI/ISO standard (well, I try to
have it reflect the ANSI/ISO standard). In version 4.4.1. several new
sections and chapters were added, among which a chapter about the
Standard Template Library (STL) and generic algorithms.
Version 4.4.0
(and subletters) was a mere construction version and was
never made available.
The version 4.3.1a
is a precursor of 4.3.2
. In 4.3.1a
most of the
typos I've received since the last update have been processed. In version
4.3.2
extra attention was paid to the syntax for function addresses and
pointers to member functions.
The decision to upgrade from version 4.2.* to 4.3.* was made after realizing
that the lexical scanner function yylex()
can be defined in the
scanner class that is derived from yyFlexLexer
. Under this approach
the yylex()
function can access the members of the class derived from
yyFlexLexer
as well as the public and protected members of
yyFlexLexer
. The result of all this is a clean implementation of the rules
defined in the flex++
specification file.
The upgrade from version 4.1.* to 4.2.* was the result of the inclusion of section 3.4.1 about the bool data type in chapter 3. The distinction between differences between C and C++ and extensions of the C programming languages is (albeit a bit fuzzy) reflected in the introduction chapter and the chapter on first impressions of C++: The introduction chapter covers some differences between C and C++, whereas the chapter about first impressions of C++ covers some extensions of the C programming language as found in C++.
Major version 4 is a major rewrite of the previous version 3.4.14. The document was rewritten from SGML to Yodl and many new sections were added. All sections got a tune-up. The distribution basis, however, hasn't changed: see the introduction.
Modifications in versions 1.*.*, 2.*.*, and 3.*.* (replace the stars by any applicable number) were not logged.
Subreleases like 4.4.2a
etc. contain bugfixes and typographical
corrections.
C++ was originally a `pre-compiler', similar to the preprocessor of C,
converting special constructions in its source code to plain C. Back then
this code was compiled by a standard C compiler. The `pre-code', which was
read by the C++ pre-compiler, was usually located in a file with the
extension .cc
, .C
or .cpp
. This file would then be converted to a
C source file with the extension .c
, which was thereupon compiled and
linked.
The nomenclature of C++ source files remains: the extensions .cc
and
.cpp
are still used. However, the preliminary work of a C++
pre-compiler is nowadays usually performed during the actual compilation
process. Often compilers determine the language used in a source file from its
extension. This holds true for Borland's and Microsoft's C++ compilers,
which assume a C++ source for an extension .cpp
. The
Gnu
compiler
g++
, which is available on many Unix platforms, assumes for
C++ the extension .cc
.
The fact that C++ used to be compiled into C code is also visible
from the fact that C++ is a superset of C: C++ offers the full
C grammar and supports all C-library functions, and adds to this
features of its own. This makes the transition from C to
C++ quite easy. Programmers familiar with C may start
`programming in C++' by using source files having extensions .cc
or
.cpp
instead of .c
, and may then comfortably slip into all the
possibilities offered by C++. No abrupt change of habits is required.
LaTeX
. After some time, Karel rewrote the
text and converted the guide to a more suitable format and (of course) to
English in september 1994.
The first version of the guide appeared on the net in october 1994. By then it
was converted to SGML
.
Gradually new chapters were added, and the contents were modified and further improved (thanks to countless readers who sent us their comment).
In major version four Frank added new chapters and converted the document from SGML to yodl.
The C++ Annotations are freely distributable. Be sure to read the legal notes.If you like this document, tell your friends about it. Even better, let us know by sending email to Frank.Reading the annotations beyond this point implies that you are aware of these notes and that you agree with them.
In the Internet, many useful hyperlinks exist to C++. Without even suggesting completeness (and without being checked regularly for existence: they might have died by the time you read this), the following might be worthwhile visiting:
.cc
and run it through
a C++ compiler:
sizeof
('c')
equals sizeof(int)
, 'c'
being
any ASCII character. The underlying philosophy is probably that char
s,
when passed as arguments to functions, are passed as integers
anyway. Furthermore, the C compiler handles a character constant like
'c'
as an integer constant. Hence, in C, the function calls
putchar(10);and
putchar('\n');are synonymous.
By contrast, in C++, sizeof('c')
is always 1 (but see also section
3.4.2). An int
is still an int
, though. As we shall see later
(section 2.5.4), the two function calls
somefunc(10);and
somefunc('\n');may be handled by different functions: C++ distinguishes functions not only by their names, but also by their argument types, which are different in these two calls. The former using an
int
argument, the latter a char
.
void func();means that a function
func()
exists, returning no value. The
declaration doesn't specify which arguments (if any) are accepted by the
function.
However, in C++ the above declaration means that the function func()
does not accept any arguments at all. Any arguments passed to it will
result in a compile-time error.
Note that the keyword
extern
is not required when declaring functions. A
function definition becomes a function declaration simply by replacing a
function's body by a semicolon. The keyword extern
is required,
though, when declaring variables.
The upcoming
C++0x standard has not yet fully been implemented in the
g++
compiler. Unless indicated otherwise, all features of the C++0x
standard covered by the C++ Annotations are available in g++ 4.4
,
unless indicated otherwise. To use these features the
compiler flag
--std=c++0x
must currently be provided. It is assumed that this flag is
used when compiling the examples given by the Annotations. The features of the
C++0x standard may or may not be available in g++
versions older than 4.4.
g++
compiler.
When visiting the above URL to obtain a
free g++
compiler, click on install now
. This will download the file
setup.exe
,
which can be run to install cygwin
. The software to be installed can be
downloaded by setup.exe
from the internet. There are alternatives (e.g.,
using a CD-ROM), which are described on the
Cygwin page. Installation
proceeds interactively. The offered defaults are sensible and should be
accepted unless you have reasons to divert.
The most recent Gnu g++
compiler can be obtained from
http://gcc.gnu.org. If the compiler that is
made available in the Cygnus distribution lags behind the latest version, the
sources of the latest version can be downloaded after which the compiler can
be built using an already available compiler. The compiler's webpage
(mentioned above) contains detailed instructions on how to proceed. In our
experience building a new compiler within the Cygnus environment works
flawlessly.
source.cc
':
g++ source.ccThis produces a binary program (
a.out
or a.exe
). If the default
name is inappropriate, the name of the executable can be specified using the
-o
flag (here producing the program source
):
g++ -o source source.cc
If a mere compilation is required, the compiled module can be produced using
the -c
flag:
g++ -c source.ccThis generates the file
source.o
, which can later on be linked to
other modules. As pointed out, provide the
compiler option
--std=c++0x
(note: two dashes). to activate the features of the C++0x standard.
C++ programs quickly become too complex to maintain `by hand'. With all
serious programming projects program maintenance tools are used. Usually the
standard
make
program is be used to maintain C++ programs, but good
alternatives exist, like the
icmake
program maintenance utility,
ccbuild or
lake
It is strongly advised to start using maintenance utilities early in the study of C++.
Concerning the above allegations of C++, we support the following, however.
struct
s, typedef
s etc.. From these types other types
can be derived, thus leading to struct
s containing struct
s and so
on. In C++ these facilities are augmented by defining data types which are
completely `self supporting', taking care of, e.g., their memory management
automatically (without having to resort to an independently operating memory
management system as used in, e.g., Java).
xmalloc
and xrealloc
are used (allocating the memory or aborting
the program when the memory pool is exhausted). However, with functions like
malloc
it is easy to err. Frequently errors in C programs can be
traced back to miscalculations when using malloc
. Instead, C++
offers facilities to allocate memory in a somewhat safer way, using its
operator new
.
static
variables can be used and special
data types such as struct
s can be manipulated by dedicated functions.
Using such techniques, data hiding can be implemented even in C; though it
must be admitted that C++ offers special syntactic constructions, making
it far easier to implement `data hiding' (and more in general:
`encapsulation') in C++ than in C.
With the C++ Annotations we hope to help the reader when transiting from C to C++ by focusing on the additions of C++ as compared to C and by leaving out plain C. It is our hope that you like this document and may benefit from it.
Enjoy and good luck on your journey into C++!
static
).
In contrast (or maybe better: in addition) to this, an object-based approach identifies the keywords used in a problem statement. These keywords are then depicted in a diagram where arrows are drawn between those keywords to depict an internal hierarchy. The keywords become the objects in the implementation and the hierarchy defines the relationship between these objects. The term object is used here to describe a limited, well-defined structure, containing all information about an entity: data types and functions to manipulate the data. As an example of an object oriented approach, an illustration follows:
The employees and owner of a car dealer and auto garage company are paid as follows. First, mechanics who work in the garage are paid a certain sum each month. Second, the owner of the company receives a fixed amount each month. Third, there are car salesmen who work in the showroom and receive their salary each month plus a bonus per sold car. Finally, the company employs second-hand car purchasers who travel around; these employees receive their monthly salary, a bonus per bought car, and a restitution of their travel expenses.When representing the above salary administration, the keywords could be mechanics, owner, salesmen and purchasers. The properties of such units are: a monthly salary, sometimes a bonus per purchase or sale, and sometimes restitution of travel expenses. When analyzing the problem in this manner we arrive at the following representation:
In the hierarchy of objects we would define the dependency between the first two objects by letting the car salesmen be `derived' from the owner and mechanics.
The overall process in the definition of a hierarchy such as the above starts with the description of the most simple type. Traditionally (and still in vogue with some popular object oriented languages) more complex types are then derived from the basic set, with each derivation adding a little extra functionality. From these derived types, more complex types can be derived ad infinitum, until a representation of the entire problem can be made. Over the years, however, this approach has become less popular in C++ as it typically results in overly tight coupling, which in turns reduces rather than enhances the understanding, maintainability and testability of complex programs. In C++ object oriented program more and more favors small, easy to understand hierarchies, limited coupling and a developmental process where design patterns (cf. Gamma et al. (1995)) play a central role.
Nonetheless, in C++ classes are frequently used to define the characteristics of objects. Classes contain the necessary functionality to do useful things. Classes generally do not offer all their functionality (and typically none of their data) to objects of other classes. As we will see, classes tend to hide their properties in such a way that they are not directly modifiable by the outside world. Instead, dedicated functions are used to reach or modify the properties of objects. Thus class-type objects are able to uphold their own integrity. The core concept here is encapsulation of which data hiding is just an example. These concepts will be further explained in chapter 7.
main
:
int main()
and int main(int argc, char **argv)
. Notes:
main
is int
, and not void
.
return
statement at the
end of main
. If omitted main
returns 0.
char **envp
parameter' is not defined by the C++
standard and should be avoided. Instead, the global variable
extern char **environ
should be declared providing access to the program's
environment variables. Its final element has the value 0.
//
and ends
at the end-of-line marker. The standard C comment, delimited by /*
and
*/
can still be used in C++:
int main() { // this is end-of-line comment // one comment per line /* this is standard-C comment, covering multiple lines */ }Despite the example, it is advised not to use C type comment inside the body of C++ functions. Sometimes existing code must temporarily be suppressed, e.g., for testing purposes. In those cases it's very practical to be able to use standard C comment. If such suppressed code itself contains such comment, it would result in nested comment-lines, resulting in compiler errors. Therefore, the rule of thumb is not to use C type comment inside the body of C++ functions (alternatively,
#if 0
until
#endif
pair of preprocessor directives could of course also be used).
int main() { printf("Hello World\n"); }often compiles under C, albeit with a warning that
printf()
is an
unknown function. But C++ compilers (should) fail to produce code in such
cases. The error is of course caused by the missing
#include <stdio.h>
(which in C++ is more commonly included as
#include <cstdio>
directive).
And while we're at it: as we've seen in C++
main
always uses
the int
return value. Although it is possible to define
int
main()
without explicitly defining a return statement, within main
it is
not possible to use a return
statement without an explicit
int
-expression. For example:
int main() { return; // won't compile: expects int expression, e.g. // return 1; }
const
attribute). An example is given
below:
#include <stdio.h> void show(int val) { printf("Integer: %d\n", val); } void show(double val) { printf("Double: %lf\n", val); } void show(char const *val) { printf("String: %s\n", val); } int main() { show(12); show(3.1415); show("Hello World\n!"); }In the above program three functions
show
are defined, only differing
in their parameter lists, expecting an int
, double
and char *
,
respectively. The functions have identical names. Functions having identical
names but different parameter lists are called overloaded. The act of
defining such functions is called `
function overloading'.
The C++ compiler implements function overloading in a rather simple
way. Although the functions share their names (in this example show
), the
compiler (and hence the linker) use quite different names. The conversion of a
name in the source file to an internally used name is called
`
name mangling'. E.g., the C++ compiler might convert the prototype
void
show
(int)
to the internal name VshowI
, while an
analogous function having a char *
argument might be called
VshowCP
. The actual names that are used internally depend on the compiler
and are not relevant for the programmer, except where these names show up in
e.g., a listing of the contents of a library.
Some additional remarks with respect to function overloading:
show
are still
somewhat related (they print information to the screen).
However, it is also quite possible to define two functions lookup
,
one of which would find a name in a list while the other would determine the
video mode. In this case the behavior of those two functions have nothing in
common. It would therefore be more practical to use names which suggest their
actions; say, findname
and videoMode
.
printf("Hello World!\n");provides no information about the return value of the function
printf
. Two functions printf
which only differ in their
return types would therefore not be distinguishable to the compiler.
const
member functions is
introduced (cf. section 7.5). Here it is merely mentioned
that classes normally have so-called member functions associated with them
(see, e.g., chapter 5 for an informal introduction to the
concept). Apart from
overloading member functions using different parameter lists, it
is then also possible to
overload member functions by their const
attributes. In those cases,
classes may have pairs of identically named member functions, having identical
parameter lists. Then, these functions are overloaded by their const
attribute. In such cases only one of these function must have the const
attribute.
#include <stdio.h> void showstring(char *str = "Hello World!\n"); int main() { showstring("Here's an explicit argument.\n"); showstring(); // in fact this says: // showstring("Hello World!\n"); }The possibility to omit arguments in situations where default arguments are defined is just a nice touch: it is the compiler who supplies the lacking argument unless it is explicitly specified at the call. The code of the program will neither be shorter nor more efficient when default arguments are used.
Functions may be defined with more than one default argument:
void two_ints(int a = 1, int b = 4); int main() { two_ints(); // arguments: 1, 4 two_ints(20); // arguments: 20, 4 two_ints(20, 5); // arguments: 20, 5 }When the function
two_ints
is called, the compiler supplies one or two
arguments whenever necessary. A statement like two_ints(,6)
is, however,
not allowed: when arguments are omitted they must be on the right-hand side.
Default arguments must be known at compile-time since at that moment arguments are supplied to functions. Therefore, the default arguments must be mentioned at the function's declaration, rather than at its implementation:
// sample header file extern void two_ints(int a = 1, int b = 4); // code of function in, say, two.cc void two_ints(int a, int b) { ... }It is an error to supply default arguments in function definitions. When the function is used by other sources the compiler reads the header file rather than the function definition. Consequently the compiler has no way to determine the values of default function arguments. Current compilers generate compile-time errors when detecting default arguments in function definitions.
0
. In C
NULL
is often used
in the context of pointers. This difference is purely stylistic, though one
that is widely adopted. In C++ NULL
should be avoided (as it is a
macro, and macros can --and therefore should-- easily be avoided in
C++). Instead 0
can almost always be used.
Almost always, but not always. As C++ allows function overloading (cf. section 2.5.4) the programmer might be confronted with an unexpected function selection in the situation shown in section 2.5.4:
#include <stdio.h> void show(int val) { printf("Integer: %d\n", val); } void show(double val) { printf("Double: %lf\n", val); } void show(char const *val) { printf("String: %s\n", val); } int main() { show(12); show(3.1415); show("Hello World\n!"); }
In this situation a programmer intending to call show(char const *)
might
call show(0)
. But this doesn't work, as 0 is interpreted as int
and so
show(int)
is called. But calling show(NULL)
doesn't work either, as
C++ usually defines
NULL
as 0, rather than ((void *)0)
. So,
show(int)
is called once again. To solve these kinds of problems the new
C++ standard introduces the keyword
nullptr
representing the 0
pointer. In the current example the programmer should call show(nullptr)
to avoid the selection of the wrong function. The nullptr
value can also
be used to initialize pointer variables. E.g.,
int *ip = nullptr; // OK int value = nullptr; // error: value is no pointer
void func();means that the argument list of the declared function is not prototyped: the compiler will not warn against calling
func
with any set of arguments. In
C the keyword
void
is used when it is the explicit intent to declare a
function with no arguments at all, as in:
void func(void);As C++ enforces strict type checking, in C++ an empty parameter list indicates the total absence of parameters. The keyword
void
is
thus omitted.
__cplusplus
: it is as if each source file were prefixed with the
preprocessor directive
#define __cplusplus
.
We shall see examples of the usage of this symbol in the following sections.
As an example, the following code fragment declares a function xmalloc
as a C function:
extern "C" void *xmalloc(int size);This declaration is analogous to a declaration in C, except that the prototype is prefixed with
extern "C"
.
A slightly different way to declare C functions is the following:
extern "C" { // C-declarations go in here }It is also possible to place preprocessor directives at the location of the declarations. E.g., a C header file
myheader.h
which declares
C functions can be included in a C++ source file as follows:
extern "C" { #include <myheader.h> }Although these two approaches may be used, they are actually seldom encountered in C++ sources. We will encounter a more frequently used method to declare external C functions in the next section.
__cplusplus
and the
possibility to define
extern "C"
functions offers the ability to
create header files for both C and C++. Such a header file might,
e.g., declare a group of functions which are to be used in both C and
C++ programs.
The setup of such a header file is as follows:
#ifdef __cplusplus extern "C" { #endif /* declaration of C-data and functions are inserted here. E.g., */ void *xmalloc(int size); #ifdef __cplusplus } #endifUsing this setup, a normal C header file is enclosed by
extern "C"
{
which occurs near the top of the file and by }
, which occurs near
the bottom of the file. The
#ifdef
directives test for the type of the
compilation: C or C++. The `standard' C header files, such as
stdio.h
, are built in this manner and are therefore usable for both C
and C++.
In addition C++ headers should support
include guards.
In C++ it is usually undesirable to include the same header file twice in
the same source file. Such multiple inclusions can easily be avoided by
including an
#ifndef
directive in the header file. For example:
#ifndef MYHEADER_H_ #define MYHEADER_H_ // declarations of the header file is inserted here, // using #ifdef __cplusplus etc. directives #endifWhen this file is initially scanned by the preprocessor, the symbol
MYHEADER_H_
is not yet defined. The #ifndef
condition succeeds and all
declarations are scanned. In addition, the symbol MYHEADER_H_
is defined.
When this file is scanned next while compiling the same source file,
the symbol MYHEADER_H_
has been defined and consequently all information
between the #ifndef
and #endif
directives is skipped by the compiler.
In this context the symbol name MYHEADER_H_
serves only for
recognition purposes. E.g., the name of the header file can be used for this
purpose, in capitals, with an underscore character instead of a dot.
Apart from all this, the custom has evolved to give C header files the
extension
.h
, and to give C++
header files no extension. For
example, the standard iostreams cin, cout
and cerr
are available
after including the header file
iostream
, rather
than
iostream.h
. In the Annotations this convention is used
with the standard C++ header files, but not necessarily everywhere else.
There is more to be said about header files. Section 7.9 provides an in-depth discussion of the preferred organization of C++ header files.
Furthermore, local variables can be defined within some statements, just prior
to their usage. A typical example is the for
statement:
#include <stdio.h> int main() { for (int i = 0; i < 20; ++i) printf("%d\n", i); }In this program the variable
i
is created in the initialization
section of the for
statement. According to the ANSI-standard, the variable
does not exist prior to the for
-statement and not beyond the
for
-statement. With some older compilers, the variable continues to exist
after the execution of the for
-statement, but nowadays a warning like
warning: name lookup of `i' changed for new ANSI `for' scoping using obsolete binding at `i'is issued when the variable is used outside of the
for
-loop.
The implication seems clear: define a variable just before the
for
-statement if it is to be used beyond that statement. Otherwise the
variable should be defined inside the for
-statement itself. This reduces
its scope as much as possible, which is a very desirable characteristic.
Defining local variables when they're needed requires a little getting used to. However, eventually it tends to produce more readable, maintainable and often more efficient code than defining variables at the beginning of compound statements. We suggest the following rules of thumb for defining local variables:
for
-statement, but
also all situations where a variable is only needed, say, half-way through the
function.
If considered appropriate,
nested blocks can be used to localize
auxiliary variables. However, situations exist where local variables are
considered appropriate inside nested statements. The just mentioned for
statement is of course a case in point, but local variables can also be
defined within the condition clauses of if-else
statements, within
selection clauses of switch
statements and condition clauses of while
statements. Variables thus defined will be available to the full
statement, including its nested statements. For example, consider the
following switch
statement:
#include <stdio.h> int main() { switch (int c = getchar()) { case 'a': case 'e': case 'i': case 'o': case 'u': printf("Saw vowel %c\n", c); break; case EOF: printf("Saw EOF\n"); break; default: printf("Saw other character, hex value 0x%2x\n", c); } }Note the location of the definition of the character `
c
': it is
defined in the expression part of the switch
statement. This implies
that `c
' is available only to the switch
statement itself,
including its nested (sub)statements, but not outside the scope of the
switch
.
The same approach can be used with if
and while
statements: a
variable that is defined in the condition part of an if
and while
statement is available in their nested statements. There are some caveats,
though:
if
or while
statement,
the value of the variable must be interpretable as either zero (false) or
non-zero (true). Usually this is no problem, but in C++ objects (like
objects of the type std::string
(cf. chapter 5)) are often
returned by functions. Such objects may or may not be interpretable as numeric
values. If not (as is the case with std::string
objects), then such
variables can not be defined at the condition or expression clauses of
condition- or repetition statements. The following example will therefore
not compile:
if (std::string myString = getString()) // assume getString returns { // a std::string value // process myString }
The above example requires additional clarification. Often a variable can profitably be given local scope, but an extra check is required immediately following its initialization. The initialization and the test cannot both be combined in one expression. Instead two nested statements are required. Consequently, the following example won't compile either:
if ((int c = getchar()) && strchr("aeiou", c)) printf("Saw a vowel\n");If such a situation occurs, either use two nested
if
statements, or
localize the definition of int c
using a nested compound statement:
if (int c = getchar()) // nested if-statements if (strchr("aeiou", c)) printf("Saw a vowel\n"); { // nested compound statement int c = getchar(); if (c && strchr("aeiou", c)) printf("Saw a vowel\n"); }
typedef
is still allowed in C++, but is not required
anymore when defining
union
,
struct
or
enum
definitions.
This is illustrated in the following example:
struct somestruct { int a; double d; char string[80]; };When a
struct
, union
or other compound type is defined, the tag of
this type can be used as type name (this is somestruct
in the above
example):
somestruct what; what.d = 3.1415;
A definition of a struct Point
is provided by the code fragment below.
In this structure, two int
data fields and one function draw
are
declared.
struct Point // definition of a screen-dot { int x; // coordinates int y; // x/y void draw(); // drawing function };A similar structure could be part of a painting program and could, e.g., represent a pixel. With respect to this
struct
it should be noted that:
draw
mentioned in the struct
definition is a
mere declaration. The actual code of the function defining the actions
performed by the function is found elsewhere (the concept of functions inside
struct
s is further discussed in section 3.2).
struct
Point
is equal to the size of its
two int
s. A function declared inside the structure does not affect its
size. The compiler implements this behavior by allowing the function
draw
to be available only in the context of a Point
.
Point
structure could be used as follows:
Point a; // two points on Point b; // the screen a.x = 0; // define first dot a.y = 10; // and draw it a.draw(); b = a; // copy a to b b.y = 20; // redefine y-coord b.draw(); // and draw itAs shown in the above example a function that is part of the structure may be selected using the dot (.) (the arrow (
->
) operator is used when
pointers to objects are available). This is therefore identical to the way
data fields of structures are selected.
The idea behind this syntactic construction is that several types may
contain
functions having identical names. E.g., a structure representing a
circle might contain three int
values: two values for the coordinates of
the center of the circle and one value for the radius. Analogously to the
Point
structure, a Circle
may now have a function draw
to draw the
circle.