Search for notes by fellow students, in your own course and all over the country.

Browse our notes for titles which look like what you need, you can preview any of the notes via a sample of the contents. After you're happy these are the notes you're after simply pop them into your shopping cart.

My Basket

3 Types of Cloud Computing£1.50

Title: C PLUS PLUS PROGRAMING LANGUAGE
Description: this is notes for c ++ programing language in computer application

Document Preview

Extracts from the notes are below, to see the PDF you'll receive please use the links above


C++:
The Complete Reference
Third Edition

About the Author…
Herb Schildt is the leading authority on C and
C++ and a best-selling author whose books
have sold more than 1
...
His
acclaimed C and C++books include Teach
Yourself C, C++ from the Ground Up, Teach
Yourself C++, C++: The Complete Reference,
Borland C++: The Complete Reference, and C++
Programmer's Reference to name a few
...
Louis San Francisco
Auckland Bogotá Hamburg London Madrid
Mexico City Milan Montreal New Delhi Panama City
Paris São Paulo Singapore Sydney
Tokyo Toronto

abc

McGraw-Hill

Copyright © 1998 by McGraw-Hill Companies
...
Manufactured in the United States of America
...

0-07-213293-0
The material in this eBook also appears in the print version of this title: 0-07-882476-1

All trademarks are trademarks of their respective owners
...
Where such designations appear in this book, they have been printed with initial caps
...
For more information, please contact George Hoare, Special Sales, at george_hoare@mcgraw-hill
...


TERMS OF USE
This is a copyrighted work and The McGraw-Hill Companies, Inc
...
Use of this work is subject to these terms
...
You may
use the work for your own noncommercial and personal use; any other use of the work is strictly prohibited
...

THE WORK IS PROVIDED “AS IS”
...
McGraw-Hill and its licensors do not
warrant or guarantee that the functions contained in the work will meet your requirements or that its operation will be uninterrupted or
error free
...
McGraw-Hill has no responsibility for the content of any information
accessed through the work
...
This limitation of liability shall apply to any claim or cause whatsoever whether such claim
or cause arises in contract, tort or otherwise
...
1036/0072132930

Contents at a Glance
Part I The Foundation of C++: The C Subset
An Overview of C
...

Statements
...

Pointers
...

Structures, Unions, Enumerations, and UserDefined Types
...

9 File I/O
...

1
2
3
4
5
6
7

3
13
57
89
113
137
161
187
211
237

Part II C++
11 An Overview of C++
...

13 Arrays, Pointers, References and the Dynamic
Allocation Operators
...

15 Operator Overloading
...

Virtual Functions and Polymorphism
...

Exception Handling
...

C++ File I/O
...

Namespaces, Conversion Functions,and Other
Advanced Topics
...

16
17
18
19
20
21
22
23

419
445
461
489
511
541
569
593
625

Part III The Standard Function Library
25
26
27
28
29
30
31

The C-Based I/O Functions
...

The Mathematical Functions
...

The Dynamic Allocation Functions
...

The Wide-Character Functions
...

The STL Container Classes
...

STL Iterators, Allocators, and Function Objects
...

The Numeric Classes
...


783
807
835
857
877
893
921

Part V Applying C++
39 Integrating New Classes: A Custom String Class
...

Index
...


xxix

Part I
The Foundation of C++: The C Subset

2

An Overview of C
...

C Is a Middle-Level Language
...

C Is a Programmer's Language
...

The Library and Linking
...

Understanding the
...
CPP File Extensions
...


13

The Five Basic Data Types
...

Identifier Names
...

Where Variables Are Declared
...

Formal Parameters
...

Access Modifiers
...

volatile
...

extern
...

register Variables
...

Constants
...

String Constants
...

Operators
...

Type Conversion in Assignments
...

Arithmetic Operators
...

Relational and Logical Operators
...

The ? Operator
...

The Compile-Time Operator sizeof
...

The Dot (
...

The [ ] and ( ) Operators
...

Expressions
...

Type Conversion in Expressions
...

Spacing and Parentheses
...


3

15
16
17
17
17
21
21
23
23
24
25
25
27
29
31
31
32
33
33
34
34
35
36
37
37
39
42
47
47
49
50
51
51
52
53
53
53
54
55
56

Statements
...


58

Contents

Selection Statements
...

Nested ifs
...

The ? Alternative
...

switch
...

Iteration Statements
...

for Loop Variations
...

for Loops with No Bodies
...

The do-while Loop
...

Jump Statements
...

The goto Statement
...

The exit( ) Function
...

Expression Statements
...


59
59
60
62
63
66
67
70
70
70
72
76
77
77
79
81
82
82
83
83
85
86
88
88

5

Arrays and Null-Terminated Strings
...

Generating a Pointer to an Array
...

Null-Terminated Strings
...

Arrays of Strings
...

Indexing Pointers
...

Unsized Array Initializations
...


4

90
92
92
94
96
100
101
102
105
106
108

Pointers
...

Pointer Variables
...

Pointer Expressions
...

Pointer Arithmetic
...

Pointers and Arrays
...

Multiple Indirection
...

Pointers to Functions
...

Problems with Pointers
...


137

The General Form of a Function
...

Function Arguments
...

Creating a Call by Reference
...

argc and argv—Arguments to main( )
...

Returning from a Function
...

Returning Pointers
...

What Does main( ) Return?
...

Function Prototypes
...

Declaring Variable-Length Parameter Lists
...

Implementation Issues
...

Efficiency
...


161

Structures
...

Structure Assignments
...

Passing Structures to Functions
...

Passing Entire Structures to Functions
...

Declaring a Structure Pointer
...

Arrays and Structures Within Structures
...

Unions
...

Using sizeof to Ensure Portability
...


8

169
170
170
173
174
176
180
183
184

C-Style Console I/O
...

Reading and Writing Characters
...

Alternatives to getchar( )
...

Formatted Console I/O
...

Printing Characters
...

Displaying an Address
...

Format Modifiers
...

The Precision Specifier
...

Handling Other Data Types
...

scanf( )
...

Inputting Numbers
...

Reading Individual Characters Using scanf( )
...

Inputting an Address
...

Using a Scanset
...

Non-White-Space Characters in the Control String
...

Format Modifiers
...


188
189
190
190
192
195
195
196
196
198
198
199
199
200
201
202
202
203
203
203
205
205
205
206
206
206
207
208
208
208
209

xi

xii

C++: The Complete Reference

10

File I/O
...

Streams and Files
...

Text Streams
...

Files
...

The File Pointer
...

Closing a File
...

Reading a Character
...

Using feof( )
...

rewind( )
...

Erasing Files
...

fread( ) and fwrite( )
...

fseek( ) and Random-Access I/O
...

The Standard Streams
...

Using freopen( ) to Redirect the Standard Streams
...


237

The Preprocessor
...

Defining Function-like Macros
...

#include
...

#if, #else, #elif, and #endif
...

#undef
...

#line
...

The # and ## Preprocessor Operators
...


238
238
240
241
242
242
243
245
246
247
248
248
248
250

Contents

C-Style Comments
...


255

The Origins of C++
...

Encapsulation
...

Inheritance
...

A Sample C++ Program
...

Declaring Local Variables
...

The bool Data Type
...
Modern C++
...

Namespaces
...

Introducing C++ Classes
...

Operator Overloading
...

Constructors and Destructors
...

The General Form of a C++ Program
...


289

Classes
...

Unions and Classes Are Related
...

Friend Functions
...

Inline Functions
...

Parameterized Constructors
...

Static Class Members
...


290
293
295
297
298
302
303
306
307
309
310
310

xiii

xiv

C++: The Complete Reference

Static Member Functions
...

The Scope Resolution Operator
...

Local Classes
...

Returning Objects
...


13

315
317
319
320
320
321
323
324

Arrays, Pointers, References, and the Dynamic Allocation
Operators
...

Creating Initialized vs
...

Pointers to Objects
...

The this Pointer
...

Pointers to Class Members
...

Reference Parameters
...

Returning References
...

References to Derived Types
...

A Matter of Style
...

Initializing Allocated Memory
...

Allocating Objects
...

The Placement Forms of new and delete
...


361

Function Overloading
...

Overloading a Constructor to Gain Flexibility
...

Copy Constructors
...

The overload Anachronism
...

Default Arguments vs
...

Using Default Arguments Correctly
...


374
378
380
380

Operator Overloading
...

Creating Prefix and Postfix Forms of the
Increment and Decrement Operators
...

Operator Overloading Restrictions
...

Using a Friend to Overload ++ or – –
...

Overloading new and delete
...

Overloading the nothrow Version of new and delete
...

Overloading [ ]
...

Overloading –>
...


15

386
391
392
392
393
395
398
400
405
408
409
409
413
415
416

17

Inheritance
...

Inheritance and protected Members
...

Inheriting Multiple Base Classes
...

When Constructor and Destructor
Functions Are Executed
...

Granting Access
...


16

420
422
426
427
428
428
432
436
439

Virtual Functions and Polymorphism
...

Calling a Virtual Function Through a Base Class
Reference
...

Virtual Functions Are Hierarchical
...

Abstract Classes
...

Early vs
...


457
460

Templates
...

A Function with Two Generic Types
...

Overloading a Function Template
...

Generic Function Restrictions
...

A Generic Sort
...

Generic Classes
...

Applying Template Classes: A Generic Array Class
...

Using Default Arguments with Template Classes
...

The typename and export Keywords
...


18

462
465
465
468
468
469
470
471
472
474
478
479
481
483
485
486
487

20

Exception Handling
...

Catching Class Types
...

Handling Derived-Class Exceptions
...

Catching All Exceptions
...

Rethrowing an Exception
...

Setting the Terminate and Unexpected Handlers
...

The exception and bad_exception Classes
...


19

490
496
497
499
500
500
502
504
505
506
507
508
508

The C++ I/O System Basics
...
Modern C++ I/O
...

The C++ Stream Classes
...

Formatted I/O
...

Setting the Format Flags
...

An Overloaded Form of setf( )
...

Setting All Flags
...

Using Manipulators to Format I/O
...

Creating Your Own Inserters
...

Creating Your Own Manipulator Functions
...


541

and the File Classes
...

Reading and Writing Text Files
...

Characters vs
...

put( ) and get( )
...

More get( ) Functions
...

Detecting EOF
...

peek( ) and putback( )
...

Random Access
...

I/O Status
...


21

542
542
545
547
547
548
550
553
553
555
557
558
558
559
563
563
565

Run-Time Type ID and the Casting Operators
...

A Simple Application of Run-Time Type ID
...

The Casting Operators
...

Replacing typeid with dynamic_cast
...

const_cast
...

reinterpret_cast
...


593

Namespaces
...

using
...

Some Namespace Options
...

Creating Conversion Functions
...

Volatile Member Functions
...

Using the asm Keyword
...

Array-Based I/O
...

Creating an Array-Based Output Stream
...

Input/Output Array-Based Streams
...

Using Binary I/O with Array-Based Streams
...


24

594
594
598
600
601
603
605
609
611
612
613
614
615
616
616
618
620
621
622

Introducing the Standard Template Library
...

Containers
...

Iterators
...

The Container Classes
...

Vectors
...

Inserting and Deleting Elements in a Vector
...

Lists
...

push_front( ) vs push_back( )
...

Merging One List with Another
...

Maps
...

Algorithms
...

Removing and Replacing Elements
...

Transforming a Sequence
...

Unary and Binary Function Objects
...

Creating a Function Object
...

The string Class
...

Strings Are Containers
...

Final Thoughts on the STL
...


695

clearerr
...

feof
...

fflush
...

fgetpos
...

fopen
...

fputc
...

fread
...

fscanf
...

fsetpos
...

fwrite
...

getchar
...


696
697
697
697
698
698
698
699
699
701
701
702
702
702
703
703
704
704
705
705
706
706

xix

xx

C++: The Complete Reference

perror
...

putc
...

puts
...

rename
...

scanf
...

setvbuf
...

sscanf
...

tmpnam
...

vprintf, vfprintf, and vsprintf
...


719

isalnum
...

iscntrl
...

isgraph
...

isprint
...

isspace
...

isxdigit
...

memcmp
...

memmove
...

strcat
...

strcmp
...

strcpy
...

strerror
...


720
720
721
721
721
721
722
722
722
723
723
723
723
724
724
725
725
725
726
726
727
727
727
727

Contents

strncat
...

strncpy
...

strrchr
...

strstr
...

strxfrm
...

toupper
...


733

acos
...

atan
...

ceil
...

cosh
...

fabs
...

fmod
...

ldexp
...

log10
...

pow
...

sinh
...

tan
...


27

728
728
729
729
729
730
730
730
731
731
731

734
734
735
735
735
736
736
736
737
737
737
737
738
738
738
739
739
739
740
740
740
741

Time, Date, and Localization Functions
...

clock
...

difftime
...

localeconv
...

mktime
...

strftime
...


30

The Dynamic Allocation Functions
...

free
...

realloc
...


757

abort
...

assert
...

atof
...

atol
...

div
...

getenv
...

ldiv
...

mblen
...

mbtowc
...

raise
...

setjmp
...

srand
...

strtol
...

system
...

wcstombs
...


758
758
759
759
759
760
760
760
761
762
762
762
763
763
763
764
764
764
765
766
766
766
767
767
768
768
769
769
770
770

Contents

The Wide-Character Functions
...

The Wide-Character I/O Functions
...

Wide-Character String Conversion Functions
...

Multibyte/Wide-Character Conversion Functions
...


783

The I/O Classes
...

The Format Flags and I/O Manipulators
...

The streamsize and streamoff Types
...

The pos_type and off_type Types
...

The iostate Type
...

The failure Class
...

The General-Purpose I/O Functions
...

clear
...

exceptions
...

fill
...

flush
...

gcount
...

getline
...

ignore
...

peek
...


784
786
787
789
789
789
789
789
790
790
790
790
791
791
791
791
792
792
792
793
793
793
794
794
795
796
796
796
797
798

xxiii

xxiv

C++: The Complete Reference

put
...

rdstate
...

readsome
...

setf
...

str
...

sync_with_stdio
...

unsetf
...

write
...


807

The Container Classes
...

deque
...

map
...

multiset
...

priority_queue
...

stack
...


33

808
810
812
815
818
820
823
825
826
827
829
830

The STL Algorithms
...

binary_search
...

copy_backward
...

count_if
...

equal_range
...

find
...

find_first_of
...

for_each
...

includes
...

iter_swap
...

lower_bound
...

max
...

merge
...

min_element
...

next_permutation
...

partial_sort
...

partition
...

prev_permutation
...

random_shuffle
...

replace, replace_copy, replace_if, and replace_copy_if
...

rotate and rotate_copy
...

search_n
...

set_intersection
...

set_union
...

sort_heap
...

stable_sort
...

swap_ranges
...

unique and unique_copy
...


840
840
840
841
841
841
842
842
842
843
843
843
844
844
844
845
845
845
846
846
846
847
847
847
848
848
849
849
850
850
850
851
851
852
852
852
853
853
853
854
854
854
855

xxv

xxvi

C++: The Complete Reference

STL Iterators, Allocators, and Function Objects
...

The Basic Iterator Types
...

The Predefined Iterators
...

Function Objects
...

Binders
...

Adaptors
...


35

858
858
859
860
868
868
869
870
871
872
875

878
890

The Numeric Classes
...


921

Exceptions
...


...

The pair Class
...

Other Classes of Interest
...

The valarray Class
...

The Helper Classes
...

accumulate
...

inner_product
...


37

The String Class
...

The char_traits Class
...


931

The StrType Class
...

I/O on Strings
...

Concatenation
...

The Relational Operators
...

The Entire StrType Class
...

Creating and Integrating New Types in General
...


40

934
935
937
938
941
943
944
945
954
957
957

An Object-Oriented Expression Parser
...

Parsing Expressions: The Problem
...

The Parser Class
...

A Simple Expression Parser
...

Adding Variables to the Parser
...

Building a Generic Parser
...


960
961
962
964
965
967
973
974
984
985
993

Index

995


...


Preface
This is the third edition of C++: The Complete Reference
...
Perhaps the most important
is that it is now a standardized language
...
This event marked the end of a very long, and at
times contentious, process
...
Near the
end, there was a world-wide, daily dialogue, conducted via e-mail, in which the pros
and cons of this or that issue were put forth, and finally resolved
...
We now have a standard for what is, without question, the most important
programming language in the world
...
Some are
relatively small
...
The net effect of the
additions was that the scope and range of the language were greatly expanded
...
Of course, the information contained in this edition

xxix

xxx

C++: The Complete Reference

reflects the International Standard for C++ defined by the ANSI/ISO committee,
including its new features
...
In fact, the length of the book has nearly doubled! The main reason for
this is that the third edition now includes comprehensive coverage of both the standard
function library and the standard class library
...
With the
standardization of C++ being complete, these topics can finally be added
...
Most is the result of features
that have been added to C++ since the previous edition was prepared
...
Also, some
fundamental changes to the way new and delete are implemented are described and
several new keywords are discussed
...

It's not the same old C++ that you learned years ago
...

The book is divided into these five parts:
s The C Subset — The foundation of C++
s The C++ language
s The Standard Function Library
s The Standard Class Library
s Sample C++ applications
Part One provides a comprehensive discussion of the C subset of C++
...
It is the C subset
that defines the bedrock features of C++, including such things as for loops and if
statements
...
Since many readers are already familiar with and proficient in C, discussing
the C subset separately in Part One prevents the knowledgeable C programmer from
having to "wade through" reams of information he or she already knows
...

Part Two discusses in detail the extensions and enhancements to C added by C++
...
Thus, Part Two covers those constructs that "make C++, C++
...
Part Five shows
two practical examples of applying C++ and object-oriented programming
...
It does assume, however, a reader able to create at least a simple program
...
Experienced C++ pros will
find the coverage of the many new features added by the International Standard
especially useful
...
C++ is
completely at home with Windows programming
...
Instead, they are console-based programs
...
The overhead required to create even a minimal Windows skeletal program
is 50 to 70 lines of code
...
Put simply, Windows is not an
appropriate environment in which to discuss the features of a programming language
...


Don't Forget: Code On The Web
Remember, the source code for all of the programs in this book is available
free-of-charge on the Web at http://www
...
com
...


xxxi

xxxii

C++: The Complete Reference

For Further Study
C++: The Complete Reference is your gateway into the "Herb Schildt" series of
programming books
...

If you want to learn more about C++, then you will find these books especially
helpful
...

Finally, if you want to program for Windows, we recommend
Windows 98 Programming From the Ground Up
Windows NT 4 From the Ground Up
MFC Programming From the Ground Up

When you need solid answers, fast, turn to Herbert Schildt,
the recognized authority on programming
...
Part One discusses the C-like features of C++
...
Part Two
describes those features specific to C++
...

As you may know, C++ was built upon the foundation of C
...
When C++
was invented, the C language was used as the starting point
...
However, the C-like aspects of
C++ were never abandoned, and the ANSI/ISO C standard is a base document for
the International Standard for C++
...

In a book such as this Complete Reference, dividing the C++ language into two
pieces—the C foundation and the C++-specific features—achieves three major benefits:
1
...

2
...

3
...

Understanding the dividing line between C and C++ is important because both are
widely used languages and it is very likely that you will be called upon to write or
maintain both C and C++ code
...
Many C++ programmers will, from time to time, be required to
write code that is limited to the "C subset
...
Knowing the
difference between C and C++ is simply part of being a top-notch professional C++
programmer
...
To
do this in a professional manner, a solid knowledge of C is required
...

Many readers already know C
...
Of course, throughout Part One, any minor differences between
C and C++ are noted
...

Although C++ contains the entire C language, not all of the features provided by
the C language are commonly used when writing "C++-style" programs
...
The preprocessor is another example
...
Discussing several of the "C-only" features in
Part One prevents them from cluttering up the remainder of the book
...
All the features described here
are part of C++ and available for your use
...
If you are particularly interested in C, you will find this
book helpful
...
Thus, the story of C++ begins with C
...
Since C++ is built upon C, this chapter provides
an important historical perspective on the roots of C++
...


T

The Origins of C
C was invented and first implemented by Dennis Ritchie on a DEC PDP-11 that used
the Unix operating system
...
BCPL was developed by Martin Richards, and it
influenced a language called B, which was invented by Ken Thompson
...

For many years, the de facto standard for C was the version supplied with the Unix
version 5 operating system
...
J
...
In the
summer of 1983 a committee was established to create an ANSI (American National
Standards Institute) standard that would define the C language once and for all
...

The ANSI C standard was finally adopted in December 1989, with the first copies
becoming available in early 1990
...
For
simplicity, this book will use the term Standard C when referring to the ANSI/ISO C
standard
...
Standard
C is the foundation upon which C++ is built
...
This does not mean that C is less
powerful, harder to use, or less developed than a high-level language such as BASIC
or Pascal, nor does it imply that C has the cumbersome nature of assembly language
(and its associated troubles)
...
Table 1-1 shows how C fits into the spectrum of computer
languages
...
Despite this fact
C code is also very portable
...
For example, if you can easily
convert a program written for DOS so that it runs under Windows, that program is
portable
...


C's Place in the World of Programming Languages

All high-level programming languages support the concept of data types
...
Common data types are integer, character, and real
...
C permits almost all type conversions
...

Unlike a high-level language, C performs almost no run-time error checking
...
These
types of checks are the responsibility of the programmer
...
As you may know from your other programming experience, a
high-level computer language will typically require that the type of an argument be
(more or less) exactly the same type as the parameter that will receive the argument
...
Instead, C allows an argument to be of any type
as long as it can be reasonably converted into the type of the parameter
...


5

6

C++: The Complete Reference

C is special in that it allows the direct manipulation of bits, bytes, words, and
pointers
...

Another important aspect of C is that it has only 32 keywords (27 from the
Kernighan and Ritchie de facto standard, and five added by the ANSI standardization
committee), which are the commands that make up the C language
...
As a comparison, consider
that most versions of BASIC have well over 100 keywords!

C Is a Structured Language
In your previous programming experience, you may have heard the term blockstructured applied to a computer language
...
It has many similarities to other structured languages, such
as ALGOL, Pascal, and Modula-2
...
Since C does not allow the creation of functions
within functions, it cannot formally be called block-structured
...
This is the ability of a language to section off and hide from the rest of the
program all information and instructions necessary to perform a specific task
...
By using local variables, you can write subroutines so that the
events that occur within them cause no side effects in other parts of the program
...
If you develop
compartmentalized functions, you only need to know what a function does, not how it
does it
...
(Anyone who has programmed in standard BASIC is well aware of this
problem
...
Specifically, in
C++, one part of your program may tightly control which other parts of your
program are allowed access
...
It
directly supports several loop constructs, such as while, do-while, and for
...
A structured language allows you to place statements
anywhere on a line and does not require a strict field concept (as some older
FORTRANs do)
...
In fact, a mark of an old computer
language is that it is nonstructured
...


Note

New versions of many older languages have attempted to add structured elements
...
However, the shortcomings of these languages can never be
fully mitigated because they were not designed with structured features from the
beginning
...
In
C, functions are the building blocks in which all program activity occurs
...
After you have created a function, you can rely on it to
work properly in various situations without creating side effects in other parts of
the program
...

Another way to structure and compartmentalize code in C is through the use of
code blocks
...
In C, you create a code block by placing a sequence of statements
between opening and closing curly braces
...
\n");
scanf("%d", &x);
}

7

8

C++: The Complete Reference

the two statements after the if and between the curly braces are both executed if x is
less than 10
...

They are a logical unit: One of the statements cannot execute without the other
executing also
...
Moreover, they help the programmer better conceptualize
the true nature of the algorithm being implemented
...
Consider
the classic examples of nonprogrammer languages, COBOL and BASIC
...
Rather,
COBOL was designed, in part, to enable nonprogrammers to read and presumably
(however unlikely) to understand the program
...

In contrast, C was created, influenced, and field-tested by working programmers
...
By using C, you can nearly achieve the efficiency of assembly code
combined with the structure of ALGOL or Modula-2
...

The fact that you can often use C in place of assembly language is a major factor in
its popularity among programmers
...
Each assembly-language
operation maps into a single task for the computer to perform
...
Furthermore, since assembly language is unstructured, the final
program tends to be spaghetti code—a tangled mess of jumps, calls, and indexes
...
Perhaps more important, assembly-language routines are not portable
between machines with different central processing units (CPUs)
...
A systems program forms a portion
of the operating system of the computer or its support utilities
...

Of course, C++ has carried on this tradition
...
Such has not been the case
...
For example, applications
such as embedded systems are still typically programmed in C
...
While C's greatest legacy is as the foundation for C++, it will continue to
be a vibrant, widely used language for many years to come
...
Of these, 27 were defined by the original version of C
...

All are, of course, part of the C++ language
...
The 32 Keywords Defined by Standard C

9

10

C++: The Complete Reference

In addition, many compilers have added several keywords that better exploit their
operating environment
...
Here is a list of some commonly used extended
keywords:
asm

_cs

_ds

_es

_ss

cdecl

far

huge

interrupt

near

pascal

Your compiler may also support other extensions that help it take better advantage
of its specific environment
...
Also, uppercase and lowercase are
different: else is a keyword; ELSE is not
...

All C programs consist of one or more functions
...
In well-written C code, main( ) contains what is, in essence, an outline of what
the program does
...
Although main( ) is not
a keyword, treat it as if it were
...

The general form of a C program is illustrated in Figure 1-1, where f1( ) through
fN( ) represent user-defined functions
...
However, this is quite
rare because neither C nor C++ provides any keywords that perform such things as
input/output (I/O) operations, high-level mathematical computations, or character
handling
...

All C++ compilers come with a standard library of functions that perform most
commonly needed tasks
...
However, your compiler will probably contain many other
functions
...

The C++ standard library can be divided into two halves: the standard function
library and the class library
...
C++ supports the entire function library defined by Standard C
...


Chapter 1:

An Overview of C

global declarations
return-type main(parameter list)
{
statement sequence
}
return-type f1(parameter list)
{
statement sequence
}
return-type f2(parameter list)
{
statement sequence
}

...


...


The general form of a C program
...

The class library provides object-oriented routines that your programs may use
...
However, both the class library and the STL are
discussed later in this book
...

The implementors of your compiler have already written most of the generalpurpose functions that you will use
...
Later, the linker combines the code you

11

12

C++: The Complete Reference

wrote with the object code already found in the standard library
...
Some compilers have their own linker, while others use the standard linker
supplied by the operating system
...
This means that the memory
addresses for the various machine-code instructions have not been absolutely
defined—only offset information has been kept
...
There are several technical manuals and books that explain this
process in more detail
...

Many of the functions that you will need as you write programs are in the standard
library
...
If you write a function that you
will use again and again, you can place it into a library, too
...
However, as a
program's length grows, so does its compile time (and long compile times make for
short tempers)
...
Once you have compiled all files, they are linked,
along with any library routines, to form the complete object code
...
On all but the simplest projects, this saves a substantial
amount of time
...


Understanding the
...
CPP File Extensions
The programs in Part One of this book are, of course, valid C++ programs and can be
compiled using any modern C++ compiler
...
Thus, if you are called upon to write C programs, the
ones shown in Part One qualify as examples
...
C, and C++ programs use the extension
...
A C++ compiler uses the file
extension to determine what type of program it is compiling
...
C extension is a C program and that
any file using
...
Unless explicitly noted otherwise, you may use
either extension for the programs in Part One
...
CPP
...
C extension)
...


C++

Chapter 2
Expressions

13

14

C++: The Complete Reference

his chapter examines the most fundamental element of the C (as well as the C++)
language: the expression
...

Expressions are formed from these atomic elements: data and operators
...
Like most other computer languages,
C/C++ supports a number of different types of data
...


T

The Five Basic Data Types
There are five atomic data types in C: character, integer, floating-point, double
floating-point, and valueless (char, int, float, double, and void, respectively)
...
The size and range
of these data types may vary between processor types and compilers
...
The size of an integer is usually the same as the word length
of the execution environment of the program
...
1, an integer is 16 bits
...
However, you cannot make assumptions about
the size of an integer if you want your programs to be portable to the widest range of
environments
...


Note

To the five basic data types defined by C, C++ adds two more: bool and wchar_t
...


The exact format of floating-point values will depend upon how they are
implemented
...
Values of type char are generally used to hold values defined by the
ASCII character set
...

The range of float and double will depend upon the method used to represent
the floating-point numbers
...
Standard
C specifies that the minimum range for a floating-point value is 1E−37 to 1E+37
...


Note

Standard C++ does not specify a minimum size or range for the basic types
...
For example, Standard
C++ states that an int will “have the natural size suggested by the architecture of
the execution environment
...
Each C++ compiler specifies the size
and range of the basic types in the header
...


All Data Types Defined by the ANSI/ISO C Standard

The type void either explicitly declares a function as returning no value or creates
generic pointers
...


Modifying the Basic Types
Except for type void, the basic data types may have various modifiers preceding them
...
The list of modifiers is shown here:
signed
unsigned
long
short

15

16

C++: The Complete Reference

You can apply the modifiers signed, short, long, and unsigned to integer base
types
...
You may also apply long to
double
...
(These values also apply to a typical C++
implementation
...
For example, on
computers that use two's complement arithmetic (which is nearly all), an integer will
have a range of at least 32,767 to –32,768
...
The most important use of signed is to modify
char in implementations in which char is unsigned by default
...
If you specify a signed integer, the compiler
generates code that assumes that the high-order bit of an integer is to be used as a sign
flag
...

In general, negative numbers are represented using the two's complement approach,
which reverses all bits in the number (except the sign flag), adds 1 to this number, and
sets the sign flag to 1
...
For example, here is 32,767:
01111111 11111111
If the high-order bit were set to 1, the number would be interpreted as −1
...


Identifier Names
In C/C++, the names of variables, functions, labels, and various other user-defined
objects are called identifiers
...

The first character must be a letter or an underscore, and subsequent characters must
be either letters, digits, or underscores
...
balance

Chapter 2:

Expressions

In C, identifiers may be of any length
...
If the identifier will be involved in an external link process, then at
least the first six characters will be significant
...
If the
identifier is not used in an external link process, then at least the first 31 characters
will be significant
...
In C++, there is no limit to the length of an
identifier, and at least the first 1,024 characters are significant
...

In an identifier, upper- and lowercase are treated as distinct
...

An identifier cannot be the same as a C or C++ keyword, and should not have the
same name as functions that are in the C or C++ library
...
All variables must be declared before they
can be used
...
Here are some declarations:
int i,j,l;
short int si;
unsigned int ui;
double balance, profit, loss;

Remember, in C/C++ the name of a variable has nothing to do with its type
...
These are local variables, formal
parameters, and global variables
...
In some C/C++
literature, these variables are referred to as automatic variables
...
Local variables may be referenced only by statements
that are inside the block in which the variables are declared
...
Remember, a block of code
begins with an opening curly brace and terminates with a closing curly brace
...
That is, a local variable is created upon entry into its block and destroyed
upon exit
...

For example, consider the following two functions:
void func1(void)
{
int x;
x = 10;
}
void func2(void)
{
int x;
x = -199;
}

The integer variable x is declared twice, once in func1( ) and once in func2( )
...
This is because each x is
only known to the code within the same block as the variable declaration
...
However, since all nonglobal variables are, by default, assumed to be auto,
this keyword is virtually never used
...

(It has been said that auto was included in C to provide for source-level compatibility
with its predecessor B
...
)
For reasons of convenience and tradition, most programmers declare all the
variables used by a function immediately after the function's opening curly brace
and before any other statements
...
The block defined by a function is simply a special case
...
*/

}
}

Here, the local variable s is created upon entry into the if code block and destroyed
upon exit
...

One advantage of declaring a local variable within a conditional block is that
memory for the variable will only be allocated if needed
...
You
might need to worry about this when producing code for dedicated controllers (like a
garage door opener that responds to a digital security code) in which RAM is in short
supply, for example
...
Since the variable does not exist outside the block in which it
is declared, it cannot be accidentally altered
...
In C, you must declare all local variables at the start of the block in
which they are defined, prior to any "action" statements
...

/* This function is in error if compiled as
a C program, but perfectly acceptable if
compiled as a C++ program
...
(The topic of C++ variable declaration is discussed in
depth in Part Two
...
This is
especially important to remember when calling a function
...
This means that
local variables cannot retain their values between calls
...
)
Unless otherwise specified, local variables are stored on the stack
...

You can initialize a local variable to some known value
...
For example,
the following program prints the number 10 ten times:
#include ...
These variables are called the formal parameters of the function
...
As shown in the following
program fragment, their declarations occur after the function name and inside
parentheses:
/* Return 1 if c is part of string s; 0 otherwise */
int is_in(char *s, char c)
{
while(*s)
if(*s==c) return 1;
else s++;
return 0;
}

The function is_in( ) has two parameters: s and c
...

You must specify the type of the formal parameters by declaring them as just shown
...
Keep in mind that,
as local variables, they are also dynamic and are destroyed upon exit from the function
...
Even though these variables receive the value of
the arguments passed to the function, you can use them like any other local variable
...
Also, they will hold their value throughout the program's
execution
...
Any
expression may access them, regardless of what block of code that expression is in
...
Although its declaration occurs before the main( ) function, you could have
placed it anywhere before its first use as long as it was not in a function
...

#include ...
');
}

Look closely at this program
...
func2( ), however, has declared a local
variable called count
...
If a global variable and a local variable have the same name, all
references to that variable name inside the code block in which the local variable is
declared will refer to that local variable and have no effect on the global variable
...

Storage for global variables is in a fixed region of memory set aside for this purpose
by the compiler
...
You should avoid using unnecessary global variables, however
...
In addition, using a global where a local variable would do makes a function
less general because it relies on something that must be defined outside itself
...
A major problem in developing large programs is the
accidental changing of a variable's value because it was used elsewhere in the program
...


Access Modifiers
There are two modifiers that control how variables may be accessed or modified
...
They must precede the type modifiers and the type
names that they qualify
...


const
Variables of type const may not be changed by your program
...
) The compiler is free to place variables of this type into
read-only memory (ROM)
...
However, you can use the variable a in other types of expressions
...

The const qualifier can be used to protect the objects pointed to by the arguments
to a function from being modified by that function
...

However, if the pointer is specified as const in the parameter declaration, the function
code won't be able to modify what it points to
...
That is,
the string "this is a test" will be printed as "this-is-a-test"
...

#include ...
For example, if you had coded sp_to_dash( ) as follows, you would
receive a compile-time error:
/* This is wrong
...

For example, the strlen( ) function has this prototype:
size_t strlen(const char *str);
Specifying str as const ensures that strlen( ) will not modify the string pointed to by str
...

You can also use const to verify that your program does not modify a variable
...
For example, a hardware device may set its value
...


volatile
The modifier volatile tells the compiler that a variable's value may be changed in ways
not explicitly specified by the program
...
In this situation, the contents of the variable are altered without any explicit
assignment statements in the program
...
Also, some compilers
change the order of evaluation of an expression during the compilation process
...

You can use const and volatile together
...
The general
form of a declaration that uses one is shown here
...


Note

C++ adds another storage-class specifier called mutable, which is described in
Part Two
...
Although C technically allows you to define a
global variable more than once, it is not good practice (and may cause problems when
linking)
...
How,
then, do you inform all the files in your program about the global variables used by
the program?
The solution to the problem is found in the distinction between the declaration
and the definition of a variable
...


25

26

C++: The Complete Reference

File One

File Two

int x, y;

extern int x, y;

char ch;

extern char ch;

int main(void)

void func22(void)

{

{

/*
...


Using global variables in separately compiled modules

A definition causes storage to be allocated for the variable
...
However, by preceding a variable name with the
extern specifier, you can declare a variable without defining it
...

In File Two, the global variable list was copied from File One and the extern
specifier was added to the declarations
...
In other words,
extern lets the compiler know what the types and names are for these global variables
without actually creating storage for them again
...

The extern keyword has this general form:
extern var-list;
There is another, optional use of extern that you may occasionally see
...


...

}

Although extern variable declarations as shown in this example are allowed, they are
not necessary
...
If it does not, the compiler then checks the global variables
...

In C++, the extern specifier has another use, which is described in Part Two
...
Unlike global
variables, they are not known outside their function or file, but they maintain their
values between calls
...
static has different
effects upon local variables and global variables
...
The key difference
between a static local variable and a global variable is that the static local variable
remains known only to the block in which it is declared
...

static local variables are very important to the creation of stand-alone functions
because several types of routines must preserve a value between calls
...
An example of a function that benefits from a static local variable is a numberseries generator that produces a new value based on the previous one
...
However, each time the function is used in a
program, you would have to declare that global variable and make sure that it did not
conflict with any other global variables already in place
...
This means that
each call to series( ) can produce a new member in the series based on the preceding
number without declaring that variable globally
...
This value is assigned
only once, at program start-up—not each time the block of code is entered, as with
normal local variables
...
While this
is acceptable for some applications, most series generators need to let the user specify
the starting point
...
However, not defining series_num
as global was the point of making it static
...


static Global Variables
Applying the specifier static to a global variable instructs the compiler to create a
global variable that is known only to the file in which you declared it
...
For the few situations
where a local static cannot do the job, you can create a small file that contains only the
functions that need the global static variable, separately compile that file, and use it
without fear of side effects
...
The entire file containing series( ), series_start( ), and series_num
is shown here:

Chapter 2:

Expressions

/* This must all be in one file - preferably by itself
...

After that, calls to series( ) generate the next element in the series
...
If you place the series( ) and series_start( ) functions in a
library, you can use the functions but cannot reference the variable series_num, which
is hidden from the rest of the code in your program
...
In
essence, the static modifier permits variables that are known only to the functions that
need them, without unwanted side effects
...

This can be a tremendous advantage when you are trying to manage a very large and
complex program
...
This means
that it is not recommended for new code
...


register Variables
The register storage specifier originally applied only to variables of type int, char, or
pointer types
...

Originally, the register specifier requested that the compiler keep the value of a
variable in a register of the CPU rather than in memory, where normal variables are

29

30

C++: The Complete Reference

stored
...

Today, the definition of register has been greatly expanded and it now may be
applied to any type of variable
...
" (Standard C++ states that register is a "hint to the implementation
that the object so declared will be heavily used
...
Larger objects like arrays obviously cannot be
stored in a register, but they may still receive preferential treatment by the compiler
...
In fact, it is technically permissible for a compiler to ignore
the register specifier altogether and treat variables modified by it as if they weren't,
but this is seldom done in practice
...
Global register variables are not allowed
...
This function computes the result of M for integers:
int int_pwr(register int m,
{
register int temp;

register int e)

temp = 1;
for(; e; e--) temp = temp * m;
return temp;
}

In this example, e, m, and temp are declared as register variables because they
are all used within the loop
...
Generally, register variables are used
where they will do the most good, which are often places where many references will
be made to the same variable
...

The number of register variables optimized for speed allowed within any one code
block is determined by both the environment and the specific implementation of
C/C++
...
(This ensures portability of code across a broad line of
processors
...
Because environments vary widely, consult your compiler's user
manual to determine if you can apply any other types of optimization options
...
This makes sense because a register variable might be
stored in a register of the CPU, which is not usually addressable
...
However, taking the address of a register variable in C++ may
prevent it from being fully optimized
...
Thus, you should probably not count on substantial speed
improvements for other variable types
...
The general form of initialization is
type variable_name = value;
Some examples are
char ch = 'a';
int first = 0;
float balance = 123
...
Local
variables (excluding static local variables) are initialized each time the block in which
they are declared is entered
...
Uninitialized global and static local
variables are automatically set to zero
...
Constants can be of any
of the basic data types
...

Constants are also called literals
...
For example 'a' and '%' are
both character constants
...
To specify a wide
character constant, precede the character with an L
...
The type of wide
characters is wchar_t
...
In C++, wchar_t is built in
...
For
example, 10 and –100 are integer constants
...
For example, 11
...
C/C++ also allows you to use scientific notation for
floating-point numbers
...
There are also several
variations of the basic types that you can generate using the type modifiers
...
Therefore, assuming 16-bit integers, 10 is int by default, but 103,000 is a long
...
The only exception to the smallest type rule are floating-point
constants, which are assumed to be doubles
...
However,
you can specify precisely the type of numeric constant you want by using a suffix
...
If you follow it with an L, the number becomes a long double
...
Here are some examples:

Data type

Constant examples

int

1 123 21000 −234

long int

35000L −34L

unsigned int

10000U 987U 40000U

float

123
...
34e−3F

double

123
...
0 −0
...
2L

Hexadecimal and Octal Constants
It is sometimes easier to use a number system based on 8 or 16 rather than 10 (our
standard decimal system)
...
In octal, the number 10 is the same as 8 in decimal
...
For example, the
hexadecimal number 10 is 16 in decimal
...
A hexadecimal constant must consist of a 0x followed by the
constant in hexadecimal form
...
Here are some
examples:
int hex = 0x80;
int oct = 012;

/* 128 in decimal */
/* 10 in decimal */

String Constants
C/C++ supports one other type of constant: the string
...
For example, "this is a test" is a string
...

Although C allows you to define string constants, it does not formally have a string
data type
...
)
You must not confuse strings with characters
...
However, "a" is a string containing only one letter
...
A
few, however, such as the carriage return, are impossible to enter into a string from the
keyboard
...

These are also referred to as escape sequences
...

For example, the following program outputs a new line and a tab and then prints
the string This is a test
...
h>
int main(void)
{
printf("\n\tThis is a test
...


Backslash Codes

Operators
C/C++ is very rich in built-in operators
...
There are four main classes
of operators: arithmetic, relational, logical, and bitwise
...


The Assignment Operator
You can use the assignment operator within any valid expression
...
The general form of the
assignment operator is
variable_name = expression;

Chapter 2:

Expressions

where an expression may be as simple as a single constant or as complex as you
require
...
The target, or left part, of the assignment
must be a variable or a pointer, not a function or a constant
...
Simply put, an lvalue is any object that can occur
on the left side of an assignment statement
...
" The term rvalue refers to expressions on the right side of an assignment and
simply means the value of an expression
...
In an assignment statement, the type conversion rule is easy: The value of
the right side (expression side) of the assignment is converted to the type of the left
side (target variable), as illustrated here:
int x;
char ch;
float f;
void func(void)
{
ch = x;
/*
x = f;
/*
f = ch;
/*
f = x;
/*
}

line
line
line
line

1
2
3
4

*/
*/
*/
*/

In line 1, the left high-order bits of the integer variable x are lopped off, leaving ch with
the lower 8 bits
...

Otherwise, the value of ch would reflect only the lower-order bits of x
...
In line 3, f will convert the 8-bit integer value stored
in ch to the same value in the floating-point format
...

When converting from integers to characters and long integers to integers, the
appropriate amount of high-order bits will be removed
...
For 32-bit environments, 24
bits will be lost when converting from an integer to a character and 16 bits will be lost
when converting from an integer to a short integer
...
Remember that the
conversion of an int to a float, or a float to a double, and so on, does not add any

35

36

C++: The Complete Reference

precision or accuracy
...
In addition, some compilers always treat a char variable as
positive, no matter what value it has, when converting it to an int or float
...
Generally speaking, you should use char variables for characters, and use
ints, short ints, or signed chars when needed to avoid possible portability problems
...
For example, to convert from double to int, first convert from double
to float and then from float to int
...
For example, this program fragment assigns x, y,
and z the value 0:
x = y = z = 0;

Target Type

Expression Type

Possible Info Loss

signed char

char

If value > 127, target is negative

char

short int

High-order 8 bits

char

int (16 bits)

High-order 8 bits

char

int (32 bits)

High-order 24 bits

char

long int

High-order 24 bits

short int

int (16 bits)

None

short int

int (32 bits)

High-order 16 bits

int (16 bits)

long int

High-order 16 bits

int (32 bits)

long int

None

int

float

Fractional part and possibly more

float

double

Precision, result rounded

double

long double

Precision, result rounded

Table 2-3
...


Arithmetic Operators
Table 2-4 lists C/C++'s arithmetic operators
...
You can apply them to almost any built-in data
type
...

For example, 5/2 will equal 2 in integer division
...
However, you cannot use it on
floating-point types
...

The unary minus multiplies its operand by –1
...


Increment and Decrement
C/C++ includes two useful operators not generally found in other computer
languages
...
The operator
++ adds 1 to its operand, and − − subtracts one
...
For example,
x = x+1;

can be written
++x;

or
x++;

There is, however, a difference between the prefix and postfix forms when you use
these operators in an expression
...
If the operator follows its operand,

Operator

Action



Subtraction, also unary minus

+

Addition

*

Multiplication

/

Division

%

Modulus

––

Decrement

++

Increment

Table 2-4
...
For
instance,
x = 10;
y = ++x;

sets y to 11
...
Either way, x is set to 11; the difference is in when it happens
...
For this reason, you should use the increment and decrement
operators when you can
...
Of course, you can use parentheses to alter the order of evaluation
...
Parentheses
force an operation, or set of operations, to have a higher level of precedence
...
In the term logical operator, logical refers to the ways these
relationships can be connected
...

The idea of true and false underlies the concepts of relational and logical operators
...
False is zero
...

C++ fully supports the zero/non-zero concept of true and false
...
In C++, a 0 value
is automatically converted into false, and a non-zero value is automatically converted
into true
...
In C++,

39

40

C++: The Complete Reference

the outcome of a relational or logical operation is true or false
...

Table 2-5 shows the relational and logical operators
...


p

q

p && q

p || q

!p

0

0

0

0

1

0

1

0

1

1

1

1

1

1

0

1

0

0

1

0

Both the relational and logical operators are lower in precedence than the
arithmetic operators
...
Of course, the result is false
...


Relational and Logical Operators

Chapter 2:

Expressions

In this case, the result is true
...

The outcome of an XOR operation is true if and only if one operand (but not both) is
true
...
h>
int xor(int a, int b);
int main(void)
{
printf("%d",
printf("%d",
printf("%d",
printf("%d",

xor(1,
xor(1,
xor(0,
xor(0,

0));
1));
1));
0));

return 0;
}
/* Perform a logical XOR operation using the
two arguments
...
For example,

41

42

C++: The Complete Reference

!0 && 0 || 0
is false
...
Therefore, the following program fragment is not only correct, but will print
the number 1
...
Since C was designed to take the place of assembly language for most
programming tasks, it needed to be able to support many operations that can be done
in assembler, including operations on bits
...
You cannot use bitwise operations on float, double, long double,
void, bool, or other, more complex types
...
These operations are applied to the individual bits of the
operands
...


Bitwise Operators

Chapter 2:

Operator

Action

>>

Shift right

<<

Expressions

Shift left

Table 2-6
...
The exclusive
OR has the truth table shown here:

p

q

p ^q

0

0

0

1

0

1

1

1

0

0

1

1

As the table indicates, the outcome of an XOR is true only if exactly one of the
operands is true; otherwise, it is false
...
(The parity bit confirms that the
rest of the bits in the byte are unchanged
...
)
Think of the bitwise AND as a way to clear a bit
...
For example, the
following function reads a character from the modem port and resets the parity bit to 0:
char get_char_from_modem(void)
{
char ch;
ch = read_modem(); /* get a character from the
modem port */
return(ch & 127);
}

43

44

C++: The Complete Reference

Parity is often indicated by the eighth bit, which is set to 0 by ANDing it with a
byte that has bits 1 through 7 set to 1 and bit 8 set to 0
...
The net
result is that the eighth bit of ch is set to 0
...
Any bit that is set
to 1 in either operand causes the corresponding bit in the outcome to be set to 1
...
For example, 127 ^120 is

01111111
01111000
^___________
00000111

127 in binary
120 in binary
bitwise XOR
result

Remember, relational and logical operators always produce a result that is either
true or false, whereas the similar bitwise operations may produce any arbitrary value
in accordance with the specific operation
...

The bit-shift operators, >> and <<, move all bits in a variable to the right or left as
specified
...
(In the case of a
signed, negative integer, a right shift will cause a 1 to be brought in so that the sign bit
is preserved
...
That is, the bits shifted off one end do
not come back around to the other
...

Bit-shift operations can be very useful when you are decoding input from an
external device, like a D/A converter, and reading status information
...
A shift right effectively divides
a number by 2 and a shift left multiplies it by 2, as shown in Table 2-7
...
*/
#include ...
Notice that information has been lost after x<<2 because
a bit was shifted off the end
...
Notice that subsequent divisions do not bring back any
lost bits
...


Multiplication and Division with Shift Operators

The one's complement operator, ~, reverses the state of each bit in its operand
...

The bitwise operators are often used in cipher routines
...
One of the simplest
methods is to complement each byte by using the one's complement to reverse each bit
in the byte, as is shown here:

Original byte
After 1st complement
After 2nd complement

00101100
11010011
00101100

Same

Notice that a sequence of two complements in a row always produces the original
number
...
The
second complement decodes the byte to its original value
...

/* A simple cipher function
...
The ternary operator ? takes the general form
Exp1 ? Exp2 : Exp3;
where Exp1, Exp2, and Exp3 are expressions
...

The ? operator works like this: Exp1 is evaluated
...
If Exp1 is false, Exp3 is evaluated and its
value becomes the value of the expression
...
If x had been less than 9, y would have received the value
200
...


The & and * Pointer Operators
A pointer is the memory address of some object
...
Knowing a
variable's address can be of great help in certain types of routines
...
They can provide a fast means of referencing
array elements
...
Lastly,
they support linked lists and other dynamic data structures
...
However, this chapter briefly covers the two operators that
are used to manipulate pointers
...
(Remember, a unary operator only requires one operand
...
This address is the computer's
internal location of the variable
...
You can
think of & as meaning "the address of
...
"
To better understand this assignment, assume that the variable count is at memory
location 2000
...
Then, after the previous
assignment, m will have the value 2000
...
The * is a unary
operator that returns the value of the variable located at the address that follows it
...
Now q has the value 100 because 100 is stored at
location 2000, the memory address that was stored in m
...
" In this case, you could read the statement as "q receives the value at
address m
...

These operators have no relationship to each other
...

Variables that will hold memory addresses (i
...
, pointers), must be declared by
putting * in front of the variable name
...
For example, to declare ch as a pointer to a character, write
char *ch;

Here, ch is not a character but a pointer to a character—there is a big difference
...
However, the pointer variable itself is a variable that holds the address to an
object of the base type
...

However, remember that a pointer should only point to data that is of that pointer's
base type
...
For example,
int x, *y, count;

declares x and count as integer types and y as a pointer to an integer type
...
As expected, this program displays the value 10 on the screen
...
h>
int main(void)
{
int target, source;
int *m;
source = 10;
m = &source;
target = *m;
printf("%d", target);
return 0;
}

The Compile-Time Operator sizeof
sizeof is a unary compile-time operator that returns the length, in bytes, of the variable
or parenthesized type-specifier that it precedes
...

Remember, to compute the size of a type, you must enclose the type name in
parentheses
...


49

50

C++: The Complete Reference

C/C++ defines (using typedef) a special type called size_t, which corresponds
loosely to an unsigned integer
...
For all practical purposes, however, you can think of it (and use it) as if it were
an unsigned integer value
...
For example, imagine a database program that needs to store six
integer values per record
...
This being the case, you could use the following routine to write a
record to a disk file:
/* Write 6 integers to a disk file
...

One final point: sizeof is evaluated at compile time, and the value it produces is
treated as a constant within your program
...
The left side of the comma
operator is always evaluated as void
...
For example,
x = (y=3, y+1);

first assigns y the value 3 and then assigns x the value 4
...

Essentially, the comma causes a sequence of operations
...

The comma operator has somewhat the same meaning as the word "and" in normal
English as used in the phrase "do this and this and this
...
) and Arrow (−>) Operators
In C, the
...
Structures and unions are compound (also called aggregate) data types that
may be referenced under a single name (see Chapter 7)
...

The dot operator is used when working with a structure or union directly
...
For example,
given the fragment
struct employee
{
char name[80];
int age;
float wage;
} emp;
struct employee *p = &emp; /* address of emp into p */

you would write the following code to assign the value 123
...
wage = 123
...
23;

The [ ] and ( ) Operators
Parentheses are operators that increase the precedence of the operations inside them
...

Given an array, the expression within square brackets provides an index into that
array
...
h>
char s[80];
int main(void)
{
s[3] = 'X';
printf("%c", s[3]);

51

52

C++: The Complete Reference

return 0;
}

first assigns the value 'X' to the fourth element (remember, all arrays begin at 0) of
array s, and then prints that element
...
Note that all operators,
except the unary operators and ?, associate from left to right
...


Highest

( ) [ ] −>
...


Lowest
Table 2-8
...


Expressions
Operators, constants, and variables are the constituents of expressions
...
Because most expressions tend to
follow the general rules of algebra, they are often taken for granted
...


Order of Evaluation
Neither C nor C++ specifies the order in which the subexpressions of an expression are
evaluated
...
However, it also means that your code should never rely upon the order
in which subexpressions are evaluated
...


Type Conversion in Expressions
When constants and variables of different types are mixed in an expression, they are
all converted to the same type
...
First, all char and short int values
are automatically elevated to int
...
) Once this
step has been completed, all other conversions are done operation by operation, as
described in the following type conversion algorithm:
IF an operand is a long double
THEN the second is converted to long double
ELSE IF an operand is a double
THEN the second is converted to double
ELSE IF an operand is a float
THEN the second is converted to float
ELSE IF an operand is an unsigned long
THEN the second is converted to unsigned long
ELSE IF an operand is long
THEN the second is converted to long
ELSE IF an operand is unsigned int
THEN the second is converted to unsigned int

53

54

C++: The Complete Reference

char ch;
int i;
float f;
double d;
result=(ch/i)
int

+

(f*d)



double

int

(f+i);
float

double

float

double
Figure 2-2
...

Once these conversion rules have been applied, each pair of operands is of the
same type and the result of each operation is the same as the type of both operands
...
First, the
character ch is converted to an integer
...
The outcome of f+i is float, because f is a float
...


Casts
You can force an expression to be of a specific type by using a cast
...
For example, to make sure that the expression x/2
evaluates to type float, write
(float) x/2

Chapter 2:

Expressions

Casts are technically operators
...

Although casts are not usually used a great deal in programming, they can be very
useful when needed
...
h>
int main(void) /* print i and i/2 with fractions */
{
int i;
for(i=1; i<=100; ++i)
printf("%d / 2 is: %f\n", i, (float) i /2);
return 0;
}

Without the cast (float), only an integer division would have been performed
...


Note

C++ adds four new casting operators, such as const_cast and static_cast
...


Spacing and Parentheses
You can add tabs and spaces to expressions to make them easier to read
...
You should use parentheses to clarify the exact order of evaluation,
both for yourself and for others
...
For
example,
x = x+10;

can be written as
x += 10;

The operator += tells the compiler to assign to x the value of x plus 10
...
In general, statements like:
var = var operator expression
can be rewritten as
var operator = expression
For another example,
x = x-100;

is the same as
x -= 100;

Shorthand notation is widely used in professionally written C/C++ programs; you
should become familiar with it
...
In the most general sense, a statement is a
part of your program that can be executed
...
C and C++ categorize statements into these groups:

s Selection
s Iteration
s Jump
s Label
s Expression
s Block
Included in the selection statements are if and switch
...
") The iteration statements are
while, for, and do-while
...
The jump
statements are break, continue, goto, and return
...
Expression statements are statements composed of a
valid expression
...
(Remember, a block
begins with a { and ends with a }
...


Note

C++ adds two additional statement types: the try block (used by exception handling)
and the declaration statement
...


Since many statements rely upon the outcome of some conditional test, let's begin
by reviewing the concepts of true and false
...
A conditional expression evaluates to either a true or
false value
...
A
false value is 0
...

C++ fully supports the zero/nonzero definition of true and false just described
...
As explained in Chapter 2, in C++, a 0 value is automatically converted into
false and a nonzero value is automatically converted into true
...
In C++, the expression that controls a
conditional statement is technically of type bool
...


Selection Statements
C/C++ supports two types of selection statements: if and switch
...


if
The general form of the if statement is
if (expression) statement;
else statement;
where a statement may consist of a single statement, a block of statements, or nothing
(in the case of empty statements)
...

If expression evaluates to true (anything other than 0), the statement or block that
forms the target of if is executed; otherwise, the statement or block that is the target of
else will be executed, if it exists
...

In C, the conditional statement controlling if must produce a scalar result
...
In C++, it may also be of
type bool
...
(It takes several instructions to perform
a floating-point operation
...
)
The following program contains an example of if
...
It prints the message ** Right ** when
the player guesses the magic number
...

rand( ) requires the header file stdlib
...
(A C++ program may also use the new-style
header
...
*/
#include ...
h>
int main(void)
{
int magic; /* magic number */

59

60

C++: The Complete Reference

int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) printf("** Right **");
return 0;
}

Taking the magic number program further, the next version illustrates the use of
the else statement to print a message in response to the wrong number
...
*/
#include ...
h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) printf("** Right **");
else printf("Wrong");
return 0;
}

Nested ifs
A nested if is an if that is the target of another if or else
...
In a nested if, an else statement always refers to the nearest if
statement that is within the same block as the else and that is not already associated
with an else
...

Rather, the final else is associated with if(i)
...

Standard C specifies that at least 15 levels of nesting must be supported
...
More importantly, Standard C++ suggests
that at least 256 levels of nested ifs be allowed in a C++ program
...

You can use a nested if to further improve the magic number program by
providing the player with feedback about a wrong guess
...
*/
#include ...
h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* get a random number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if (guess == magic) {
printf("** Right **");
printf(" %d is the magic number\n", magic);
}
else {
printf("Wrong, ");
if(guess > magic) printf("too high\n");
else printf("too low\n");
}

61

62

C++: The Complete Reference

return 0;
}

The if-else-if Ladder
A common programming construct is the if-else-if ladder, sometimes called the if-else-if
staircase because of its appearance
...


...

else statement;
The conditions are evaluated from the top downward
...
If none of the conditions are true, the final else is executed
...
If the final else is not
present, no action takes place if all other conditions are false
...
For this reason, the if-else-if ladder is generally
indented like this:
if (expression)
statement;
else if (expression)
statement;
else if (expression)
statement;

...


...
*/

Chapter 3:

Statements

#include ...
h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) {
printf("** Right ** ");
printf("%d is the magic number", magic);
}
else if(guess > magic)
printf("Wrong, too high");
else printf("Wrong, too low");
return 0;
}

The ? Alternative
You can use the ? operator to replace if-else statements of the general form:
if(condition) expression;
else expression;
However, the target of both if and else must be a single expression—not another
statement
...
It takes the
general form
Exp1 ? Exp2 : Exp3
where Exp1, Exp2, and Exp3 are expressions
...

The value of a ? expression is determined as follows: Exp1 is evaluated
...
If Exp1 is false, then
Exp3 is evaluated and its value becomes the value of the expression
...
If x had been less than 9, y would have
received the value 200
...
However, this program preserves the sign (10 squared is 100 and −10 squared
is −100)
...
h>
int main(void)
{
int isqrd, i;
printf("Enter a number: ");
scanf("%d", &i);
isqrd = i>0 ? i*i : -(i*i);
printf("%d squared is %d", i, isqrd);
return 0;
}

The use of the ? operator to replace if-else statements is not restricted to
assignments only
...
Thus, you can use one or more function calls in a ? expression
...
Therefore, you can execute one or more function calls using the ? operator
by placing the calls in the expressions that form the ?'s operands
...

#include ...
\n");
return 0;
}
int f1(int n)
{
printf("%d ", n);
return 0;
}
int f2(void)
{
printf("entered
...
If you enter any other number, both f1( ) and f2( ) execute
...
You don't need to assign it to anything
...
This could cause functions that
form the operands of the ? operator to execute in an unintended sequence
...

/* Magic number program #5
...
h>
#include ...


The Conditional Expression
Sometimes newcomers to C/C++ are confused by the fact that you can use any valid
expression to control the if or the ? operator
...
The expression must simply evaluate to either a true or false
(zero or nonzero) value
...
It uses an if statement, controlled by the
second number, to avoid a divide-by-zero error
...
*/
#include ...
\n");
return 0;
}

Chapter 3:

Statements

This approach works because if b is 0, the condition controlling the if is false and the
else executes
...

One other point: Writing the if statement as shown here
if(b != 0) printf("%d\n", a/b);

is redundant, potentially inefficient, and is considered bad style
...


switch
C/C++ has a built-in multiple-branch selection statement, called switch, which
successively tests the value of an expression against a list of integer or character
constants
...
The general form of the switch statement is
switch (expression) {
case constant1:
statement sequence
break;
case constant2:
statement sequence
break;
case constant3:
statement sequence
break;

...


...
Floating-point expressions,
for example, are not allowed
...
When a match is found, the
statement sequence associated with that case is executed until the break statement or
the end of the switch statement is reached
...
The default is optional and, if it is not present, no action takes place
if all matches fail
...
Standard
C++ recommends that at least 16,384 case statements be supported! In practice, you
will want to limit the number of case statements to a smaller amount for efficiency
...


67

68

C++: The Complete Reference

The break statement is one of C/C++'s jump statements
...
When break is
encountered in a switch, program execution "jumps" to the line of code following the
switch statement
...

s No two case constants in the same switch can have identical values
...

s If character constants are used in the switch statement, they are automatically
converted to integers
...
As shown here, the function menu( ) displays a menu for a spelling-checker
program and calls the proper procedures:
void menu(void)
{
char ch;
printf("1
...
Correct Spelling Errors\n");
printf("3
...
They
terminate the statement sequence associated with each constant
...
For example, the following function uses the
"drop through" nature of the cases to simplify the code for a device-driver input
handler:
/* Process a value */
void inp_handler(int i)
{
int flag;
flag = -1;
switch(i) {
case 1: /* These cases have common */
case 2: /* statement sequences
...
First, you can have case statements
that have no statement sequence associated with them
...
In this example, the first three cases all execute
the same statements, which are
flag = 0;
break;

69

70

C++: The Complete Reference

Second, execution of one statement sequence continues into the next case if no
break statement is present
...
If i had matched 5, error(flag) would have been called with a flag value of −1
(rather than 1)
...


Nested switch Statements
You can have a switch as part of the statement sequence of an outer switch
...

For example, the following code fragment is perfectly acceptable:
switch(x) {
case 1:
switch(y) {
case 0: printf("Divide by zero error
...


...


Iteration Statements
In C/C++, and all other modern programming languages, iteration statements (also
called loops) allow a set of instructions to be executed repeatedly until a certain
condition is reached
...


The for Loop
The general design of the for loop is reflected in some form or another in all procedural
programming languages
...

The general form of the for statement is
for(initialization; condition; increment) statement;
The for loop allows many variations, but its most common form works like this
...
The

Chapter 3:

Statements

condition is a relational expression that determines when the loop exits
...
You must
separate these three major sections by semicolons
...
Once the condition becomes false, program execution
resumes on the statement following the for
...
h>
int main(void)
{
int x;
for(x=1; x <= 100; x++) printf("%d ", x);
return 0;
}

In the loop, x is initially set to 1 and then compared with 100
...
This causes x to be increased by 1 and again
tested to see if it is still less than or equal to 100
...
This process
repeats until x is greater than 100, at which point the loop terminates
...

The following example is a for loop that iterates multiple statements:
for(x=100; x != 65; x -= 5) {
z = x*x;
printf("The square of %d, %f", x, z);
}

Both the squaring of x and the call to printf( ) are executed until x equals 65
...

In for loops, the conditional test is always performed at the top of the loop
...
For example, in
x = 10;
for(y=10; y!=x; ++y) printf("%d", y);
printf("%d", y); /* this is the only printf()
statement that will execute */

71

72

C++: The Complete Reference

the loop will never execute because x and y are equal when the loop is entered
...
Hence, y still has the value 10, and the
only output produced by the fragment is the number 10 printed once on the screen
...
However,
several variations of the for are allowed that increase its power, flexibility, and
applicability to certain programming situations
...
(Remember, you use the comma operator to string
together a number of expressions in a "do this and this" fashion
...
) For
example, the variables x and y control the following loop, and both are initialized
inside the for statement:
for(x=0, y=0; x+y<10; ++x) {
y = getchar();
y = y - '0'; /* subtract the ASCII code for 0
from y */

...


...
Each time the loop repeats, x is
incremented and y's value is set by keyboard input
...
Even though y's value is set by keyboard input, y must
be initialized to 0 so that its value is defined before the first evaluation of the
conditional expression
...
)
The converge( ) function, shown next, demonstrates multiple loop control variables
in action
...

/* Demonstrate multiple loop control variables
...
h>
#include ...
");
printf("Final string: %s\n", target);
return 0;
}
/* This function copies one string into another
...
*/
void converge(char *targ, char *src)
{
int i, j;
printf("%s\n", targ);
for(i=0, j=strlen(src); i<=j; i++, j--) {
targ[i] = src[i];
targ[j] = src[j];
printf("%s\n", targ);
}
}

Here is the output produced by the program
...

ThiXXXXXXXXXXXXXXXXXXXXXXXX)
...

This XXXXXXXXXXXXXXXXXXXXe()
...

This isXXXXXXXXXXXXXXXXrge()
...

This is aXXXXXXXXXXXXverge()
...

This is a tXXXXXXXXonverge()
...

This is a tesXXXX converge()
...

This is a test of converge()
...


Statements

73

74

C++: The Complete Reference

In converge( ), the for loop uses two loop control variables, i and j, to index the
string from opposite ends
...
The
loop stops when i is greater than j, thus ensuring that all characters are copied
...
In fact, the condition may be any relational or
logical statement
...

For example, you could use the following function to log a user onto a remote
system
...
The loop terminates when the
three tries are used up or the user enters the correct password
...
*/
}

This function uses strcmp( ), the standard library function that compares two strings
and returns 0 if they match
...
The expressions need not actually have anything to do with what the
sections are generally used for
...
h>
int sqrnum(int num);
int readnum(void);
int prompt(void);
int main(void)
{
int t;
for(prompt(); t=readnum(); prompt())

Chapter 3:

Statements

sqrnum(t);
return 0;
}
int prompt(void)
{
printf("Enter a number: ");
return 0;
}
int readnum(void)
{
int t;
scanf("%d", &t);
return t;
}
int sqrnum(int num)
{
printf("%d\n", num*num);
return num*num;
}

Look closely at the for loop in main( )
...
If the number entered is 0, the loop terminates because the conditional
expression will be false
...
Thus, this for loop uses the
initialization and increment portions in a nontraditional but completely valid sense
...
In fact, there need not be an expression present for any of the sections—the
expressions are optional
...
This means that each
time the loop repeats, x is tested to see if it equals 123, but no further action takes place
...


75

76

C++: The Complete Reference

The initialization of the loop control variable can occur outside the for statement
...


The Infinite Loop
Although you can use any loop statement to create an infinite loop, for is traditionally
used for this purpose
...
\n");

When the conditional expression is absent, it is assumed to be true
...

Actually, the for(;;) construct does not guarantee an infinite loop because a break
statement, encountered anywhere inside the body of a loop, causes immediate
termination
...
) Program control then
resumes at the code following the loop, as shown here:
ch = '\0';
for( ; ; ) {
ch = getchar(); /* get a character */
if(ch=='A') break; /* exit the loop */
}
printf("you typed an A");

This loop will run until the user types an A at the keyboard
...
This means that the body of the for loop (or any other loop)
may also be empty
...

Removing spaces from an input stream is a common programming task
...
" The database needs to have each word fed to it separately, without leading
spaces
...
The
following loop shows one way to accomplish this
...

for( ; *str == ' '; str++) ;

As you can see, this loop has no body—and no need for one either
...
The following code shows how to
create one by using for:
for(t=0; t
The while Loop
The second loop available in C/C++ is the while loop
...
The condition may be any expression, and true is any nonzero value
...
When the condition becomes false, program
control passes to the line of code immediately following the loop
...
As a local variable, its value is not known when
wait_for_char( ) is executed
...

Because ch was initialized to null, the test is true and the loop begins
...
Once you enter an A, the condition becomes
false because ch equals A, and the loop terminates
...

This feature may eliminate the need to perform a separate conditional test before the
loop
...
It adds spaces to the end
of a string to fill the string to a predefined length
...

#include ...
h>
void pad(char *s, int length);
int main(void)
{
char str[80];
strcpy(str, "this is a test");
pad(str, 40);
printf("%d", strlen(str));
return 0;
}
/* Add spaces to the end of a string
...
If the length of string s is already equal to or
greater than length, the code inside the while loop does not execute
...
The strlen( ) function, part of the
standard library, returns the length of the string
...
The value of this variable is set at various
points throughout the loop
...
e
...

There need not be any statements in the body of the while loop
...
If you feel uncomfortable putting the
assignment inside the while conditional expression, remember that the equal sign is
just an operator that evaluates to the value of the right-hand operand
...
This means that a
do-while loop always executes at least once
...
The
do-while loop iterates until condition becomes false
...

do {
scanf("%d", &num);
} while(num > 100);

Perhaps the most common use of the do-while loop is in a menu selection function
...

Invalid responses cause a reprompt
...
Check Spelling\n");
printf("2
...
Display Spelling Errors\n");
printf("
Enter your choice: ");
do {
ch = getchar(); /* read the selection from
the keyboard */
switch(ch) {
case '1':
check_spelling();
break;
case '2':
correct_errors();
break;
case '3':
display_errors();
break;
}
} while(ch!='1' && ch!='2' && ch!='3');
}

Chapter 3:

Statements

Here, the do-while loop is a good choice because you will always want a menu
function to execute at least once
...


Declaring Variables within Selection and
Iteration Statements
In C++ (but not C), it is possible to declare a variable within the conditional expression
of an if or switch, within the conditional expression of a while loop, or within the
initialization portion of a for loop
...
For example, a variable
declared within a for loop will be local to that loop
...
*/
int j;
for(int i = 0; i<10; i++)
j = i * i;
/* i = 10; // *** Error *** -- i not known here! */

Here, i is declared within the initialization portion of the for and is used to control the
loop
...

Since often a loop control variable in a for is needed only by that loop, the
declaration of the variable in the initialization portion of the for is becoming common
practice
...


Tip

Whether a variable declared within the initialization portion of a for loop is local to
that loop has changed over time
...

However, Standard C++ restricts the variable to the scope of the for loop
...
For
example, this fragment,
if(int x = 20) {
x = x - y;

81

82

C++: The Complete Reference

if(x>10) y = 0;
}

declares x and assigns it the value 20
...
Variables declared within a conditional statement have their scope limited
to the block of code controlled by that statement
...
Frankly, not all programmers believe that declaring variables within
conditional statements is good practice, and this technique will not be used in
this book
...
Of these, you may use return and goto anywhere in your program
...
As discussed earlier in this chapter, you can also use break with switch
...
It is categorized as a jump
statement because it causes execution to return (jump back) to the point at which the
call to the function was made
...

If return has a value associated with it, that value becomes the return value of the
function
...
If no
return value is specified, a garbage value is returned
...
That is, in C++, if a function is specified as returning a
value, any return statement within it must have a value associated with it
...
)
The general form of the return statement is
return expression;
The expression is present only if the function is declared as returning a value
...

You can use as many return statements as you like within a function
...
The } that ends a
function also causes the function to return
...
If this occurs within a non-void function, then the return value of the
function is undefined
...
Since a void function has no return value, it makes sense that no return
statement within a void function can return a value
...


The goto Statement
Since C/C++ has a rich set of control structures and allows additional control using
break and continue, there is little need for goto
...
Nevertheless, although
the goto statement fell out of favor some years ago, it occasionally has its uses
...
Rather, it is a convenience, which, if
used wisely, can be a benefit in a narrow set of programming situations, such as
jumping out of a set of deeply nested loops
...

The goto statement requires a label for operation
...
) Furthermore, the label must be in the same function as the goto
that uses it—you cannot jump between functions
...


...

label:
where label is any valid label either before or after goto
...
You can use it to terminate a case in the switch
statement (covered in the section on switch earlier in this chapter)
...

When the break statement is encountered inside a loop, the loop is immediately
terminated and program control resumes at the next statement following the loop
...
h>
int main(void)

83

84

C++: The Complete Reference

{
int t;
for(t=0; t<100; t++) {
printf("%d ", t);
if(t==10) break;
}
return 0;
}

prints the numbers 0 through 10 on the screen
...

Programmers often use the break statement in loops in which a special condition
can cause immediate termination
...
*/
if(kbhit()) break;
} while(!found);
/* process match */
}

The kbhit( ) function returns 0 if you do not press a key
...
Because of the wide differences between computing environments,
neither Standard C nor Standard C++ defines kbhit( ), but you will almost certainly
have it (or one with a slightly different name) supplied with your compiler
...
For example,
for(t=0; t<100; ++t) {
count = 1;
for(;;) {
printf("%d ", count);
count++;
if(count==10) break;
}
}

Chapter 3:

Statements

prints the numbers 1 through 10 on the screen 100 times
...

A break used in a switch statement will affect only that switch
...


The exit( ) Function
Although exit( ) is not a program control statement, a short digression that discusses it
is in order at this time
...
This function causes immediate
termination of the entire program, forcing a return to the operating system
...

The general form of the exit( ) function is
void exit(int return_code);
The value of return_code is returned to the calling process, which is usually the
operating system
...
Other arguments are used to indicate some sort of error
...
The exit( )
function requires the header stdlib
...
A C++ program may also use the new-style
header
...
For example, imagine a virtual reality computer game that
requires a special graphics adapter
...
h>
int main(void)
{
if(!virtual_graphics()) exit(1);
play();
/*
...
*/

where virtual_graphics( ) is a user-defined function that returns true if the
virtual-reality graphics adapter is present
...

As another example, this version of menu( ) uses exit( ) to quit the program and
return to the operating system:

85

86

C++: The Complete Reference

void menu(void)
{
char ch;
printf("1
...

printf("3
...

printf("

Check Spelling\n");
Correct Spelling Errors\n");
Display Spelling Errors\n");
Quit\n");
Enter your choice: ");

do {
ch = getchar(); /* read the selection from
the keyboard */
switch(ch) {
case '1':
check_spelling();
break;
case '2':
correct_errors();
break;
case '3':
display_errors();
break;
case '4':
exit(0); /* return to OS */
}
} while(ch!='1' && ch!='2' && ch!='3');
}

The continue Statement
The continue statement works somewhat like the break statement
...
For the for loop, continue causes the conditional test
and increment portions of the loop to execute
...
For example, the following program
counts the number of spaces contained in the string entered by the user:
/* Count spaces */
#include ...
If it is not, the continue statement forces
the for to iterate again
...

The following example shows how you can use continue to expedite the exit from a
loop by forcing the conditional test to be performed sooner:
void code(void)
{
char done, ch;
done = 0;
while(!done) {
ch = getchar();
if(ch=='$') {
done = 1;
continue;
}
putchar(ch+1); /* shift the alphabet one
position higher */
}
}

This function codes a message by shifting all characters you type one letter higher
...
The function will terminate when you type a $
...


87

88

C++: The Complete Reference

Expression Statements
Chapter 2 covered expressions thoroughly
...
Remember, an expression statement is simply a valid expression
followed by a semicolon, as in
func();
a = b+c;
b+f();
;

/*
/*
/*
/*

a function call */
an assignment statement */
a valid, but strange statement */
an empty statement */

The first expression statement executes a function call
...

The third expression, though strange, is still evaluated by the C++ compiler because
the function f( ) may perform some necessary task
...


Block Statements
Block statements are simply groups of related statements that are treated as a unit
...
Block statements are also
called compound statements
...

Programmers use block statements most commonly to create a multistatement target
for some other statement, such as if
...
For example, this is perfectly valid
(although unusual) C/C++ code:
#include ...
A specific element in an array is accessed by an index
...
The lowest address
corresponds to the first element and the highest address to the last element
...
The most common array is the
null-terminated string, which is simply an array of characters terminated by a null
...
This chapter focuses on arrays, while Chapter 5 looks closely at pointers
...


A

Single-Dimension Arrays
The general form for declaring a single-dimension array is
type var_name[size];
Like other variables, arrays must be explicitly declared so that the compiler may
allocate space for them in memory
...
For example, to declare a 100-element array called balance of type double,
use this statement:
double balance[100];

An element is accessed by indexing the array name
...
For example,
balance[3] = 12
...
23
...
Therefore, when
you write
char p[10];

you are declaring a character array that has ten elements, p[0] through p[9]
...
h>
int main(void)

Chapter 4:

Arrays and Null-Terminated Strings

{
int x[100]; /* this declares a 100-integer array */
int t;
/* load x with values 0 through 99 */
for(t=0; t<100; ++t) x[t] = t;
/* display contents of x */
for(t=0; t<100; ++t) printf("%d ", x[t]);
return 0;
}

The amount of storage required to hold an array is directly related to its type and
size
...
You could overwrite either end of an
array and write into some other variable's data or even into the program's code
...
For example,
this code will compile without error, but is incorrect because the for loop will cause the
array count to be overrun
...
For example, Figure 4-1 shows
how array a appears in memory if it starts at memory location 1000 and is declared as
shown here:
char a[7];

Element
Address
Figure 4-1
...
For example, given
int sample[10];

you can generate a pointer to the first element by using the name sample
...
For example, sample and &sample[0] both produce the same results
...


Passing Single-Dimension Arrays to Functions
In C/C++, you cannot pass an entire array as an argument to a function
...
For example, the following program fragment passes the address of i
to func1( ):
int main(void)
{
int i[10];
func1(i);

...


...

For example, to receive i, a function called func1( ) can be declared as

Chapter 4:

Arrays and Null-Terminated Strings

void func1(int *x) /* pointer */
{

...


...


...

}

or finally as
void func1(int x[]) /* unsized array */
{

...


...
The first declaration actually
uses a pointer
...
In the final version,
a modified version of an array declaration simply specifies that an array of type int of
some length is to be received
...
In fact,
as far as the compiler is concerned,
void func1(int x[32])
{

...


...


93

94

C++: The Complete Reference

Null-Terminated Strings
By far the most common use of the one-dimensional array is as a character string
...
The first is the null-terminated string, which is a
null-terminated character array
...
) Thus a null-terminated string contains
the characters that comprise the string followed by a null
...
Sometimes null-terminated strings
are called C-strings
...
It is described later in this book
...

When declaring a character array that will hold a null-terminated string, you need
to declare it to be one character longer than the largest string that it is to hold
...

When you use a quoted string constant in your program, you are also creating a
null-terminated string
...

For example,
"hello there"
You do not need to add the null to the end of string constants manually—the compiler
does this for you automatically
...

The most common are

Name

Function

strcpy(s1, s2)

Copies s2 into s1
...


strlen(s1)

Returns the length of s1
...


strchr(s1, ch)

Returns a pointer to the first occurrence of ch in s1
...


These functions use the standard header file string
...
(C++ programs can also use the
new-style header
...
h>
#include ...
\n");
printf(s1);
if(strchr("hello", 'e')) printf("e is in hello\n");
if(strstr("hi there", "hi")) printf("found hi");
return 0;
}

If you run this program and enter the strings "hello" and "hello", the output is
lengths: 5 5
The strings are equal
hellohello
This is a test
...
Be sure to use the logical
operator ! to reverse the condition, as just shown, if you are testing for equality
...
They will probably stay in wide use because they offer a
high level of efficiency and afford the programmer detailed control of string
operations
...


95

96

C++: The Complete Reference

Two-Dimensional Arrays
C/C++ supports multidimensional arrays
...
A two-dimensional array is, essentially, an array of
one-dimensional arrays
...
Some other computer languages use commas
to separate the array dimensions; C/C++, in contrast, places each dimension in its own
set of brackets
...

#include ...
The value of num[2][3] will be 12
...
This means that the rightmost
index changes faster than the leftmost when accessing the elements in the array in the
order in which they are actually stored in memory
...

In the case of a two-dimensional array, the following formula yields the number of
bytes of memory needed to hold it:
bytes = size of 1st index x size of 2nd index x sizeof(base type)
Therefore, assuming 4-byte integers, an integer array with dimensions 10,5 would have
10 x 5 x 4
or 200 bytes allocated
...


A two-dimensional array in memory

97

98

C++: The Complete Reference

When a two-dimensional array is used as an argument to a function, only a
pointer to the first element is actually passed
...
(You
can specify the left dimension if you like, but it is not necessary
...
For example, a function that receives a two-dimensional
integer array with dimensions 10,10 is declared like this:
void func1(int x[][10])
{

...


...
If the length of the rows is not known, the compiler cannot
determine where the third row begins
...
The program assumes that the teacher has
three classes and a maximum of 30 students per class
...

/* A simple student grades database
...
h>
#include ...
h>
#define CLASSES 3
#define GRADES 30
int grade[CLASSES][GRADES];
void enter_grades(void);
int get_grade(int num);
void disp_grades(int g[][GRADES]);

Chapter 4:

Arrays and Null-Terminated Strings

int main(void)
{
char ch, str[80];
for(;;) {
do {
printf("(E)nter grades\n");
printf("(R)eport grades\n");
printf("(Q)uit\n");
gets(str);
ch = toupper(*str);
} while(ch!='E' && ch!='R' && ch!='Q');
switch(ch) {
case 'E':
enter_grades();
break;
case 'R':
disp_grades(grade);
break;
case 'Q':
exit(0);
}
}
return 0;
}
/* Enter the student's grades
...
*/
int get_grade(int num)

99

100

C++: The Complete Reference

{
char s[80];
printf("Enter grade for student # %d:\n", num+1);
gets(s);
return(atoi(s));
}
/* Display grades
...
For example, the input
processor to a database may verify user commands against an array of valid
commands
...
The size of the left index determines the number of strings and the size
of the right index specifies the maximum length of each string
...
For
example, the following statement calls gets( ) with the third string in str_array
...


Chapter 4:

Arrays and Null-Terminated Strings

To better understand how string arrays work, study the following short program,
which uses a string array as the basis for a very simple text editor:
/* A very simple text editor
...
h>
#define MAX 100
#define LEN 80
char text[MAX][LEN];
int main(void)
{
register int t, i, j;
printf("Enter an empty line to quit
...
Then it redisplays each
line one character at a time
...
The exact limit, if any, is
determined by your compiler
...
[SizeN];

101

102

C++: The Complete Reference

Arrays of more than three dimensions are not often used because of the amount of
memory they require
...
If the array held 2-byte integers, 4,320 bytes would be needed
...
The
storage required increases exponentially with the number of dimensions
...

In multidimensional arrays, it takes the computer time to compute each index
...

When passing multidimensional arrays into functions, you must declare all but the
leftmost dimension
...


...

}

Of course, you can include the first dimension if you like
...
As you know, an array name
without an index is a pointer to the first element in the array
...

char p[10];

The following statements are identical:

Chapter 4:

Arrays and Null-Terminated Strings

p
&p[0]

Put another way,
p == &p[0]

evaluates to true because the address of the first element of an array is the same as the
address of the array
...
Conversely, a
pointer can be indexed as if it were declared to be an array
...
The first
statement indexes p; the second uses pointer arithmetic
...
(Chapter 5 discusses pointers and pointer arithmetic
...
For example,
assuming that a is a 10-by-10 integer array, these two statements are equivalent:
a
&a[0][0]

Furthermore, the 0,4 element of a may be referenced two ways: either by array
indexing, a[0][4], or by the pointer, *((int *)a+4)
...
In general, for any two-dimensional array
a[j][k] is equivalent to *((base-type *)a+(j*row length)+k)
The cast of the pointer to the array into a pointer of its base type is necessary in order
for the pointer arithmetic to operate properly
...

A two-dimensional array can be reduced to a pointer to an array of onedimensional arrays
...
The
following function illustrates this technique
...


...

void pr_row(int j)
{
int *p, t;
p = (int *) &num[j][0]; /* get address of first
element in row j */
for(t=0; t<10; ++t) printf("%d ", *(p+t));
}

You can generalize this routine by making the calling arguments be the row, the row
length, and a pointer to the first array element, as shown here:
void pr_row(int j, int row_dimension, int *p)
{
int t;
p = p + (j * row_dimension);
for(t=0; tprintf("%d ", *(p+t));
}

...


...
For
example, a three-dimensional array can be reduced to a pointer to a two-dimensional
array, which can be reduced to a pointer to a single-dimension array
...
This
new array can be reduced again with the same method
...


Chapter 4:

Arrays and Null-Terminated Strings

Array Initialization
C/C++ allows the initialization of arrays at the time of their declaration
...
[sizeN] = { value_list };
The value_list is a comma-separated list of values whose type is compatible with
type_specifier
...
Note that a semicolon follows the }
...

Character arrays that hold strings allow a shorthand initialization that takes
the form:
char array_name[size] = "string";
For example, this code fragment initializes str to the phrase "I like C++"
...
This is why str is 11 characters long even
though "I like C++" is only 10
...

Multidimensional arrays are initialized the same as single-dimension ones
...

int sqrs[10][2] = {
1, 1,

105

106

C++: The Complete Reference

2, 4,
3, 9,
4, 16,
5, 25,
6, 36,
7, 49,
8, 64,
9, 81,
10, 100
};

When initializing a multidimensional array, you may add braces around the
initializers for each dimension
...
For example, here is
another way to write the preceding declaration
...


Unsized Array Initializations
Imagine that you are using array initialization to build a table of error messages, as
shown here:
char e1[12] = "Read error\n";
char e2[13] = "Write error\n";
char e3[18] = "Cannot open file\n";

As you might guess, it is tedious to count the characters in each message manually
to determine the correct array dimension
...
If, in an array initialization
statement, the size of the array is not specified, the C/C++ compiler automatically
creates an array big enough to hold all the initializers present
...
Using this approach, the message table becomes
char e1[] = "Read error\n";
char e2[] = "Write error\n";
char e3[] = "Cannot open file\n";

Given these initializations, this statement
printf("%s has length %d\n",

e2,

sizeof e2);

will print
Write error has length 13

Besides being less tedious, unsized array initialization allows you to change any of the
messages without fear of using incorrect array dimensions
...
For
multidimensional arrays, you must specify all but the leftmost dimension
...
) In this way,
you may build tables of varying lengths and the compiler automatically allocates
enough storage for them
...


107

108

C++: The Complete Reference

A Tic-Tac-Toe Example
The longer example that follows illustrates many of the ways that you can manipulate
arrays with C/C++
...

Two-dimensional arrays are commonly used to simulate board game matrices
...
When it is the computer's turn, it uses
get_computer_move( ) to scan the matrix, looking for an unoccupied cell
...
If it cannot find an empty location, it reports a draw game and
exits
...
The
upper-left corner is location 1,1; the lower-right corner is 3,3
...
Each move made by the player or
the computer changes a space into either an X or an O
...

Each time a move has been made, the program calls the check( ) function
...
It scans the rows, the columns, and then the diagonals, looking for
one that contains either all X's or all O's
...
Notice how
initializing the matrix with spaces simplified this function
...
Study them to
make sure that you understand each array operation
...
*/
#include ...
h>
char matrix[3][3];
char
void
void
void
void

/* the tic tac toe matrix */

check(void);
init_matrix(void);
get_player_move(void);
get_computer_move(void);
disp_matrix(void);

int main(void)
{
char done;
printf("This is the game of Tic Tac Toe
...
\n");
done =

' ';

Chapter 4:

Arrays and Null-Terminated Strings

init_matrix();
do{
disp_matrix();
get_player_move();
done = check(); /* see if winner */
if(done!= ' ') break; /* winner!*/
get_computer_move();
done = check(); /* see if winner */
} while(done== ' ');
if(done=='X') printf("You won!\n");
else printf("I won!!!!\n");
disp_matrix(); /* show final positions */
return 0;
}
/* Initialize the matrix
...
*/
void get_player_move(void)
{
int x, y;
printf("Enter X,Y coordinates for your move: ");
scanf("%d%*c%d", &x, &y);
x--; y--;
if(matrix[x][y]!= ' '){
printf("Invalid move, try again
...
*/
void get_computer_move(void)
{
int i, j;
for(i=0; i<3; i++){
for(j=0; j<3; j++)
if(matrix[i][j]==' ') break;
if(matrix[i][j]==' ') break;
}
if(i*j==9) {
printf("draw\n");
exit(0);
}
else
matrix[i][j] = 'O';
}
/* Display the matrix on the screen
...
*/
char check(void)
{
int i;
for(i=0; i<3; i++) /* check rows */
if(matrix[i][0]==matrix[i][1] &&
matrix[i][0]==matrix[i][2]) return matrix[i][0];
for(i=0; i<3; i++) /* check columns */
if(matrix[0][i]==matrix[1][i] &&

Chapter 4:

Arrays and Null-Terminated Strings

matrix[0][i]==matrix[2][i]) return matrix[0][i];
/* test diagonals */
if(matrix[0][0]==matrix[1][1] &&
matrix[1][1]==matrix[2][2])
return matrix[0][0];
if(matrix[0][2]==matrix[1][1] &&
matrix[1][1]==matrix[2][0])
return matrix[0][2];
return ' ';
}

111

This page intentionally left blank
...
There are three reasons for this: First, pointers provide the means
by which functions can modify their calling arguments
...
Third, pointers can improve the efficiency of certain routines
...

Pointers are one of the strongest but also one of the most dangerous features in
C/C++
...
Perhaps worse, it is easy to use pointers incorrectly,
causing bugs that are very difficult to find
...


T

What Are Pointers?
A pointer is a variable that holds a memory address
...
For example, if one variable
contains the address of another variable, the first variable is said to point to the second
...


Figure 5-1
...
A pointer
declaration consists of a base type, an *, and the variable name
...
The name of
the pointer variable is specified by name
...

Technically, any type of pointer can point anywhere in memory
...
(Pointer arithmetic is discussed later in this chapter
...
We will take a closer look at them
here, beginning with a review of their basic operation
...
The & is a unary operator that returns the memory address of its
operand
...
)
For example,
m = &count;

places into m the memory address of the variable count
...
It has nothing to do with the value of count
...
" Therefore, the preceding assignment statement
means "m receives the address of count
...
Also assume that count has a value of 100
...

The second pointer operator, *, is the complement of &
...
For example, if m contains the
memory address of the variable count,
q = *m;

places the value of count into q
...
You can think of

115

116

C++: The Complete Reference

* as "at address
...
"
Both & and * have a higher precedence than all other arithmetic operators except
the unary minus, with which they are equal
...
For example, when you declare a pointer to be of type int, the compiler assumes
that any address that it holds points to an integer variable—whether it actually does
or not
...
h>
int main(void)
{
double x = 100
...
*/
p = &x;
/* The next statement does not operate as
expected
...
1 */
return 0;
}

This will not assign the value of x to y
...


Note

In C++, it is illegal to convert one type of pointer into another without the use of an
explicit type cast
...
However, the type of
error described can still occur in C++ in a more roundabout manner
...
This section examines a few special aspects of pointer expressions
...
For example,
#include ...
The address of x is displayed by using the %p printf( )
format specifier, which causes printf( ) to display an address in the format used by the
host computer
...
To understand what occurs in pointer arithmetic, let p1 be an
integer pointer with a current value of 2000
...

After the expression
p1++;

p1 contains 2002, not 2001
...
The same is true of decrements
...

Generalizing from the preceding example, the following rules govern pointer
arithmetic
...
Each time it is decremented, it points to the
location of the previous element
...
All other
pointers will increase or decrease by the length of the data type they point to
...
Figure 5-2 illustrates this concept
...
For example, you
may add or subtract integers to or from pointers
...

Besides addition and subtraction of a pointer and an integer, only one other
arithmetic operation is allowed: You may subtract one pointer from another in
order to find the number of objects of their base type that separate the two
...
Specifically, you may not multiply or
divide pointers; you may not add two pointers; you may not aypply the bitwise
operators to them; and you may not add or subtract type float or double to or
from pointers
...


All pointer arithmetic is relative to its base type (assume 2-byte
integers)

Chapter 5:

Pointers

Pointer Comparisons
You can compare two pointers in a relational expression
...
As an example, a pair of stack routines are
developed that store and retrieve integer values
...
It is often compared to a stack of plates on a table—the first
one set down is the last one to be used
...
To create a stack,
you need two functions: push( ) and pop( )
...
These routines are shown here with a simple
main( ) function to drive them
...
If you enter 0, a value is popped from the stack
...

#include ...
h>
#define SIZE 50
void push(int i);
int pop(void);
int

*tos, *p1, stack[SIZE];

int main(void)
{
int value;
tos = stack; /* tos points to the top of stack */
p1 = stack; /* initialize p1 */
do {
printf("Enter value: ");
scanf("%d", &value);
if(value!=0) push(value);
else printf("value on top is %d\n", pop());
} while(value!=-1);

119

120

C++: The Complete Reference

return 0;
}
void push(int i)
{
p1++;
if(p1==(tos+SIZE)) {
printf("Stack Overflow
...
\n");
exit(1);
}
p1--;
return *(p1+1);
}

You can see that memory for the stack is provided by the array stack
...
The p1 variable accesses the stack
...
It is used to prevent
stack overflows and underflows
...
Both the push( ) and pop( ) functions perform a relational test
on the pointer p1 to detect limit errors
...
This prevents an overflow
...

In pop( ), the parentheses are necessary in the return statement
...


Pointers and Arrays
There is a close relationship between pointers and arrays
...
To access the fifth
element in str, you could write
str[4]

or
*(p1+4)

Both statements will return the fifth element
...
To access
the fifth element, you must use 4 to index str
...
(Recall
that an array name without an index returns the starting address of the array, which
is the address of the first element
...
In essence, C/C++ provides
two methods of accessing array elements: pointer arithmetic and array indexing
...
Since speed is often a consideration in programming,
C/C++ programmers commonly use pointers to access array elements
...
The putstr( ) function
writes a string to the standard output device one character at a time
...
*/
void putstr(char *s)
{
register int t;
for(t=0; s[t]; ++t) putchar(s[t]);
}
/* Access s as a pointer
...
In fact, the pointer version is the way routines of this sort
are commonly written in C/C++
...
The declaration for an int pointer
array of size 10 is
int *x[10];

To assign the address of an integer variable called var to the third element of the
pointer array, write
x[2] = &var;

To find the value of var, write
*x[2]

If you want to pass an array of pointers into a function, you can use the same
method that you use to pass other arrays—simply call the function with the array name
without any indexes
...
Therefore you need to declare the parameter q as an array of integer pointers,
as just shown
...

Pointer arrays are often used to hold pointers to strings
...
As you can see, printf( ) inside
syntax_error( ) is called with a character pointer that points to one of the
various error messages indexed by the error number passed to the function
...

As a point of interest, note that the command line argument argv is an
array of character pointers
...
)

Multiple Indirection
You can have a pointer point to another pointer that points to the target value
...
Pointers to pointers can
be confusing
...
As you can
see, the value of a normal pointer is the address of the object that contains the value
desired
...

Multiple indirection can be carried on to whatever extent rquired, but more than a
pointer to a pointer is rarely needed
...


Note

Do not confuse multiple indirection with high-level data structures, such as linked
lists, that use pointers
...


A variable that is a pointer to a pointer must be declared as such
...
For example, the following
declaration tells the compiler that newbalance is a pointer to a pointer of type float:
float **newbalance;

You should understand that newbalance is not a pointer to a floating-point number
but rather a pointer to a float pointer
...
h>
int main(void)
{
int x, *p, **q;
x = 10;
p = &x;
q = &p;
printf("%d", **q); /* print the value of x */
return 0;
}

Here, p is declared as a pointer to an integer and q as a pointer to a pointer to an
integer
...


Initializing Pointers
After a local pointer is declared but before it has been assigned a value, it contains
an unknown value
...
) Should
you try to use the pointer before giving it a valid value, you will probably crash

Figure 5-3
...
By convention, any pointer that is
null implies that it points to nothing and should not be used
...
" The use of null is simply a convention
that programmers follow
...
For
example, if you use a null pointer on the left side of an assignment statement, you still
run the risk of crashing your program or operating system
...
For example,
you could use a null pointer to mark the end of a pointer array
...
The
search( ) function shown here illustrates this type of approach
...
Assuming the end of the array is marked with a null, the condition
controlling the loop fails when it is reached
...
You saw an example of this in
the syntax_error( ) function in the section "Arrays of Pointers
...
The reason this sort of initialization
works is because of the way the compiler operates
...
Therefore, the preceding declaration statement places the address
of hello world, as stored in the string table, into the pointer p
...
For example, the following program is
perfectly valid:
#include ...
h>
char *p = "hello world";
int main(void)
{
register int t;
/* print the string forward and backwards */
printf(p);
for(t=strlen(p)-1; t>-1; t--) printf("%c", p[t]);
return 0;
}

In Standard C++, the type of a string literal is technically const char *
...
Thus, the preceding program is still valid
...
For new programs, you should assume that
string literals are constants and the declaration of p in the preceding program should
be written like this
...
Even
though a function is not a variable, it still has a physical location in memory that
can be assigned to a pointer
...
Once a pointer points to a function, the
function can be called through that pointer
...

You obtain the address of a function by using the function's name without any
parentheses or arguments
...
) To see how this is done, study the
following program, paying close attention to the declarations:

Chapter 5:

Pointers

#include ...
h>
void check(char *a, char *b,
int (*cmp)(const char *, const char *));
int main(void)
{
char s1[80], s2[80];
int (*p)(const char *, const char *);
p = strcmp;
gets(s1);
gets(s2);
check(s1, s2, p);
return 0;
}
void check(char *a, char *b,
int (*cmp)(const char *, const char *))
{
printf("Testing for equality
...
Inside the function check( ), the arguments are declared as
character pointers and a function pointer
...

You must use a similar form when declaring other function pointers, although the
return type and parameters of the function may differ
...

Inside check( ), the expression
(*cmp)(a, b)

calls strcmp( ), which is pointed to by cmp, with the arguments a and b
...
This is one way to call a function through
a pointer
...


127

128

C++: The Complete Reference

cmp(a, b);

The reason that you will frequently see the first style is that it tips off anyone reading
your code that a function is being called through a pointer
...
) Other than that, the two
expressions are equivalent
...

You may wonder why anyone would write a program in this way
...

However, at times it is advantageous to pass functions as parameters or to create an
array of functions
...
), perform
I/O, or access system resources
...
In this
approach, the proper function is selected by its index
...
In this
program, check( ) can be made to check for either alphabetical equality or numeric
equality by simply calling it with a different comparison function
...
h>
...
h>
...
\n");
if(!(*cmp)(a, b)) printf("Equal");
else printf("Not Equal");
}
int numcmp(const char *a, const char *b)
{
if(atoi(a)==atoi(b)) return 0;
else return 1;
}

In this program, if you enter a letter, strcmp( ) is passed to check( )
...
Since check( ) calls the function that it is passed, it can use different
comparison functions in different cases
...
Dynamic
allocation is the means by which a program can obtain memory while it is running
...
Local variables
use the stack
...
Yet there will be times when the storage needs of a program
cannot be known ahead of time
...
However, because the amount of available
RAM varies between computers, such programs will not be able to do so using
normal variables
...

C++ actually supports two complete dynamic allocation systems: the one
defined by C and the one specific to C++
...
Here, C's dynamic allocation functions are described
...
Although the size of the heap is unknown, it generally
contains a fairly large amount of free memory
...

(Most compilers supply several other dynamic allocation functions, but these two
are the most important
...
The malloc( ) function allocates
memory and the free( ) function releases it
...
Each time a
free( ) memory release call is made, memory is returned to the system
...
h
...
)
The malloc( ) function has this prototype:
void *malloc(size_t number_of_bytes);
Here, number_of_bytes is the number of bytes of memory you wish to allocate
...
h as, more or less, an unsigned integer
...
After a successful call, malloc( ) returns a pointer to the first byte
of the region of memory allocated from the heap
...

The code fragment shown here allocates 1,000 bytes of contiguous memory:
char *p;
p = malloc(1000); /* get 1000 bytes */

After the assignment, p points to the start of 1,000 bytes of free memory
...
In C, a void * pointer is automatically converted to the type
of the pointer on the left side of an assignment
...
In C++, an explicit type cast is
needed when a void * pointer is assigned to another type of pointer
...
This is one of the few fundamental
differences between C and C++
...
Notice the use of sizeof to ensure
portability
...

Using a null pointer will almost certainly crash your program
...
\n");
exit(1);
}

Of course, you can substitute some other sort of error handler in place of the call to
exit( )
...

The free( ) function is the opposite of malloc( ) in that it returns previously
allocated memory to the system
...
The function free( ) has this prototype:
void free(void *p);
Here, p is a pointer to memory that was previously allocated using malloc( )
...


Problems with Pointers
Nothing will get you into more trouble than a wild pointer! Pointers are a mixed
blessing
...

At the same time, when a pointer accidentally contains a wrong value, it can be the
most difficult bug to find
...
The problem is that each time you perform an operation using the bad
pointer, you are reading or writing to some unknown piece of memory
...
However, if you write to
it, you might be writing over other pieces of your code or data
...
There may be little or no evidence to suggest that the pointer is

131

132

C++: The Complete Reference

the original cause of the problem
...

Because pointer errors are such nightmares, you should do your best never to
generate one
...
The classic example of a pointer error is the uninitialized pointer
...

/* This program is wrong
...
Here is why:
Since the pointer p has never been given a value, it contains an unknown value when
the assignment *p = x takes place
...
This type of problem often goes unnoticed when your
program is small because the odds are in favor of p containing a "safe" address—one
that is not in your code, data area, or operating system
...
Eventually, your
program stops working
...

A second common error is caused by a simple misunderstanding of how to use a
pointer
...
*/
#include ...
It prints
some unknown value because the assignment
p = x;

is wrong
...
However, p is supposed
to contain an address, not a value
...
You can never know where your data will be
placed in memory, or if it will be placed there the same way again, or whether each
compiler will treat it in the same way
...

For example,
char s[80], y[80];
char *p1, *p2;
p1 = s;
p2 = y;
if(p1 < p2)
...
(In very unusual situations, you might use something
like this to determine the relative position of the variables
...
)
A related error results when you assume that two adjacent arrays may be indexed
as one by simply incrementing a pointer across the array boundaries
...
Even though it may work on some compilers under certain circumstances,
it assumes that both arrays will be placed back to back in memory with first first
...

The next program illustrates a very dangerous type of bug
...

/* This program has a bug
...
h>
#include ...
The problem is that p1 is assigned the address of s only once
...
However, the second
time through, it continues where it left off because it is not reset to the start of s
...
*/
#include ...
h>
int main(void)
{
char *p1;

Chapter 5:

Pointers

char s[80];
do {
p1 = s;
gets(s);

/* read a string */

/* print the decimal equivalent of each
character */
while(*p1) printf(" %d", *p1++);
} while(strcmp(s, "done"));
return 0;
}

Here, each time the loop iterates, p1 is set to the start of the string
...

The fact that handling pointers incorrectly can cause tricky bugs is no reason to
avoid using them
...


135

This page intentionally left blank
...
This chapter examines their C-like features, including passing
arguments, returning values, prototypes, and recursion
...


F

The General Form of a Function
The general form of a function is
ret-type function-name(parameter list)
{
body of the function
}
The ret-type specifies the type of data that the function returns
...
The parameter list is a comma-separated list of variable
names and their associated types that receive the values of the arguments when the
function is called
...
However, even if there are no parameters, the parentheses are still required
...
In contrast, all function parameters
must be declared individually, each including both the type and name
...
, type varnameN)
For example, here are correct and incorrect function parameter declarations:
f(int i, int k, int j) /* correct */
f(int i, k, float j)
/* incorrect */

Scope Rules of Functions
The scope rules of a language are the rules that govern whether a piece of code knows
about or has access to another piece of code or data
...
A function's code is private to that function
and cannot be accessed by any statement in any other function except through a call to
that function
...
) The code that constitutes the body of a function is hidden from the rest of the
program and, unless it uses global variables or data, it can neither affect nor be affected

Chapter 6:

Functions

by other parts of the program
...

Variables that are defined within a function are called local variables
...
That is, local variables cannot hold their value between function calls
...
This causes the compiler to treat the variable as if it were a global variable
for storage purposes, but limits its scope to within the function
...
)
In C (and C++) you cannot define a function within a function
...


Function Arguments
If a function is to use arguments, it must declare variables that accept the values
of the arguments
...

They behave like other local variables inside the function and are created upon entry
into the function and destroyed upon exit
...
*/
int is_in(char *s, char c)
{
while(*s)
if(*s==c) return 1;
else s++;
return 0;
}

The function is_in( ) has two parameters: s and c
...

As with local variables, you may make assignments to a function's formal
parameters or use them in an expression
...


Call by Value, Call by Reference
In a computer language, there are two ways that arguments can be passed to a
subroutine
...
This method copies the value of an

139

140

C++: The Complete Reference

argument into the formal parameter of the subroutine
...

Call by reference is the second way of passing arguments to a subroutine
...
Inside the subroutine,
the address is used to access the actual argument used in the call
...

By default, C/C++ uses call by value to pass arguments
...
Consider
the following program:
#include ...
When the assignment x = x*x takes place, only the local variable x is modified
...
Hence, the output is 100 10
...
What occurs inside the function has no effect on the variable used in the call
...

Since the address of the argument is passed to the function, code within the function
can change the value of the argument outside the function
...
Of course, you need
to declare the parameters as pointer types
...

void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;

/* save the value at address x */
/* put y into x */
/* put x into y */

}

swap( ) is able to exchange the values of the two variables pointed to by x and y
because their addresses (not their values) are passed
...

Remember that swap( ) (or any other function that uses pointer parameters) must
be called with the addresses of the arguments
...
Then swap( ) is called with the addresses of i and j
...
) Therefore, the addresses of i and j, not their
values, are passed into the function swap( )
...
This feature is described in Part Two
...
However, this section discusses passing
arrays as arguments to functions because it is an exception to the normal call-by-value
parameter passing
...

This is an exception to the call-by-value parameter passing convention
...
For example, consider the function print_upper( ),
which prints its string argument in uppercase:
#include ...
h>
void print_upper(char *string);
int main(void)
{
char s[80];
gets(s);
print_upper(s);
printf("\ns is now uppercase: %s", s);
return 0;
}
/* Print a string in uppercase
...
If this is not what you want, you could write the program like this:
#include ...
h>

Chapter 6:

Functions

void print_upper(char *string);
int main(void)
{
char s[80];
gets(s);
print_upper(s);
printf("\ns is unchanged: %s", s);
return 0;
}
void print_upper(char *string)
{
register int t;
for(t=0; string[t]; ++t)
putchar(toupper(string[t]));
}

In this version, the contents of array s remain unchanged because its values are not
altered inside print_upper( )
...
Although the gets( ) in your standard library is more sophisticated, the
following simpler version, called xgets( ), will give you an idea of how it works
...
*/
char *xgets(char *s)
{
char ch, *p;
int t;
p = s;

/* gets() returns a pointer to s */

for(t=0; t<80; ++t){
ch = getchar();
switch(ch) {

143

144

C++: The Complete Reference

case '\n':
s[t] = '\0'; /* terminate the string */
return p;
case '\b':
if(t>0) t--;
break;
default:
s[t] = ch;
}
}
s[79] = '\0';
return p;
}

The xgets( ) function must be called with a character pointer
...
Upon entry,
xgets( ) establishes a for loop from 0 to 79
...
If more than 80 characters are entered, the function returns
...
) Because C/C++ has no built-in
bounds checking, you should make sure that any array used to call xgets( ) can accept
at least 80 characters
...
If you type a backspace, the counter t is reduced by 1, effectively removing the
previous character from the array
...
Because the actual array used to call xgets( ) is
modified, upon return it contains the characters that you type
...
Generally,
you pass information into the main( ) function via command line arguments
...
For example, when you compile a program,
you might type something like the following after the command prompt:
cc program_name
where program_name is a command line argument that specifies the name of the
program you wish to compile
...
The argc parameter holds the number of arguments on

Chapter 6:

Functions

the command line and is an integer
...
The argv parameter is a pointer to an array of
character pointers
...
All
command line arguments are strings—any numbers will have to be converted by the
program into the proper internal format
...

#include ...
h>
int main(int argc, char *argv[])
{
if(argc!=2) {
printf("You forgot to type your name
...
The output from the program would be Hello Tom
...
Commas, semicolons, and the like are not considered separators
...

Some environments allow you to enclose within double quotes a string containing
spaces
...
Check your
operating system documentation for details on the definition of command line
parameters for your system
...
The most common method is
char *argv[];

145

146

C++: The Complete Reference

The empty brackets indicate that the array is of undetermined length
...
For example, argv[0] points to the
first string, which is always the program's name; argv[1] points to the first argument,
and so on
...
It counts down from a starting value (which is specified on
the command line) and beeps when it reaches 0
...
If
the string "display" is the second command line argument, the countdown will also be
displayed on the screen
...
*/
#include ...
h>
#include ...
h>
int main(int argc, char *argv[])
{
int disp, count;
if(argc<2) {
printf("You must enter the length of the count\n");
printf("on the command line
...
\n");
exit(1);
}
if(argc==3 && !strcmp(argv[2], "display")) disp = 1;
else disp = 0;
for(count=atoi(argv[1]); count; --count)
if(disp) printf("%d\n", count);
putchar('\a'); /* this will ring the bell */
printf("Done");
return 0;
}

Notice that if no command line arguments have been specified, an error message is
printed
...

To access an individual character in one of the command line arguments, add a
second index to argv
...
h>
int main(int argc, char *argv[])
{
int t, i;
for(t=0; ti = 0;
while(argv[t][i]) {
putchar(argv[t][i]);
++i;
}
printf("\n");
}
return 0;
}

Remember, the first index accesses the string, and the second index accesses the
individual characters of the string
...
In
theory, you can have up to 32,767 arguments, but most operating systems do not allow
more than a few
...

Using command line arguments gives your program a professional appearance and
facilitates its use in batch files
...
For C programs this is
accomplished by using the void keyword in its parameter list
...
) However, for C++ programs you
may simply specify an empty parameter list
...

The names argc and argv are traditional but arbitrary
...
Also, some compilers may support additional
arguments to main( ), so be sure to check your user's manual
...
As explained, it has two important
uses
...
That is, it causes
program execution to return to the calling code
...
This section examines how the return statement is used
...
The
first occurs when the last statement in the function has executed and, conceptually,

147

148

C++: The Complete Reference

the function's ending curly brace (}) is encountered
...
) For example, the
pr_reverse( ) function in this program simply prints the string "I like C++" backwards
on the screen and then returns
...
h>
#include ...

Actually, not many functions use this default method of terminating their
execution
...

A function may contain several return statements
...

#include ...
*/
int find_substr(char *s1, char *s2)
{
register int t;
char *p, *p2;
for(t=0; s1[t]; t++) {
p = &s1[t];
p2 = s2;
while(*p2 && *p2==*p) {
p++;
p2++;
}
if(!*p2) return t; /* 1st return */
}
return -1; /* 2nd return */
}

Returning Values
All functions, except those of type void, return a value
...
In C, if a non-void function does not explicitly return a value via a
return statement, then a garbage value is returned
...
That is, in C++, if a function is specified
as returning a value, any return statement within it must have a value associated with
it
...
Although this condition is not a syntax error, it is still a fundamental error
and should be avoided
...
Therefore, each of the following expressions is valid:
x = power(y);
if(max(x,y) > 100) printf("greater");
for(ch=getchar(); isdigit(ch); )
...
A statement
such as

149

150

C++: The Complete Reference

swap(x,y) = 100; /* incorrect statement */

is wrong
...
(As is discussed in Part Two, C++ allows some interesting exceptions
to this general rule, enabling some types of functions to occur on the left side of an
assignment
...
The
first type is simply computational
...

A computational function is a "pure" function
...

The second type of function manipulates information and returns a value that
simply indicates the success or failure of that manipulation
...
If the close operation is successful, the
function returns 0; if the operation is unsuccessful, it returns EOF
...
In essence, the function is
strictly procedural and produces no value
...
All functions that do not return values should be declared as returning type
void
...

Sometimes, functions that really don't produce an interesting result return
something anyway
...

Yet it would be unusual to find a program that actually checked this
...
A common question concerning function return values
is, "Don't I have to assign this value to some variable since a value is being returned?"
The answer is no
...
Consider the following program, which uses the function mul( ):
#include ...
In line 2, the return value is not
actually assigned, but it is used by the printf( ) function
...


Returning Pointers
Although functions that return pointers are handled just like any other type of
function, a few important concepts need to be discussed
...
They are the
memory addresses of a certain type of data
...
For example, if an integer pointer is
incremented, it will contain a value that is 4 greater than its previous value (assuming
4-byte integers)
...
Since the length of different data types
may differ, the compiler must know what type of data the pointer is pointing to
...
For example, you should not use a return type of int * to return
a char * pointer!
To return a pointer, a function must be declared as having a pointer return type
...
*/
char *match(char c, char *s)
{
while(c!=*s && *s) s++;
return(s);
}

If no match is found, a pointer to the null terminator is returned
...
h>
char *match(char c, char *s);

/* prototype */

int main(void)
{
char s[80], *p, ch;
gets(s);
ch = getchar();
p = match(ch, s);
if(*p) /* there is a match */
printf("%s ", p);
else
printf("No match found
...
If the character is in the string, the
program prints the string from the point of match
...


Functions of Type void
One of void's uses is to explicitly declare functions that do not return values
...
For example,
the function print_vertical( ) prints its string argument vertically down the side of
the screen
...

void print_vertical(char *str)
{
while(*str)
printf("%c\n", *str++);
}

Here is an example that uses print_vertical( )
...
h>
void print_vertical(char *str);

/* prototype */

Chapter 6:

Functions

int main(int argc, char *argv[])
{
if(argc > 1) print_vertical(argv[1]);
return 0;
}
void print_vertical(char *str)
{
while(*str)
printf("%c\n", *str++);
}

One last point: Early versions of C did not define the void keyword
...

Therefore, don't be surprised to see many examples of this in older code
...
Returning a value from main( ) is the equivalent of calling exit( )
with the same value
...
In practice, most C/C++ compilers
automatically return 0, but do not rely on this if portability is a concern
...
A function is said to be recursive if a statement in
the body of the function calls itself
...

A simple example of a recursive function is factr( ), which computes the factorial of
an integer
...
For example, 3 factorial is 1 x 2 x 3, or 6
...
It uses a loop that runs from 1 to
n and progressively multiplies each number by the moving product
...
When factr( ) is
called with an argument of 1, the function returns 1
...
To evaluate this expression, factr( ) is called with n−1
...

Computing the factorial of 2, the first call to factr( ) causes a second, recursive call
with the argument of 1
...
The answer is then 2
...
(You might want to insert printf( ) statements into factr( ) to see the level of
each call and what the intermediate answers are
...
A recursive call does not make a new copy of the function
...
As each recursive call returns, the old local
variables and parameters are removed from the stack and execution resumes at the
point of the function call inside the function
...

Most recursive routines do not significantly reduce code size or improve memory
utilization
...
In
fact, many recursive calls to a function could cause a stack overrun
...
However, you probably will not
have to worry about this unless a recursive function runs wild
...
For example, the quicksort algorithm is
difficult to implement in an iterative way
...
Finally, some people
seem to think recursively more easily than iteratively
...
If you don't, the function will never return once you call it
...
Use
printf( ) liberally during program development so that you can watch what is going
on and abort execution if you see a mistake
...
This is normally
accomplished using a function prototype
...
They were, however, added when C was standardized
...

Prototypes have always been required by C++
...
Prototypes enable both C and C++ to provide stronger type
checking, somewhat like that provided by languages such as Pascal
...
The
compiler will also catch differences between the number of arguments used to call a
function and the number of parameters in the function
...
,
type parm_nameN);
The use of parameter names is optional
...

The following program illustrates the value of function prototypes
...
(It is illegal to convert an integer into a pointer
...
*/
void sqr_it(int *i); /* prototype */
int main(void)
{

155

156

C++: The Complete Reference

int x;
x = 10;
sqr_it(x);

/* type mismatch */

return 0;
}
void sqr_it(int *i)
{
*i = *i * *i;
}

A function's definition can also serve as its prototype if the definition occurs prior
to the function's first use in the program
...

#include ...
*/
void f(int a, int b)
{
printf("%d ", a % b);
}
int main(void)
{
f(10,3);
return 0;
}

In this example, since f( ) is defined prior to its use in main( ), no separate
prototype is required
...
The programs in this book include a separate prototype for
each function because that is the way C/C++ code is normally written in practice
...

Because of the need for compatibility with the original version of C, there is a
small but important difference between how C and C++ handle the prototyping of a

Chapter 6:

Functions

function that has no parameters
...
For example,
int f(); /* C++ prototype for a function with no parameters */

However, in C this prototype means something different
...
As far as the
compiler is concerned, the function could have several parameters or no parameters
...

For example, here is f( )'s prototype as it would appear in a C program
...
In C++, the use of void inside an empty parameter list
is still allowed, but is redundant
...


Function prototypes help you trap bugs before they occur
...

One last point: Since early versions of C did not support the full prototype syntax,
prototypes are technically optional in C
...
If you are porting older C code to C++, you may need to add full function
prototypes before it will compile
...
This means that every function in a C++ program must be
fully prototyped
...
To
accomplish this, you must include the appropriate header for each library function
...
In C, all headers are files
that use the
...
In C++, headers may be either separate files or built into
the compiler itself
...
For example,
stdio
...
The headers for the standard library are described in
Part Three
...
The most
common example is printf( )
...
For example, this prototype specifies that func( )
will have at least two integer parameters and an unknown number (including 0)
of parameters after that
...
);

This form of declaration is also used by a function's definition
...
For example, this is incorrect:
int func(
...
This early approach is sometimes called the classic form
...
Standard C supports
both forms, but strongly recommends the modern form
...
However, you should know the old-style
form because many older C programs still use it
...
The general form of the old-style parameter definition is
type func_name(parm1, parm2,
...


...

type parmN;
{
function code
}

Chapter 6:

Functions

For example, this modern declaration:
float f(int a, int b, char ch)
{
/*
...
*/
}

Notice that the old-style form allows the declaration of more than one parameter in a
list after the type name
...


Implementation Issues
There are a few important things to remember about functions that affect their
efficiency and usability
...


Parameters and General-Purpose Functions
A general-purpose function is one that will be used in a variety of situations, perhaps
by many different programmers
...
All of the information a function needs should be passed
to it by its parameters
...

Besides making your functions general purpose, parameters keep your code
readable and less susceptible to bugs resulting from side effects
...
However, in certain specialized applications, you may need to eliminate
a function and replace it with inline code
...
For this reason,
inline code is often used instead of function calls when execution time is critical
...
First, a CALL instruction
takes time to execute
...
For most applications, this very slight increase in
execution time is of no significance
...
For example, the
following are two versions of a program that prints the square of the numbers from 1
to 10
...

in line
#include ...
h>
int sqr(int a);
int main(void)
{
int x;

for(x=1; x<11; ++x)
printf("%d", x*x);

for(x=1; x<11; ++x)
printf("%d", sqr(x));

return 0;
}

return 0;
}
int sqr(int a)
{
return a*a;
}

Note

In C++, the concept of inline functions is expanded and formalized
...


C++

Chapter 7
Structures, Unions,
Enumerations, and
User-Defined Types

161

162

C++: The Complete Reference

T

he C language gives you five ways to create a custom data type:

1
...
(The terms aggregate or conglomerate are also commonly
used
...
The bit-field, which is a variation on the structure and allows easy access to
individual bits
...
The union, which enables the same piece of memory to be defined as two or
more different types of variables
...
The enumeration, which is a list of named integer constants
...
The typedef keyword, which defines a new name for an existing type
...

The other methods of creating custom data types are described here
...
This chapter discusses only their C-like, non-object-oriented features
...


Structures
A structure is a collection of variables referenced under one name, providing a
convenient means of keeping related information together
...
The variables that make up the structure are called members
...
)
Generally, all of the members of a structure are logically related
...
The following code fragment shows how to declare a structure that defines
the name and address fields
...

struct addr
{
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
};

Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

Notice that the declaration is terminated by a semicolon
...
The type name of the structure is addr
...

At this point, no variable has actually been created
...
When you define a structure, you are defining a compound variable type, not
a variable
...
In C, to
declare a variable (i
...
, a physical object) of type addr, write
struct addr addr_info;

This declares a variable of type addr called addr_info
...

addr addr_info;

As you can see, the keyword struct is not needed
...
The reason for this difference is that
in C, a structure's name does not define a complete type name
...
In C, you must precede the tag with the keyword
struct when declaring variables
...
Keep in mind, however,
that it is still perfectly legal to use the C-style declaration in a C++ program
...
Just remember that C++ allows the shorter form
...
Figure 7-1
shows how addr_info appears in memory assuming 1-byte characters and 4-byte
long integers
...

For example,
struct addr {
char name[30];
char street[40];
char city[20];
char state[3];
unsigned long int zip;
} addr_info, binfo, cinfo;

defines a structure type called addr and declares variables addr_info, binfo, and cinfo
of that type
...


The addr_info structure in memory

If you only need one structure variable, the structure type name is not needed
...

The general form of a structure declaration is
struct struct-type-name {
type member-name;
type member-name;
type member-name;

...


...


Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

Accessing Structure Members
Individual members of a structure are accessed through the use of the
...
For example, the following code assigns the ZIP
code 12345 to the zip field of the structure variable addr_info declared earlier:
addr_info
...
The general form for accessing a member of a structure is
structure-name
...
zip);

This prints the ZIP code contained in the zip member of the structure variable
addr_info
...
name can be used to call
gets( ), as shown here:
gets(addr_info
...

Since name is a character array, you can access the individual characters of
addr_info
...
For example, you can print the contents of
addr_info
...
name[t]; ++t)
putchar(addr_info
...
That is, you do not need to assign the
value of each member separately
...
h>
int main(void)

165

166

C++: The Complete Reference

{
struct {
int a;
int b;
} x, y;
x
...
a);
return 0;
}

After the assignment, y
...


Arrays of Structures
Perhaps the most common usage of structures is in arrays of structures
...
For example, to declare a 100-element array of structures of
type addr, defined earlier, write
struct addr addr_info[100];

This creates 100 sets of variables that are organized as defined in the structure addr
...
For example, to print the
ZIP code of structure 3, write
printf("%d", addr_info[2]
...


Passing Structures to Functions
This section discusses passing structures and their members to functions
...
Therefore, you are passing a simple
variable (unless, of course, that element is compound, such as an array)
...
x);
func2(mike
...
z);
func4(mike
...
s[2]);

/*
/*
/*
/*
/*

passes
passes
passes
passes
passes

character value of x */
integer value of y */
float value of z */
address of string s */
character value of s[2] */

If you wish to pass the address of an individual structure member, put the & operator
before the structure name
...
x);
func2(&mike
...
z);
func4(mike
...
s[2]);

/*
/*
/*
/*
/*

passes
passes
passes
passes
passes

address
address
address
address
address

of
of
of
of
of

character x */
integer y */
float z */
string s */
character s[2] */

Remember that the & operator precedes the structure name, not the individual
member name
...


Passing Entire Structures to Functions
When a structure is used as an argument to a function, the entire structure is passed
using the standard call-by-value method
...

When using a structure as a parameter, remember that the type of the argument
must match the type of the parameter
...

#include ...
*/
struct struct_type {
int a, b;
char ch;
} ;
void f1(struct struct_type parm);
int main(void)
{
struct struct_type arg;
arg
...
a);
}

As this program illustrates, if you will be declaring parameters that are structures,
you must make the declaration of the structure type global so that all parts of your
program can use it
...

As just stated, when passing structures, the type of the argument must match
the type of the parameter
...
For example, the following version of the preceding
program is incorrect and will not compile because the type name of the argument
used to call f1( ) differs from the type name of its parameter
...
*/
#include ...
*/
struct struct_type {
int a, b;
char ch;
} ;
/* Define a structure similar to struct_type,
but with a different name
...
a = 1000;
f1(arg); /* type mismatch */
return 0;
}
void f1(struct struct_type2 parm)
{
printf("%d", parm
...
However, there are some special aspects to structure pointers that
you should know
...
For example, assuming the previously defined structure addr, the
following declares addr_pointer as a pointer to data of that type:
struct addr *addr_pointer;

Remember, in C++ it is not necessary to precede this declaration with the keyword
struct
...
This chapter covers the first use
...
(Recall that arguments are passed to functions on the stack
...
If the structure contains
many members, however, or if some of its members are arrays, run-time performance
may degrade to unacceptable levels
...

When a pointer to a structure is passed to a function, only the address of the
structure is pushed on the stack
...
A second
advantage, in some cases, is when a function needs to reference the actual structure
used as the argument, instead of a copy
...

To find the address of a structure variable, place the & operator before the
structure's name
...

To access the members of a structure using a pointer to that structure, you must
use the −> operator
...
The arrow is used in place of the dot operator when you are
accessing a structure member through a pointer to the structure
...

/* Display a software timer
...
h>
#define DELAY 128000
struct my_time {
int hours;
int minutes;
int seconds;
} ;
void display(struct my_time *t);
void update(struct my_time *t);
void delay(void);
int main(void)
{
struct my_time systime;
systime
...
minutes = 0;
systime
...

As you can see, a global structure called my_time is defined but no variable is
declared
...

This means that systime is known directly only to the main( ) function
...
In both functions, their arguments are
declared as a pointer to a my_time structure
...

Because update( ) receives a pointer to the systime structure, it can update its value
...

Remember, use the dot operator to access structure elements when operating on
the structure itself
...


Arrays and Structures Within Structures
A member of a structure may be either a simple or compound type
...

You have already seen one type of compound element: the character arrays used in
addr
...

A member of a structure that is an array is treated as you might expect from
the earlier examples
...
a[3][7]

When a structure is a member of another structure, it is called a nested structure
...
The first is a structure
of type addr, which contains an employee's address
...
The following code fragment assigns 93456 to the zip element
of address
...
address
...
Standard C specifies that structures may be nested to at least 15 levels
...


Bit-Fields
Unlike some other computer languages, C/C++ has a built-in feature called a bit-field
that allows you to access a single bit
...

s Certain devices transmit status information encoded into one or more bits
within a byte
...

Although these tasks can be performed using the bitwise operators, a bit-field can
add more structure (and possibly efficiency) to your code
...
In fact,
a bit-field is really just a special type of structure member that defines how long,
in bits, the field is to be
...


...

type nameN : length;
} variable_list;
Here, type is the type of the bit-field and length is the number of bits in the field
...
Bit-fields of length
1 should be declared as unsigned, because a single bit cannot have a sign
...

For example, the status port of a serial communications adapter might return a
status byte organized like this:

Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

Bit

Meaning When Set

0

Change in clear-to-send line

1

Change in data-set-ready

2

Trailing edge detected

3

Change in receive line

4

Clear-to-send

5

Data-set-ready

6

Telephone ringing

7

Received signal

You can represent the information in a status byte using the following bit-field:
struct status_type {
unsigned delta_cts:
unsigned delta_dsr:
unsigned tr_edge:
unsigned delta_rec:
unsigned cts:
unsigned dsr:
unsigned ring:
unsigned rec_line:
} status;

1;
1;
1;
1;
1;
1;
1;
1;

You might use a routine similar to that shown here to enable a program to determine
when it can send or receive data
...
cts) printf("clear to send");
if(status
...
For example, this code fragment clears the ring field:
status
...

However, if the structure is referenced through a pointer, you must use the −> operator
...
This makes it easy to reach the bit you
want, bypassing unused ones
...

It is valid to mix normal structure members with bit-fields
...

Without the bit-field, this information would have taken 3 bytes
...
You cannot take the address of a bit-field
...
They cannot be declared as static
...

Other restrictions may be imposed by various specific implementations, so check the
user manual for your compiler
...
Declaring a union is similar to
declaring a structure
...


...

} union-variables;
For example:
union u_type {
int i;
char ch;
};

This declaration does not create any variables
...
In C, to declare a union variable called cnvt of type u_type using the
definition just given, write
union u_type cnvt;

When declaring union variables in C++, you need use only the type name—
you don't need to precede it with the keyword union
...

In C++, the name of a union defines a complete type name
...
(This is similar to the situation
with structures described earlier
...

In cnvt, both integer i and character ch share the same memory location
...
Figure 7-2
shows how i and ch share the same address
...

When a union variable is declared, the compiler automatically allocates enough
storage to hold the largest member of the union
...


177

178

C++: The Complete Reference

Figure 7-2
...
If you are operating on the union directly,
use the dot operator
...
For example, to assign the integer 10 to element i of cnvt, write
cnvt
...
Because the compiler keeps track of the actual sizes of the union members,
no unnecessary machine dependencies are produced
...

Unions are used frequently when specialized type conversions are needed
because you can refer to the data held in the union in fundamentally different
ways
...


Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

To get an idea of the usefulness of a union when nonstandard type conversions
are needed, consider the problem of writing a short integer to a disk file
...
While you can write any type of data to a file using fwrite( ), using fwrite( )
incurs excessive overhead for such a simple operation
...
(This example assumes that short integers
are 2 bytes long
...

#include ...
tmp", "wb+");
putw(1000, fp);
fclose(fp);

/* write the value 1000 as an integer */

return 0;
}
int putw(short int num, FILE *fp)
{
union pw word;

179

180

C++: The Complete Reference

word
...
ch[0], fp); /* write first half */
return putc(word
...


Note

C++ supports a special type of union called an anonymous union which is
discussed in Part Two of this book
...
Enumerations are common in everyday life
...
The general form for enumerations is
enum enum-type-name { enumeration list } variable_list;
Here, both the type name and the variable list are optional
...
) The following code fragment defines an enumeration called coin:
enum coin { penny, nickel, dime, quarter,
half_dollar, dollar};

The enumeration type name can be used to declare variables of its type
...

enum coin money;

In C++, the variable money may be declared using this shorter form:
coin money;

Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

In C++, an enumeration name specifies a complete type
...
(This is similar to the situation
as it applies to structures and unions, described earlier
...
\n");

The key point to understand about an enumeration is that each of the symbols
stands for an integer value
...
Each symbol is given a value one greater than the symbol that precedes it
...
Therefore,
printf("%d %d", penny, dime);

displays 0 2 on the screen
...

Do this by following the symbol with an equal sign and an integer value
...
For example, the following code assigns the value of 100 to quarter:
enum coin { penny, nickel, dime, quarter=100,
half_dollar, dollar};

Now, the values of these symbols are
penny

0

nickel

1

dime

2

quarter

100

half_dollar

101

dollar

102

One common but erroneous assumption about enumerations is that the symbols
can be input and output directly
...
For example, the following code
fragment will not perform as desired:

181

182

C++: The Complete Reference

/* this will not work */
money = dollar;
printf("%s", money);

Remember, dollar is simply a name for an integer; it is not a string
...

Actually, creating code to input and output enumeration symbols is quite tedious
(unless you are willing to settle for their integer values)
...
For example, this code also
outputs the proper string:
char name[][12]={
"penny",
"nickel",
"dime",
"quarter",
"half_dollar",
"dollar"

Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

};
printf("%s", name[money]);

Of course, this only works if no symbol is initialized, because the string array must
be indexed starting at 0
...
An enumeration is often used to define a compiler's symbol table,
for example
...


Using sizeof to Ensure Portability
You have seen that structures and unions can be used to create variables of different
sizes, and that the actual size of these variables may change from machine to machine
...
This operator is especially useful where
structures or unions are concerned
...

For example,

183

184

C++: The Complete Reference

struct s {
char ch;
int i;
double f;
} s_var;

Here, sizeof(s_var) is at least 13 (8 + 4 + 1)
...
(A paragraph is 16 bytes
...

Since sizeof is a compile-time operator, all the information necessary to compute
the size of any variable is known at compile time
...

For example, consider
union u {
char ch;
int i;
double f;
} u_var;

Here, the sizeof(u_var) is 8
...
All that matters is the size of its largest member, because any union must
be as large as its largest element
...
You are not
actually creating a new data type, but rather defining a new name for an existing
type
...
If
you define your own type name for each machine-dependent data type used by your
program, then only the typedef statements have to be changed when compiling for a
new environment
...
The general form of the typedef
statement is

Chapter 7:

Structures, Unions, Enumerations, and User-Defined Types

typedef type newname;
where type is any valid data type and newname is the new name for this type
...

For example, you could create a new name for float by using
typedef float balance;

This statement tells the compiler to recognize balance as another name for float
...

Now that balance has been defined, it can be used in another typedef
...

Using typedef can make your code easier to read and easier to port to a new
machine, but you are not creating a new physical type
...


C++

Chapter 8
C-Style Console I/O

187

188

C++: The Complete Reference

++ supports two complete I/O systems
...
The second
is the object-oriented I/O system defined by C++
...
(Part Two examines C++ I/O
...

In C, input and output are accomplished through library functions
...
Technically, there is little distinction between console
I/O and file I/O, but conceptually they are in very different worlds
...
The next chapter presents the file I/O
system and describes how the two systems relate
...
Standard C++ does not define any functions that perform various
screen control operations (such as cursor positioning) or that display graphics,
because these operations vary widely between machines
...
Instead, the console
I/O functions perform only TTY-based output
...
And, of course, you may use
C++ to write Windows programs, but keep in mind that the C++ language does not
directly define functions that perform these tasks
...
h
...

This chapter refers to the console I/O functions as performing input from the
keyboard and output to the screen
...
Furthermore, standard input and standard output may be
redirected to other devices
...


C

An Important Application Note
Part One of this book uses the C-like I/O system because it is the only style of I/O
that is defined for the C subset of C++
...
For most C++ applications, you will want to use the
C++-specific I/O system, not the C I/O system described in this chapter
...
In this case, you will need to use the C-like I/O
functions
...
Also, many programs will be
hybrids of both C and C++ code
...
Thus, knowledge of both the C and the C++

Chapter 8:

C-Style Console I/O

I/O system will be necessary
...


s An understanding of the basic principles behind the C-like I/O system is
crucial to an understanding of the C++ object-oriented I/O system
...
)
s In certain situations (for example, in very short programs), it may be easier to
use C's non-object-oriented approach to I/O than it is to use the object-oriented
I/O defined by C++
...
If you don't know how to use the C I/O system, you will be limiting your
professional horizons
...
The getchar( )
function waits until a key is pressed and then returns its value
...
The putchar( ) function writes a character to the
screen at the current cursor position
...

However, you can assign this value to a char variable, as is usually done, because the
character is contained in the low-order byte
...
)
getchar( ) returns EOF if an error occurs
...
Only the low-order byte of its
parameter is actually output to the screen
...
(The EOF macro is defined in stdio
...
)
The following program illustrates getchar( ) and putchar( )
...
To stop the program, enter a period
...
h>
#include ...
\n");
do {
ch = getchar();
if(islower(ch)) ch = toupper(ch);
else ch = tolower(ch);
putchar(ch);
} while (ch != '
...
Normally, getchar( ) is implemented
in such a way that it buffers input until ENTER is pressed
...

Also, since getchar( ) inputs only one character each time it is called, line-buffering may
leave one or more characters waiting in the input queue, which is annoying in interactive
environments
...
Therefore, if the preceding program did not
behave as you expected, you now know why
...
If this is the case, you might want to use a different function
to read characters from the keyboard
...
Although
these functions are not defined by Standard C++, they are commonly used since
getchar( ) does not fill the needs of most programmers
...
h
...
For example,
in Microsoft's Visual C++, they are called _getch( ) and _getche( )
...

It does not echo the character to the screen
...
You will frequently see getche( ) or getch( ) used
instead of getchar( ) when a character needs to be read from the keyboard in an
interactive program
...

For example, the previous program is shown here using getch( ) instead of getchar( ):
#include ...
h>
#include ...
\n");
do {
ch = getch();
if(islower(ch)) ch = toupper(ch);
else ch = tolower(ch);
putchar(ch);
} while (ch != '
...
Input is
no longer line-buffered
...


Note

At the time of this writing, when using Microsoft's Visual C++ compiler,
_getche( ) and _getch( ) are not compatible with the standard C/C++
input functions, such as scanf( ) or gets( )
...
You will
need to examine the Visual C++ documentation for details
...
They enable you to read and write strings of characters
...
You may type characters at
the keyboard until you press ENTER
...
In fact, you
cannot use gets( ) to return a carriage return (although getchar( ) can do so)
...
The
prototype for gets( ) is
char *gets(char *str);
where str is a character array that receives the characters input by the user
...
The following program reads a string into the array str and prints its length:
#include ...
h>
int main(void)
{
char str[80];
gets(str);
printf("Length is %d", strlen(str));
return 0;
}

You need to be careful when using gets( ) because it performs no boundary checks on
the array that is receiving input
...
While gets( ) is fine for sample programs and simple utilities
that only you will use, you will want to avoid its use in commercial code
...

The puts( ) function writes its string argument to the screen followed by a newline
...
A call
to puts( ) requires far less overhead than the same call to printf( ) because puts( ) can
only output a string of charactersit cannot output numbers or do format
conversions
...
For
this reason, the puts( ) function is often used when it is important to have highly
optimized code
...
Otherwise, it
returns a nonnegative value
...

The following statement displays hello:
puts("hello");

Table 8-1 summarizes the basic console I/O functions
...
It prompts the user to enter a word and then checks
to see if the word matches one in its built-in database
...
Pay special attention to the indirection used
in this program
...
Notice that the list must be terminated by
two nulls
...


getche( )

Reads a character with echo; does not
wait for carriage return; not defined by
Standard C/C++, but a common extension
...


putchar( )

Writes a character to the screen
...


puts( )

Writes a string to the screen
...


The Basic I/O Functions

193

194

C++: The Complete Reference

/* A simple dictionary
...
h>
#include ...
h>
/* list of words and meanings */
char *dic[][40] = {
"atlas", "A volume of maps
...
",
"telephone", "A communication device
...
",
"", "" /* null terminate the list */
};
int main(void)
{
char word[80], ch;
char **p;
do {
puts("\nEnter word: ");
scanf("%s", word);
p = (char **)dic;
/* find matching word and print its meaning */
do {
if(!strcmp(*p, word)) {
puts("Meaning:");
puts(*(p+1));
break;
}
if(!strcmp(*p, word)) break;
p = p + 2; /* advance through the list */
} while(*p);
if(!*p) puts("Word not in dictionary
...
The printf( )
function writes data to the console
...
Both functions can operate on any of the built-in data types,
including characters, strings, and numbers
...
);
The printf( ) function returns the number of characters written or a negative value if an
error occurs
...
The first type is composed of
characters that will be printed on the screen
...
A format specifier begins
with a percent sign and is followed by the format code
...
For example, this printf( ) call
printf("I like %c%s", 'C', "++ very much!");

displays
I like C++ very much!

The printf( ) function accepts a wide variety of format specifiers, as shown in
Table 8-2
...


printf( ) Format Specifiers

195

196

C++: The Complete Reference

Code

Format

%i

Signed decimal integers

%e

Scientific notation (lowercase e)

%E

Scientific notation (uppercase E)

%f

Decimal floating point

%g

Uses %e or %f, whichever is shorter

%G

Uses %E or %F, whichever is shorter

%o

Unsigned octal

%s

String of characters

%u

Unsigned decimal integers

%x

Unsigned hexadecimal (lowercase letters)

%X

Unsigned hexadecimal (uppercase letters)

%p

Displays a pointer

%n

The associated argument must be a pointer to
an integer
...


%%

Prints a % sign

Table 8-2
...
This causes its matching argument to be
output, unmodified, to the screen
...


Printing Numbers
You may use either %d or %i to indicate a signed decimal number
...

To output an unsigned value, use %u
...


Chapter 8:

C-Style Console I/O

The %e and %E specifiers tell printf( ) to display a double argument in scientific
notation
...
dddddE+/−yy
If you want to display the letter "E" in uppercase, use the %E format; otherwise use %e
...

This causes printf( ) to select the format specifier that produces the shortest output
...
The
following program demonstrates the effect of the %g format specifier:
#include ...
0; f<1
...

1 10 100 1000 10000 100000 1e+006 1e+007 1e+008 1e+009

You can display unsigned integers in octal or hexadecimal format using %o and
%x, respectively
...
For uppercase, use the %X format specifier; for lowercase, use %x, as shown
here:
#include ...
This format specifier causes printf( ) to
display a machine address in a format compatible with the type of addressing used
by the computer
...
h>
int sample;
int main(void)
{
printf("%p", &sample);
return 0;
}

The %n Specifier
The %n format specifier is different from the others
...
In other
words, the value that corresponds to the %n format specifier must be a pointer to a
variable
...
Examine this
program to understand this somewhat unusual format code
...
h>
int main(void)
{
int count;
printf("this%n is a test\n", &count);
printf("%d", count);
return 0;
}

Chapter 8:

C-Style Console I/O

This program displays this is a test followed by the number 4
...


Format Modifiers
Many format specifiers may take modifiers that alter their meaning slightly
...
The format modifier goes between the percent sign and the
format code
...


The Minimum Field Width Specifier
An integer placed between the % sign and the format code acts as a minimum field width
specifier
...
If the string or number is longer than that minimum, it will still be printed in
full
...
If you wish to pad with 0's, place a 0
before the field width specifier
...
The following program demonstrates the
minimum field width specifier:
#include ...
12304;
printf("%f\n", item);
printf("%10f\n", item);
printf("%012f\n", item);
return 0;
}

This program produces the following output:
10
...
123040
00010
...
For example, the next program produces a table of squares and
cubes for the numbers between 1 and 19:

199

200

C++: The Complete Reference

#include ...
It
consists of a period followed by an integer
...

When you apply the precision specifier to floating-point data using the %f, %e,
or %E specifiers, it determines the number of decimal places displayed
...
4f displays a number at least ten characters wide with four decimal places
...

When the precision specifier is applied to %g or %G, it specifies the number of
significant digits
...
For
example, %5
...

If the string is longer than the maximum field width, the end characters will be
truncated
...
Leading zeros are added to achieve
the required number of digits
...
h>
int main(void)
{
printf("%
...
1234567);
printf("%3
...
15s\n", "This is a simple test
...
1235
00001000
This is a simpl

Justifying Output
By default, all output is right-justified
...
You can force output to
be left-justified by placing a minus sign directly after the %
...
2f leftjustifies a floating-point number with two decimal places in a 10-character field
...
h>
int main(void)
{

201

202

C++: The Complete Reference

printf("right-justified:%8d\n", 100);
printf("left-justified:%-8d\n", 100);
return 0;
}

Handling Other Data Types
There are two format modifiers that allow printf( ) to display short and long integers
...
The l (ell) modifier
tells printf( ) that a long data type follows
...
The h modifier instructs printf( ) to display a short integer
...

The L modifier may prefix the floating-point specifiers e, f, and g, and indicates that
a long double follows
...

Preceding g, G, f, E, or e specifiers with a # ensures that there will be a decimal
point even if there are no decimal digits
...
Preceding the o specifier
with # causes the number to be printed with a leading zero
...

Instead of constants, the minimum field width and precision specifiers may be
provided by arguments to printf( )
...

When the format string is scanned, printf( ) will match the * to an argument in the
order in which they occur
...
3
...
h>
int main(void)
{
printf("%x %#x\n", 10, 10);
printf("%*
...
34);
return 0;
}

Chapter 8:

C-Style Console I/O

printf("%*
...
3);

Figure 8-1
...
It can read all the built-in
data types and automatically convert numbers into the proper internal format
...
The prototype for scanf( ) is
int scanf(const char *control_string,
...
If an error occurs, scanf( ) returns EOF
...

The control string consists of three classifications of characters:

s Format specifiers
s White-space characters
s Non-white-space characters
Let's take a look at each of these now
...
These codes are listed in Table 8-3
...
Let's look
at some examples
...
To read a floating-point number
represented in either standard or scientific notation, use %e, %f, or %g
...
The %x may be in either upper- or

203

204

C++: The Complete Reference

lowercase
...
The following program reads an octal and hexadecimal
number:
#include ...


%d

Read a decimal integer
...


%e

Read a floating-point number
...


%g

Read a floating-point number
...


%s

Read a string
...


%p

Read a pointer
...


%u

Read an unsigned decimal integer
...


%%

Read a percent sign
...


scanf( ) Format Specifiers

Chapter 8:

C-Style Console I/O

The scanf( ) function stops reading a number when the first nonnumeric character is
encountered
...
For example,
unsigned num;
scanf("%u", &num);

reads an unsigned number and puts its value into num
...
You can also use scanf( ) for this purpose if
you use the %c format specifier
...
This makes
it somewhat troublesome in an interactive environment
...
For example, with an input stream of "x y," this code fragment
scanf("%c%c%c", &a, &b, &c);

returns with the character x in a, a space in b, and the character y in c
...
Using %s causes scanf( ) to read characters until it encounters a
white-space character
...
As it
applies to scanf( ), a white-space character is either a space, a newline, a tab, a vertical
tab, or a form feed
...
This means that you cannot
use scanf( ) to read a string like "this is a test" because the first space terminates the
reading process
...

#include ...


Inputting an Address
To input a memory address, use the %p format specifier
...
For example,
this program inputs an address and then displays what is at that memory address:
#include ...


Using a Scanset
The scanf( ) function supports a general-purpose format specifier called a scanset
...
When scanf( ) processes a scanset, it will input
characters as long as those characters are part of the set defined by the scanset
...
You define a scanset by putting the characters to scan for
inside square brackets
...
For example, the following scanset tells scanf( ) to read only the characters X, Y,
and Z
...

Upon return from scanf( ), this array will contain a null-terminated string that consists
of the characters that have been read
...
h>
int main(void)
{
int i;
char str[80], str2[80];
scanf("%d%[abcdefg]%s", &i, str, str2);
printf("%d %s %s", i, str, str2);
return 0;
}

Enter 123abcdtye followed by ENTER
...

Because the "t" is not part of the scanset, scanf( ) stops reading characters into str
when it encounters the "t
...

You can specify an inverted set if the first character in the set is a ^
...

In most implementations you can specify a range using a hyphen
...
If you want
to scan for both upper- and lowercase letters, you must specify them individually
...
A white-space character is either a

207

208

C++: The Complete Reference

space, a tab, vertical tab, form feed, or a newline
...


Non-White-Space Characters in the Control String
A non-white-space character in the control string causes scanf( ) to read and discard
matching characters in the input stream
...
If the specified
character is not found, scanf( ) terminates
...


You Must Pass scanf( ) Addresses
All the variables used to receive values through scanf( ) must be passed by their
addresses
...
Recall that this is one way of creating a call by reference, and it allows
a function to alter the contents of an argument
...
So, to read a string into the character array
str, you would use
scanf("%s", str);

In this case, str is already a pointer and need not be preceded by the & operator
...

The format specifiers can include a maximum field length modifier
...
For example, to read no more than 20 characters into
str, write
scanf("%20s", str);

If the input stream is greater than 20 characters, a subsequent call to input begins
where this call leaves off
...
This means
that the remaining characters, UVWXYZ, have not yet been used
...
Input for a field may terminate before the
maximum field length is reached if a white space is encountered
...

To read a long integer, put an l (ell) in front of the format specifier
...
These modifiers can be used with the
d, i, o, u, and x format codes
...
If you
put an l (ell) in front of one of these specifiers, scanf( ) assigns the data to a double
...


Suppressing Input
You can tell scanf( ) to read a field but not assign it to any variable by preceding that
field's format code with an *
...
The comma would be correctly read, but not
assigned to anything
...


209

This page intentionally left blank
...
As explained in Chapter 8, C++ supports
two complete I/O systems: the one inherited from C and the object-oriented
system defined by C++
...
(The C++ file
system is discussed in Part Two
...


T

C Versus C++ File I/O
There is sometimes confusion over how C's file system relates to C++
...
Thus, if you will be porting older C code
to C++, you will not have to change all of your I/O routines right away
...
The C++ I/O system completely duplicates the functionality of the C
I/O system and renders the C file system redundant
...
Of course,
most C++ programmers elect to use the C++ I/O system for reasons that are made
clear in Part Two of this book
...
The C I/O system supplies a consistent
interface to the programmer independent of the actual device being accessed
...
This abstraction is called a stream and the actual device is called a file
...


Note

The concept of streams and files is also important to the C++ I/O system discussed
in Part Two
...
Even though each device is very different, the
buffered file system transforms each into a logical device called a stream
...
Because streams are largely device independent, the same function
that can write to a disk file can also be used to write to another type of device, such as
the console
...


Chapter 9:

File I/O

Text Streams
A text stream is a sequence of characters
...
However,
the newline character is optional on the last line
...
) In a text stream, certain character
translations may occur as required by the host environment
...
Therefore, there may not be a
one-to-one relationship between the characters that are written (or read) and those
on the external device
...


Binary Streams
A binary stream is a sequence of bytes that have a one-to-one correspondence to those
in the external devicethat is, no character translations occur
...
However,
an implementation-defined number of null bytes may be appended to a binary stream
...


Files
In C/C++, a file may be anything from a disk file to a terminal or printer
...
Once a file is open,
information may be exchanged between it and your program
...
For example, a disk file can support random
access while some printers cannot
...

If the file can support position requests, opening that file also initializes the file
position indicator to the start of the file
...

You disassociate a file from a specific stream with a close operation
...
This process is generally referred to as flushing the stream, and
guarantees that no information is accidentally left in the disk buffer
...
Files are not closed when a
program terminates abnormally, such as when it crashes or when it calls abort( )
...

Never modify this file control block
...
Just remember that its main purpose is to provide a
consistent interface
...
The I/O system automatically converts the
raw input or output from each device into an easily managed stream
...
The most common of
these are shown in Table 9-1
...
h
...


Name

Function

fopen( )

Opens a file
...


putc( )

Writes a character to a file
...


getc( )

Reads a character from a file
...


fgets( )

Reads a string from a file
...


fseek( )

Seeks to a specified byte in a file
...


fprintf( )

Is to a file what printf( ) is to the console
...


feof( )

Returns true if end-of-file is reached
...


rewind( )

Resets the file position indicator to the
beginning of the file
...


fflush( )

Flushes a file
...


Commonly Used C File-System Functions

Chapter 9:

File I/O

The header file stdio
...
The size_t type is some
variety of unsigned integer, as is fpos_t
...

Also defined in stdio
...
The ones relevant to this
chapter are NULL, EOF, FOPEN_MAX, SEEK_SET, SEEK_CUR, and SEEK_END
...
The EOF macro is generally defined as −1
and is the value returned when an input function tries to read past the end of the file
...
The other macros are used with fseek( ), which is the function
that performs random access on a file
...
A file pointer is a
pointer to a structure of type FILE
...
In essence,
the file pointer identifies a specific file and is used by the associated stream to direct the
operation of the I/O functions
...
To obtain a file pointer variable, use a statement like this:
FILE *fp;

Opening a File
The fopen( ) function opens a stream for use and links a file with that stream
...
Most often (and for the rest of this
discussion), the file is a disk file
...
The string pointed to by mode determines how the file
will be opened
...
Strings like "r+b" may also be
represented as "rb+
...


w

Create a text file for writing
...


Table 9-2
...


wb

Create a binary file for writing
...


r+

Open a text file for read/write
...


a+

Append or create a text file for
read/write
...


w+b

Create a binary file for read/write
...


Table 9-2
...
Your program should
never alter the value of this pointer
...

The following code uses fopen( ) to open a file named TEST for output
...
\n");
exit(1);
}

Chapter 9:

File I/O

This method will detect any error in opening a file, such as a write-protected or a full
disk, before your program attempts to write to it
...

Although most of the file modes are self-explanatory, a few comments are in
order
...
When opening a file using append mode, if the file does not exist, it will be
created
...
The original contents will remain unchanged
...
If it does exist, the
contents of the original file will be destroyed and a new file created
...
Further, if the file already exists, opening it with w+ destroys its contents;
opening it with r+ does not
...
In most
implementations, in text mode, carriage return/linefeed sequences are translated to
newline characters on input
...
No such translations occur on binary files
...

This value will usually be at least 8, but you must check your compiler manual for its
exact value
...
It writes
any data still remaining in the disk buffer to the file and does a formal operatingsystem-level close on the file
...

fclose( ) also frees the file control block associated with the stream, making it available
for reuse
...

The fclose( ) function has this prototype:
int fclose(FILE *fp);
where fp is the file pointer returned by the call to fopen( )
...
The function returns EOF if an error occurs
...

Generally, fclose( ) will fail only when a disk has been prematurely removed from the
drive or there is no more space on the disk
...
(Actually, putc( ) is usually implemented as a macro
...
This book uses
putc( ), but you can use fputc( ) if you like
...
The prototype of this function is
int putc(int ch, FILE *fp);
where fp is the file pointer returned by fopen( ) and ch is the character to be output
...
For historical reasons, ch is defined
as an int but only the low-order byte is written
...
Otherwise, it
returns EOF
...
Both
are defined to preserve compatibility with older versions of C
...

The getc( ) function reads characters from a file opened in read mode by fopen( )
...
getc( ) returns an integer,
but the character is contained in the low-order byte
...

The getc( ) function returns an EOF when the end of the file has been reached
...
You can use ferror( ) to determine
precisely what has occurred
...
The following program, KTOD, is a simple example of using putc( ), fopen( ),

Chapter 9:

File I/O

and fclose( )
...
The filename is specified from the command line
...

/* KTOD: A key to disk program
...
h>
#include ...
\n");
exit(1);
}
if((fp=fopen(argv[1], "w"))==NULL) {
printf("Cannot open file
...

/* DTOS: A program that reads files and displays them
on the screen
...
h>
#include ...
\n");
exit(1);
}
if((fp=fopen(argv[1], "r"))==NULL) {
printf("Cannot open file
...
Then read its
contents using DTOS
...

However, testing the value returned by getc( ) may not be the best way to determine
when you have arrived at the end of a file
...
When a file is opened for binary input, an integer value that will
test equal to EOF may be read
...

Second, getc( ) returns EOF when it fails and when it reaches the end of the file
...
To solve these

Chapter 9:

File I/O

problems, the C file system includes the function feof( ), which determines when the
end of the file has been encountered
...

Therefore, the following routine reads a binary file until the end of the file is
encountered:
while(!feof(fp)) ch = getc(fp);

Of course, you can apply this method to text files as well as binary files
...
The files are opened in binary mode and feof( ) checks for the end of the file
...
*/
#include ...
h>
int main(int argc, char *argv[])
{
FILE *in, *out;
char ch;
if(argc!=3) {
printf("You forgot to enter a filename
...
\n");
exit(1);
}
if((out=fopen(argv[2], "wb")) == NULL) {
printf("Cannot open destination file
...
*/
while(!feof(in)) {
ch = getc(in);

221

222

C++: The Complete Reference

if(!feof(in)) putc(ch, out);
}
fclose(in);
fclose(out);
return 0;
}

Working with Strings: fputs( ) and fgets( )
In addition to getc( ) and putc( ), the C file system supports the related functions
fgets( ) and fputs( ), which read and write character strings from and to a disk file
...
They have the following prototypes:
int fputs(const char *str, FILE *fp);
char *fgets(char *str, int length, FILE *fp);
The fputs( ) function writes the string pointed to by str to the specified stream
...

The fgets( ) function reads a string from the specified stream until either a newline
character is read or length −1 characters have been read
...
The resultant string will be null terminated
...

The following program demonstrates fputs( )
...
To terminate the program, enter a blank line
...

#include ...
h>
#include ...
\n");

Chapter 9:

File I/O

exit(1);
}
do {
printf("Enter a string (CR to quit):\n");
gets(str);
strcat(str, "\n"); /* add a newline */
fputs(str, fp);
} while(*str!='\n');
return 0;
}

rewind( )
The rewind( ) function resets the file position indicator to the beginning of the file
specified as its argument
...
Its prototype is
void rewind(FILE *fp);
where fp is a valid file pointer
...
To accomplish this, the
program rewinds the file after input is complete and then uses fgets( ) to read back
the file
...

#include ...
h>
#include ...
\n");
exit(1);
}

223

224

C++: The Complete Reference

do {
printf("Enter a string (CR to quit):\n");
gets(str);
strcat(str, "\n"); /* add a newline */
fputs(str, fp);
} while(*str!='\n');
/* now, read and display the file */
rewind(fp); /* reset file position indicator to
start of the file
...
The
ferror( ) function has this prototype:
int ferror(FILE *fp);
where fp is a valid file pointer
...
Because each file operation sets the error
condition, ferror( ) should be called immediately after each file operation; otherwise,
an error may be lost
...
The tab size is defined by TAB_SIZE
...
To use the program, specify the
names of the input and output files on the command line
...
*/
#include ...
h>
#define TAB_SIZE 8

Chapter 9:

#define IN 0
#define OUT 1
void err(int e);
int main(int argc, char *argv[])
{
FILE *in, *out;
int tab, i;
char ch;
if(argc!=3) {
printf("usage: detab \n");
exit(1);
}
if((in = fopen(argv[1], "rb"))==NULL) {
printf("Cannot open %s
...
\n", argv[1]);
exit(1);
}
tab = 0;
do {
ch = getc(in);
if(ferror(in)) err(IN);
/* if tab found, output appropriate number of spaces */
if(ch=='\t') {
for(i=tab; i<8; i++) {
putc(' ', out);
if(ferror(out)) err(OUT);
}
tab = 0;
}
else {
putc(ch, out);
if(ferror(out)) err(OUT);

File I/O

225

226

C++: The Complete Reference

tab++;
if(tab==TAB_SIZE) tab = 0;
if(ch=='\n' || ch=='\r') tab = 0;
}
} while(!feof(in));
fclose(in);
fclose(out);
return 0;
}
void err(int e)
{
if(e==IN) printf("Error on input
...
\n");
exit(1);
}

Erasing Files
The remove( ) function erases the specified file
...

The following program erases the file specified on the command line
...
A utility like this might be useful to new
computer users
...
*/
#include ...
h>
#include ...
\n");
exit(1);
}
return 0;
}

Flushing a Stream
If you wish to flush the contents of an output stream, use the fflush( ) function, whose
prototype is shown here:
int fflush(FILE *fp);
This function writes the contents of any buffered data to the file associated with fp
...

The fflush( ) function returns 0 if successful; otherwise, it returns EOF
...
These functions allow the reading and writing
of blocks of any type of data
...
For fwrite( ), buffer is a pointer to the information that will be written to the
file
...
(Remember, the type size_t is defined as some
type of unsigned integer
...

The fread( ) function returns the number of items read
...
The fwrite( ) function returns
the number of items written
...


227

228

C++: The Complete Reference

Using fread( ) and fwrite( )
As long as the file has been opened for binary data, fread( ) and fwrite( ) can read
and write any type of information
...
Notice how it
uses sizeof to determine the length of each data type
...
*/
#include ...
h>
int main(void)
{
FILE *fp;
double d = 12
...
\n");
exit(1);
}
fwrite(&d, sizeof(double), 1, fp);
fwrite(&i, sizeof(int), 1, fp);
fwrite(&l, sizeof(long), 1, fp);
rewind(fp);
fread(&d, sizeof(double), 1, fp);
fread(&i, sizeof(int), 1, fp);
fread(&l, sizeof(long), 1, fp);
printf("%f %d %ld", d, i, l);
fclose(fp);
return 0;
}

As this program illustrates, the buffer can be (and often is) merely the memory used to
hold a variable
...
In the real world, however, you should check their return values for errors
...
For example, given this
structure:
struct struct_type {
float balance;
char name[80];
} cust;

the following statement writes the contents of cust to the file pointed to by fp
...
Its prototype is shown here:
int fseek(FILE *fp, long numbytes, int origin);
Here, fp is a file pointer returned by a call to fopen( )
...
To
seek from the current position, use SEEK_CUR; and to seek from the end of the file,
use SEEK_END
...

The following program illustrates fseek( )
...
Specify the filename and then the byte to seek to on the
command line
...
h>
#include ...
\n");
exit(1);
}
if(fseek(fp, atol(argv[2]), SEEK_SET)) {
printf("Seek error
...
\n", atol(argv[2]), getc(fp));
fclose(fp);
return 0;
}

You can use fseek( ) to seek in multiples of any type of data by simply multiplying
the size of the data by the number of the item you want to reach
...
To seek to the
tenth address in the file that holds the addresses, use this statement:
fseek(fp, 9*sizeof(struct list_type), SEEK_SET);

You can determine the current location of a file using ftell( )
...
If a failure
occurs, it returns −1
...
The reason
for this is simple
...
The only time you should use

Chapter 9:

File I/O

fseek( ) with a text file is when seeking to a position previously determined by ftell( ),
using SEEK_SET as the origin
...
There is no inherent restriction about random access on files
containing text
...


fprintf( ) and fscanf( )
In addition to the basic I/O functions already discussed, the C I/O system includes
fprintf( ) and fscanf( )
...
The prototypes of fprintf( ) and fscanf( ) are
int fprintf(FILE *fp, const char *control_string,
...
);
where fp is a file pointer returned by a call to fopen( )
...

As an example, the following program reads a string and an integer from the
keyboard and writes them to a disk file called TEST
...
After running this program, examine the
TEST file
...

/* fscanf() - fprintf() example */
#include ...
h>
#include ...
\n");
exit(1);
}
printf("Enter a string and a number: ");
fscanf(stdin, "%s%d", s, &t); /* read from keyboard */

231

232

C++: The Complete Reference

fprintf(fp, "%s %d", s, t); /* write to file */
fclose(fp);
if((fp=fopen("test","r")) == NULL) {
printf("Cannot open file
...

Because formatted ASCII data is being written as it would appear on the screen
(instead of in binary), extra overhead is incurred with each call
...


The Standard Streams
As it relates to the C file system, when a program starts execution, three streams are
opened automatically
...
Normally, these streams refer to the console, but they may be
redirected by the operating system to some other device in environments that support
redirectable I/O
...
)
Because the standard streams are file pointers, they may be used by the C I/O
system to perform I/O operations on the console
...


Chapter 9:

File I/O

You may use stdin, stdout, and stderr as file pointers in any function that uses a
variable of type FILE *
...
As mentioned earlier in this
chapter, when using gets( ) it is possible to overrun the array that is being used to
receive the characters entered by the user because gets( ) provides no bounds checking
...
The only trouble
is that fgets( ) does not remove the newline character and gets( ) does, so you will have
to manually remove it, as shown in the following program
...
h>
#include ...
Also, just as these file pointers are
created automatically at the start of your program, they are closed automatically at the
end; you should not try to close them
...

The console I/O functions described in Chapter 8 actually direct their I/O operations to
either stdin or stdout
...
The reason they exist is as a convenience to you, the
programmer
...
However, what might surprise you is that you can perform disk
file I/O using console I/O functions, such as printf( )! This is because all of the console
I/O functions operate on stdin and stdout
...
For example, consider this program:
#include ...
If you execute TEST normally, it displays
its prompt on the screen, reads a string from the keyboard, and displays that string on
the display
...
For example, in a DOS or Windows
environment, executing TEST like this:
TEST > OUTPUT

causes the output of TEST to be written to a file called OUTPUT
...

When a program terminates, any redirected streams are reset to their default status
...
This function
associates an existing stream with a new file
...
Its prototype is
FILE *freopen(const char *filename, const char *mode, FILE *stream);
where filename is a pointer to the filename you wish associated with the stream
pointed to by stream
...
freopen( ) returns stream if successful
or NULL on failure
...
h>
int main(void)
{
char str[80];
freopen("OUTPUT", "w", stdout);
printf("Enter a string: ");
gets(str);
printf(str);
return 0;
}

In general, redirecting the standard streams by using freopen( ) is useful in special
situations, such as debugging
...


235

This page intentionally left blank
...
These are called preprocessor directives, and although not
actually part of the C or C++ language per se, they expand the scope of the
programming environment
...


Y

The Preprocessor
Before beginning, it is important to put the preprocessor in historical perspective
...
Moreover, the
C++ preprocessor is virtually identical to the one defined by C
...
In C, each preprocessor directive is necessary
...
In fact,
one of the long-term design goals of C++ is the elimination of the preprocessor
altogether
...

The preprocessor contains the following directives:
#define

#elif

#else

#endif

#error

#if

#ifdef

#ifndef

#include

#line

#pragma

#undef

As you can see, all preprocessor directives begin with a # sign
...
For example,
#include ...
h>

will not work
...
e
...
The identifier is referred to as a macro name and the replacement process as
macro replacement
...
There may be any number of spaces
between the identifier and the character sequence, but once the character sequence
begins, it is terminated only by a newline
...
For example, the following prints 0 1 2 on the screen:
printf("%d %d %d", RIGHT, LEFT, LEFT+1);

Once a macro name has been defined, it may be used as part of the definition of other
macro names
...
Therefore, if you wish to define a standard error message,
you might write something like this:
#define E_MS "standard error on input\n"
/*
...
To the compiler, the printf( ) statement will actually
appear to be
printf("standard error on input\n");

No text substitutions occur if the identifier is within a quoted string
...

If the character sequence is longer than one line, you may continue it on the next by
placing a backslash at the end of the line, as shown here:
#define LONG_STRING "this is a very long \
string that is used as an example"

C/C++ programmers commonly use uppercase letters for defined identifiers
...
Also, it is usually best to put all #defines at the start of the
file or in a separate header file rather than sprinkling them throughout the program
...
For example, you may have a program that defines an array and has
several routines that access that array
...
In this way, if you need to change the size of
the array, you will only need to change the #define statement and then recompile your
program
...
*/
float balance[MAX_SIZE];
/*
...
*/
for(i=0; i
Since MAX_SIZE defines the size of the array balance, if the size of balance needs to
be changed in the future, you need only change the definition of MAX_SIZE
...


Note

C++ provides a better way of defining constants, which uses the const keyword
...


Defining Function-like Macros
The #define directive has another powerful feature: the macro name can have
arguments
...
This form of a
macro is called a function-like macro
...
h>
#define ABS(a)

(a)<0 ? -(a) : (a)

int main(void)
{
printf("abs of -1 and 1: %d %d", ABS(-1), ABS(1));
return 0;
}

When this program is compiled, a in the macro definition will be substituted with
the values –1 and 1
...
For example, if the parentheses around a were removed, this expression
ABS(10-20)

would be converted to
10-20<0 ? -10-20 : 10-20

after macro replacement and would yield the wrong result
...

However, if the size of the function-like macro is very large, this increased speed may
be paid for with an increase in the size of the program because of duplicated code
...


#error
The #error directive forces the compiler to stop compilation
...
The general form of the #error directive is
#error error-message
The error-message is not between double quotes
...


241

242

C++: The Complete Reference

#include
The #include directive instructs the compiler to read another source file in addition to
the one that contains the #include directive
...
For example,
#include "stdio
...
h>

both instruct the compiler to read and compile the header for the C I/O system library
functions
...
This is referred to as nested
includes
...
However,
Standard C stipulates that at least eight nested inclusions will be available
...

Whether the filename is enclosed by quotes or by angle brackets determines
how the search for the specified file is conducted
...

Often, this means searching some special directory set aside for include files
...
For many compilers, this means searching the current working directory
...

Typically, most programmers use angle brackets to include the standard header
files
...
However, there is no hard and fast rule that demands this usage
...
C++ defines a set of standard headers that provide the information necessary
to the various C++ libraries
...
Thus, a header is simply an abstraction that guarantees that the
appropriate information required by your program is included
...


Conditional Compilation Directives
There are several directives that allow you to selectively compile portions of your
program's source code
...


Chapter 10:

The Preprocessor and Comments

#if, #else, #elif, and #endif
Perhaps the most commonly used conditional compilation directives are the #if, #else,
#elif, and #endif
...

The general form of #if is
#if constant-expression
statement sequence
#endif
If the constant expression following #if is true, the code that is between it and
#endif is compiled
...
The #endif directive
marks the end of an #if block
...
*/
#include ...
\n");
#endif
return 0;
}

This program displays the message on the screen because MAX is greater than 99
...
The expression that follows the #if is
evaluated at compile time
...

The #else directive works much like the else that is part of the C++ language: it
establishes an alternative if #if fails
...
*/
#include ...
\n");
#else
printf("Compiled for small array
...
The #else alternative is compiled, however, and the message Compiled for
small array is displayed
...
This is necessary because there can only be one #endif associated with
any #if
...
#elif is followed by a constant expression
...

Otherwise, the next block in the series is checked
...


...

#elif expression N
statement sequence
#endif

Chapter 10:

The Preprocessor and Comments

For example, the following fragment uses the value of ACTIVE_COUNTRY to
define the currency sign:
#define US 0
#define ENGLAND 1
#define FRANCE 2
#define ACTIVE_COUNTRY US
#if ACTIVE_COUNTRY == US
char currency[] = "dollar";
#elif ACTIVE_COUNTRY == ENGLAND
char currency[] = "pound";
#else
char currency[] = "franc";
#endif

Standard C states that #ifs and #elifs may be nested at least eight levels
...
When nested, each #endif,
#else, or #elif associates with the nearest #if or #elif
...
The general form of #ifdef is
#ifdef macro-name
statement sequence
#endif

245

246

C++: The Complete Reference

If macro-name has been previously defined in a #define statement, the block of code
will be compiled
...

Both #ifdef and #ifndef may use an #else or #elif statement
...
h>
#define TED 10
int main(void)
{
#ifdef TED
printf("Hi Ted\n");
#else
printf("Hi anyone\n");
#endif
#ifndef RALPH
printf("RALPH not defined\n");
#endif
return 0;
}

will print Hi Ted and RALPH not defined
...

You may nest #ifdefs and #ifndefs to at least eight levels in Standard C
...


#undef
The #undef directive removes a previously defined definition of the macro name that
follows it
...
The general form for #undef is
#undef macro-name

Chapter 10:

The Preprocessor and Comments

For example,
#define LEN 100
#define WIDTH 100
char array[LEN][WIDTH];
#undef LEN
#undef WIDTH
/* at this point both LEN and WIDTH are undefined */

Both LEN and WIDTH are defined until the #undef statements are encountered
...


Using defined
In addition to #ifdef, there is a second way to determine if a macro name is defined
...

The defined operator has this general form:
defined macro-name
If macro-name is currently defined, then the expression is true
...
For
example, to determine if the macro MYFILE is defined, you can use either of these two
preprocessing commands:
#if defined MYFILE

or
#ifdef MYFILE

You may also precede defined with the ! to reverse the condition
...

#if !defined DEBUG
printf("Final version!\n");
#endif

247

248

C++: The Complete Reference

One reason for using defined is that it allows the existence of a macro name to be
determined by a #elif statement
...
The _ _LINE_ _ identifier contains the line
number of the currently compiled line of code
...
The general form for #line is
#line number "filename"
where number is any positive integer and becomes the new value of _ _LINE_ _ , and
the optional filename is any valid file identifier, which becomes the new value of
_ _FILE_ _
...

For example, the following code specifies that the line count will begin with 100
...

#include ...
For example, a compiler may have an option that supports
program execution tracing
...
You must check the compiler's documentation for details and options
...
These operators are used with the
#define statement
...
For example, consider this program
...
h>
#define mkstr(s)

# s

int main(void)
{
printf(mkstr(I like C++));
return 0;
}

The preprocessor turns the line
printf(mkstr(I like C++));

into
printf("I like C++");

The ## operator, called the pasting operator, concatenates two tokens
...
h>
#define concat(a, b)

a ## b

int main(void)
{
int xy = 10;
printf("%d", concat(x, y));
return 0;
}

The preprocessor transforms
printf("%d", concat(x, y));

249

250

C++: The Complete Reference

into
printf("%d", xy);

If these operators seem strange to you, keep in mind that they are not needed or
used in most programs
...


Predefined Macro Names
C++ specifies six built-in predefined macro names
...
Each will be described here, in turn
...

Briefly, they contain the current line number and filename of the program when it is
being compiled
...

The _ _TIME_ _ macro contains the time at which the program was compiled
...

The meaning of _ _STDC_ _ is implementation-defined
...

A compiler conforming to Standard C++ will define_ _cplusplus as a value
containing at least six digits
...


C-Style Comments
A C-style comment begins with the character pair /* and ends with */
...
The compiler ignores any text between the
beginning and ending comment symbols
...
h>
int main(void)
{
printf("hello");
/* printf("there"); */
return 0;
}

A C-style comment is commonly called a multiline comment because the text of the
comment may extend over two or more lines
...
For example, this comment is valid:
x = 10+ /* add the numbers */5;

while
swi/*this will not work*/tch(c) {
...
However, you should not
generally place comments in the middle of expressions because it obscures their
meaning
...
That is, one comment may not contain
another comment
...
However, C++ supports two types of comments
...
The second is the single-line comment
...
For example,

251

252

C++: The Complete Reference

// this is a single-line comment

Although Standard C does not currently define the single-line comment, most C
compilers will accept it and it will probably be formally incorporated into Standard C
within the next year or two
...

You should include comments whenever they are needed to explain the operation
of the code
...


Part II
C++

P

art One examined the C subset of C++
...
That is, it
discusses those features of C++ that it does not have in common
with C
...
We will begin
with an overview of C++
...


C++

Chapter 11
An Overview of C++

255

256

C++: The Complete Reference

his chapter provides an overview of the key concepts embodied in C++
...
In several instances, this interrelatedness makes it difficult
to describe one feature of C++ without implicitly involving several others
...
To address this
problem, this chapter presents a quick overview of the most important aspects of
C++, including its history, its key features, and the difference between traditional
and Standard C++
...


T

The Origins of C++
C++ began as an expanded version of C
...
He
initially called the new language "C with Classes
...

Although C was one of the most liked and widely used professional programming
languages in the world, the invention of C++ was necessitated by one major programming factor: increasing complexity
...
Even though C is an excellent programming language, it has
its limits
...
The purpose of C++ is to allow this
barrier to be broken
...

Most additions made by Stroustrup to C support object-oriented programming,
sometimes referred to as OOP
...
) Stroustrup states that some of C++'s object-oriented features
were inspired by another object-oriented language called Simula67
...

Since C++ was first invented, it has undergone three major revisions, with each
adding to and altering the language
...
The third occurred during the standardization of C++
...
Toward that end, a joint ANSI (American National
Standards Institute) and ISO (International Standards Organization) standardization
committee was formed
...
In that draft, the ANSI/ISO C++ committee (of which I am a member)
kept the features first defined by Stroustrup and added some new ones as well
...

Soon after the completion of the first draft of the C++ standard, an event occurred
that caused the language to be greatly expanded: the creation of the Standard Template
Library (STL) by Alexander Stepanov
...
It is both powerful and elegant, but also quite large
...
The
addition of the STL expanded the scope of C++ well beyond its original definition
...

It is fair to say that the standardization of C++ took far longer than anyone had
expected when it began
...
In fact, the version of C++ defined by the C++
committee is much larger and more complex than Stroustrup's original design
...
The final draft was passed out of committee
on November 14, 1997
...

The material in this book describes Standard C++, including all of its newest
features
...


What Is Object-Oriented Programming?
Since object-oriented programming (OOP) drove the creation of C++, it is necessary to
understand its foundational principles
...
Programming methodologies have changed dramatically since the
invention of the computer, primarily to accommodate the increasing complexity of
programs
...
As
long as programs were just a few hundred instructions long, this approach worked
...
As programs continued to grow, high-level languages were
introduced that gave the programmer more tools with which to handle complexity
...
Although FORTRAN was a
very impressive first step, it is hardly a language that encourages clear, easy-tounderstand programs
...
This is the method encouraged by
languages such as C and Pascal
...
Structured languages are characterized by
their support for stand-alone subroutines, local variables, rich control constructs, and
their lack of reliance upon the GOTO
...

Consider this: At each milestone in the development of programming, techniques
and tools were created to allow the programmer to deal with increasingly greater
complexity
...
Prior to the invention of OOP, many projects
were nearing (or exceeding) the point where the structured approach no longer

257

258

C++: The Complete Reference

worked
...

Object-oriented programming took the best ideas of structured programming
and combined them with several new concepts
...
In the most general sense, a program can be organized in
one of two ways: around its code (what is happening) or around its data (who is being
affected)
...
This approach can be thought of as "code acting on data
...

Object-oriented programs work the other way around
...
" In an
object-oriented language, you define the data and the routines that are permitted
to act on that data
...

To support the principles of object-oriented programming, all OOP languages
have three traits in common: encapsulation, polymorphism, and inheritance
...


Encapsulation
Encapsulation is the mechanism that binds together code and the data it manipulates,
and keeps both safe from outside interference and misuse
...
When code and data are linked together in this fashion, an object is
created
...

Within an object, code, data, or both may be private to that object or public
...
That is,
private code or data may not be accessed by a piece of the program that exists outside
the object
...
Typically, the public parts of an object are used to
provide a controlled interface to the private elements of the object
...
It may
seem strange that an object that links both code and data can be thought of as a
variable
...
Each
time you define a new type of object, you are creating a new data type
...


Polymorphism
Object-oriented programming languages support polymorphism, which is characterized
by the phrase "one interface, multiple methods
...
The

Chapter 11:

An Overview of C++

specific action selected is determined by the exact nature of the situation
...
No matter what type of furnace your house
has (gas, oil, electric, etc
...
In this case, the
thermostat (which is the interface) is the same no matter what type of furnace (method)
you have
...
It doesn't matter what type of furnace actually provides the heat
...
For example, you might
have a program that defines three different types of stacks
...
Because
of polymorphism, you can define one set of names, push( ) and pop( ), that can be used
for all three stacks
...
The
compiler will automatically select the right function based upon the data being stored
...
The individual versions of these functions
define the specific implementations (methods) for each type of data
...
It is the compiler's job to select the specific action
(i
...
, method) as it applies to each situation
...
You need only remember and utilize the general interface
...
However, C++ is a compiled
language
...


Inheritance
Inheritance is the process by which one object can acquire the properties of another
object
...
If you think
about it, most knowledge is made manageable by hierarchical classifications
...
Without the use of classifications,
each object would have to define explicitly all of its characteristics
...
It is the inheritance mechanism that makes it possible for one object to
be a specific instance of a more general case
...


Some C++ Fundamentals
In Part One, the C subset of C++ was described and C programs were used to
demonstrate those features
...
" That is, they will be making use of features unique to C++
...

If you come from a C background, or if you have been studying the C subset
programs in Part One, be aware that C++ programs differ from C programs in some
important respects
...
But C++ programs differ from C programs in other ways,
including how I/O is performed and what headers are included
...

Before moving on to C++'s object-oriented constructs, an understanding of the
fundamental elements of a C++ program is required
...
Along the
way, some important differences with C and earlier versions of C++ are pointed out
...

#include
using namespace std;
int main()
{
int i;
cout << "This is output
...
A line-by-line commentary will be useful
...
This header supports C++-style I/O operations
...
h is to C
...
h extension to the

Chapter 11:

An Overview of C++

name iostream
...
New-style headers do not use the
...

The next line in the program is
using namespace std;

This tells the compiler to use the std namespace
...
A namespace creates a declarative region in which various program elements
can be placed
...
The using
statement informs the compiler that you want to use the std namespace
...
By using the std
namespace you simplify access to the standard library
...


Note

Since both new-style headers and namespaces are recent additions to C++, you may
encounter older code that does not use them
...
Instructions for using an older compiler are found later in
this chapter
...

int main()

Notice that the parameter list in main( ) is empty
...
This differs from C
...
However, in
C++, the use of void is redundant and unnecessary
...

The next line contains two C++ features
...
\n";

First, the statement
cout << "This is output
...
to be displayed on the screen, followed by a carriage returnlinefeed combination
...
It is still the left shift
operator, but when it is used as shown in this example, it is also an output operator
...
(Actually, like C, C++ supports
I/O redirection, but for the sake of discussion, assume that cout refers to the screen
...

Note that you can still use printf( ) or any other of C's I/O functions in a C++
program
...

Further, while using printf( ) to output a string is virtually equivalent to using << in
this case, the C++ I/O system can be expanded to perform operations on objects that
you define (something that you cannot do using printf( ))
...
As mentioned in
Chapter 10, C++ defines two types of comments
...
You can also define a single-line comment by
using //; whatever follows such a comment is ignored by the compiler until the end of
the line is reached
...

Next, the program prompts the user for a number
...
However, when used as
shown, it also is C++'s input operator
...
The identifier cin refers to the standard input device, which is
usually the keyboard
...


Note

The line of code just described is not misprinted
...
When inputting information using a C-based function like
scanf( ), you have to explicitly pass a pointer to the variable that will receive the
information
...
However, because of the way the >> operator is implemented in C++, you do not
need (in fact, must not use) the &
...


Although it is not illustrated by the example, you are free to use any of the
C-based input functions, such as scanf( ), instead of using >>
...

Another interesting line in the program is shown here:
cout << i << "squared is " << i*i << "\n";

Chapter 11:

An Overview of C++

Assuming that i has the value 10, this statement causes the phrase 10 squared is 100
to be displayed, followed by a carriage return-linefeed
...

The program ends with this statement:
return 0;

This causes zero to be returned to the calling process (which is usually the operating
system)
...
Returning zero indicates that the
program terminated normally
...
You may also use the values EXIT_SUCCESS and EXIT_
FAILURE if you like
...
For example, this program inputs a float, a double, and
a string and then outputs them:
#include
using namespace std;
int main()
{
float f;
char str[80];
double d;
cout << "Enter two floating point numbers: ";
cin >> f >> d;
cout << "Enter a string: ";
cin >> str;
cout << f << " " << d << " " << str;
return 0;
}

When you run this program, try entering This is a test
...
When the program redisplays the information you entered, only the word "This"
will be displayed
...
Thus, "is a test" is

263

264

C++: The Complete Reference

never read by the program
...

The C++ I/O operators recognize the entire set of backslash character constants
described in Chapter 2
...


Declaring Local Variables
If you come from a C background, you need to be aware of an important difference
between C and C++ regarding when local variables can be declared
...
You cannot
declare a variable in a block after an "action" statement has occurred
...
OK in C++
...
However, when compiling it as a C++
program, this fragment is perfectly acceptable
...

Here is another example
...

#include
using namespace std;
int main()
{
float f;

Chapter 11:

An Overview of C++

double d;
cout << "Enter two floating point numbers: ";
cin >> f >> d;
cout << "Enter a string: ";
char str[80]; // str declared here, just before 1st use
cin >> str;
cout << f << " " << d << " " << str;
return 0;
}

Whether you declare all variables at the start of a block or at the point of first use is
completely up to you
...
In the preceding example, the declarations
are separated simply for illustration, but it is easy to imagine more complex examples in
which this feature of C++ is more valuable
...
However, the greatest benefit of declaring variables at the point of first use is
gained in large functions
...
For
this reason, this book will declare variables at the point of first use only when it seems
warranted by the size or complexity of a function
...
Opponents suggest that sprinkling declarations throughout a block makes
it harder, not easier, for someone reading the code to find quickly the declarations
of all variables used in that block, making the program harder to maintain
...
This book
will not take a stand either way on this issue
...


No Default to int
There has been a fairly recent change to C++ that may affect older C++ code as well as
C code being ported to C++
...

However, the "default-to-int" rule was dropped from C++ a couple of years ago, during
standardization
...
The
"default-to-int" rule is also applied in much older C++ code
...
It
was common practice to not specify int explicitly when a function returned an integer
result
...

func(int i)
{
return i*i;
}

In Standard C++, this function must have the return type of int specified, as shown here
...
However, you should not use this feature for new code
because it is no longer allowed
...
At the time of this writing, Standard C
does not
...
As explained in Part One, automatic conversions take place which allow
bool values to be converted to integers, and vice versa
...
The reverse also occurs; true is
converted to 1 and false is converted to zero
...


Old-Style vs
...
As a result, there are really two versions of C++
...
This is the version of C++ that has been used by programmers for the past
decade
...
While these two versions of C++ are
very similar at their core, Standard C++ contains several enhancements not found
in traditional C++
...


Chapter 11:

An Overview of C++

This book describes Standard C++
...
The code in this book reflects the contemporary coding style and practices
as encouraged by Standard C++
...
Here's why
...

As these features were defined, they were implemented by compiler developers
...
Since features were added to C++
over a period of years, an older compiler might not support one or more of them
...
If you are using an older compiler that does not
accept these new features, don't worry
...

The key differences between old-style and modern code involve two features:
new-style headers and the namespace statement
...
The
first version shown here reflects the way C++ programs were written using old-style
coding
...

*/
#include ...
It includes the file iostream
...
Also notice that no namespace statement is present
...

/*
A modern-style C++ program that uses
the new-style headers and a namespace
...
Both of these
features were mentioned in passing earlier
...


The New C++ Headers
As you know, when you use a library function in a program, you must include its
header file
...
For example, in C, to include the
header file for the I/O functions, you include stdio
...
h>

Here, stdio
...
The key point is that this
#include statement includes a file
...
That is, it used header files
...

However, Standard C++ created a new kind of header that is used by the Standard
C++ library
...
Instead, they simply
specify standard identifiers that may be mapped to files by the compiler, although
they need not be
...

Since the new-style headers are not filenames, they do not have a
...
They
consist solely of the header name contained between angle brackets
...









The new-style headers are included using the #include statement
...

Because C++ includes the entire C function library, it still supports the standard
C-style header files associated with that library
...
h
or ctype
...
However, Standard C++ also defines new-style headers
that you can use in place of these header files
...
h
...
h is
...
h is
...
For this
reason, from this point forward, this book will use new-style C++ headers in all
#include statements
...

Since the new-style header is a recent addition to C++, you will still find many,
many older programs that don't use it
...
As the old-style skeletal program shows, the traditional
way to include the I/O header is as shown here
...
h>

This causes the file iostream
...
In general, an old-style
header file will use the same name as its corresponding new-style header with
a
...

As of this writing, all C++ compilers support the old-style headers
...
This is why they are not used in this book
...


Namespaces
When you include a new-style header in your program, the contents of that header
are contained in the std namespace
...
The
purpose of a namespace is to localize the names of identifiers to avoid name collisions
...

Originally, the names of the C++ library functions, etc
...
However, with the advent of the new-style headers, the
contents of these headers were placed in the std namespace
...
For now, you won't need to worry about them because
the statement
using namespace std;

brings the std namespace into visibility (i
...
, it puts std into the global namespace)
...

One other point: for the sake of compatibility, when a C++ program includes a C
header, such as stdio
...
This allows a
C++ compiler to compile C-subset programs
...
While all new C++ compilers
support these features, older compilers may not
...
If this is the case, there is an easy work-around: simply use an
old-style header and delete the namespace statement
...
h>

This change transforms a modern program into an old-style one
...

One other point: for now and for the next few years, you will see many C++
programs that use the old-style headers and do not include a using statement
...
However, for new programs, you
should use the modern style because it is the only style of program that complies with
the C++ Standard
...


Introducing C++ Classes
This section introduces C++'s most important feature: the class
...
A class is
similar syntactically to a structure
...
The following class defines a
type called stack, which will be used to create a stack:
#define SIZE 100
// This creates the class stack
...
By default, all items defined in
a class are private
...
This means that
they cannot be accessed by any function that is not a member of the class
...
Although it is not shown in this example, you can
also define private functions, which then may be called only by other members of the
class
...
All variables or functions defined
after public can be accessed by all other functions in the program
...
Although you can
have public variables, good practice dictates that you should try to limit their use
...
One other point: Notice that the public keyword is followed by a colon
...
The variables stck and tos are called member variables (or data
members)
...
Only member
functions have access to the private members of their class
...

Once you have defined a class, you can create an object of that type by using the
class name
...
For example,
this creates an object called mystack of type stack:
stack mystack;

When you declare an object of a class, you are creating an instance of that class
...
You may also create objects when the class is
defined by putting their names after the closing curly brace, in exactly the same way as
you would with a structure
...
Therefore, an object is an instance of a class in just the same way that some
other variable is an instance of the int data type, for example
...
(That is, an object exists inside the memory
of the computer
...

Inside the declaration of stack, member functions were identified using their
prototypes
...
Prototypes are not optional
...

When it comes time to actually code a function that is the member of a class, you
must tell the compiler which class the function belongs to by qualifying its name with
the name of the class of which it is a member
...
\n";
return;
}
stck[tos] = i;
tos++;
}

The :: is called the scope resolution operator
...
In C++, several different classes can use the same function name
...

When you refer to a member of a class from a piece of code that is not part of the
class, you must always do so in conjunction with an object of that class
...

This rule applies whether you are accessing a data member or a function member
...

stack stack1, stack2;
stack1
...

Understand that stack1 and stack2 are two separate objects
...
The only
relationship stack1 has with stack2 is that they are objects of the same type
...
It is only when a member is
referred to by code that does not belong to the class that the object name and the dot
operator must be used
...

class stack {
int stck[SIZE];
int tos;
public:
void init();
void push(int i);
int pop();
};
void stack::init()
{
tos = 0;
}
void stack::push(int i)
{
if(tos==SIZE) {
cout << "Stack is full
...
\n";
return 0;
}
tos--;

273

274

C++: The Complete Reference

return stck[tos];
}
int main()
{
stack stack1, stack2;

// create two stack objects

stack1
...
init();
stack1
...
push(2);
stack1
...
push(4);
cout
cout
cout
cout

<<
<<
<<
<<

stack1
...
pop()
stack2
...
pop()

<<
<<
<<
<<

" ";
" ";
" ";
"\n";

return 0;
}

The output from this program is shown here
...
For example, a statement like
stack1
...


could not be in the main( ) function of the previous program because tos is private
...

In C++, two or more functions can share the same name as long as their parameter
declarations are different
...


Chapter 11:

An Overview of C++

To see why function overloading is important, first consider three functions defined
by the C subset: abs( ), labs( ), and fabs( )
...
Although these functions perform almost identical actions,
in C three slightly different names must be used to represent these essentially similar
tasks
...
Even
though the underlying concept of each function is the same, the programmer has to
remember three things, not just one
...
0) << "\n";
cout << abs(-9L) << "\n";
return 0;
}
int abs(int i)
{
cout << "Using integer abs()\n";
return i<0 ? -i : i;
}
double abs(double d)
{
cout << "Using double abs()\n";
return d<0
...

Using integer abs()
10
Using double abs()
11
Using long abs()
9

This program creates three similar but different functions called abs( ), each of
which returns the absolute value of its argument
...
The value of overloaded
functions is that they allow related sets of functions to be accessed with a common
name
...
It is
left to the compiler to choose the right specific method for a particular circumstance
...
Due to polymorphism, three
things to remember have been reduced to one
...

In general, to overload a function, simply declare different versions of it
...
You must observe one important restriction when
overloading a function: the type and/or number of the parameters of each overloaded
function must differ
...
They must differ in the types or number of their parameters
...
) Of course, overloaded functions may differ in their return types, too
...
One version concatenates
two strings (just like strcat( ) does)
...
Here, overloading is used to create one interface that appends
either a string or an integer to another string
...

For example, you could use the name sqr( ) to create functions that return the
square of an int and the square root of a double
...
In practice, you should
overload only closely related operations
...
As you know, in
C++, it is possible to use the << and >> operators to perform console I/O operations
...
When an operator is overloaded, it takes on an additional
meaning relative to a certain class
...

In general, you can overload most of C++'s operators by defining what they mean
relative to a specific class
...
It is possible to overload the + operator relative to objects of type stack
so that it appends the contents of one stack to the contents of another
...

Because operator overloading is, in practice, somewhat more complex than function
overloading, examples are deferred until Chapter 14
...
In C++, inheritance is supported by allowing one
class to incorporate another class into its declaration
...
The process involves
first defining a base class, which defines those qualities common to all objects to be
derived from the base
...
The
classes derived from the base are usually referred to as derived classes
...
To demonstrate how this works, the next example creates classes that
categorize different types of buildings
...
It will serve as the base for
two derived classes
...
The member functions beginning
with set set the values of the private data
...

You can now use this broad definition of a building to create derived classes that
describe specific types of buildings
...
The general form for inheritance is
class derived-class : access base-class {
// body of new class
}
Here, access is optional
...

(These options are further examined in Chapter 12
...
Using public means that all of the public members of the base class
will become public members of the derived class
...

However, house's member functions do not have access to the private elements of
building
...
Even though house inherits building, it has
access only to the public members of building
...


Remember

A derived class has direct access to both its own members and the public members of
the base class
...
It creates two derived classes of building
using inheritance; one is house, the other, school
...
set_rooms(12);
h
...
set_area(4500);
h
...
set_baths(3);
cout << "house has " << h
...
set_rooms(200);
s
...
set_offices(5);
s
...
get_classrooms();
cout << " classrooms\n";
cout << "Its area is " << s
...

house has 5 bedrooms
school has 180 classrooms
Its area is 25000

As this program shows, the major advantage of inheritance is that you can create a
general classification that can be incorporated into more specific ones
...

When writing about C++, the terms base and derived are generally used to describe
the inheritance relationship
...
You
may also see the terms superclass and subclass
...
(Refer to Chapter 16 for details
...
For example, think back to the stack class developed earlier in this chapter
...
This was performed by
using the function init( )
...
This automatic
initialization is performed through the use of a constructor function
...
For example, here is how the stack class looks when
converted to use a constructor function for initialization:
// This creates the class stack
...
In C++, constructor
functions cannot return values and, thus, have no return type
...
In actual practice, most constructor functions will not output or input
anything
...

An object's constructor is automatically called when the object is created
...
If you are accustomed
to thinking of a declaration statement as being passive, this is not the case for C++
...
This distinction is not just
academic
...
An object's
constructor is called once for global or static local objects
...

The complement of the constructor is the destructor
...
Local objects
are created when their block is entered, and destroyed when the block is left
...
When an object is destroyed, its
destructor (if it has one) is automatically called
...
For example, an object may need to deallocate
memory that it had previously allocated or it may need to close a file that it had
opened
...
The
destructor has the same name as the constructor, but it is preceded by a ~
...
(Keep in mind that
the stack class does not require a destructor; the one shown here is just for illustration
...

class stack {
int stck[SIZE];
int tos;

Chapter 11:

An Overview of C++

public:
stack(); // constructor
~stack(); // destructor
void push(int i);
int pop();
};
// stack's constructor function
stack::stack()
{
tos = 0;
cout << "Stack Initialized\n";
}
// stack's destructor function
stack::~stack()
{
cout << "Stack Destroyed\n";
}

Notice that, like constructor functions, destructor functions do not have return values
...
Observe that init( ) is no longer needed
...

#include
using namespace std;
#define SIZE 100
// This creates the class stack
...
\n";
return;
}
stck[tos] = i;
tos++;
}
int stack::pop()
{
if(tos==0) {
cout << "Stack underflow
...
push(1);
b
...
push(3);
b
...
pop() << " ";

Chapter 11:

An Overview of C++

cout << a
...
pop() << " ";
cout << b
...
These are shown in Table
11-1
...

Also, early versions of C++ defined the overload keyword, but it is obsolete
...


asm

auto

bool

break

case

catch

char

class

const

const_cast

continue

default

delete

do

double

dynamic_cast

else

enum

explicit

export

extern

false

float

for

friend

goto

if

inline

int

long

mutable

namespace

new

operator

private

protected

Table 11-1
...


The C++ keywords (continued)

The General Form of a C++ Program
Although individual styles will differ, most C++ programs will have this general form:
#includes
base-class declarations
derived class declarations
nonmember function prototypes
int main( )
{
//
...
But the general organization of a program remains the same
...


C++

Chapter 12
Classes and Objects

289

290

C++: The Complete Reference

I

n C++, the class forms the basis for object-oriented programming
...
This
chapter examines classes and objects in detail
...
A class declaration defines a new type
that links code and data
...

Thus, a class is a logical abstraction, but an object has physical existence
...

A class declaration is similar syntactically to a structure
...
Here is the entire general form of a class
declaration that does not inherit any other class
...

access-specifier:
data and functions
} object-list;
The object-list is optional
...
Here,
access-specifier is one of these three C++ keywords:
public
private
protected
By default, functions and data declared within a class are private to that
class and may be accessed only by other members of the class
...
The protected access specifier is needed only when inheritance is
involved (see Chapter 15)
...


Chapter 12:

Classes and Objects

You may change access specifications as often as you like within a class declaration
...
The class declaration in the following example illustrates this feature:
#include
#include
using namespace std;
class employee {
char name[80]; // private by default
public:
void putname(char *n); // these are public
void getname(char *n);
private:
double wage; // now, private again
public:
void putwage(double w); // back to public
double getwage();
};
void employee::putname(char *n)
{
strcpy(name, n);
}
void employee::getname(char *n)
{
strcpy(n, name);
}
void employee::putwage(double w)
{
wage = w;
}
double employee::getwage()
{
return wage;
}
int main()
{
employee ted;

291

292

C++: The Complete Reference

char name[80];
ted
...
putwage(75000);
ted
...
getwage() << " per year
...
Notice that the public access specifier is used twice
...
However, to the compiler, using multiple access specifiers makes no difference
...
For example, most programmers would code the
employee class as shown here, with all private elements grouped together and all
public elements grouped together:
class employee {
char name[80];
double wage;
public:
void putname(char *n);
void getname(char *n);
void putwage(double w);
double getwage();
};

Functions that are declared within a class are called member functions
...
This includes all
private elements
...
Collectively, any element of a class can be referred to as a member
of that class
...
A non-static member
variable cannot have an initializer
...
(Although a member can be a pointer to the class that is being
declared
...

In general, you should make all data members of a class private to that class
...
However, there may be situations in
which you will need to make one or more variables public
...
)
When a variable is public, it may be accessed directly by any other part of your
program
...

This simple program illustrates the use of a public variable:
#include
using namespace std;
class myclass {
public:
int i, j, k; // accessible to entire program
};
int main()
{
myclass a, b;
a
...
j = 4;
a
...
i * a
...
k = 12; // remember, a
...
k are different
cout << a
...
k;
return 0;
}

Structures and Classes Are Related
Structures are part of the C subset and were inherited from the C language
...
But the relationship between a
class and a struct is closer than you may at first think
...
In fact, the only
difference between a class and a struct is that by default all members are public in a
struct and private in a class
...


293

294

C++: The Complete Reference

That is, in C++, a structure defines a class type
...

#include
#include
using namespace std;
struct mystr {
void buildstr(char *s); // public
void showstr();
private: // now go private
char str[255];
} ;
void mystr::buildstr(char *s)
{
if(!*s) *str = '\0'; // initialize string
else strcat(str, s);
}
void mystr::showstr()
{
cout << str << "\n";
}
int main()
{
mystr s;
s
...
buildstr("Hello ");
s
...
showstr();
return 0;
}

This program displays the string Hello there!
...
This seeming redundancy is justified for several reasons
...
In C, structures
already provide a means of grouping data
...
Second, because structures and classes are related, it may be
easier to port existing C programs to C++
...
In order for C++ to remain compatible with C, the definition
of struct must always be tied to its C definition
...

Usually it is best to use a class when you want a class, and a struct when you want a
C-like structure
...
Sometimes the acronym
POD is used to describe a C-style structure—one that does not contain member
functions, constructors, or destructors
...
(Actually, the term
POD is a bit more narrowly defined in the Standard C++ specification, but means
essentially the same thing
...


Unions and Classes Are Related
Like a structure, a union may also be used to define a class
...
They may also include constructor
and destructor functions
...
Like the
structure, union members are public by default and are fully compatible with C
...
(This example assumes that short integers are 2 bytes long
...
set_byte(49034);
b
...
show_word();
return 0;
}

Like a structure, a union declaration in C++ defines a special type of class
...

There are several restrictions that must be observed when you use C++ unions
...
Further, a union cannot be a
base class
...
(Virtual functions are
discussed in Chapter 17
...
A reference
member cannot be used
...
Finally, no object can be a member of a union if the object has an
explicit constructor or destructor function
...


Anonymous Unions
There is a special type of union in C++ called an anonymous union
...

Instead, an anonymous union tells the compiler that its member variables are to
share the same location
...
For example, consider this program:
#include
#include
using namespace std;
int main()
{
// define anonymous union
union {
long l;
double d;
char s[4];
} ;
// now, reference union elements directly
l = 100000;
cout << l << " ";
d = 123
...
In fact, relative to your program, that is exactly how
you will use them
...
This
implies that the names of the members of an anonymous union must not conflict with
other identifiers known within the same scope
...

First, the only elements contained within an anonymous union must be data
...
Anonymous unions cannot contain private or protected
elements
...


297

298

C++: The Complete Reference

Friend Functions
It is possible to grant a nonmember function access to the private members of a class
by using a friend
...
To declare a friend function, include its prototype
within the class, preceding it with the keyword friend
...

int sum(myclass x)
{
/* Because sum() is a friend of myclass, it can
directly access a and b
...
a + x
...
set_ab(3, 4);
cout << sum(n);
return 0;
}

Chapter 12:

Classes and Objects

In this example, the sum( ) function is not a member of myclass
...
Also, notice that sum( ) is called without the use
of the dot operator
...

Although there is nothing gained by making sum( ) a friend rather than a member
function of myclass, there are some circumstances in which friend functions are quite
valuable
...
Second, friend functions make the creation of some types of I/O
functions easier (see Chapter 18)
...
Let's examine this third usage now
...
Other parts of your program may wish
to know if a pop-up message is currently being displayed before writing to the screen
so that no message is accidentally overwritten
...
If the condition needs to be checked frequently, this additional
overhead may not be acceptable
...

Thus, in situations like this, a friend function allows you to generate more efficient
code
...

public:
void set_status(int state);
friend int idle(C1 a, C2 b);
};
class C2 {
int status; // IDLE if off, INUSE if on screen
//
...
status || b
...
set_status(IDLE);
y
...
\n";
else cout << "In use
...
set_status(INUSE);
if(idle(x, y)) cout << "Screen can be used
...
\n";
return 0;
}

Notice that this program uses a forward declaration (also called a forward reference) for
the class C2
...
To create a forward declaration to a class, simply use the
form shown in this program
...
For example, here is the
preceding program rewritten so that idle( ) is a member of C1:
#include
using namespace std;
const int IDLE = 0;
const int INUSE = 1;
class C2;

// forward declaration

class C1 {
int status; // IDLE if off, INUSE if on screen
//
...

public:
void set_status(int state);
friend int C1::idle(C2 b);
};
void C1::set_status(int state)
{
status = state;
}
void C2::set_status(int state)
{
status = state;
}
// idle() is member of C1, but friend of C2
int C1::idle(C2 b)
{

301

302

C++: The Complete Reference

if(status || b
...
set_status(IDLE);
y
...
idle(y)) cout << "Screen can be used
...
\n";
x
...
idle(y)) cout << "Screen can be used
...
\n";
return 0;
}

Because idle( ) is a member of C1, it can access the status variable of objects of type
C1 directly
...

There are two important restrictions that apply to friend functions
...
Second, friend functions may not have a
storage-class specifier
...


Friend Classes
It is possible for one class to be a friend of another class
...
For example,
// Using a friend class
...
a < x
...
a : x
...
min(ob);
return 0;
}

In this example, class Min has access to the private variables a and b declared within
the TwoValues class
...
It does not inherit the other class
...

Friend classes are seldom used
...


Inline Functions
There is an important feature in C++, called an inline function, that is commonly used
with classes
...

In C++, you can create short functions that are not actually called; rather, their code
is expanded in line at the point of each invocation
...
To cause a function to be expanded in line rather than called,

303

304

C++: The Complete Reference

precede its definition with the inline keyword
...
Since classes typically require several frequently
executed interface functions (which provide access to private data), the efficiency of
these functions is of critical concern
...
Typically, arguments are pushed onto the stack and various registers are
saved when a function is called, and then restored when the function returns
...
However, when a function is expanded in
line, none of those operations occur
...
For this reason, it is best to inline only very small functions
...

Like the register specifier, inline is actually just a request, not a command, to the
compiler
...
Also, some compilers may not inline all
types of functions
...
You will need to check your compiler's user manual for any restrictions to
inline
...

Inline functions may be class member functions
...

inline void myclass::init(int i, int j)
{
a = i;
b = j;
}
// Create another inline function
...
init(10, 20);
x
...
When a
function is defined inside a class declaration, it is automatically made into an inline
function (if possible)
...
For example, the preceding program is rewritten here with
the definitions of init( ) and show( ) contained within the declaration of myclass:
#include
using namespace std;
class myclass {
int a, b;
public:
// automatic inline
void init(int i, int j) { a=i; b=j; }
void show() { cout << a << " " << b << "\n"; }
};
int main()
{
myclass x;
x
...
show();
return 0;
}

Notice the format of the function code within myclass
...
However, you are free
to use any format you like
...
However, it is extremely common to see all short member functions
defined inside their class in C++ programs
...
)
Constructor and destructor functions may also be inlined, either by default, if
defined within their class, or explicitly
...
Typically, these arguments
help initialize an object when it is created
...
When you
define the constructor's body, use the parameters to initialize the object
...
show();
return 0;
}

307

308

C++: The Complete Reference

Notice that in the definition of myclass( ), the parameters i and j are used to give initial
values to a and b
...
Specifically, this
statement
myclass ob(3, 4);

causes an object called ob to be created and passes the arguments 3 and 4 to the i and j
parameters of myclass( )
...
Actually, there is a small technical difference
between the two types of declarations that relates to copy constructors
...
)
Here is another example that uses a parameterized constructor function
...

#include
#include
using namespace std;
const int IN = 1;
const int CHECKED_OUT = 0;
class book {
char author[40];
char title[40];
int status;
public:
book(char *n, char *t, int s);
int get_status() {return status;}
void set_status(int s) {status = s;}
void show();
};
book::book(char *n, char *t, int s)
{

Chapter 12:

Classes and Objects

strcpy(author, n);
strcpy(title, t);
status = s;
}
void book::show()
{
cout << title << " by " << author;
cout << " is ";
if(status==IN) cout << "in
...
\n";
}
int main()
{
book b1("Twain", "Tom Sawyer", IN);
book b2("Melville", "Moby Dick", CHECKED_OUT);
b1
...
show();
return 0;
}

Parameterized constructor functions are very useful because they allow you to
avoid having to make an additional function call simply to initialize one or more
variables in an object
...
Also, notice that the short get_status( ) and set_status( ) functions are defined
in line, within the book class
...


Constructors with One Parameter: A Special Case
If a constructor only has one parameter, there is a third way to pass an initial value to
that constructor
...

#include
using namespace std;
class X {
int a;

309

310

C++: The Complete Reference

public:
X(int j) { a = j; }
int geta() { return a; }
};
int main()
{
X ob = 99; // passes 99 to j
cout << ob
...
Pay special attention to how ob
is declared in main( )
...
That is, the declaration statement is handled by the
compiler as if it were written like this:
X ob = X(99);

In general, any time you have a constructor that requires only one argument, you
can use either ob(i) or ob = i to initialize an object
...

Remember that the alternative shown here applies only to constructors that have
exactly one parameter
...
This section explains the
consequences of each
...
Unlike regular data members, individual copies of a static
member variable are not made for each object
...
Thus, all objects of that class
use that same variable
...


Chapter 12:

Classes and Objects

When you declare a static data member within a class, you are not defining it
...
) Instead, you must provide a global definition
for it elsewhere, outside the class
...
This causes
storage for the variable to be allocated
...
)
To understand the usage and effect of a static data member, consider this program:
#include
using namespace std;
class shared {
static int a;
int b;
public:
void set(int i, int j) {a=i; b=j;}
void show();
} ;
int shared::a; // define a
void shared::show()
{
cout << "This is static a: " << a;
cout << "\nThis is non-static b: " << b;
cout << "\n";
}
int main()
{
shared x, y;
x
...
show();
y
...
show();
x
...
*/
return 0;
}

311

312

C++: The Complete Reference

This program displays the following output when run
...
As
mentioned earlier, this is necessary because the declaration of a inside shared does
not allocate storage
...
However, this convenience gave rise to serious
inconsistencies and it was eliminated several years ago
...
In these cases, you
will need to add the required definitions
...
For example,
in the following short program, a is both public and static
...
Further, since a exists before an object of shared is
created, a can be given a value at any time
...
For this reason, both output statements
display the same value: 99
...
a: " << x
...
In general, to refer to a static member independently of an object, you must
qualify it by using the name of the class of which it is a member
...
For example, you might create several objects,
each of which needs to write to a specific disk file
...
In this case, you will want to declare a
static variable that indicates when the file is in use and when it is free
...
The following program shows how
you might use a static variable of this type to control access to a scarce resource:
#include
using namespace std;
class cl {
static int resource;
public:
int get_resource();
void free_resource() {resource = 0;}
};
int cl::resource; // define resource
int cl::get_resource()
{
if(resource) return 0; // resource already in use
else {
resource = 1;
return 1; // resource allocated to this object
}
}

313

314

C++: The Complete Reference

int main()
{
cl ob1, ob2;
if(ob1
...
get_resource()) cout << "ob2 denied resource\n";
ob1
...
get_resource())
cout << "ob2 can now use resource\n";
return 0;
}

Another interesting use of a static member variable is to keep track of the number
of objects of a particular class type that are in existence
...

Objects
Objects
Objects
Objects

in
in
in
in

existence:
existence:
existence:
existence:

1
2
3
2

As you can see, the static member variable count is incremented whenever an object is
created and decremented when an object is destroyed
...

By using static member variables, you should be able to virtually eliminate any
need for global variables
...


Static Member Functions
Member functions may also be declared as static
...
They may only directly refer to other static members of the
class
...
) A static member function does not have a this pointer
...
) There cannot be a static and a non-static version of the same
function
...
Finally, they cannot be declared
as const or volatile
...
Notice that get_resource( ) is now declared as static
...

#include
using namespace std;
class cl {
static int resource;
public:
static int get_resource();
void free_resource() { resource = 0; }
};
int cl::resource; // define resource
int cl::get_resource()
{
if(resource) return 0; // resource already in use
else {
resource = 1;
return 1; // resource allocated to this object
}
}
int main()
{
cl ob1, ob2;
/* get_resource() is static so may be called independent
of any object
...
free_resource();
if(ob2
...
For
example, this is a perfectly valid C++ program:
#include
using namespace std;
class static_type {
static int i;
public:
static void init(int x) {i = x;}
void show() {cout << i;}
};
int static_type::i; // define i
int main()
{
// init static data before object creation
static_type::init(100);
static_type x;
x
...
Precisely when these
events occur is discussed here
...
The destructor functions for local objects are executed
in the reverse order of the constructor functions
...
Global constructors are executed in order of their declaration, within
the same file
...
Global destructors execute in reverse order after main( ) has
terminated
...
\n";
myclass local_ob2(4);
return 0;
}

It displays this output:
Initializing 1
Initializing 2
Initializing 3
This will not be first line displayed
...


The Scope Resolution Operator
As you know, the :: operator links a class name with a member name in order to
tell the compiler what class the member belongs to
...
For example, consider this
fragment:
int i;

// global i

void f()
{
int i; // local i
i = 10; // uses local i

...


...
But what if
function f( ) needs to access the global version of i? It may do so by preceding the i
with the :: operator, as shown here
...


...

}

319

320

C++: The Complete Reference

Nested Classes
It is possible to define one class within another
...
Since
a class declaration does, in fact, define a scope, a nested class is valid only within the
scope of the enclosing class
...
Because of
C++'s flexible and powerful inheritance mechanism, the need for nested classes is
virtually nonexistent
...
For example, this is a valid C++ program:
#include
using namespace std;
void f();
int main()
{
f();
// myclass not known here
return 0;
}
void f()
{
class myclass {
int i;
public:
void put_i(int n) { i=n; }
int get_i() { return i; }
} ob;
ob
...
get_i();
}

When a class is declared within a function, it is known only to that function and
unknown outside of it
...
First, all member functions must be
defined within the class declaration
...
It may
access type names and enumerators defined by the enclosing function, however
...
Because of these restrictions, local
classes are not common in C++ programming
...
Objects are passed to functions through the use of the standard call-byvalue mechanism
...
However, the fact that a copy is created means, in essence, that another
object is created
...
The answer to these two questions may surprise you
...

#include
using namespace std;
class myclass {
int i;
public:
myclass(int n);
~myclass();
void set_i(int n) { i=n; }
int get_i() { return i; }
};
myclass::myclass(int n)
{
i = n;
cout << "Constructing " << i << "\n";
}
myclass::~myclass()
{
cout << "Destroying " << i << "\n";
}
void f(myclass ob);

321

322

C++: The Complete Reference

int main()
{
myclass o(1);
f(o);
cout << "This is i in main: ";
cout << o
...
set_i(2);
cout << "This is local i: " << ob
...
As the output illustrates, the constructor function
is not called when the copy of o (in main( )) is passed to ob (within f( ))
...
When you pass an object to a function, you want the current state of that
object
...
Thus, the constructor function cannot be executed when
the copy of an object is generated in a function call
...
(The copy
is destroyed like any other local variable, when the function terminates
...
This means that
the copy could be performing operations that will require a destructor function to
be called when the copy is destroyed
...
For this reason, the destructor
function must be executed when the copy is destroyed
...
However, when the copy of the
object inside the function is destroyed, its destructor function is called
...
This means
that the new object is an exact duplicate of the original
...
Even though objects are passed to functions
by means of the normal call-by-value parameter passing mechanism, which, in theory,
protects and insulates the calling argument, it is still possible for a side effect to occur
that may affect, or even damage, the object used as an argument
...
This will leave the original object damaged and effectively useless
...


Returning Objects
A function may return an object to the caller
...

#include
using namespace std;
class myclass {
int i;
public:
void set_i(int n) { i=n; }
int get_i() { return i; }
};
myclass f();
int main()
{
myclass o;
o = f();

// return object of type myclass

323

324

C++: The Complete Reference

cout << o
...
set_i(1);
return x;
}

When an object is returned by a function, a temporary object is automatically
created that holds the return value
...
After the value has been returned, this object is destroyed
...
For
example, if the object returned by the function has a destructor that frees dynamically
allocated memory, that memory will be freed even though the object that is receiving
the return value is still using it
...


Object Assignment
Assuming that both objects are of the same type, you can assign one object to another
...
For example, this program displays 99:
// Assigning objects
...
set_i(99);
ob2 = ob1; // assign data from ob1 to ob2
cout << "This is ob2's i: " << ob2
...
However, it is possible to overload the assignment operator and define some
other assignment procedure (see Chapter 15)
...


C++

Chapter 13
Arrays, Pointers, References,
and the Dynamic Allocation
Operators

327

328

C++: The Complete Reference

n Part One, pointers and arrays were examined as they relate to C++'s built-in
types
...
This chapter also looks at a
feature related to the pointer called a reference
...


I

Arrays of Objects
In C++, it is possible to have arrays of objects
...
For example, this
program uses a three-element array of objects:
#include
using namespace std;
class cl {
int i;
public:
void set_i(int j) { i=j; }
int get_i() { return i; }
};
int main()
{
cl ob[3];
int i;
for(i=0; i<3; i++) ob[i]
...
get_i() << "\n";
return 0;
}

This program displays the numbers 1, 2, and 3 on the screen
...

However, the exact form of the initialization list will be decided by the number of
parameters required by the object's constructor function
...
As each element in the array is created, a

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

value from the list is passed to the constructor's parameter
...
get_i() << "\n";
return 0;
}

As before, this program displays the numbers 1, 2, and 3 on the screen
...
Of course, the short form used in the
program is more common
...
Thus, the short
form can only be used to initialize object arrays whose constructors only require one
argument
...
For example,
#include
using namespace std;

329

330

C++: The Complete Reference

class cl {
int h;
int i;
public:
cl(int j, int k) { h=j; i=k; } // constructor with 2 parameters
int get_i() {return i;}
int get_h() {return h;}
};
int main()
{
cl ob[3] = {
cl(1, 2), // initialize
cl(3, 4),
cl(5, 6)
};
int i;
for(i=0; i<3; i++) {
cout << ob[i]
...
get_i() << "\n";
}
return 0;
}

Here, cl's constructor has two parameters and, therefore, requires two arguments
...


Creating Initialized vs
...
Consider the following class
...
This implies that
any array declared of this type must be initialized
...
However,
as it stands, cl does not have a parameterless constructor
...
To
solve this problem, you need to overload the constructor function, adding one that
takes no parameters
...

class cl {
int i;
public:
cl() { i=0; } // called for non-initialized arrays
cl(int j) { i=j; } // called for initialized arrays
int get_i() { return i; }
};

Given this class, both of the following statements are permissible:
cl a1[3] = {3, 5, 6}; // initialized
cl a2[34]; // uninitialized

Pointers to Objects
Just as you can have pointers to other types of variables, you can have pointers to
objects
...
The next program illustrates how to access an
object given a pointer to it:
#include
using namespace std;

331

332

C++: The Complete Reference

class cl {
int i;
public:
cl(int j) { i=j; }
int get_i() { return i; }
};
int main()
{
cl ob(88), *p;
p = &ob; // get address of ob
cout << p->get_i(); // use -> to call get_i()
return 0;
}

As you know, when a pointer is incremented, it points to the next element of its
type
...
In general, all
pointer arithmetic is relative to the base type of the pointer
...
) The same is true of pointers to
objects
...
For example, this is a valid C++ program that
displays the number 1 on the screen:
#include
using namespace std;
class cl {
public:
int i;
cl(int j) { i=j; }
};
int main()
{
cl ob(1);
int *p;
p = &ob
...
i
cout << *p; // access ob
...
It is irrelevant
that i is a member of ob in this situation
...
For example, given:

333

334

C++: The Complete Reference

int *pi;
float *pf;

in C++, the following assignment is illegal:
pi = pf; // error -- type mismatch

Of course, you can override any type incompatibilities using a cast, but doing so
bypasses C++'s type-checking mechanism
...


The this Pointer
When a member function is called, it is automatically passed an implicit argument that
is a pointer to the invoking object (that is, the object on which the function is called)
...
To understand this, first consider a program that creates a
class called pwr that computes the result of a number raised to some power:
#include
using namespace std;
class pwr {
double b;
int e;
double val;
public:
pwr(double base, int exp);
double get_pwr() { return val; }
};
pwr::pwr(double base, int exp)
{
b = base;
e = exp;
val = 1;
if(exp==0) return;
for( ; exp>0; exp--) val = val * b;
}

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

int main()
{
pwr x(4
...
5, 1), z(5
...
get_pwr() << " ";
cout << y
...
get_pwr() << "\n";
return 0;
}

Within a member function, the members of a class can be accessed directly, without
any object or class qualification
...
However, the same statement can also be written like this:
this->b = base;

The this pointer points to the object that invoked pwr( )
...
For example, if pwr( ) had been invoked by x (as in x(4
...
Writing the statement
without using this is really just shorthand
...
However, the this pointer is very important
when operators are overloaded and whenever a member function must utilize a
pointer to the object that invoked it
...

Therefore, get_pwr( ) could also be rewritten as shown here:
double get_pwr() { return this->val; }

In this case, if get_pwr( ) is invoked like this:
y
...

Two final points about this
...
Second, static member functions do not have a
this pointer
...
However,
there is an important exception to this rule that relates only to derived classes
...
Further, assume that D is derived from the
base class B
...
More generally, a base class pointer can also be used as a pointer to an object of any
class derived from that base
...
A pointer of type D * may not point to an object of type B
...
That is, you won't be
able to access any members added by the derived class
...
)
Here is a short program that illustrates the use of a base pointer to access
derived objects
...
You can't access element of
a derived class using a base class pointer
...

Although you must be careful, it is possible to cast a base pointer into a pointer of
the derived type to access a member of the derived class through the base pointer
...
For this reason, when a base pointer is pointing to a derived object,
incrementing the pointer does not cause it to point to the next object of the derived
type
...
This, of

337

338

C++: The Complete Reference

course, usually spells trouble
...

// This program contains an error
...
set_i(1);
d[1]
...


Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

Pointers to Class Members
C++ allows you to generate a special type of pointer that "points" generically to a
member of a class, not to a specific instance of that member in an object
...
A pointer to
a member is not the same as a normal C++ pointer
...
Since member pointers are not true pointers, the
...
To access a member of a class given a pointer to it, you must use the special
pointer-to-member operators
...
Their job is to allow you to access a member of
a class given a pointer to that member
...
*data << " " << ob2
...
*func)() << " ";
cout << (ob2
...
Note
carefully the syntax of each declaration
...
The program also creates
objects of cl called ob1 and ob2
...
Next, the program obtains the addresses of val and
double_val( )
...
Next, to display the values
of each object's val, each is accessed through data
...
The extra parentheses are necessary in order to
correctly associate the
...

When you are accessing a member of an object by using an object or a reference
(discussed later in this chapter), you must use the
...
However, if you are
using a pointer to the object, you need to use the –>* operator, as illustrated in this
version of the preceding program:
#include
using namespace std;
class cl {
public:
cl(int i) { val=i; }
int val;
int double_val() { return val+val; }
};
int main()
{
int cl::*data; // data member pointer
int (cl::*func)(); // function member pointer
cl ob1(1), ob2(2); // create objects
cl *p1, *p2;
p1 = &ob1; // access objects through a pointer
p2 = &ob2;
data = &cl::val; // get offset of val
func = &cl::double_val; // get offset of double_val()
cout << "Here are values: ";
cout << p1->*data << " " << p2->*data << "\n";
cout << "Here they are doubled: ";

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

cout << (p1->*func)() << " ";
cout << (p2->*func)() << "\n";
return 0;
}

In this version, p1 and p2 are pointers to objects of type cl
...

Remember, pointers to members are different from pointers to specific instances of
elements of an object
...
val // this is address of a specific val
d = &cl::val // this is offset of generic val

Here, p is a pointer to an integer inside a specific object
...

In general, pointer-to-member operators are applied in special-case situations
...


References
C++ contains a feature that is related to the pointer called a reference
...
There are three ways that a reference can be used: as a
function parameter, as a function return value, or as a stand-alone reference
...


Reference Parameters
Probably the most important use for a reference is to allow you to create functions that
automatically use call-by-reference parameter passing
...
When using call-by-value, a copy of the argument is passed to the
function
...
By
default, C++ uses call-by-value, but it provides two ways to achieve call-by-reference
parameter passing
...
Second, you

341

342

C++: The Complete Reference

can use a reference parameter
...

To fully understand what a reference parameter is and why it is valuable, we will
begin by reviewing how a call-by-reference can be generated using a pointer
parameter
...

// Manually create a call-by-reference using a pointer
...
Therefore, neg( ) must be explicitly called with the address of x
...
This is
how you generate a "manual" call-by-reference in C++, and it is the only way to obtain
a call-by-reference using the C subset
...

To create a reference parameter, precede the parameter's name with an &
...
Any operations that are applied to i actually affect the calling
argument
...
Once i has been made into a reference, it is no
longer necessary (or even legal) to apply the * operator
...
Further, when calling neg( ), it is no longer necessary (or legal) to precede
the argument's name with the & operator
...
Here is the reference version of the preceding program:
// Use a reference parameter
...
Therefore, in the preceding program,
the statement
i = -i ;

actually operates on x, not on a copy of x
...
Also, inside the function, the reference parameter is used directly

343

344

C++: The Complete Reference

without the need to apply the * operator
...

Inside the function, it is not possible to change what the reference parameter is
pointing to
...
It does not cause i to
point to some new location
...
This program uses reference parameters to swap the
values of the variables it is called with
...

#include
using namespace std;
void swap(int &i, int &j);
int main()
{
int a, b, c, d;
a
b
c
d

=
=
=
=

1;
2;
3;
4;

cout << "a and b: " << a << " " << b << "\n";
swap(a, b); // no & operator needed
cout << "a and b: " << a << " " << b << "\n";
cout << "c and d: " << c << " " << d << "\n";
swap(c, d);
cout << "c and d: " << c << " " << d << "\n";
return 0;
}
void swap(int &i, int &j)
{
int t;

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

t = i; // no * operator needed
i = j;
j = t;
}

This program displays the following:
a
a
c
c

and
and
and
and

b:
b:
d:
d:

1
2
3
4

2
1
4
3

Passing References to Objects
In Chapter 12 it was explained that when an object is passed as an argument to a
function, a copy of that object is made
...
If for some reason you do not want the destructor function to be
called, simply pass the object by reference
...
) When you pass by reference, no copy of the object is made
...
For example, try this program:
#include
using namespace std;
class cl {
int id;
public:
int i;
cl(int i);
~cl();
void neg(cl &o) { o
...
i; } // no temporary created
};
cl::cl(int num)
{
cout << "Constructing " << num << "\n";
id = num;
}
cl::~cl()

345

346

C++: The Complete Reference

{
cout << "Destructing " << id << "\n";
}
int main()
{
cl o(1);
o
...
neg(o);
cout << o
...
Had o been passed
by value, a second object would have been created inside neg( ), and the destructor
would have been called a second time when that object was destroyed at the time
neg( ) terminated
...
The arrow operator is reserved for use with
pointers only
...

One other point: Passing all but the smallest objects by reference is faster than
passing them by value
...
Thus, large objects
take a considerable number of CPU cycles to push onto and pop from the stack
...
This has the rather startling effect of allowing a
function to be used on the left side of an assignment statement! For example, consider
this simple program:

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

#include
using namespace std;
char &replace(int i); // return a reference
char s[80] = "Hello There";
int main()
{
replace(5) = 'X'; // assign X to space after Hello
cout << s;
return 0;
}
char &replace(int i)
{
return s[i];
}

This program replaces the space between Hello and There with an X
...
Take a look at how this is accomplished
...
As replace( ) is coded, it returns a
reference to the element of s that is specified by its argument i
...

One thing to beware of when returning references is that the object being referred
to does not go out of scope after the function terminates
...
However, you can
declare a reference that is simply a variable
...

When you create an independent reference, all you are creating is another name for
an object variable
...
The reason for this is easy to understand
...
Therefore, it must be initialized when
it is declared
...
)
The following program illustrates an independent reference:

347

348

C++: The Complete Reference

#include
using namespace std;
int main()
{
int a;
int &ref = a; // independent reference
a = 10;
cout << a << " " << ref << "\n";
ref = 100;
cout << a << " " << ref << "\n";
int b = 19;
ref = b; // this puts b's value into a
cout << a << " " << ref << "\n";
ref--; // this decrements a
// it does not affect what ref refers to
cout << a << " " << ref << "\n";
return 0;
}

The program displays this output:
10 10
100 100
19 19
18 18

Actually, independent references are of little real value because each one is,
literally, just another name for another variable
...


References to Derived Types
Similar to the situation as described for pointers earlier, a base class reference can be
used to refer to an object of a derived class
...
A base class reference parameter can receive objects of
the base class as well as any other type derived from that base
...
You cannot reference
another reference
...
You
cannot create arrays of references
...
You
cannot reference a bit-field
...
Null references are prohibited
...
For
example, here are two functionally equivalent declarations:
int& p; // & associated with type
int &p; // & associated with variable

Associating the * or & with the type name reflects the desire of some programmers
for C++ to contain a separate pointer type
...
Thus, misleading
declarations are easily created
...

int* a, b;

Here, b is declared as an integer (not an integer pointer) because, as specified by the
C++ syntax, when used in a declaration, the * (or &) is linked to the individual variable
that it precedes, not to the type that it follows
...
This visual confusion not only misleads novice C++ programmers,
but occasionally old pros, too
...
Thus, if you prefer to associate the *
or & with the type rather than the variable, feel free to do so
...


C++'s Dynamic Allocation Operators
C++ provides two dynamic allocation operators: new and delete
...
Dynamic allocation is an important part

349

350

C++: The Complete Reference

of almost all real-world programs
...
These are included
for the sake of compatibility with C
...

The new operator allocates memory and returns a pointer to the start of it
...
The general forms of
new and delete are shown here:
p_var = new type;
delete p_var;
Here, p_var is a pointer variable that receives a pointer to memory that is large enough
to hold an item of type type
...
If there is insufficient available
memory to fill an allocation request, then new will fail and a bad_alloc exception will be
generated
...
Your program should handle
this exception and take appropriate action if a failure occurs
...
) If this exception is not handled by your program, then your
program will be terminated
...
The
trouble is that not all compilers, especially older ones, will have implemented new in
compliance with Standard C++
...
Later, this was changed such that new caused an exception on failure
...
Thus, new has been implemented
differently, at different times, by compiler manufacturers
...

Since Standard C++ specifies that new generates an exception on failure, this is the
way the code in this book is written
...

Here is a program that allocates memory to hold an integer:
#include
#include
using namespace std;
int main()
{
int *p;
try {

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

p = new int; // allocate space for an int
} catch (bad_alloc xa) {
cout << "Allocation Failure\n";
return 1;
}
*p = 100;
cout << "At " << p << " ";
cout << "is the value " << *p << "\n";
delete p;
return 0;
}

This program assigns to p an address in the heap that is large enough to hold an
integer
...
Finally, it frees the dynamically allocated memory
...

The delete operator must be used only with a valid pointer previously allocated by
using new
...

Although new and delete perform functions similar to malloc( ) and free( ), they
have several advantages
...
You do not need to use the sizeof operator
...
Second,
new automatically returns a pointer of the specified type
...
Finally, both
new and delete can be overloaded, allowing you to create customized allocation systems
...
There is no guarantee that they are
mutually compatible
...
Here is the general form of new when an
initialization is included:
p_var = new var_type (initializer);

351

352

C++: The Complete Reference

Of course, the type of the initializer must be compatible with the type of data for which
memory is being allocated
...

To free an array, use this form of delete:
delete [ ] p_var;
Here, the [ ] informs delete that an array is being released
...


Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

#include
#include
using namespace std;
int main()
{
int *p, i;
try {
p = new int [10]; // allocate 10 integer array
} catch (bad_alloc xa) {
cout << "Allocation Failure\n";
return 1;
}
for(i=0; i<10; i++ )
p[i] = i;
for(i=0; i<10; i++)
cout << p[i] << " ";
delete [] p; // release the array
return 0;
}

Notice the delete statement
...
(As
you will see in the next section, this is especially important when you are allocating
arrays of objects
...

That is, you may not specify an initializer when allocating arrays
...
When you do this, an object is
created and a pointer is returned to it
...
When it is created, its constructor function (if it has one) is called
...

Here is a short program that creates a class called balance that links a person's
name with his or her account balance
...


353

354

C++: The Complete Reference

#include
#include
#include
using namespace std;
class balance {
double cur_bal;
char name[80];
public:
void set(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};
int main()
{
balance *p;
char s[80];
double n;
try {
p = new balance;
} catch (bad_alloc xa) {
cout << "Allocation Failure\n";
return 1;
}
p->set(12387
...

As stated, dynamically allocated objects may have constructors and destructors
...
Examine this version of the
previous program:
#include
#include
#include
using namespace std;
class balance {
double cur_bal;
char name[80];
public:
balance(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
~balance() {
cout << "Destructing ";
cout << name << "\n";
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};
int main()
{
balance *p;
char s[80];
double n;
// this version uses an initializer
try {
p = new balance (12387
...

You can allocate arrays of objects, but there is one catch
...
If you don't, the C++ compiler will not find a
matching constructor when you attempt to allocate the array and will not compile your
program
...

#include
#include
#include
using namespace std;
class balance {
double cur_bal;
char name[80];
public:
balance(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
balance() {} // parameterless constructor
~balance() {
cout << "Destructing ";

Chapter 13:

Arrays, Pointers, References, and the Dynamic Allocation Operators

cout << name << "\n";
}
void set(double n, char *s) {
cur_bal = n;
strcpy(name, s);
}
void get_bal(double &n, char *s) {
n = cur_bal;
strcpy(s, name);
}
};
int main()
{
balance *p;
char s[80];
double n;
int i;
try {
p = new balance [3]; // allocate entire array
} catch (bad_alloc xa) {
cout << "Allocation Failure\n";
return 1;
}
// note use of dot, not arrow operators
p[0]
...
87, "Ralph Wilson");
p[1]
...
00, "A
...
Conners");
p[2]
...
23, "I
...
Overdrawn");
for(i=0; i<3; i++) {
p[i]
...

Ralph Wilson's balance is: 12387
...
C
...
M
...
23
Destructing I
...
Overdrawn
Destructing A
...
Conners
Destructing Ralph Wilson

One reason that you need to use the delete [ ] form when deleting an array of
dynamically allocated objects is so that the destructor function can be called for each
object in the array
...
This form of new is most useful when you
are compiling older code with a modern C++ compiler
...
(This is common when updating C code to C++
...
The nothrow form of new works like the
original version of new from years ago
...
However, for
new code, exceptions provide a better alternative
...

The following program shows how to use the new(nothrow) alternative
...

#include
#include
using namespace std;
int main()
{
int *p, i;
p = new(nothrow) int[32]; // use nothrow option
if(!p) {
cout << "Allocation failure
...


The Placement Forms of new and delete
There is a special form of new, called the placement form, that can be used to specify an
alternative method of allocating memory
...
There is a default implementation of the
placement new operator, which has this general form:
p_var = new (location) type;
Here, location specifies an address that is simply returned by new
...


359

This page intentionally left blank
...
Function overloading is one of the defining aspects of the C++
programming language
...
Some of the most commonly
overloaded functions are constructors
...
Closely related to function overloading
are default arguments
...


T

Function Overloading
Function overloading is the process of using the same name for two or more functions
...
It is only through
these differences that the compiler knows which function to call in any
given situation
...

#include
using namespace std;
int myfunc(int i); // these differ in types of parameters
double myfunc(double i);
int main()
{
cout << myfunc(10) << " "; // calls myfunc(int i)
cout << myfunc(5
...
Two functions differing
only in their return types cannot be overloaded
...


Sometimes, two function declarations will appear to differ, when in fact they do not
...

void f(int *p);
void f(int p[]); // error, *p is same as p[]

Remember, to the compiler *p is the same as p[ ]
...


363

364

C++: The Complete Reference

Overloading Constructor Functions
Constructor functions can be overloaded; in fact, overloaded constructors are very
common
...
In this section, the first two of these are
examined
...


Overloading a Constructor to Gain Flexibility
Many times you will create a class for which there are two or more possible ways to
construct an object
...
This is a self-enforcing rule because if you attempt to create an
object for which there is no matching constructor, a compile-time error results
...
The user is free to
choose the best way to construct an object given the specific circumstance
...
Notice that
the constructor is overloaded two ways:
#include
#include
using namespace std;
class date {
int day, month, year;
public:
date(char *d);
date(int m, int d, int y);
void show_date();
};
// Initialize using string
...

date::date(int m, int d, int y)
{

Chapter 14:

Function Overloading, Copy Constructors, and Default Arguments

day = d;
month = m;
year = y;
}
void date::show_date()
{
cout << month << "/" << day;
cout << "/" << year << "\n";
}
int main()
{
date ob1(12, 4, 2001), ob2("10/22/2001");
ob1
...
show_date();
return 0;
}

In this program, you can initialize an object of type date, either by specifying the
date using three integers to represent the month, day, and year, or by using a string
that contains the date in this general form:
mm/dd/yyyy
Since both are common ways to represent a date, it makes sense that date allow both
when constructing an object
...
For example, in the following main( ),
the user is prompted for the date, which is input to array s
...
There is no need for it to be converted to any other form
...

int main()
{
char s[80];

365

366

C++: The Complete Reference

cout << "Enter new date: ";
cin >> s;
date d(s);
d
...
For example, if the date is generated by some sort of
computational method, then creating a date object using date(int, int, int) is the most
natural and appropriate constructor to employ
...
This increased
flexibility and ease of use are especially important if you are creating class libraries that
will be used by other programmers
...
This is especially important if you want to be able to create dynamic arrays
of objects of some class, since it is not possible to initialize a dynamically allocated
array
...

For example, the following program declares two arrays of type powers; one is
initialized and the other is not
...

#include
#include
using namespace std;
class powers {
int x;
public:
// overload constructor two ways
powers() { x = 0; } // no initializer
powers(int n) { x = n; } // initializer
int getx() { return x; }

Chapter 14:

Function Overloading, Copy Constructors, and Default Arguments

void setx(int i) { x = i; }
};
int main()
{
powers ofTwo[] = {1, 2, 4, 8, 16}; // initialized
powers ofThree[5]; // uninitialized
powers *p;
int i;
// show powers of two
cout << "Powers of two: ";
for(i=0; i<5; i++) {
cout << ofTwo[i]
...
setx(1);
ofThree[1]
...
setx(9);
ofThree[3]
...
setx(81);
// show powers of three
cout << "Powers of three: ";
for(i=0; i<5; i++) {
cout << ofThree[i]
...
setx(ofTwo[i]
...
getx() << " ";
}
cout << "\n\n";
delete [] p;
return 0;
}

In this example, both constructors are necessary
...
The
parameterized constructor is called to create the objects for the ofTwo array
...

Defining a copy constructor can help you prevent problems that might occur when one
object is used to initialize another
...

By default, when one object is used to initialize another, C++ performs a bitwise copy
...

Although this is perfectly adequate for many cases—and generally exactly what you
want to happen—there are situations in which a bitwise copy should not be used
...
For
example, assume a class called MyClass that allocates memory for each object when it is
created, and an object A of that class
...
Further, assume that A is used to initialize B, as shown here:
MyClass B = A;
If a bitwise copy is performed, then B will be an exact copy of A
...
Clearly, this is not the desired outcome
...
Remember, temporary
objects are automatically created to hold the return value of a function and they may
also be created in certain other circumstances
...
When a copy
constructor exists, the default, bitwise copy is bypassed
...
It is permissible
for a copy constructor to have additional parameters as long as they have default
arguments defined for them
...

It is important to understand that C++ defines two distinct types of situations in
which the value of one object is given to another
...
The second is
initialization, which can occur any of three ways:
s When one object explicitly initializes another, such as in a declaration
s When a copy of an object is made to be passed to a function
s When a temporary object is generated (most commonly, as a return value)
The copy constructor applies only to initializations
...

myclass x = y; // y explicitly initializing x
func(y);
// y passed as a parameter
y = func();
// y receiving a temporary, return object

Following is an example where an explicit copy constructor function is needed
...
(Chapter 15 shows a better way to create a safe array
that uses overloaded operators
...


369

370

C++: The Complete Reference

/* This program creates a "safe" array class
...

*/
#include
#include
#include
using namespace std;
class array {
int *p;
int size;
public:
array(int sz) {
try {
p = new int[sz];
} catch (bad_alloc xa) {
cout << "Allocation Failure\n";
exit(EXIT_FAILURE);
}
size = sz;
}
~array() { delete [] p; }
// copy constructor
array(const array &a);
void put(int i, int j) {
if(i>=0 && i}
int get(int i) {
return p[i];
}
};
// Copy Constructor
array::array(const array &a) {
int i;
try {
p = new int[a
...
size; i++) p[i] = a
...
put(i, i);
for(i=9; i>=0; i--) cout << num
...
get(i);
return 0;
}

Let's look closely at what happens when num is used to initialize x in the statement
array x(num); // invokes copy constructor

The copy constructor is called, memory for the new array is allocated and stored in x
...
In this way, x and num have arrays
that contain the same values, but each array is separate and distinct
...
p
and x
...
) If the copy constructor had not
been created, the default bitwise initialization would have resulted in x and num
sharing the same memory for their arrays
...
p and x
...
)
Remember that the copy constructor is called only for initializations
...

array b(10);
b = a; // does not call copy constructor

371

372

C++: The Complete Reference

In this case, b = a performs the assignment operation
...
Therefore, in some cases, you may need to overload
the = operator as well as create a copy constructor to avoid certain types of problems
(see Chapter 15)
...
One reason to do
so is to assign the address of the function to a pointer and then call that function
through that pointer
...

However, for overloaded functions, the process requires a little more subtlety
...
However, if myfunc( ) is
overloaded, how does the compiler know which version's address to assign to p? The
answer is that it depends upon how p is declared
...
Both return int, but one takes a single
integer argument; the other requires two integer arguments
...
When fp is assigned the address of myfunc( ), C++ uses this information to
select the myfunc(int a) version of myfunc( )
...

In general, when you assign the address of an overloaded function to a function
pointer, it is the declaration of the pointer that determines which function's address is
obtained
...


The overload Anachronism
When C++ was created, the keyword overload was required to create an overloaded
function
...
Indeed, it is not even a
reserved word in Standard C++
...
Here is its general form:
overload func-name;
Here, func-name is the name of the function that you will be overloading
...
For example, this tells an
old-style compiler that you will be overloading a function called test( ):
overload test;

373

374

C++: The Complete Reference

Default Function Arguments
C++ allows a function to assign a parameter a default value when no argument
corresponding to that parameter is specified in a call to that function
...
For example,
this declares myfunc( ) as taking one double argument with a default value of 0
...
0)
{
//
...
234); // pass an explicit value
myfunc();
// let function use default

The first call passes the value 198
...
The second call automatically gives d the
default value zero
...
To handle the
widest variety of situations, quite frequently a function contains more parameters than
are required for its most common usage
...
For example, many of the C++ I/O functions make
use of default arguments for just this reason
...
The clrscr( ) function clears the screen
by outputting a series of linefeeds (not the most efficient way, but sufficient for this
example)
...
However, because some terminals can display more or less
than 25 lines (often depending upon what type of video mode is used), you can
override the default argument by specifying one explicitly
...
get();
clrscr(); // clears 25 lines
for(i=0; i<30; i++ ) cout << i << endl;
cin
...
However, it is still possible to
override the default and give size a different value when needed
...
To illustrate this usage, a function called iputs( ) is developed here
that automatically indents a string by a specified amount
...
Although there is nothing wrong with writing iputs( ) this
way, you can improve its usability by providing a default argument for the indent
parameter that tells iputs( ) to indent to the previously specified level
...
In this situation,
instead of having to supply the same indent argument over and over, you can give

375

376

C++: The Complete Reference

indent a default value that tells iputs( ) to indent to the level of the previous call
...
This value tells the function
to reuse the previous value
...
In the preceding example, the default
argument was specified in iputs( )'s prototype
...
Even though default arguments for the same function cannot
be redefined, you can specify different default arguments for each version of an
overloaded function
...
For example, it is incorrect to define iputs( ) like this:
// wrong!
void iputs(int indent = -1, char *str);

Once you begin to define parameters that take default values, you cannot specify a
nondefaulting parameter
...

You can also use default parameters in an object's constructor function
...
Its
constructor function defaults all dimensions to zero if no other arguments are supplied,
as shown here:
#include
using namespace std;
class cube {
int x, y, z;
public:
cube(int i=0, int j=0, int k=0) {
x=i;
y=j;
z=k;
}
int volume() {
return x*y*z;
}

377

378

C++: The Complete Reference

};
int main()
{
cube a(2,3,4), b;
cout << a
...
volume();
return 0;
}

There are two advantages to including default arguments, when appropriate, in a
constructor function
...
For example, if the parameters to cube( ) were
not given defaults, the second constructor shown here would be needed to handle the
declaration of b (which specified no arguments)
...


Default Arguments vs
...
The cube class's constructor just shown is one example
...
Imagine that you want to create two customized versions of the standard
strcat( ) function
...
The second version takes a third argument
that specifies the number of characters to concatenate
...
Thus, assuming that you call your customized functions mystrcat( ), they will
have the following prototypes:
void mystrcat(char *s1, char *s2, int len);
void mystrcat(char *s1, char *s2);

The first version would copy len characters from s2 to the end of s1
...


Chapter 14:

Function Overloading, Copy Constructors, and Default Arguments

While it would not be wrong to implement two versions of mystrcat( ) to create the
two versions that you desire, there is an easier way
...
The following
program demonstrates this
...

#include
#include
using namespace std;
void mystrcat(char *s1, char *s2, int len = -1);
int main()
{
char str1[80] = "This is a test";
char str2[80] = "0123456789";
mystrcat(str1, str2, 5); // concatenate 5 chars
cout << str1 << '\n';
strcpy(str1, "This is a test"); // reset str1
mystrcat(str1, str2); // concatenate entire string
cout << str1 << '\n';
return 0;
}
// A custom version of strcat()
...
However, if len is –1, as it will be when it is
allowed to default, mystrcat( ) concatenates the entire string pointed to by s2 onto s1
...
) By
using a default argument for len, it is possible to combine both operations into one
function
...


Using Default Arguments Correctly
Although default arguments can be a very powerful tool when used correctly, they can
also be misused
...
Toward
this end, all default arguments should reflect the way a function is generally used, or a
reasonable alternate usage
...
In fact, declaring
default arguments when there is insufficient basis for doing so destructures your code,
because they are liable to mislead and confuse anyone reading your program
...
That is, the
accidental use of a default argument should not cause a catastrophe
...
When this happens, the situation is said to be ambiguous
...

By far the main cause of ambiguity involves C++'s automatic type conversions
...
For example, consider this
fragment:
int myfunc(double d);
//
...
In C++, very few type conversions of this sort
are actually disallowed
...
For example, consider the following program:
#include
using namespace std;
float myfunc(float i);
double myfunc(double i);
int main()
{
cout << myfunc(10
...
In the unambiguous line, myfunc(double) is called because, unless
explicitly specified as float, all floating-point constants in C++ are automatically of
type double
...
However, when myfunc( ) is called by
using the integer 10, ambiguity is introduced because the compiler has no way of
knowing whether it should be converted to a float or to a double
...

As the preceding example illustrates, it is not the overloading of myfunc( ) relative
to double and float that causes the ambiguity
...
Put differently, the
error is not caused by the overloading of myfunc( ), but by the specific invocation
...
However, when
myfunc( ) is called by using the integer 88, the compiler does not know which function
to call
...
To see how, examine this program:
#include
using namespace std;
int myfunc(int i);
int myfunc(int i, int j=1);
int main()
{
cout << myfunc(4, 5) << " "; // unambiguous
cout << myfunc(10); // ambiguous

Chapter 14:

Function Overloading, Copy Constructors, and Default Arguments

return 0;
}
int myfunc(int i)
{
return i;
}
int myfunc(int i, int j)
{
return i*j;
}

Here, in the first call to myfunc( ), two arguments are specified; therefore, no
ambiguity is introduced and myfunc(int i, int j) is called
...

Some types of overloaded functions are simply inherently ambiguous even if, at
first, they may not seem so
...

// This program contains an error
...
In this situation, the compiler has no way of knowing which
version of the function is intended when it is called
...


C++

Chapter 15
Operator Overloading

385

386

C++: The Complete Reference

losely related to function overloading is operator overloading
...
For example, a class that maintains a stack might
overload + to perform a push operation and – – to perform a pop
...
Instead, the type of objects it can be
applied to is expanded
...
It allows
the full integration of new class types into the programming environment
...
Operator overloading also forms the
basis of C++'s approach to I/O
...
An operator function defines
the operations that the overloaded operator will perform relative to the
class upon which it will work
...
Operator functions can be either members or nonmembers of a class
...
The way operator functions are written differs between member and
nonmember functions
...


C

Creating a Member Operator Function
A member operator function takes this general form:
ret-type class-name::operator#(arg-list)
{
// operations
}
Often, operator functions return an object of the class they operate on, but ret-type can
be any valid type
...
When you create an operator function,
substitute the operator for the #
...
When you are overloading a unary operator, arg-list will be empty
...
(The reasons
for this seemingly unusual situation will be made clear in a moment
...
This program creates a class
called loc, which stores longitude and latitude values
...
Examine this program carefully, paying special attention to the
definition of operator+( ):

Chapter 15:

#include
using namespace std;
class loc {
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
loc operator+(loc op2);
};
// Overload + for loc
...
longitude = op2
...
latitude = op2
...
show(); // displays 10 20
ob2
...
show(); // displays 15 50
return 0;
}

Operator Overloading

387

388

C++: The Complete Reference

As you can see, operator+( ) has only one parameter even though it overloads the
binary + operator
...
) The reason that operator+( ) takes only one parameter
is that the operand on the left side of the + is passed implicitly to the function through
the this pointer
...
The fact that
the left operand is passed using this also implies one important point: When binary
operators are overloaded, it is the object on the left that generates the call to the
operator function
...
By doing so, it allows the operator to be used in larger
expressions
...

Further, having operator+( ) return an object of type loc makes possible the
following statement:
(ob1+ob2)
...

It is important to understand that an operator function can return any type and that
the type returned depends solely upon your specific application
...

One last point about the operator+( ) function: It does not modify either operand
...
(For example, 5+7 yields 12, but
neither 5 nor 7 is changed
...

The next program adds three additional overloaded operators to the loc class: the –,
the =, and the unary ++
...

#include
using namespace std;
class loc {
int longitude, latitude;

Chapter 15:

public:
loc() {} // needed to construct temporaries
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
loc
loc
loc
loc

operator+(loc op2);
operator-(loc op2);
operator=(loc op2);
operator++();

};
// Overload + for loc
...
longitude = op2
...
latitude = op2
...

loc loc::operator-(loc op2)
{
loc temp;
// notice order of operands
temp
...
longitude;
temp
...
latitude;
return temp;
}
// Overload asignment for loc
...
longitude;
latitude = op2
...
e
...

loc loc::operator++()
{
longitude++;
latitude++;
return *this;
}
int main()
{
loc ob1(10, 20), ob2( 5, 30), ob3(90, 90);
ob1
...
show();
++ob1;
ob1
...
show(); // displays 12 22
ob2
...
show(); // displays 90 90
ob2
...
Notice the order of the operands in the
subtraction
...
Because it is the object on
the left that generates the call to the operator–( ) function, op2's data must be

Chapter 15:

Operator Overloading

subtracted from the data pointed to by this
...

In C++, if the = is not overloaded, a default assignment operation is created
automatically for any class you define
...
By overloading the =, you can define explicitly
what the assignment does relative to a class
...
Notice that the operator=( ) function returns *this, which is the object that
generated the call
...
As you can see, it takes no parameters
...

Notice that both operator=( ) and operator++( ) alter the value of an operand
...
In the case of the ++, the operand is
incremented
...


Creating Prefix and Postfix Forms of the
Increment and Decrement Operators
In the preceding program, only the prefix form of the increment operator was
overloaded
...
To accomplish this, you must
define two versions of the operator++( ) function
...
The other is declared like this:
loc operator++(int x);

If the ++ precedes its operand, the operator++( ) function is called
...

The preceding example can be generalized
...

// Prefix increment
type operator++( ) {
// body of prefix operator
}

391

392

C++: The Complete Reference

// Postfix increment
type operator++(int x) {
// body of postfix operator
}
// Prefix decrement
type operator– –( ) {
// body of prefix operator
}
// Postfix decrement
type operator– –(int x) {
// body of postfix operator
}
You should be careful when working with older C++ programs where the increment
and decrement operators are concerned
...
The prefix
form was used for both
...
For
example, this function overloads += relative to loc:
loc loc::operator+=(loc op2)
{
longitude = op2
...
latitude + latitude;
return *this;
}

When overloading one of these operators, keep in mind that you are simply
combining an assignment with another type of operation
...
You cannot alter the
precedence of an operator
...
(You can choose to ignore an operand, however
...
Finally,
these operators cannot be overloaded:

...
For example, if you want to overload the + operator in such a way that it
writes I like C++ 10 times to a disk file, you can do so
...
When someone reading your program sees a statement
like Ob1+Ob2, he or she expects something resembling addition to be taking
place—not a disk access, for example
...
One
good example where decoupling is successful is found in the way C++ overloads the
<< and >> operators for I/O
...
In general, however, it is best to stay
within the context of the expected meaning of an operator when overloading it
...

However, a derived class is free to overload any operator (including those overloaded
by the base class) it chooses relative to itself
...
Since a friend function is not a member of the class, it does
not have a this pointer
...
This means that a friend function that overloads a binary operator
has two parameters, and a friend function that overloads a unary operator has one
parameter
...

In this program, the operator+( ) function is made into a friend:
#include
using namespace std;
class loc {
int longitude, latitude;
public:

393

394

C++: The Complete Reference

loc() {} // needed to construct temporaries
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
friend loc operator+(loc op1, loc op2); // now a friend
loc operator-(loc op2);
loc operator=(loc op2);
loc operator++();
};
// Now, + is overloaded using friend function
...
longitude = op1
...
longitude;
temp
...
latitude + op2
...

loc loc::operator-(loc op2)
{
loc temp;
// notice order of operands
temp
...
longitude;
temp
...
latitude;
return temp;
}
// Overload assignment for loc
...
longitude;
latitude = op2
...
e
...

loc loc::operator++()
{
longitude++;
latitude++;
return *this;
}
int main()
{
loc ob1(10, 20), ob2( 5, 30);
ob1 = ob1 + ob2;
ob1
...
First, you may
not overload the =, ( ), [ ], or –> operators by using a friend function
...


Using a Friend to Overload ++ or – –
If you want to use a friend function to overload the increment or decrement operators,
you must pass the operand as a reference parameter
...
Assuming that you stay true to the original meaning of the
++ and – – operators, these operations imply the modification of the operand they
operate upon
...
This means that a friend operator function
has no way to modify the operand
...
However, you can remedy this
situation by specifying the parameter to the friend operator function as a reference
parameter
...
For example, this program uses friend functions to
overload the prefix versions of ++ and – – operators relative to the loc class:
#include
using namespace std;
class loc {
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
loc operator=(loc op2);
friend loc operator++(loc &op);
friend loc operator--(loc &op);
};
// Overload assignment for loc
...
longitude;
latitude = op2
...
e
...

loc operator++(loc &op)
{

Chapter 15:

Operator Overloading

op
...
latitude++;
return op;
}
// Make op-- a friend; use reference
...
longitude--;
op
...
show();
++ob1;
ob1
...
show(); // displays 12 22
--ob2;
ob2
...
For example, this
shows the prototype for the friend, postfix version of the increment operator relative
to loc
...
In those cases, it is usually best to overload by
using member functions
...
Let's examine this
case now
...

Further, a pointer to that object is passed in the this pointer
...
Given an object of that class called Ob, the following expression
is valid:
Ob + 100 // valid
In this case, Ob generates the call to the overloaded + function, and the addition is
performed
...
Since an integer is a built-in type,
no operation between an integer and an object of Ob's type is defined
...
As you can imagine, in some applications,
having to always position the object on the left could be a significant burden and cause
of frustration
...
When this is done, both arguments are explicitly passed to the
operator function
...
Thus, when you overload
an operator by using two friend functions, the object may appear on either the left or
right side of the operator
...

loc operator+(loc op1, int op2)
{
loc temp;
temp
...
longitude + op2;
temp
...
latitude + op2;
return temp;
}
// + is overloaded for int + loc
...
longitude = op1 + op2
...
latitude = op1 + op2
...
show();

Operator Overloading

399

400

C++: The Complete Reference

ob2
...
show();
ob1 = ob2 + 10; // both of these
ob3 = 10 + ob2; // are valid
ob1
...
show();
return 0;
}

Overloading new and delete
It is possible to overload new and delete
...
For example, you may want allocation routines
that automatically begin using a disk file as virtual memory when the heap has
been exhausted
...

The skeletons for the functions that overload new and delete are shown here:
// Allocate an object
...
Throw bad_alloc on failure
...
*/
return pointer_to_memory;
}
// Delete an object
...

Destructor called automatically
...
(size_t is essentially an unsigned integer
...
This is the amount of memory that your version of new must allocate
...
Beyond these constraints, the

Chapter 15:

Operator Overloading

overloaded new function can do anything else you require
...

The delete function receives a pointer to the region of memory to be freed
...
When an object is
deleted, its destructor function is automatically called
...
They may also be overloaded relative to one or
more classes
...
For the sake of simplicity, no new allocation scheme will be used
...
(In your own application, you may, of course, implement any alternative
allocation scheme you like
...
For example, here the new and delete operators are
overloaded for the loc class:
#include
#include
#include
using namespace std;
class loc {
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
void *operator new(size_t size);
void operator delete(void *p);
};
// new overloaded relative to loc
...
\n";
p = malloc(size);
if(!p) {
bad_alloc ba;
throw ba;
}
return p;
}
// delete overloaded relative to loc
...
\n";
free(p);
}
int main()
{
loc *p1, *p2;
try {
p1 = new loc (10, 20);
} catch (bad_alloc xa) {
cout << "Allocation error for p1
...
\n";
return 1;;
}
p1->show();
p2->show();
delete p1;

Chapter 15:

Operator Overloading

delete p2;
return 0;
}

Output from this program is shown here
...

new
...

delete
...
The overloaded
operators are only applied to the types for which they are defined
...
When new and delete are overloaded globally, C++'s default
new and delete are ignored and the new operators are used for all allocation
requests
...
In other words, when new or delete are
encountered, the compiler first checks to see whether they are defined relative to the
class they are operating on
...
If not, C++ uses the
globally defined new and delete
...

To see an example of overloading new and delete globally, examine this program:
#include
#include
#include
using namespace std;
class loc {

403

404

C++: The Complete Reference

int longitude, latitude;
public:
loc() {}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
};
// Global new
void *operator new(size_t size)
{
void *p;
p = malloc(size);
if(!p) {
bad_alloc ba;
throw ba;
}
return p;
}
// Global delete
void operator delete(void *p)
{
free(p);
}
int main()
{
loc *p1, *p2;
float *f;
try {
p1 = new loc (10, 20);
} catch (bad_alloc xa) {

Chapter 15:

Operator Overloading

cout << "Allocation error for p1
...
\n";
return 1;;
}
try {
f = new float; // uses overloaded new, too
} catch (bad_alloc xa) {
cout << "Allocation error for f
...
10F;
cout << *f << "\n";
p1->show();
p2->show();
delete p1;
delete p2;
delete f;
return 0;
}

Run this program to prove to yourself that the built-in new and delete operators
have indeed been overloaded
...
To allocate and free arrays,
you must use these forms of new and delete
...

void *operator new[](size_t size)
{
/* Perform allocation
...

Constructor for each element called automatically
...

void operator delete[](void *p)
{
/* Free memory pointed to by p
...

*/
}

When allocating an array, the constructor function for each object in the array is
automatically called
...
You do not have to provide explicit code to accomplish these actions
...

#include
#include
#include
using namespace std;
class loc {
int longitude, latitude;
public:
loc() {longitude = latitude = 0;}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
void *operator new(size_t size);

Chapter 15:

void operator delete(void *p);
void *operator new[](size_t size);
void operator delete[](void *p);
};
// new overloaded relative to loc
...
\n";
p = malloc(size);
if(!p) {
bad_alloc ba;
throw ba;
}
return p;
}
// delete overloaded relative to loc
...
\n";
free(p);
}
// new overloaded for loc arrays
...
\n";
p = malloc(size);
if(!p) {
bad_alloc ba;
throw ba;
}
return p;
}

Operator Overloading

407

408

C++: The Complete Reference

// delete overloaded for loc arrays
...
\n";
return 1;;
}
try {
p2 = new loc [10]; // allocate an array
} catch (bad_alloc xa) {
cout << "Allocation error for p2
...
show();
delete p1; // free an object
delete [] p2; // free an array
return 0;
}

Overloading the nothrow Version of new and delete
You can also create overloaded nothrow versions of new and delete
...


Chapter 15:

Operator Overloading

// Nothrow version of new
...

if(success) return pointer_to_memory;
else return 0;
}
// Nothrow version of new for arrays
...

if(success) return pointer_to_memory;
else return 0;
}
void operator delete(void *p, const nothrow_t &n)
{
// free memory
}
void operator delete[](void *p, const nothrow_t &n)
{
// free memory
}

The type nothrow_t is defined in
...
The
nothrow_t parameter is unused
...
The operators that perform these functions are the [ ], ( ), and –>,
respectively
...

One important restriction applies to overloading these three operators: They must
be nonstatic member functions
...


Overloading [ ]
In C++, the [ ] is considered a binary operator when you are overloading it
...

}
Technically, the parameter does not have to be of type int, but an operator[ ]( ) function
is typically used to provide array subscripting, and as such, an integer value is
generally used
...
operator[](3)

That is, the value of the expression within the subscripting operators is passed to the
operator[ ]( ) function in its explicit parameter
...

In the following program, atype declares an array of three integers
...
The overloaded
operator[ ]( ) function returns the value of the array as indexed by the value of its
parameter
...
To do this, simply specify the
return value of operator[ ]( ) as a reference
...
(Of
course, it may still be used on the right side as well
...
As you know, in C++, it is possible to
overrun (or underrun) an array boundary at run time without generating a run-time
error message
...
For example, this program adds a range check to the
preceding program and proves that it works:
// A safe array example
...

int &atype::operator[](int i)
{
if(i<0 || i> 2) {
cout << "Boundary Error\n";
exit(1);
}
return a[i];
}
int main()
{
atype ob(1, 2, 3);

Chapter 15:

Operator Overloading

cout << ob[1]; // displays 2
cout << " ";
ob[1] = 25; // [] appears on left
cout << ob[1]; // displays 25
ob[3] = 44; // generates runtime error, 3 out-of-range
return 0;
}

In this program, when the statement
ob[3] = 44;

executes, the boundary error is intercepted by operator[]( ), and the program is
terminated before any damage can be done
...
)

Overloading ( )
When you overload the ( ) function call operator, you are not, per se, creating a new
way to call a function
...
Let's begin with an example
...
34, "hi");

translates into this call to the operator( ) function
...
operator()(10, 23
...
When you use the ( ) operator in your program, the

413

414

C++: The Complete Reference

arguments you specify are copied to those parameters
...

Here is an example of overloading ( ) for the loc class
...

#include
using namespace std;
class loc {
int longitude, latitude;
public:
loc() {}
loc(int lg, int lt) {
longitude = lg;
latitude = lt;
}
void show() {
cout << longitude << " ";
cout << latitude << "\n";
}
loc operator+(loc op2);
loc operator()(int i, int j);
};
// Overload ( ) for loc
...

loc loc::operator+(loc op2)
{
loc temp;
temp
...
longitude + longitude;
temp
...
latitude + latitude;

Chapter 15:

Operator Overloading

return temp;
}
int main()
{
loc ob1(10, 20), ob2(1, 1);
ob1
...
show();
ob1 = ob2 + ob1(10, 10); // can be used in expressions
ob1
...

10 20
7 8
11 11

Remember, when overloading ( ), you can use any type of parameters and return
any type of value
...
You
can also specify default arguments
...
Its general usage is shown here:
object->element;
Here, object is the object that activates the call
...
The element must be
some member accessible within the object
...
i and ob–>i when operator–>( ) returns the this pointer:

415

416

C++: The Complete Reference

#include
using namespace std;
class myclass {
public:
int i;
myclass *operator->() {return this;}
};
int main()
{
myclass ob;
ob->i = 10; // same as ob
...
i << " " << ob->i;
return 0;
}

An operator–>( ) function must be a member of the class upon which it works
...
The comma is a binary operator, and like all
overloaded operators, you can make an overloaded comma perform any operation you
want
...
The rightmost value becomes the result of the comma operation
...

Here is a program that illustrates the effect of overloading the comma operator
...
longitude = op2
...
latitude = op2
...
longitude << " " << op2
...
longitude = op2
...
latitude = op2
...
show();
ob2
...
show();

417

418

C++: The Complete Reference

cout << "\n";
ob1 = (ob1, ob2+ob2, ob3);
ob1
...

Remember, the left-hand operand is passed via this, and its value is discarded by
the operator,( ) function
...
This causes the overloaded comma to behave similarly to its default
operation
...


C++

Chapter 16
Inheritance

419

420

C++: The Complete Reference

nheritance is one of the cornerstones of OOP because it allows the creation of
hierarchical classifications
...
This class may then be inherited by
other, more specific classes, each adding only those things that are unique to the
inheriting class
...
The class that does the inheriting is called the derived class
...
In this way, multiple
inheritance is achieved
...
Inheritance was introduced in
Chapter 11
...


I

Base-Class Access Control
When a class inherits another, the members of the base class become members of the
derived class
...
The base-class access specifier must be either public, private, or protected
...
If the derived class is a struct, then public is the default in the absence of an
explicit access specifier
...
(The protected specifier is examined in the next section
...
In all cases, the base's private elements
remain private to the base and are not accessible by members of the derived class
...
set(1, 2); // access member of base
ob
...
showk(); // uses member of derived class
return 0;
}

When the base class is inherited by using the private access specifier, all public and
protected members of the base class become private members of the derived class
...

#include
using namespace std;
class base {
int i, j;
public:
void set(int a, int b) { i=a; j=b; }
void show() { cout << i << " " << j << "\n";}
};
// Public elements of base are private in derived
...
set(1, 2); // error, can't access set()
ob
...
This means that they are still
accessible by members of the derived class but cannot be accessed by parts of your
program that are not members of either the base or derived class
...
When a member of a class is declared as protected, that
member is not accessible by other, nonmember elements of the program
...
The sole exception to
this is when a protected member is inherited
...

As explained in the preceding section, a private member of a base class is not
accessible by other parts of your program, including any derived class
...
If the base class is inherited as public, then the
base class' protected members become protected members of the derived class and are,
therefore, accessible by the derived class
...
Here is an example:
#include
using namespace std;
class base {

Chapter 16:

Inheritance

protected:
int i, j; // private to base, but accessible by derived
public:
void set(int a, int b) { i=a; j=b; }
void show() { cout << i << " " << j << "\n"; }
};
class derived : public base {
int k;
public:
// derived may access base's i and j
void setk() { k=i*j; }
void showk() { cout << k << "\n"; }
};
int main()
{
derived ob;
ob
...
show(); // OK, known to derived
ob
...
showk();
return 0;
}

In this example, because base is inherited by derived as public and because i and j
are declared as protected, derived's function setk( ) may access them
...

When a derived class is used as a base class for another derived class, any protected
member of the initial base class that is inherited (as public) by the first derived class
may also be inherited as protected again by a second derived class
...

#include
using namespace std;
class base {

423

424

C++: The Complete Reference

protected:
int i, j;
public:
void set(int a, int b) { i=a; j=b; }
void show() { cout << i << " " << j << "\n"; }
};
// i and j inherited as protected
...

class derived2 : public derived1 {
int m;
public:
void setm() { m = i-j; } // legal
void showm() { cout << m << "\n"; }
};
int main()
{
derived1 ob1;
derived2 ob2;
ob1
...
show();
ob1
...
showk();
ob2
...
show();
ob2
...
setm();
ob2
...
showm();
return 0;
}

Chapter 16:

Inheritance

If, however, base were inherited as private, then all members of base would
become private members of derived1, which means that they would not be accessible
by derived2
...
) This situation is
illustrated by the following program, which is in error (and won't compile)
...

#include
using namespace std;
class base {
protected:
int i, j;
public:
void set(int a, int b) { i=a; j=b; }
void show() { cout << i << " " << j << "\n"; }
};
// Now, all elements of base are private in derived1
...

class derived2 : public derived1 {
int m;
public:
// illegal because i and j are private to derived1
void setm() { m = i-j; } // Error
void showm() { cout << m << "\n"; }
};
int main()
{
derived1 ob1;
derived2 ob2;
ob1
...
show(); // error, can't use show()
ob2
...
show(); // error, can't use show()
return 0;
}

Even though base is inherited as private by derived1, derived1 still has access to
base's public and protected elements
...


Note

Protected Base-Class Inheritance
It is possible to inherit a base class as protected
...

For example,
#include
using namespace std;
class base {
protected:
int i, j; // private to base, but accessible by derived
public:
void setij(int a, int b) { i=a; j=b; }
void showij() { cout << i << " " << j << "\n"; }
};
// Inherit base as protected
...

void setk() { setij(10, 12); k = i*j; }
// may access showij() here
void showall() { cout << k << " "; showij(); }
};
int main()

Chapter 16:

Inheritance

{
derived ob;
//
//

ob
...
setk(); // OK, public member of derived
ob
...
showij(); // illegal, showij() is protected
//
member of derived
return 0;
}

As you can see by reading the comments, even though setij( ) and showij( ) are
public members of base, they become protected members of derived when it is
inherited using the protected access specifier
...


Inheriting Multiple Base Classes
It is possible for a derived class to inherit two or more base classes
...

// An example of multiple base classes
...

class derived: public base1, public base2 {
public:
void set(int i, int j) { x=i; y=j; }
};
int main()
{
derived ob;
ob
...
showx(); // from base1
ob
...
Further, be sure to use an access-specifier for each base
inherited
...
First, when are base-class and derived-class constructor and
destructor functions called? Second, how can parameters be passed to base-class
constructor functions? This section examines these two important topics
...
It is important to understand the order in which these functions
are executed when an object of a derived class comes into existence and when it goes
out of existence
...
When executed, this program
displays
Constructing base
Constructing derived
Destructing derived
Destructing base

As you can see, first base's constructor is executed followed by derived's
...

The results of the foregoing experiment can be generalized
...
When a derived object is destroyed, its
destructor is called first, followed by the base class' destructor, if it exists
...
Destructor
functions are executed in reverse order of derivation
...
Because a base class has no knowledge of any derived class, any

429

430

C++: The Complete Reference

initialization it needs to perform is separate from and possibly prerequisite to any
initialization performed by the derived class
...

Likewise, it is quite sensible that destructors be executed in reverse order of
derivation
...
Therefore, the derived
destructor must be called before the object is fully destroyed
...
For example, this program
#include
using namespace std;
class base {
public:
base() { cout << "Constructing base\n"; }
~base() { cout << "Destructing base\n"; }
};
class derived1 : public base {
public:
derived1() { cout << "Constructing derived1\n"; }
~derived1() { cout << "Destructing derived1\n"; }
};
class derived2: public derived1 {
public:
derived2() { cout << "Constructing derived2\n"; }
~derived2() { cout << "Destructing derived2\n"; }
};
int main()
{
derived2 ob;
// construct and destruct ob
return 0;
}

Chapter 16:

Inheritance

displays this output:
Constructing base
Constructing derived1
Constructing derived2
Destructing derived2
Destructing derived1
Destructing base

The same general rule applies in situations involving multiple base classes
...
Destructors are called in reverse order, right to left
...
In cases where only the derived class' constructor requires one or
more parameters, you simply use the standard parameterized constructor syntax (see
Chapter 12)
...
The general form
of this expanded derived-class constructor declaration is shown here:
derived-constructor(arg-list) : base1(arg-list),
base2(arg-list),
//
...
Notice that a colon separates the derived class' constructor declaration from the
base-class specifications, and that the base-class specifications are separated from each
other by commas, in the case of multiple base classes
...

derived(int x, int y): base(y)
{ j=x; cout << "Constructing derived\n"; }
~derived() { cout << "Destructing derived\n"; }
void show() { cout << i << " " << j << "\n"; }
};
int main()
{
derived ob(3, 4);
ob
...

However, derived( ) uses only x; y is passed along to base( )
...
As the example illustrates, any parameters required by the
base class are passed to it in the base class' argument list specified after the colon
...
show(); // displays 4 3 5
return 0;
}

It is important to understand that arguments to a base-class constructor are passed
via arguments to the derived class' constructor
...
In this situation, the arguments passed to the derived class are simply
passed along to the base
...

*/
derived(int x, int y): base1(x), base2(y)
{ cout << "Constructing derived\n"; }
~derived() { cout << "Destructing derived\n"; }
void show() { cout << i << " " << k << "\n"; }
};
int main()
{
derived ob(3, 4);
ob
...
Put
differently, passing an argument along to a base class does not preclude its use by the
derived class as well
...

derived(int x, int y): base(x, y)
{ j = x*y; cout << "Constructing derived\n"; }

One final point to keep in mind when passing arguments to base-class constructors:
The argument can consist of any expression valid at the time
...
This is in keeping with the fact that C++ allows dynamic
initialization
...
However, in certain circumstances, you
may want to restore one or more inherited members to their original access
specification
...
In Standard C++, you have two ways to accomplish this
...
The using statement is designed
primarily to support namespaces and is discussed in Chapter 23
...
Access declarations are currently supported by Standard C++,
but they are deprecated
...
Since
there are still many, many existing programs that use access declarations, they will be
examined here
...
Notice that no type declaration is required (or, indeed, allowed) in an
access declaration
...

class derived: private base {
public:
// here is access declaration
base::j; // make j public again

...


...
However, by including
base::j;

as the access declaration under derived's public heading, j is restored to its public status
...
However, you cannot use an access declaration to raise or lower a
member's access status
...
(If C++ allowed this to occur, it would
destroy its encapsulation mechanism!)
The following program illustrates the access declaration; notice how it uses access
declarations to restore j, seti( ), and geti( ) to public status
...

class derived: private base {
public:
/* The next three statements override
base's inheritance as private and restore j,
seti(), and geti() to public access
...
i = 10; // illegal because i is private in derived
ob
...
k = 30; // illegal because k is private in derived
ob
...
seti(10);
cout << ob
...
j << " " << ob
...


Chapter 16:

Remember

Inheritance

While Standard C++ still supports access declarations, they are deprecated
...

Instead, the standard suggests achieving the same effect by applying the using
keyword
...
For example, consider this incorrect program:
// This program contains an error and will not compile
...

class derived1 : public base {
public:
int j;
};
// derived2 inherits base
...

This means that there are two copies of base
in derived3! */
class derived3 : public derived1, public derived2 {
public:
int sum;
};
int main()
{

439

440

C++: The Complete Reference

derived3 ob;
ob
...
j = 20;
ob
...
sum = ob
...
j + ob
...
i << " ";
cout << ob
...
k << " ";
cout << ob
...

However, derived3 inherits both derived1 and derived2
...
Therefore, in an expression like
ob
...
is! As you can see, the
statement is inherently ambiguous
...
The first is to apply the
scope resolution operator to i and manually select one i
...

#include
using namespace std;
class base {
public:
int i;
};
// derived1 inherits base
...

class derived2 : public base {
public:
int k;
};
/* derived3 inherits both derived1 and derived2
...
derived1::i = 10; // scope resolved, use derived1's i
ob
...
k = 30;
// scope resolved
ob
...
derived1::i + ob
...
k;
// also resolved here
cout << ob
...
j << " " << ob
...
sum;
return 0;
}

As you can see, because the :: was applied, the program has manually selected
derived1's version of base
...
This
solution is achieved using virtual base classes
...
You accomplish this
by preceding the base class' name with the keyword virtual when it is inherited
...

#include
using namespace std;
class base {
public:
int i;
};
// derived1 inherits base as virtual
...

class derived2 : virtual public base {
public:
int k;
};
/* derived3 inherits both derived1 and derived2
...
*/
class derived3 : public derived1, public derived2 {
public:
int sum;
};
int main()
{
derived3 ob;
ob
...
j = 20;
ob
...
sum = ob
...
j + ob
...
i << " ";
cout << ob
...
k << " ";
cout << ob
...
Now that both derived1 and derived2 have inherited base as virtual, any
multiple inheritance involving them will cause only one copy of base to be present
...
i = 10 is perfectly valid
and unambiguous
...
For example, the
following sequence is perfectly valid:
// define a class of type derived1
derived1 myclass;
myclass
...
If virtual base classes are used, then only
one base class is present in the object
...


443

This page intentionally left blank
...
As
discussed in earlier chapters, compile-time polymorphism is achieved by
overloading functions and operators
...


P

Virtual Functions
A virtual function is a member function that is declared within a base class and
redefined by a derived class
...
When a class containing a
virtual function is inherited, the derived class redefines the virtual function to fit its
own needs
...
The virtual function within the
base class defines the form of the interface to that function
...
That is, the redefinition creates a specific method
...
However, what makes virtual functions important and capable of
supporting run-time polymorphism is how they behave when accessed via a pointer
...
When a base pointer points to a derived object that
contains a virtual function, C++ determines which version of that function to call based
upon the type of object pointed to by the pointer
...
Thus, when different objects are pointed to, different versions of the virtual
function are executed
...

To begin, examine this short example:
#include
using namespace std;
class base {
public:
virtual void vfunc() {
cout << "This is base's vfunc()
...
\n";
}

Chapter 17:

Virtual Functions and Polymorphism

};
class derived2 : public base {
public:
void vfunc() {
cout << "This is derived2's vfunc()
...

This is derived1's vfunc()
...


As the program illustrates, inside base, the virtual function vfunc( ) is declared
...
When
vfunc( ) is redefined by derived1 and derived2, the keyword virtual is not needed
...
)

447

448

C++: The Complete Reference

In this program, base is inherited by both derived1 and derived2
...
Inside main( ), four
variables are declared:

Name

Type

p

base class pointer

b

object of base

d1

object of derived1

d2

object of derived2

Next, p is assigned the address of b, and vfunc( ) is called via p
...
Next, p is set to the
address of d1, and again vfunc( ) is called by using p
...
This causes derived1::vfunc( ) to be executed
...
The key point here is that the kind of object to which p points
determines which version of vfunc( ) is executed
...

Although you can call a virtual function in the "normal" manner by using an
object's name and the dot operator, it is only when access is through a base-class
pointer (or reference) that run-time polymorphism is achieved
...
vfunc(); // calls derived2's vfunc()

Although calling a virtual function in this manner is not wrong, it simply does not take
advantage of the virtual nature of vfunc( )
...
However, this is not the case, and the term overloading is not
applied to virtual function redefinition because several differences exist
...
This differs from overloading a normal function,
in which return types and the number and type of parameters may differ
...
) However, when a virtual function is redefined, all aspects of its prototype
must be the same
...
Another important restriction is that virtual functions must be

Chapter 17:

Virtual Functions and Polymorphism

nonstatic members of the classes of which they are part
...
Finally,
constructor functions cannot be virtual, but destructor functions can
...


Calling a Virtual Function Through a
Base Class Reference
In the preceding example, a virtual function was called through a base-class pointer,
but the polymorphic nature of a virtual function is also available when called through a
base-class reference
...

Thus, a base-class reference can be used to refer to an object of the base class or any
object derived from that base
...

The most common situation in which a virtual function is invoked through a base
class reference is when the reference is a function parameter
...

/* Here, a base class reference is used to access
a virtual function
...
\n";
}
};
class derived1 : public base {
public:
void vfunc() {
cout << "This is derived1's vfunc()
...
\n";
}
};
// Use a base class reference parameter
...
vfunc();
}
int main()
{
base b;
derived1 d1;
derived2 d2;
f(b); // pass a base object to f()
f(d1); // pass a derived1 object to f()
f(d2); // pass a derived2 object to f()
return 0;
}

This program produces the same output as its preceding version
...
Inside main( ), the function is
called using objects of type base, derived1, and derived2
...

For the sake of simplicity, the rest of the examples in this chapter will call virtual
functions through base-class pointers, but the effects are same for base-class references
...
This means that
when a derived class that has inherited a virtual function is itself used as a base class
for another derived class, the virtual function can still be overridden
...
For
example, consider this program:
#include
using namespace std;

Chapter 17:

Virtual Functions and Polymorphism

class base {
public:
virtual void vfunc() {
cout << "This is base's vfunc()
...
\n";
}
};
/* derived2 inherits virtual function vfunc()
from derived1
...
\n";
}
};
int main()
{
base *p, b;
derived1 d1;
derived2 d2;
// point to base
p = &b;
p->vfunc(); // access base's vfunc()
// point to derived1
p = &d1;
p->vfunc(); // access derived1's vfunc()
// point to derived2
p = &d2;
p->vfunc(); // access derived2's vfunc()

451

452

C++: The Complete Reference

return 0;
}

As expected, the preceding program displays this output:
This is base's vfunc()
...

This is derived2's vfunc()
...


Virtual Functions Are Hierarchical
As explained, when a function is declared as virtual by a base class, it may be
overridden by a derived class
...

When a derived class fails to override a virtual function, then when an object of that
derived class accesses that function, the function defined by the base class is used
...
\n";
}
};
class derived1 : public base {
public:
void vfunc() {
cout << "This is derived1's vfunc()
...

This is derived1's vfunc()
...


Because derived2 does not override vfunc( ), the function defined by base is used
when vfunc( ) is referenced relative to objects of type derived2
...
Because
inheritance is hierarchical in C++, it makes sense that virtual functions are also
hierarchical
...
For example, in the
following program, derived2 is derived from derived1, which is derived from base
...
This means that, relative to derived2,

453

454

C++: The Complete Reference

the closest version of vfunc( ) is in derived1
...

#include
using namespace std;
class base {
public:
virtual void vfunc() {
cout << "This is base's vfunc()
...
\n";
}
};
class derived2 : public derived1 {
public:
/* vfunc() not overridden by derived2
...

*/
};
int main()
{
base *p, b;
derived1 d1;
derived2 d2;
// point to base
p = &b;
p->vfunc(); // access base's vfunc()
// point to derived1
p = &d1;
p->vfunc(); // access derived1's vfunc()

Chapter 17:

Virtual Functions and Polymorphism

// point to derived2
p = &d2;
p->vfunc(); // use derived1's vfunc()
return 0;
}

The program displays the following:
This is base's vfunc()
...

This is derived1's vfunc()
...

However, in many situations there can be no meaningful definition of a virtual
function within a base class
...
Further, in some
situations you will want to ensure that all derived classes override a virtual function
...

A pure virtual function is a virtual function that has no definition within the base
class
...
If the derived class fails to override the pure virtual function, a compile-time
error will result
...
The
base class, number, contains an integer called val, the function setval( ), and the pure
virtual function show( )
...

#include
using namespace std;
class number {

455

456

C++: The Complete Reference

protected:
int val;
public:
void setval(int i) { val = i; }
// show() is a pure virtual function
virtual void show() = 0;
};
class hextype : public number {
public:
void show() {
cout << hex << val << "\n";
}
};
class dectype : public number {
public:
void show() {
cout << val << "\n";
}
};
class octtype : public number {
public:
void show() {
cout << oct << val << "\n";
}
};
int main()
{
dectype d;
hextype h;
octtype o;
d
...
show(); // displays 20 - decimal
h
...
show(); // displays 14 - hexadecimal

Chapter 17:

Virtual Functions and Polymorphism

o
...
show(); // displays 24 - octal
return 0;
}

Although this example is quite simple, it illustrates how a base class may not be
able to meaningfully define a virtual function
...
There is no reason to define show( )
inside number since the base of the number is undefined
...
However, making show( ) pure
also ensures that all derived classes will indeed redefine it to meet their own needs
...
If a derived class fails to do this, a compile-time error will result
...
Because an
abstract class contains one or more functions for which there is no definition (that is, a
pure virtual function), no objects of an abstract class may be created
...

Although you cannot create objects of an abstract class, you can create pointers and
references to an abstract class
...


Using Virtual Functions
One of the central aspects of object-oriented programming is the principle of "one
interface, multiple methods
...
In concrete C++ terms, a base class can be used to define the nature of the
interface to a general class
...

One of the most powerful and flexible ways to implement the "one interface,
multiple methods" approach is to use virtual functions, abstract classes, and run-time
polymorphism
...
Following this philosophy, you define all common
features and interfaces in a base class
...
In essence, in the base

457

458

C++: The Complete Reference

class you create and define everything you can that relates to the general case
...

Following is a simple example that illustrates the value of the "one interface,
multiple methods" philosophy
...
(For example, liters to gallons
...
It also defines the functions getinit( ) and getconv( ), which return
the initial value and the converted value
...
However, the function that
will actually perform the conversion, compute( ), is a pure virtual function that must be
defined by the classes derived from convert
...

// Virtual function practical example
...

class l_to_g : public convert {
public:
l_to_g(double i) : convert(i) { }
void compute() {
val2 = val1 / 3
...
8;
}
};
int main()
{
convert *p;

// pointer to base class

l_to_g lgob(4);
f_to_c fcob(70);
// use virtual function mechanism to convert
p = &lgob;
cout << p->getinit() << " liters is ";
p->compute();
cout << p->getconv() << " gallons\n"; // l_to_g
p = &fcob;
cout << p->getinit() << " in Fahrenheit is ";
p->compute();
cout << p->getconv() << " Celsius\n"; // f_to_c
return 0;
}

The preceding program creates two derived classes from convert, called l_to_g and
f_to_c
...
Each derived class overrides compute( ) in its own way to
perform the desired conversion
...

One of the benefits of derived classes and virtual functions is that handling a new
case is a very easy matter
...
28;
}
};

An important use of abstract classes and virtual functions is in class libraries
...

Another programmer will inherit your general class, which defines the interface and
all elements common to all classes derived from it, and will add those functions
specific to the derived class
...

One final point: The base class convert is an example of an abstract class
...
The class convert simply does not contain sufficient
information for compute( ) to be defined
...


Early vs
...

Early binding refers to events that occur at compile time
...
(Put
differently, early binding means that an object and a function call are bound during
compilation
...
The
main advantage to early binding is efficiency
...

The opposite of early binding is late binding
...
Virtual functions are used to
achieve late binding
...
Because in most cases this cannot be determined at compile time, the object
and the function are not linked until run time
...
Unlike early binding, late binding allows you to create programs that can
respond to events occurring while the program executes without having to create a
large amount of "contingency code
...


C++

Chapter 18
Templates

461

462

C++: The Complete Reference

he template is one of C++'s most sophisticated and high-powered features
...
Using templates, it
is possible to create generic functions and classes
...

Thus, you can use one function or class with several different types of data without
having to explicitly recode specific versions for each data type
...


T

Generic Functions
A generic function defines a general set of operations that will be applied to various
types of data
...
Through a generic function, a single general procedure can be applied to a
wide range of data
...
For example, the Quicksort sorting
algorithm is the same whether it is applied to an array of integers or an array of floats
...
By creating a generic function,
you can define the nature of the algorithm, independent of any data
...
In essence, when you create a
generic function you are creating a function that can automatically overload itself
...
The normal meaning of
the word "template" accurately reflects its use in C++
...
The general form of a template function definition is shown here:
template ret-type func-name(parameter list)
{
// body of function
}
Here, Ttype is a placeholder name for a data type used by the function
...
However, it is only a placeholder that the
compiler will automatically replace with an actual data type when it creates a specific
version of the function
...

The following example creates a generic function that swaps the values of the two
variables with which it is called
...


Chapter 18:

Templates

// Function template example
...

template void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}
int main()
{
int i=10, j=20;
double x=10
...
3;
char a='x', b='z';
cout << "Original i, j: " << i << ' ' << j << '\n';
cout << "Original x, y: " << x << ' ' << y << '\n';
cout << "Original a, b: " << a << ' ' << b << '\n';
swapargs(i, j); // swap integers
swapargs(x, y); // swap floats
swapargs(a, b); // swap chars
cout << "Swapped i, j: " << i << ' ' << j << '\n';
cout << "Swapped x, y: " << x << ' ' << y << '\n';
cout << "Swapped a, b: " << a << ' ' << b << '\n';
return 0;
}

Let's look closely at this program
...
Here, X is a generic type that is used as a placeholder
...
In main( ), the swapargs( ) function is called using three

463

464

C++: The Complete Reference

different types of data: ints, doubles, and chars
...

Here are some important terms related to templates
...

Both terms will be used interchangeably in this book
...
This is also called
a generated function
...
Put
differently, a generated function is a specific instance of a template function
...
The following example shows another common way to format the
swapargs( ) function
...
For
example, the fragment shown next will not compile
...

template
int i; // this is an error
void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}

As the comments imply, the template specification must directly precede the
function definition
...
For example, this program creates a template function that has
two generic types
...
6, 19L);
return 0;
}

In this example, the placeholder types type1 and type2 are replaced by the compiler
with the data types int and char *, and double and long, respectively, when the
compiler generates the specific instances of myfunc( ) within main( )
...


Explicitly Overloading a Generic Function
Even though a generic function overloads itself as needed, you can explicitly overload
one, too
...
If you overload a generic function,
that overloaded function overrides (or "hides") the generic function relative to that
specific version
...

// Overriding a template function
...
\n";
}
// This overrides the generic version of swapargs() for ints
...
\n";
}
int main()
{
int i=10, j=20;
double x=10
...
3;
char a='x', b='z';
cout << "Original i, j: " << i << ' ' << j << '\n';
cout << "Original x, y: " << x << ' ' << y << '\n';
cout << "Original a, b: " << a << ' ' << b << '\n';
swapargs(i, j); // calls explicitly overloaded swapargs()
swapargs(x, y); // calls generic swapargs()
swapargs(a, b); // calls generic swapargs()
cout << "Swapped i, j: " << i << ' ' << j << '\n';
cout << "Swapped x, y: " << x << ' ' << y << '\n';
cout << "Swapped a, b: " << a << ' ' << b << '\n';
return 0;
}

Chapter 18:

Templates

This program displays the following output
...
1 23
...

Inside template swapargs
...

Swapped i, j: 20 10
Swapped x, y: 23
...
1
Swapped a, b: z x

As the comments inside the program indicate, when swapargs(i, j) is called, it
invokes the explicitly overloaded version of swapargs( ) defined in the program
...

Recently, a new-style syntax was introduced to denote the explicit specialization of
a function
...
For example, using the
new-style specialization syntax, the overloaded swapargs( ) function from the
preceding program looks like this
...

template<> void swapargs(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "Inside swapargs int specialization
...
The type of data for which the specialization is being created is placed
inside the angle brackets following the function name
...
While there is no advantage to using one
specialization syntax over the other at this time, the new-style is probably a better
approach for the long term
...
However, as a
general rule, if you need to have different versions of a function for different data
types, you should use overloaded functions rather than templates
...
To do so, simply create another version of the
template that differs from any others in its parameter list
...

#include
using namespace std;
// First version of f() template
...

template void f(X a, Y b)
{
cout << "Inside f(X a, Y b)\n";
}
int main()
{
f(10);
// calls f(X)
f(10, 20); // calls f(X, Y)
return 0;
}

Here, the template for f( ) is overloaded to accept either one or two parameters
...

These nongeneric parameters work just like they do with any other function
...

#include
using namespace std;

Chapter 18:

Templates

const int TABWIDTH = 8;
// Display data at specified tab position
...

This is a test
100
X
3

In the program, the function tabOut( ) displays its first argument at the tab position
requested by its second argument
...
The tab parameter is a standard, call-by-value
parameter
...


Generic Function Restrictions
Generic functions are similar to overloaded functions except that they are more
restrictive
...
But a generic function must perform the same general
action for all versions—only the type of data can differ
...
These functions could not be replaced by
a generic function because they do not do the same thing
...
2);
return 0;
}

Applying Generic Functions
Generic functions are one of C++'s most useful features
...
As mentioned earlier, whenever you have a function that defines a
generalizable algorithm, you can make it into a template function
...
Before moving on
to generic classes, two examples of applying generic functions will be given
...


Chapter 18:

Templates

A Generic Sort
Sorting is exactly the type of operation for which generic functions were designed
...
The following program illustrates this by creating a generic bubble sort
...
The bubble( ) function will
sort any type of array
...

// A Generic bubble sort
...
3, 2
...
9, 100
...
0};
int i;
cout << "Here is unsorted integer array: ";
for(i=0; i<7; i++)

471

472

C++: The Complete Reference

cout << iarray[i] << ' ';
cout << endl;
cout << "Here is unsorted double array: ";
for(i=0; i<5; i++)
cout << darray[i] << ' ';
cout << endl;
bubble(iarray, 7);
bubble(darray, 5);
cout << "Here is sorted integer array: ";
for(i=0; i<7; i++)
cout << iarray[i] << ' ';
cout << endl;
cout << "Here is sorted double array: ";
for(i=0; i<5; i++)
cout << darray[i] << ' ';
cout << endl;
return 0;
}

The output produced by the program is shown here
...
3 2
...
9 100
...
9 2
...
3 100
...

It then sorts each
...
You might want to try
using bubble( ) to sort other types of data, including classes that you create
...


Compacting an Array
Another function that benefits from being made into a template is called compact( )
...
It is not uncommon to want to remove
elements from the middle of an array and then move the remaining elements down so

Chapter 18:

Templates

that all unused elements are at the end
...
The
generic compact( ) function shown in the following program is called with a pointer to
the first element in the array, the number of elements in the array, and the starting and
ending indexes of the elements to be removed
...
For the purposes of illustration, it also zeroes the
unused elements at the end of the array that have been freed by the compaction
...

#include
using namespace std;
template void compact(
X *items, // pointer to array to be compacted
int count, // number of items in array
int start, // starting index of compacted region
int end)
// ending index of compacted region
{
register int i;
for(i=end+1; iitems[start] = items[i];
/* For the sake of illustration, the remainder of
the array will be zeroed
...
One is an integer array, and the
other is a string
...
The
output from this program in shown here
...
As long as the underlying logic of a
function is independent of the data, it can be made into a generic function
...
When you do this,
you create a class that defines all the algorithms used by that class; however, the actual
type of the data being manipulated will be specified as a parameter when objects of
that class are created
...
For
example, the same algorithms that maintain a queue of integers will also work for a
queue of characters, and the same mechanism that maintains a linked list of mailing

Chapter 18:

Templates

addresses will also maintain a linked list of auto part information
...
The compiler will automatically generate the correct
type of object, based upon the type you specify when the object is created
...


...

}
Here, Ttype is the placeholder type name, which will be specified when a class is
instantiated
...

Once you have created a generic class, you create a specific instance of that class
using the following general form:
class-name ob;
Here, type is the type name of the data that the class will be operating upon
...
You need not use
template to explicitly specify them as such
...
Thus, it can be used to store objects of any type
...

// This function demonstrates a generic stack
...

template void stack::push(StackType ob)
{
if(tos==SIZE) {
cout << "Stack is full
...

template StackType stack::pop()
{
if(tos==0) {
cout << "Stack is empty
...

stack s1, s2; // create two character stacks
int i;
s1
...
push('x');
s1
...
push('y');
s1
...
push('z');
for(i=0; i<3; i++) cout << "Pop s1: " << s1
...
pop() << "\n";
// demonstrate double stacks
stack ds1, ds2; // create two double stacks

Chapter 18:

Templates

ds1
...
1);
ds2
...
2);
ds1
...
3);
ds2
...
4);
ds1
...
5);
ds2
...
6);
for(i=0; i<3; i++) cout << "Pop ds1: " << ds1
...
pop() << "\n";
return 0;
}

As you can see, the declaration of a generic class is similar to that of a generic
function
...

It is not until an object of the stack is declared that the actual data type is determined
...
In this example, two
different types of stacks are declared
...
Two are stacks of
doubles
...
By changing the
type of data specified when stack objects are created, you can change the type of data
stored in that stack
...

stack chrptrQ;

You can also create stacks to store data types that you create
...
You are
saved from the tedium of creating separate implementations for each data type with
which you want the algorithm to work
...


An Example with Two Generic Data Types
A template class can have more than one generic data type
...

For example, the following short example creates a class that uses two generic data types
...

*/
#include
using namespace std;
template class myclass
{
Type1 i;
Type2 j;
public:
myclass(Type1 a, Type2 b) { i = a; j = b; }
void show() { cout << i << ' ' << j << '\n'; }
};
int main()
{
myclass ob1(10, 0
...
");

Chapter 18:

Templates

ob1
...
show(); // show char, char *
return 0;
}

This program produces the following output:
10 0
...


The program declares two types of objects
...
ob2 uses a
character and a character pointer
...


Applying Template Classes: A Generic Array Class
To illustrate the practical benefits of template classes, let's look at one way in which
they are commonly applied
...
Doing so allows you to create your own array implementations, including
"safe arrays" that provide run-time boundary checking
...
However, if you create a class that contains the array, and
allow access to that array only through the overloaded [ ] subscripting operator, then
you can intercept an out-of-range index
...
This
type of array is shown in the following program:
// A generic safe array example
...

template AType &atype::operator[](int i)
{
if(i<0 || i> SIZE-1) {
cout << "\nIndex value of ";
cout << i << " is out-of-bounds
...
You should try creating other
types of arrays
...


Using Non-Type Arguments with Generic Classes
In the template specification for a generic class, you may also specify non-type
arguments
...
The syntax
to accomplish this is essentially the same as for normal function parameters: simply
include the type and name of the argument
...

// Demonstrate non-type template arguments
...

template class atype {
AType a[size]; // length of array is passed in size
public:
atype() {
register int i;
for(i=0; i}
AType &operator[](int i);
};
// Provide range checking for atype
...
\n";
exit(1);
}
return a[i];
}
int main()
{

481

482

C++: The Complete Reference

atype intob;
// integer array of size 10
atype doubleob; // double array of size 15
int i;
cout << "Integer array: ";
for(i=0; i<10; i++) intob[i] = i;
for(i=0; i<10; i++) cout << intob[i] << "
cout << '\n';

";

cout << "Double array: ";
for(i=0; i<15; i++) doubleob[i] = (double) i/3;
for(i=0; i<15; i++) cout << doubleob[i] << " ";
cout << '\n';
intob[12] = 100; // generates runtime error
return 0;
}

Look carefully at the template specification for atype
...
This parameter is then used within atype to declare the size of the array a
...
This allows it to be used to set the size of the array
...
Within main( ), notice how the integer and
floating-point arrays are created
...

Non-type parameters are restricted to integers, pointers, or references
...
The arguments that you pass to a non-type parameter
must consist of either an integer constant, or a pointer or reference to a global function
or object
...
For example, inside operator[ ]( ), the following
statement is not allowed
...

As the safe-array example illustrates, the use of non-type parameters greatly
expands the utility of template classes
...


Chapter 18:

Templates

Using Default Arguments with Template Classes
A template class can have a default argument associated with a generic type
...


Here, the type int will be used if no other type is specified when an object of type
myclass is instantiated
...
The default
value is used when no explicit value is specified when the class is instantiated
...

Here is another version of the safe-array class that uses default arguments for both
the type of data and the size of the array
...

#include
#include
using namespace std;
// Here, AType defaults to int and size defaults to 10
...

template
AType &atype::operator[](int i)
{
if(i<0 || i> size-1) {
cout << "\nIndex value of ";
cout << i << " is out-of-bounds
...
As the program illustrates,
atype objects can be created three ways:
s explicitly specifying both the type and size of the array
s explicitly specifying the type, but letting the size default to 10
s letting the type default to int and the size default to 10

Chapter 18:

Templates

The use of default arguments—especially default types—adds versatility to your
template classes
...


Explicit Class Specializations
As with template functions, you can create an explicit specialization of a generic class
...
For example:
// Demonstrate class specialization
...

template <> class myclass {
int x;
public:
myclass(int a) {
cout << "Inside myclass specialization\n";
x = a * a;
}
int getx() { return x; }
};
int main()
{
myclass d(10
...
getx() << "\n\n";
myclass i(5);

485

486

C++: The Complete Reference

cout << "int: " << i
...
1
Inside myclass specialization
int: 25

In the program, pay close attention to this line:
template <> class myclass {

It tells the compiler that an explicit integer specialization of myclass is being created
...

Explicit class specialization expands the utility of generic classes because it lets you
easily handle one or two special cases while allowing all others to be automatically
processed by the compiler
...


The typename and export Keywords
Recently, two keywords were added to C++ that relate specifically to templates:
typename and export
...
Each is
briefly examined
...
First, as mentioned earlier, it can be
substituted for the keyword class in a template declaration
...
There is no difference between using class
and using typename in this context
...
For example,
typename X::Name someObject;

ensures that X::Name is treated as a type name
...
It allows other files to use
a template declared in a different file by specifying only its declaration rather than
duplicating its entire definition
...
Through the use of template classes you can create frameworks that
can be applied over and over again to a variety of programming situations
...
When first shown in Chapter 11, it could only be
used to store integer values
...
However, by making stack into a generic class, it can create a
stack for any type of data
...
Once you have written and debugged a template class, you
have a solid software component that you can use with confidence in a variety of
different situations
...

While it is true that the template syntax can seem a bit intimidating at first, the
rewards are well worth the time it takes to become comfortable with it
...
For example, the STL (Standard Template Library)
defined by C++ is, as its name implies, built upon templates
...


487

This page intentionally left blank
...
Exception handling allows
you to manage run-time errors in an orderly fashion
...
The principal advantage of exception handling is that it automates much of the
error-handling code that previously had to be coded "by hand" in any large program
...
In the
most general terms, program statements that you want to monitor for exceptions are
contained in a try block
...
e
...
The exception is caught, using catch, and processed
...

Code that you want to monitor for exceptions must have been executed from
within a try block
...
) Exceptions that can be thrown by the monitored code are caught by a catch
statement, which immediately follows the try statement in which the exception was
thrown
...

try {
// try block
}
catch (type1 arg) {
// catch block
}
catch (type2 arg) {
// catch block
}
catch (type3 arg) {
// catch block
}

...


...


Chapter 19:

Exception Handling

When an exception is thrown, it is caught by its corresponding catch statement,
which processes the exception
...
Which catch statement is used is determined by the type of the exception
...
When an exception is caught,
arg will receive its value
...
If no exception is thrown (that is, no error occurs within the try block), then no
catch statement is executed
...
If this exception is to be caught,
then throw must be executed either from within a try block itself, or from any function
called from within the try block (directly or indirectly)
...
Throwing an unhandled exception causes
the standard library function terminate( ) to be invoked
...

Here is a simple example that shows the way C++ exception handling operates
...

#include
using namespace std;
int main()
{
cout << "Start\n";
try { // start a try block
cout << "Inside try block\n";
throw 100; // throw an error
cout << "This will not execute";
}
catch (int i) { // catch an error
cout << "Caught an exception -- value is: ";
cout << i << "\n";
}
cout << "End";

491

492

C++: The Complete Reference

return 0;
}

This program displays the following output:
Start
Inside try block
Caught an exception -- value is: 100
End

Look carefully at this program
...
Within the
try block, only two of the three statements will execute: the first cout statement and the
throw
...
That is, catch is not called
...
(The program's stack is automatically reset as needed to accomplish
this
...

Usually, the code within a catch statement attempts to remedy an error by taking
appropriate action
...
However, often an error cannot be fixed and a catch
block will terminate the program with a call to exit( ) or abort( )
...
For example, in the preceding example, if you change the type in the catch
statement to double, the exception will not be caught and abnormal termination will
occur
...

// This example will not work
...

Start
Inside try block
Abnormal program termination

An exception can be thrown from outside the try block as long as it is thrown by a
function that is called from within try block
...

/* Throwing an exception from a function outside the
try block
...
When this is the case, each time the
function is entered, the exception handling relative to that function is reset
...

#include
using namespace std;
// Localize a try/catch to a function
...
After each exception, the function returns
...

It is important to understand that the code associated with a catch statement will
be executed only if it catches an exception
...
(That is, execution never flows into a catch statement
...

#include
using namespace std;
int main()
{
cout << "Start\n";
try { // start a try block
cout << "Inside try block\n";
cout << "Still inside try block\n";
}
catch (int i) { // catch an error
cout << "Caught an exception -- value is: ";
cout << i << "\n";
}

495

496

C++: The Complete Reference

cout << "End";
return 0;
}

The preceding program produces the following output
...


Catching Class Types
An exception can be of any type, including class types that you create
...

Perhaps the most common reason that you will want to define a class type for an
exception is to create an object that describes the error that occurred
...
The following
example demonstrates this
...

#include
#include
using namespace std;
class MyException {
public:
char str_what[80];
int what;
MyException() { *str_what = 0; what = 0; }
MyException(char *s, int e) {
strcpy(str_what, s);
what = e;
}
};

Chapter 19:

Exception Handling

int main()
{
int i;
try {
cout << "Enter a positive number: ";
cin >> i;
if(i<0)
throw MyException("Not Positive", i);
}
catch (MyException e) { // catch an error
cout << e
...
what << "\n";
}
return 0;
}

Here is a sample run:
Enter a positive number: -4
Not Positive: -4

The program prompts the user for a positive number
...
Thus, MyException
encapsulates information about the error
...
In general, you will want to create exception classes that will encapsulate
information about an error to enable the exception handler to respond effectively
...
In fact, it is common
to do so
...
For example,
this program catches both integers and strings
...


497

498

C++: The Complete Reference

void Xhandler(int test)
{
try{
if(test) throw test;
else throw "Value is zero";
}
catch(int i) {
cout << "Caught Exception #: " << i << '\n';
}
catch(const char *str) {
cout << "Caught a string: ";
cout << str << '\n';
}
}
int main()
{
cout << "Start\n";
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout << "End";
return 0;
}

This program produces the following output:
Start
Caught
Caught
Caught
Caught
End

Exception
Exception
a string:
Exception

#: 1
#: 2
Value is zero
#: 3

As you can see, each catch statement responds only to its own type
...
Only a matching statement is executed
...


Chapter 19:

Exception Handling

Handling Derived-Class Exceptions
You need to be careful how you order your catch statements when trying to catch
exception types that involve base and derived classes because a catch clause for a base
class will also match any class derived from that base
...
If you don't do this, the base class catch will also catch all
derived classes
...


// Catching derived classes
...
\n";
}
catch(D d) {
cout << "This won't execute
...
Some compilers will flag
this condition with a warning message
...
Either way, to fix
this condition, reverse the order of the catch clauses
...
These attributes are discussed here
...
This is easy to accomplish
...

catch(
...
The following program illustrates catch(
...

#include
using namespace std;
void Xhandler(int test)
{
try{
if(test==0) throw test; // throw int
if(test==1) throw 'a'; // throw char
if(test==2) throw 123
...
) { // catch all exceptions
cout << "Caught One!\n";
}
}
int main()
{
cout << "Start\n";
Xhandler(0);
Xhandler(1);
Xhandler(2);
cout << "End";

Chapter 19:

Exception Handling

return 0;
}

This program displays the following output
...

One very good use for catch(
...
In this
capacity it provides a useful default or "catch all" statement
...
) to catch all others
...
) as a default
...
23; // throw double
}
catch(int i) { // catch an int exception
cout << "Caught an integer\n";
}
catch(
...

Start
Caught an integer
Caught One!
Caught One!
End

As this example suggests, using catch(
...
Also, by catching all exceptions,
you prevent an unhandled exception from causing an abnormal program termination
...
In
fact, you can also prevent a function from throwing any exceptions whatsoever
...
The
general form of this is shown here:
ret-type func-name(arg-list) throw(type-list)
{
//
...
Throwing any other type of expression will cause abnormal program
termination
...

Attempting to throw an exception that is not supported by a function will cause the
standard library function unexpected( ) to be called
...
However, you can specify your
own unexpected handler if you like, as described later in this chapter
...

// Restricting function throw types
...

void Xhandler(int test) throw(int, char, double)
{
if(test==0) throw test; // throw int
if(test==1) throw 'a'; // throw char
if(test==2) throw 123
...
If it attempts to throw any other type of exception, an abnormal
program termination will occur
...
) To see an
example of this, remove int from the list and retry the program
...
That is, a try block within a

503

504

C++: The Complete Reference

function may throw any type of exception so long as it is caught within that function
...

The following change to Xhandler( ) prevents it from throwing any exceptions
...
Instead,
they will cause an abnormal program termination
...
23;
}

Note

At the time of this writing, Microsoft's Visual C++ does not support the throw( )
clause for functions
...
This causes the current exception to be
passed on to an outer try/catch sequence
...
For example, perhaps one exception
handler manages one aspect of an exception and a second handler copes with another
...
When you rethrow an exception, it will not be recaught
by the same catch statement
...
The
following program illustrates rethrowing an exception, in this case a char * exception
...

#include
using namespace std;
void Xhandler()
{
try {
throw "hello"; // throw a char *
}
catch(const char *) { // catch a char *
cout << "Caught char * inside Xhandler\n";
throw ; // rethrow char * out of function
}
}

Chapter 19:

Exception Handling

int main()
{
cout << "Start\n";
try{
Xhandler();
}
catch(const char *) {
cout << "Caught char * inside main\n";
}
cout << "End";
return 0;
}

This program displays this output:
Start
Caught char * inside Xhandler
Caught char * inside main
End

Understanding terminate( ) and unexpected( )
As mentioned earlier, terminate( ) and unexpected( ) are called when something goes
wrong during the exception handling process
...
Their prototypes are shown here:
void terminate( );
void unexpected( );
These functions require the header
...
It is also called if your program
attempts to rethrow an exception when no exception was originally thrown
...

For example, such a circumstance could occur when, in the process of unwinding the
stack because of an exception, a destructor for an object being destroyed throws an
exception
...
By default, terminate( ) calls abort( )
...
By default, unexpected( ) calls terminate( )
...
As just explained, by default terminate( ) calls abort( ), and
unexpected( ) calls terminate( )
...
However, you can change the
functions that are called by terminate( ) and unexpected( )
...

To change the terminate handler, use set_terminate( ), shown here:
terminate_handler set_terminate(terminate_handler newhandler) throw( );
Here, newhandler is a pointer to the new terminate handler
...
The new terminate handler must be of type
terminate_handler, which is defined like this:
typedef void (*terminate_handler) ( );
The only thing that your terminate handler must do is stop program execution
...

To change the unexpected handler, use set_unexpected( ), shown here:
unexpected_handler set_unexpected(unexpected_handler newhandler) throw( );
Here, newhandler is a pointer to the new unexpected handler
...
The new unexpected handler must be of type
unexpected_handler, which is defined like this:
typedef void (*unexpected_handler) ( );
This handler may itself throw an exception, stop the program, or call terminate( )
...

Both set_terminate( ) and set_unexpected( ) require the header
...

// Set a new terminate handler
...

}
return 0;
}

The output from this program is shown here
...
Its prototype is shown here:
bool uncaught_exception( );
This function returns true if an exception has been thrown but not yet caught
...


507

508

C++: The Complete Reference

The exception and bad_exception Classes
When a function supplied by the C++ standard library throws an exception, it will be
an object derived from the base class exception
...
These classes require the header
...
This implies that the error handler must do something
rational when an error occurs
...
It
inputs two numbers and divides the first by the second
...

#include
using namespace std;
void divide(double a, double b);
int main()
{
double i, j;
do {
cout << "Enter numerator (0 to stop): ";
cin >> i;
cout << "Enter denominator: ";
cin >> j;
divide(i, j);
} while(i != 0);
return 0;
}
void divide(double a, double b)
{
try {
if(!b) throw b; // check for divide-by-zero
cout << "Result: " << a/b << endl;
}
catch (double b) {
cout << "Can't divide by zero
...
Since division by zero is illegal, the program cannot
continue if a zero is entered for the second number
...
The program then reprompts the user
for two more numbers
...
The same basic concepts will apply to more
complex applications of exception handling
...
In this regard, C++'s exception handling is
designed to replace the rather clumsy C-based setjmp( ) and longjmp( ) functions
...
This means rectifying the situation, if possible
...


C++

Chapter 20
The C++ I/O System Basics

511

512

C++: The Complete Reference

++ supports two complete I/O systems: the one inherited from C and the
object-oriented I/O system defined by C++ (hereafter called simply the C++ I/O
system)
...
Here we will begin
to examine the C++ I/O system
...
The different aspects of C++'s I/O system, such as console I/O and disk
I/O, are actually just different perspectives on the same mechanism
...
Although the examples in this
chapter use "console" I/O, the information is applicable to other devices, including
disk files (discussed in Chapter 21)
...
The answer is that C's I/O
system knows nothing about objects
...
In addition to support for objects, there are several
benefits to using C++'s I/O system even in programs that don't make extensive (or
any) use of user-defined objects
...
The C I/O is supported by C++ only for compatibility
...


C

Old vs
...
The old I/O library is supported by the header file
...
The new I/O library is supported by the header
...
This is because the
new I/O library is, in essence, simply an updated and improved version of the old
one
...

From the programmer's perspective, there are two main differences between the
old and new C++ I/O libraries
...
Thus, the new I/O library is essentially a
superset of the old one
...
Second, the
old-style I/O library was in the global namespace
...
(Recall that the std namespace is used by all of the Standard C++ libraries
...


Chapter 20:

The C++ I/O System Basics

C++ Streams
Like the C-based I/O system, the C++ I/O system operates through streams
...

However, to summarize: A stream is a logical device that either produces or consumes
information
...
All streams
behave in the same way even though the actual physical devices they are connected to
may differ substantially
...
For example, you can use the
same function that writes to a file to write to the printer or to the screen
...


The C++ Stream Classes
As mentioned, Standard C++ provides support for its I/O system in
...
The I/O classes begin with a system of template classes
...
Once a template class has been defined, specific
instances of it can be created
...
This book will use only the 8-bit character classes since they are by far
the most common
...

The C++ I/O system is built upon two related but different template class
hierarchies
...

This class supplies the basic, low-level input and output operations, and provides the
underlying support for the entire C++ I/O system
...
The class hierarchy
that you will most commonly be working with is derived from basic_ios
...
(A base class for basic_ios is called ios_base, which defines
several nontemplate traits used by basic_ios
...
These
classes are used to create streams capable of input, output, and input/output,
respectively
...
Here is
a list of the mapping of template class names to their character and wide-character
versions
...
They are also
the same names that were used by the old I/O library
...

One last point: The ios class contains many member functions and variables that
control or monitor the fundamental operation of a stream
...
Just remember that if you include in your program, you will
have access to this important class
...

They are:

Stream

Meaning

Default Device

cin

Standard input

Keyboard

cout

Standard output

Screen

cerr

Standard error output

Screen

clog

Buffered version of cerr

Screen

Streams cin, cout, and cerr correspond to C's stdin, stdout, and stderr
...

However, in environments that support I/O redirection (such as DOS, Unix, OS/2, and
Windows), the standard streams can be redirected to other devices or files
...


Chapter 20:

The C++ I/O System Basics

Standard C++ also defines these four additional streams: win, wout, werr, and
wlog
...
Wide characters are
of type wchar_t and are generally 16-bit quantities
...


Formatted I/O
The C++ I/O system allows you to format I/O operations
...
There are two related but conceptually different ways that
you can format data
...

Specifically, you can set various format status flags defined inside the ios class or
call various ios member functions
...

We will begin the discussion of formatted I/O by using the ios member functions
and flags
...
The ios class declares a bitmask enumeration called fmtflags
in which the following values are defined
...
)
adjustfield

basefield

boolalpha

dec

fixed

floatfield

hex

internal

left

oct

right

scientific

showbase

showpoint

showpos

skipws

unitbuf

uppercase

These values are used to set or clear the format flags
...
In this case, the format flags
will be encoded into a long integer
...
When skipws is cleared,
white-space characters are not discarded
...
When right is set, output is right
justified
...
If none of these flags are set,
output is right justified by default
...
However, it is possible to change
the number base
...
Setting the
hex flag causes output to be displayed in hexadecimal
...

Setting showbase causes the base of numeric values to be shown
...

By default, when scientific notation is displayed, the e is in lowercase
...
When uppercase is set, these
characters are displayed in uppercase
...

Setting showpoint causes a decimal point and trailing zeros to be displayed for all
floating-point output—whether needed or not
...
When fixed is set, floating-point values are displayed using normal
notation
...

When unitbuf is set, the buffer is flushed after each insertion operation
...

Since it is common to refer to the oct, dec, and hex fields, they can be collectively
referred to as basefield
...
Finally, the scientific and fixed fields can be referenced as floatfield
...
This function is a member of ios
...
For example, to turn on the showpos flag, you can use this statement:
stream
...
Notice the use of ios:: to qualify showpos
...

The following program displays the value 100 with the showpos and showpoint
flags turned on
...
setf(ios::showpoint);
cout
...
0; // displays +100
...
Therefore, any call to setf( ) is done relative to a
specific stream
...
Put differently, there is
no concept in C++ of global format status
...

Although there is nothing technically wrong with the preceding program, there
is a more efficient way to write it
...
For example, this single call
accomplishes the same thing:
// You can OR together two or more flags,
cout
...
For example, showbase by
itself will not be recognized
...


Clearing Format Flags
The complement of setf( ) is unsetf( )
...
Its general form is
void unsetf(fmtflags flags);
The flags specified by flags are cleared
...
) The previous
flag settings are returned
...
It first sets both the uppercase and
scientific flags
...
12 in scientific notation
...
Next, it clears the uppercase flag and again
outputs 100
...
"
#include
using namespace std;
int main()
{
cout
...
12;

// displays 1
...
unsetf(ios::uppercase); // clear uppercase
cout << " \n" << 100
...
0012e+02
return 0;
}

An Overloaded Form of setf( )
There is an overloaded form of setf( ) that takes this general form:
fmtflags setf(fmtflags flags1, fmtflags flags2);
In this version, only the flags specified by flags2 are affected
...
Note that even if flags1 contains other
flags, only those specified by flags2 will be affected
...
For example,
#include
using namespace std;
int main( )
{
cout
...
0; // displays 100
...
0
return 0;
}

Chapter 20:

The C++ I/O System Basics

Here, showpoint is set, but not showpos, since it is not specified in the second
parameter
...
As explained, references to the oct,
dec, and hex fields can collectively be referred to as basefield
...
Finally, the scientific and fixed
fields can be referenced as floatfield
...
For
example, the following program sets output to hexadecimal
...
This is most easily accomplished using the
two-parameter form of setf( )
...
setf(ios::hex, ios::basefield);
cout << 100; // this displays 64
return 0;
}

Here, the basefield flags (i
...
, dec, oct, and hex) are first cleared and then the hex flag
is set
...
For example, in this program, the first attempt to set the showpos flag fails
...

#include
using namespace std;
int main()
{
cout
...
setf(ios::showpos, ios::showpos); // this is correct

519

520

C++: The Complete Reference

cout << 100; // now displays +100
return 0;
}

Keep in mind that most of the time you will want to use unsetf( ) to clear flags and
the single parameter version of setf( ) (described earlier) to set flags
...
Another good use may involve a situation in which you are using a
flag template that specifies the state of all format flags but wish to alter only one or
two
...


Examining the Formatting Flags
There will be times when you only want to know the current format settings but not
alter any
...
Its prototype is shown here:
fmtflags flags( );
The following program uses flags( ) to display the setting of the format flags
relative to cout
...
You might find it
useful in programs you write
...
setf(ios::right | ios::showpoint | ios::fixed);
showflags();
return 0;
}

Chapter 20:

The C++ I/O System Basics

// This function displays the status of the format flags
...
flags(); // get flag settings
// check each flag
for(i=0x4000; i; i = i >> 1)
if(i & f) cout << "1 ";
else cout << "0 ";
cout << " \n";
}

The output from the program is shown here:
0 0 0 0 0 1 0 0 0 0 0 0 0 0 1
0 1 0 0 0 1 0 1 0 0 1 0 0 0 1

Setting All Flags
The flags( ) function has a second form that allows you to set all format flags associated
with a stream
...
Thus, all format flags are affected
...

The next program illustrates this version of flags( )
...
All other flags are off
...
The function
showflags( ) verifies that the flags are set as indicated
...
)
#include
using namespace std;

521

522

C++: The Complete Reference

void showflags();
int main()
{
// show default condition of format flags
showflags();
// showpos, showbase, oct, right are on, others off
long f = ios::showpos | ios::showbase | ios::oct | ios::right;
cout
...

The functions that do these things are width( ), precision( ), and fill( ), respectively
...

By default, when a value is output, it occupies only as much space as the number
of characters it takes to display it
...
Its prototype is shown here:
streamsize width(streamsize w);
Here, w becomes the field width, and the previous field width is returned
...
If it isn't, the default
field width is used
...

After you set a minimum field width, when a value uses less than the specified
width, the field will be padded with the current fill character (space, by default) to
reach the field width
...
No values are truncated
...
Its prototype is
shown here:
streamsize precision(streamsize p);

Chapter 20:

The C++ I/O System Basics

Here, the precision is set to p, and the old value is returned
...

In some implementations, the precision must be set before each floating-point output
...

By default, when a field needs to be filled, it is filled with spaces
...
Its prototype is
char fill(char ch);
After a call to fill( ), ch becomes the new fill character, and the old one is returned
...
precision(4) ;
cout
...
12345 << "\n";

// displays 10
...
fill('*');
cout
...
12345 << "\n"; // displays *****10
...
width(10);
cout << "Hi!" << "\n"; // displays *******Hi!
cout
...
setf(ios::left); // left justify
cout << 10
...
12*****
return 0;
}

This program's output is shown here:
10
...
12
*******Hi!
10
...
These forms are shown here:
char fill( );
streamsize width( );
streamsize precision( );

Using Manipulators to Format I/O
The second way you can alter the format parameters of a stream is through the use of
special functions called manipulators that can be included in an I/O expression
...
As you can see by examining the table,
many of the I/O manipulators parallel member functions of the ios class
...


Manipulator

Purpose

Input/Output

boolalpha

Turns on boolapha flag
...


Input/Output

endl

Output a newline character
and flush the stream
...


Output

fixed

Turns on fixed flag
...


Output

hex

Turns on hex flag
...


Output

left

Turns on left flag
...


Input/Output

noshowbase

Turns off showbase flag
...


Output

noshowpos

Turns off showpos flag
...


The C++ Manipulators

Chapter 20:

The C++ I/O System Basics

Manipulator

Purpose

Input/Output

noskipws

Turns off skipws flag
...


Output

nouppercase

Turns off uppercase flag
...


Input/Output

resetiosflags (fmtflags f )

Turn off the flags
specified in f
...


Output

scientific

Turns on scientific flag
...


Input/Output

setfill(int ch)

Set the fill character to ch
...


Input/output

setprecision (int p)

Set the number of digits of
precision
...


Output

showbase

Turns on showbase flag
...


Output

showpos

Turns on showpos flag
...


Input

unitbuf

Turns on unitbuf flag
...


Output

ws

Skip leading white space
...


The C++ Manipulators (continued)

To access manipulators that take parameters (such as setw( )), you must include
in your program
...
0;
return 0;
}

This displays
64
??????2343

Notice how the manipulators occur within a larger I/O expression
...
This is because it is the address of the function that is passed
to the overloaded << operator
...
setf(ios::hex, ios::basefield);
cout << 100 << "\n"; // 100 in hex
cout
...
width(10);
cout << 2343
...

You can use the setiosflags( ) manipulator to directly set the various format flags
related to a stream
...

One of the more interesting manipulators is boolapha
...

For example,
#include
using namespace std;
int main()
{
bool b;
b = true;
cout << b << " " << boolalpha << b << endl;
cout << "Enter a Boolean value: ";
cin >> boolalpha >> b;
cout << "Here is what you entered:
return 0;
}

" << b;

527

528

C++: The Complete Reference

Here is a sample run
...
You can also overload these operators so that they
perform I/O operations on types that you create
...
Likewise, the >> input operator is called the
extraction operator because it extracts characters from a stream
...


Creating Your Own Inserters
It is quite simple to create an inserter for a class that you create
...
(Remember,
ostream is a class derived from ios that supports output
...
The second parameter is the object
being inserted
...
) The last thing the inserter must do before exiting is return stream
...

Within an inserter function, you may put any type of procedures or operations that
you want
...
However,
for the inserter to be in keeping with good programming practices, you should limit its
operations to outputting information to a stream
...

class phonebook {
public:

Chapter 20:

The C++ I/O System Basics

char name[80];
int areacode;
int prefix;
int num;
phonebook(char *n, int a, int p, int nm)
{
strcpy(name, n);
areacode = a;
prefix = p;
num = nm;
}
};

This class holds a person's name and telephone number
...

// Display name and phone number
ostream &operator<<(ostream &stream, phonebook o)
{
stream << o
...
areacode << ") ";
stream << o
...
num << "\n";
return stream; // must return stream
}

Here is a short program that illustrates the phonebook inserter function:
#include
#include
using namespace std;
class phonebook {
public:
char name[80];
int areacode;
int prefix;
int num;
phonebook(char *n, int a, int p, int nm)
{
strcpy(name, n);
areacode = a;
prefix = p;
num = nm;

529

530

C++: The Complete Reference

}
};
// Display name and phone number
...
name << " ";
stream << "(" << o
...
prefix << "-" << o
...
Although this may seem weird at first, the reason is easy to understand
...

Further, this object is an object of the class for which the operator function is a member
...
If an overloaded operator function is a member of a
class, the left operand must be an object of that class
...

Therefore, overloaded inserters cannot be members of the class for which they are
overloaded
...


Chapter 20:

The C++ I/O System Basics

The fact that inserters cannot be members of the class for which they are defined
seems to be a serious flaw in C++
...
However, encapsulation is an essential component of objectoriented programming
...
Fortunately, there is a solution to this dilemma: Make the inserter
a friend of the class
...
Here is the same program modified
to make the inserter into a friend function:
#include
#include
using namespace std;
class phonebook {
// now private
char name[80];
int areacode;
int prefix;
int num;
public:
phonebook(char *n, int a, int p, int nm)
{
strcpy(name, n);
areacode = a;
prefix = p;
num = nm;
}
friend ostream &operator<<(ostream &stream, phonebook o);
};
// Display name and phone number
...
name << " ";
stream << "(" << o
...
prefix << "-" << o
...
For example, the inserter shown in the preceding example can be used with
any stream because the body of the function directs its output to stream, which is the
stream that invoked the inserter
...
name << " ";

as
cout << o
...
The original
version will work with any stream, including those linked to disk files
...
In general, the more flexible
your inserters are, the more valuable they are
...
To fix
this, you can either make num into a string or you can set the fill character to zero
and use the width( ) format function to generate the leading zeroes
...


Before moving on to extractors, let's look at one more example of an inserter
function
...
An inserter can be used
to output data in any form that makes sense
...
Another inserter might
generate graphics images
...
To sample the flavor of outputting things other than text, examine the
following program, which draws boxes on the screen
...
)
#include
using namespace std;
class box {
int x, y;
public:
box(int i, int j) { x=i; y=j; }
friend ostream &operator<<(ostream &stream, box o);
};
// Output a box
...
x; i++)
stream << "*";
stream << "\n";
for(j=1; j ...
x; i++)
if(i==0 || i==o
...
x; i++)
stream << "*";
stream << "\n";
return stream;
}
int main()
{
box a(14, 6), b(30, 7), c(40, 5);

533

534

C++: The Complete Reference

cout << "Here are some boxes:\n";
cout << a << b << c;
return 0;
}

The program displays the following:
Here are some boxes:
**************
*
*
*
*
*
*
*
*
**************
******************************
*
*
*
*
*
*
*
*
*
*
******************************
****************************************
*
*
*
*
*
*
****************************************

Creating Your Own Extractors
Extractors are the complement of inserters
...
The
first parameter must also be a reference to a stream of type istream
...
This is so the object can be modified by the input (extraction) operation
...
name;
cout << "Enter area code: ";
stream >> o
...
prefix;
cout << "Enter number: ";
stream >> o
...
The point is that although the main purpose of an extractor is input, it can
perform any operations necessary to achieve that end
...
If you
don't, you run the risk of losing much in terms of structure and clarity
...

ostream &operator<<(ostream &stream, phonebook o)
{
stream << o
...
areacode << ") ";
stream << o
...
num << "\n";
return stream; // must return stream
}
// Input name and telephone number
...
name;
cout << "Enter area code: ";
stream >> o
...
prefix;
cout << "Enter number: ";
stream >> o
...
If the extractor is used on a
stream connected to a disk file, for example, then the cout statements would not be
applicable
...
For example, you might use if statements such as
the one shown here
...


Creating Your Own Manipulator Functions
In addition to overloading the insertion and extraction operators, you can further
customize C++'s I/O system by creating your own manipulator functions
...
First, you can consolidate a sequence
of several separate I/O operations into one manipulator
...
In these cases you can use a custom manipulator to
perform these actions, thus simplifying your source code and preventing accidental
errors
...
For example, you might use a manipulator to send
control codes to a special type of printer or to an optical recognition system
...
As you will see, custom manipulators can help
make any I/O-intensive program clearer and more efficient
...
In addition to these two broad
categories, there is a secondary division: those manipulators that take an argument and
those that don't
...
For this reason, you must consult the documentation to
your compiler for instructions on creating parameterized manipulators
...
It is described here
...
Notice that a reference to a stream of
type ostream is returned
...
It is important to note that even though the manipulator has as its
single argument a reference to the stream upon which it is operating, no argument is
used when the manipulator is inserted in an output operation
...

#include
#include
using namespace std;
// A simple output manipulator
...
setf(ios::showbase);
stream
...
As you can see, sethex is used as part of an I/O
expression in the same way as any of the built-in manipulators
...
For example, the simple
manipulators la( ) and ra( ) display a left and right arrow for emphasis, as shown here:
#include
#include
using namespace std;
// Right Arrow
ostream &ra(ostream &stream)
{
stream << "-------> ";
return stream;

Chapter 20:

The C++ I/O System Basics

}
// Left Arrow
ostream &la(ostream &stream)
{
stream << " <-------";
return stream;
}
int main()
{
cout << "High balance " << ra << 1233
...
66 << la;
return 0;
}

This program displays:
High balance -------> 1233
...
66 <-------

If used frequently, these simple manipulators save you from some tedious typing
...
For example, a printer may be able to accept various codes that change the type
size or font, or that position the print head in a special location
...

All parameterless input manipulator functions have this skeleton:
istream &manip-name(istream &stream)
{
// your code here
return stream;
}
An input manipulator receives a reference to the stream for which it was invoked
...

The following program creates the getpass( ) input manipulator, which rings the
bell and then prompts for a password:
#include
#include

539

540

C++: The Complete Reference

using namespace std;
// A simple input manipulator
...
If it does not, your
manipulator cannot be used in a series of input or output operations
...
In part, this is because the most common file is a disk file, and disk files
have capabilities and features that most other devices don't
...


A

and the File Classes
To perform file I/O, you must include the header in your program
...
These classes are
derived from istream, ostream, and iostream, respectively
...

Another class used by the file system is filebuf, which provides low-level facilities to
manage a file stream
...


Opening and Closing a File
In C++, you open a file by linking it to a stream
...
There are three types of streams: input, output, and input/output
...
To
create an output stream, you must declare it as class ofstream
...
For
example, this fragment creates one input stream, one output stream, and one stream
capable of both input and output:
ifstream in; // input
ofstream out; // output
fstream io;
// input and output

Once you have created a stream, one way to associate it with a file is by using
open( )
...
The prototype for
each is shown here:
void ifstream::open(const char *filename, ios::openmode mode = ios::in);
void ofstream::open(const char *filename, ios::openmode mode = ios::out | ios::trunc);
void fstream::open(const char *filename, ios::openmode mode = ios::in | ios::out);

Chapter 21:

C++ File I/O

Here, filename is the name of the file; it can include a path specifier
...
It must be one or more of the following values defined
by openmode, which is an enumeration defined by ios (through its base class ios_base)
...

Including ios::app causes all output to that file to be appended to the end
...
Including ios::ate causes a seek to
the end of the file to occur when the file is opened
...

The ios::in value specifies that the file is capable of input
...

The ios::binary value causes a file to be opened in binary mode
...
In text mode, various character translations may take place,
such as carriage return/linefeed sequences being converted into newlines
...

Understand that any file, whether it contains formatted text or raw data, can be opened
in either binary or text mode
...

The ios::trunc value causes the contents of a preexisting file by the same name to be
destroyed, and the file is truncated to zero length
...

The following fragment opens a normal output file
...
open("test", ios::out);

However, you will seldom see open( ) called as shown, because the mode parameter
provides default values for each type of stream
...
Therefore, the preceding statement will usually look like this:
out
...
Therefore, you might need to specify this explicitly
...

Therefore, before using a file, you should test to make sure that the open operation
succeeded
...
\n";
// handle error
}

Although it is entirely proper to open a file by using the open( ) function, most of
the time you will not do so because the ifstream, ofstream, and fstream classes have
constructor functions that automatically open the file
...
Therefore, you will most
commonly see a file opened as shown here:
ifstream mystream("myfile"); // open file for input

As stated, if for some reason the file cannot be opened, the value of the associated
stream variable will evaluate to false
...

You can also check to see if you have successfully opened a file by using the
is_open( ) function, which is a member of fstream, ifstream, and ofstream
...
For example,
the following checks if mystream is currently open:
if(!mystream
...
\n";
//
...
For example, to close the file linked
to a stream called mystream, use this statement:
mystream
...


Chapter 21:

C++ File I/O

Reading and Writing Text Files
It is very easy to read from or write to a text file
...
For example, this program creates a
short inventory file that contains each item's name and its cost:
#include
#include
using namespace std;
int main()
{
ofstream out("INVNTRY"); // output, normal file
if(!out) {
cout << "Cannot open INVENTORY file
...
95 << endl;
out << "Toasters " << 19
...
80 << endl;
out
...
\n";
return 1;

545

546

C++: The Complete Reference

}
char item[20];
float cost;
in >> item >> cost;
cout << item << " " << cost << "\n";
in >> item >> cost;
cout << item << " " << cost << "\n";
in >> item >> cost;
cout << item << " " << cost << "\n";
in
...
All information is stored in the file in the
same format as it would be displayed on the screen
...
This program reads strings entered at
the keyboard and writes them to disk
...
To use the program, specify the name of the output file on the
command line
...
\n";
return 1;
}

Chapter 21:

C++ File I/O

char str[80];
cout << "Write strings to disk
...
\n";
do {
cout << ": ";
cin >> str;
out << str << endl;
} while (*str != '!');
out
...
For example, white-space characters are omitted
...

When inputting, if end-of-file is encountered, the stream linked to that file will
evaluate as false
...
)

Unformatted and Binary I/O
While reading and writing formatted text files is very easy, it is not always the most
efficient way to handle files
...
The functions that allow you to do this are
described here
...
Although the unformatted file functions will work on files
opened for text mode, some character translations may occur
...


Characters vs
...
For many years, I/O in C and C++ was thought of as byte oriented
...
However, with the advent of wide characters (of type wchar_t) and
their attendant streams, we can no longer say that C++ I/O is byte oriented
...
Of course, char streams are still byte oriented
and we can continue to think in terms of bytes, especially when operating on

547

548

C++: The Complete Reference

nontextual data
...

As explained in Chapter 20, all of the streams used in this book are char streams
since they are by far the most common
...


put( ) and get( )
One way that you may read and write unformatted data is by using the member
functions get( ) and put( )
...
That is, get( ) will
read a character and put( ) will write a character
...

The get( ) function has many forms, but the most commonly used version is shown
here along with put( ):
istream &get(char &ch);
ostream &put(char ch);
The get( ) function reads a single character from the invoking stream and puts that
value in ch
...
The put( ) function writes ch to the
stream and returns a reference to the stream
...
It uses the get( ) function
...
";

Chapter 21:

C++ File I/O

return 1;
}
while(in) { // in will be false when eof is reached
in
...
Therefore, when in reaches the end of the file, it
will be false, causing the while loop to stop
...
get(ch))
cout << ch;

This works because get( ) returns a reference to the stream in, and in will be false when
the end of the file is encountered
...
As you probably know, the ASCII characters occupy only about half the
available values that can be held by a char
...
(Not all systems support the extended character set, but most do
...
\n";
return 1;
}

549

550

C++: The Complete Reference

// write all characters to disk
for(i=0; i<256; i++) out
...
close();
return 0;
}

You might find it interesting to examine the contents of the CHARS file to see what
extended characters your computer has available
...
Their prototypes are
istream &read(char *buf, streamsize num);
ostream &write(const char *buf, streamsize num);
The read( ) function reads num characters from the invoking stream and puts them in
the buffer pointed to by buf
...
As mentioned in the preceding chapter,
streamsize is a type defined by the C++ library as some form of integer
...

The next program writes a structure to disk and then reads it back in:
#include
#include
#include
using namespace std;
struct status {
char name[80];
double balance;
unsigned long account_num;
};
int main()
{
struct status acc;

Chapter 21:

C++ File I/O

strcpy(acc
...
balance = 1123
...
account_num = 34235678;
// write data
ofstream outbal("balance", ios::out | ios::binary);
if(!outbal) {
cout << "Cannot open file
...
write((char *) &acc, sizeof(struct status));
outbal
...
\n";
return 1;
}
inbal
...
name << endl;
cout << "Account # " << acc
...
precision(2);
cout
...
balance;
inbal
...
Each individual field need not be read or written separately
...


Note

The type casts inside the calls to read( ) and write( ) are necessary when operating
on a buffer that is not defined as a character array
...


551

552

C++: The Complete Reference

If the end of the file is reached before num characters have been read, then read( )
simply stops, and the buffer contains as many characters as were available
...
The
following program shows another example of read( ) and write( ) and illustrates the
use of gcount( ):
#include
#include
using namespace std;
int main()
{
double fnum[4] = {99
...
4, 1776
...
1};
int i;
ofstream out("numbers", ios::out | ios::binary);
if(!out) {
cout << "Cannot open file
...
write((char *) &fnum, sizeof fnum);
out
...
0;
ifstream in("numbers", ios::in | ios::binary);
in
...
gcount() << " bytes read\n";
for(i=0; i<4; i++) // show values read from file
cout << fnum[i] << " ";

Chapter 21:

C++ File I/O

in
...
After the call to read( ), gcount( ) is used to determine how many
bytes were just read
...
The prototypes for the three most commonly used overloaded forms
are shown here:
istream &get(char *buf, streamsize num);
istream &get(char *buf, streamsize num, char delim);
int get( );
The first form reads characters into the array pointed to by buf until either num-1
characters have been read, a newline is found, or the end of the file has been
encountered
...
If the
newline character is encountered in the input stream, it is not extracted
...

The second form reads characters into the array pointed to by buf until either num-1
characters have been read, the character specified by delim has been found, or the end
of the file has been encountered
...
If the delimiter character is encountered in the input stream, it is not extracted
...

The third overloaded form of get( ) returns the next character from the stream
...
This form of get( ) is similar to C's
getc( ) function
...
It is a member of each input stream
class
...
The array pointed to by buf will be null terminated by getline( )
...

The second form reads characters into the array pointed to by buf until either num−1
characters have been read, the character specified by delim has been found, or the end
of the file has been encountered
...
If the delimiter character is encountered in the input stream, it is extracted,
but is not put into buf
...
Both read characters from input and
put them into the array pointed to by buf until either num−1 characters have been read
or until the delimiter character is encountered
...

Here is a program that demonstrates the getline( ) function
...

// Read and display a text file line by line
...
\n";
return 1;
}
char str[255];

Chapter 21:

C++ File I/O

while(in) {
in
...
close();
return 0;
}

Detecting EOF
You can detect when the end of the file is reached by using the member function eof( ),
which has this prototype:
bool eof( );
It returns true when the end of the file has been reached; otherwise it returns false
...

/* Display contents of specified file
in both ASCII and in hex
...
\n";
return 1;
}
register int i, j;
int count = 0;
char c[16];
cout
...
eof()) {
for(i=0; i<16 && !in
...
get(c[i]);
}
if(i<16) i--; // get rid of eof
for(j=0; jcout << setw(3) << hex << (int) c[j];
for(; j<16; j++) cout << " ";
cout << "\t";
for(j=0; jif(isprint(c[j])) cout << c[j];
else cout << "
...
get();
cout << endl;
}
}
in
...

in bot
h ASCII and in h
ex
...
#i
nclude

...
#include omanip>
...


...
if(argc!
=2) {
...
It has this prototype:
istream &ignore(streamsize num=1, int_type delim=EOF);
It reads and discards characters until either num characters have been ignored (1 by
default) or the character specified by delim is encountered (EOF by default)
...
Here,
int_type is defined as some form of integer
...
It ignores characters until either a space
is encountered or 10 characters have been read
...

#include
#include
using namespace std;
int main()
{
ifstream in("test");

557

558

C++: The Complete Reference

if(!in) {
cout << "Cannot open file
...
*/
in
...
get(c);
if(in) cout << c;
}
in
...
It has this prototype:
int_type peek( );
It returns the next character in the stream or EOF if the end of the file is encountered
...
)
You can return the last character read from a stream to that stream by using
putback( )
...


flush( )
When output is performed, data is not necessarily immediately written to the physical
device linked to the stream
...
Only then are the contents of that buffer written to disk
...
Its prototype is
ostream &flush( );
Calls to flush( ) might be warranted when a program is going to be used in adverse
environments (for example, in situations where power outages occur frequently)
...


Random Access
In C++'s I/O system, you perform random access by using the seekg( ) and seekp( )
functions
...
seekdir is an enumeration defined by ios that
determines how the seek will take place
...
One is the get
pointer, which specifies where in the file the next input operation will occur
...

Each time an input or output operation takes place, the appropriate pointer is
automatically sequentially advanced
...

The seekg( ) function moves the associated file's current get pointer offset number of
characters from the specified origin, which must be one of these three values:
ios::beg

Beginning-of-file

ios::cur

Current location

ios::end

End-of-file

The seekp( ) function moves the associated file's current put pointer offset number
of characters from the specified origin, which must be one of the values just shown
...
The character translations that may occur on text files could cause a
position request to be out of sync with the actual contents of the file
...
It allows you to change
a specific character in a file
...

Notice that the file is opened for read/write operations
...
";
return 1;
}
out
...
put(*argv[3]);
out
...
It displays the contents of a file beginning with the
location you specify on the command line
...
";
return 1;
}
in
...
get(ch))
cout << ch;
return 0;
}

The following program uses both seekp( ) and seekg( ) to reverse the first
characters in a file
...
\n";
return 1;
}
long e, i, j;
char c1, c2;
e = atol(argv[2]);
for(i=0, j=e; iinout
...
get(c1);
inout
...
get(c2);
inout
...
put(c2);
inout
...
put(c1);
}
inout
...
For example, to reverse the first 10 characters of
a file called TEST, use this command line:
reverse test 10

If the file had contained this:
This is a test
...


Chapter 21:

C++ File I/O

Obtaining the Current File Position
You can determine the current position of each file pointer by using these functions:
pos_type tellg( );
pos_type tellp( );
Here, pos_type is a type defined by ios that is capable of holding the largest value that
either function can return
...

istream &seekg(pos_type pos);
ostream &seekp(pos_type pos);
These functions allow you to save the current file location, perform other file
operations, and then reset the file location to its previously saved location
...
The current state of the I/O system is held in an object of type iostate, which
is an enumeration defined by ios that includes the following members
...
First, you can
call the rdstate( ) function
...
As you can probably guess from looking
at the preceding list of flags, rdstate( ) returns goodbit when no error has occurred
...


563

564

C++: The Complete Reference

The following program illustrates rdstate( )
...
If
an error occurs, the program reports it, using checkstatus( )
...
\n";
return 1;
}
char c;
while(in
...
close();
return 0;

// check final status

}
void checkstatus(ifstream &in)
{
ios::iostate i;
i = in
...
" After the while loop ends, the final
call to checkstatus( ) reports, as expected, that an EOF has been encountered
...

The other way that you can determine if an error has occurred is by using one or
more of these functions:
bool bad( );
bool eof( );
bool fail( );
bool good( );
The bad( ) function returns true if badbit is set
...
The fail( ) returns true if failbit is set
...
Otherwise, it returns false
...
To do this, use the clear( ) function, which has this prototype:
void clear(iostate flags=ios::goodbit);
If flags is goodbit (as it is by default), all error flags are cleared
...


Customized I/O and Files
In Chapter 20 you learned how to overload the insertion and extraction operators
relative to your own classes
...

As an example, the following program reworks the phone book example in Chapter 20
so that it stores a list on disk
...
It uses custom inserters and extractors to
input and output the telephone numbers
...


565

566

C++: The Complete Reference

#include
#include
#include
using namespace std;
class phonebook {
char name[80];
char areacode[4];
char prefix[4];
char num[5];
public:
phonebook() { };
phonebook(char *n, char *a, char *p, char *nm)
{
strcpy(name, n);
strcpy(areacode, a);
strcpy(prefix, p);
strcpy(num, nm);
}
friend ostream &operator<<(ostream &stream, phonebook o);
friend istream &operator>>(istream &stream, phonebook &o);
};
// Display name and phone number
...
name << " ";
stream << "(" << o
...
prefix << "-";
stream << o
...

istream &operator>>(istream &stream, phonebook &o)
{
cout << "Enter name: ";
stream >> o
...
areacode;
cout << "Enter prefix: ";
stream >> o
...
num;
cout << "\n";
return stream;
}
int main()
{
phonebook a;
char c;
fstream pb("phone", ios::in | ios::out | ios::app);
if(!pb) {
cout << "Cannot open phone book file
...
Enter numbers\n";
cout << "2
...
Quit\n";
cout << "\nEnter a choice: ";
cin >> c;
} while(c<'1' || c>'3');
switch(c) {
case '1':
cin >> a;
cout << "Entry is: ";
cout << a; // show on screen
pb << a; // write to disk
break;
case '2':
char ch;
pb
...
eof()) {
pb
...
eof()) cout << ch;
}
pb
...
close();
return 0;
}
}
}

Notice that the overloaded << operator can be used to write to a disk file or to the
screen without any changes
...


C++

Chapter 22
Run-Time Type ID and the
Casting Operators

569

570

C++: The Complete Reference

tandard C++ contains two features that help support modern, object-oriented
programming: run-time type identification (RTTI for short) and the new casting
operators
...
RTTI allows
you to identify the type of an object during the execution of your program
...
Since one of the casting
operators, dynamic_cast, relates directly to RTTI, it makes sense to discuss them in the
same chapter
...
In nonpolymorphic languages there is no need
for run-time type information because the type of each object is known at compile
time (i
...
, when the program is written)
...
As explained in Chapter 17, C++ implements polymorphism through the use
of class hierarchies, virtual functions, and base-class pointers
...
This determination must be made at run time,
using run-time type identification
...
You must include the header in
order to use typeid
...
It may be of any type,
including the built-in types and class types that you create
...

The type_info class defines the following public members:
bool operator==(const type_info &ob);
bool operator!=(const type_info &ob);
bool before(const type_info &ob);
const char *name( );
The overloaded == and != provide for the comparison of types
...
(This function is mostly for internal use only
...
) The name( ) function returns a
pointer to the name of the type
...

// A simple example that uses typeid
...

};
class myclass2 {
//
...
name();
endl;
"The type of f is: " << typeid(f)
...
name();
endl;

cout
cout
cout
cout

<<
<<
<<
<<

"The type of ob1 is: " << typeid(ob1)
...
name();
"\n\n";

if(typeid(i) == typeid(j))
cout << "The types of i and j are the same\n";
if(typeid(i) != typeid(f))
cout << "The types of i and f are not the same\n";

571

572

C++: The Complete Reference

if(typeid(ob1) != typeid(ob2))
cout << "ob1 and ob2 are of differing types\n";
return 0;
}

The output produced by this program is shown here:
The
The
The
The
The

type
type
type
type
type

of
of
of
of
of

i is: int
f is: float
p is: char *
ob1 is: class myclass1
ob2 is: class myclass2

The types of i and j are the same
The types of i and f are not the same
ob1 and ob2 are of differing types

The most important use of typeid occurs when it is applied through a pointer of a
polymorphic base class
...
(Remember, a base-class pointer can point to objects of the base class or of any
class derived from that base
...
The following
program demonstrates this principle
...

#include
#include
using namespace std;
class Mammal {
public:
virtual bool lays_eggs() { return false; } // Mammal is polymorphic
//
...


Chapter 22:

Run-Time Type ID and the Casting Operators

};
class Platypus: public Mammal {
public:
bool lays_eggs() { return true; }
//
...
name() << endl;
p = &cat;
cout << "p is pointing to an object of type ";
cout << typeid(*p)
...
name() << endl;
return 0;
}

The output produced by this program is shown here:
p is pointing to an object of type class Mammal
p is pointing to an object of type class Cat
p is pointing to an object of type class Platypus

As explained, when typeid is applied to a base-class pointer of a polymorphic type, the
type of object pointed to will be determined at run time, as shown by the output
produced by the program
...
That is, no determination of
what that pointer is actually pointing to is made
...
You will see the following output
...

Since typeid is commonly applied to a dereferenced pointer (i
...
, one to which the *
operator has been applied), a special exception has been created to handle the situation
in which the pointer being dereferenced is null
...

References to an object of a polymorphic class hierarchy work the same as pointers
...
The
circumstance where you will most often make use of this feature is when objects are
passed to functions by reference
...
This
means that WhatMammal( ) can be passed references to objects of type Mammal or
any class derived from Mammal
...

// Use a reference with typeid
...

};
class Cat: public Mammal {
public:
//
...

};
// Demonstrate typeid with a reference parameter
...
name() << endl;
}
int main()
{
Mammal AnyMammal;
Cat cat;
Platypus platypus;
WhatMammal(AnyMammal);
WhatMammal(cat);
WhatMammal(platypus);
return 0;
}

The output produced by this program is shown here:
ob is referencing an object of type class Mammal
ob is referencing an object of type class Cat
ob is referencing an object of type class Platypus

There is a second form of typeid that takes a type name as its argument
...
name();

575

576

C++: The Complete Reference

The main use of this form of typeid is to obtain a type_info object that describes the
specified type so that it can be used in a type comparison statement
...
name() << endl;
if(typeid(ob) == typeid(Cat))
cout << "Cats don't like water
...
In the program, the function called
factory( ) creates instances of various types of objects derived from the class Mammal
...
) The specific type
of object created is determined by the outcome of a call to rand( ), C++'s random
number generator
...
The program creates 10 objects and counts the number of each type of
mammal
...

// Demonstrating run-time type id
...

};
class Cat: public Mammal {
public:
//
...


Chapter 22:

Run-Time Type ID and the Casting Operators

};
class Dog: public Mammal {
public:
//
...

Mammal *factory()
{
switch(rand() % 3 ) {
case 0: return new Dog;
case 1: return new Cat;
case 2: return new Platypus;
}
return 0;
}
int main()
{
Mammal *ptr; // pointer to base class
int i;
int c=0, d=0, p=0;
// generate and count objects
for(i=0; i<10; i++) {
ptr = factory(); // generate an object
cout << "Object is " << typeid(*ptr)
...

Object
Object
Object
Object
Object
Object
Object
Object
Object
Object

is
is
is
is
is
is
is
is
is
is

class
class
class
class
class
class
class
class
class
class

Platypus
Platypus
Cat
Cat
Platypus
Cat
Dog
Dog
Cat
Platypus

Animals generated:
Dogs: 2
Cats: 4
Platypuses: 4

typeid Can Be Applied to Template Classes
The typeid operator can be applied to template classes
...
Two instances of the same template class that are
created using different data are therefore different types
...

#include
using namespace std;
template class myclass {
T a;
public:
myclass(T i) { a = i; }
//
...
2);
cout << "Type of o1 is ";
cout << typeid(o1)
...
name() << endl;
cout << "Type of o3 is ";
cout << typeid(o3)
...

Type of o1 is class myclass
Type of o2 is class myclass
Type of o3 is class myclass
o1 and o2 are the same type
o1 and o3 are different types

As you can see, even though two objects are of the same template class type, if their
parameterized data does not match, they are not equivalent types
...
Thus, they are of
different types
...

However, when you are working with polymorphic types, it allows you to know what
type of object is being operated upon in any given situation
...
The first is the traditional-style cast inherited from
C
...
They are dynamic_cast, const_cast,
reinterpret_cast, and static_cast
...


dynamic_cast
Perhaps the most important of the new casting operators is dynamic_cast
...
If the cast is
invalid at the time dynamic_cast is executed, then the cast fails
...
The target type must be a pointer or reference type, and the
expression being cast must evaluate to a pointer or reference
...

The purpose of dynamic_cast is to perform casts on polymorphic types
...
This is because a base
pointer can always point to a derived object
...
In general,
dynamic_cast will succeed if the pointer (or reference) being cast is a pointer (or
reference) to either an object of the target type or an object derived from the target type
...
If the cast fails, then dynamic_cast evaluates to null if the
cast involves pointers
...

Here is a simple example
...

Base *bp, b_ob;
Derived *dp, d_ob;
bp = &d_ob; // base pointer points to Derived object

Chapter 22:

Run-Time Type ID and the Casting Operators

dp = dynamic_cast (bp); // cast to derived pointer OK
if(dp) cout << "Cast OK";

Here, the cast from the base pointer bp to the derived pointer dp works because bp is
actually pointing to a Derived object
...
But in the
next fragment, the cast fails because bp is pointing to a Base object and it is illegal to
cast a base object into a derived object
...

The following program demonstrates the various situations that dynamic_cast can
handle
...

#include
using namespace std;
class Base {
public:
virtual void f() { cout << "Inside Base\n"; }
//
...
\n";
dp->f();
} else

581

582

C++: The Complete Reference

cout << "Error\n";
cout << endl;
bp = dynamic_cast (&d_ob);
if(bp) {
cout << "Cast from Derived * to Base * OK
...
\n";
bp->f();
} else
cout << "Error\n";
cout << endl;
dp = dynamic_cast (&b_ob);
if(dp)
cout << "Error\n";
else
cout << "Cast from Base * to Derived * not OK
...
\n";
dp->f();
} else
cout << "Error\n";
cout << endl;

Chapter 22:

Run-Time Type ID and the Casting Operators

bp = &b_ob; // bp points to Base object
dp = dynamic_cast (bp);
if(dp)
cout << "Error";
else {
cout << "Now casting bp to a Derived *\n" <<
"is not OK because bp is really \n" <<
"pointing to a Base object
...
\n";
bp->f();
} else
cout << "Error\n";
return 0;
}

The program produces the following output:
Cast from Derived * to Derived * OK
...

Inside Derived
Cast from Base * to Base * OK
...

Casting bp to a Derived * OK
because bp is really pointing
to a Derived object
...

Casting dp to a Base * is OK
...

For example, again assume that Base is a polymorphic base class for Derived
...

Base *bp;
Derived *dp;
//
...
This is safe
because the if statement checks the legality of the cast using typeid before the cast
actually occurs
...

dp = dynamic_cast (bp);

Since dynamic_cast succeeds only if the object being cast is either an object of the target
type or an object derived from the target type, after this statement executes dp will
contain either a null or a pointer to an object of type Derived
...
The
following program illustrates how a dynamic_cast can be used to replace typeid
...

// Use dynamic_cast to replace typeid
...
\n";
}
};
int main()
{
Base *bp, b_ob;
Derived *dp, d_ob;
// ************************************
// use typeid
// ************************************
bp = &b_ob;
if(typeid(*bp) == typeid(Derived)) {
dp = (Derived *) bp;
dp->derivedOnly();
}
else
cout << "Cast from Base to Derived failed
...
\n";

585

586

C++: The Complete Reference

bp = &d_ob;
dp = dynamic_cast (bp);
if(dp) dp->derivedOnly();
else
cout << "Error, cast should work!\n";
return 0;
}

As you can see, the use of dynamic_cast simplifies the logic required to cast a base
pointer into a derived pointer
...

Derived Object
...

Derived Object
...
For example,
// Demonstrate dynamic_cast on template classes
...

};
template class SqrNum : public Num {
public:
SqrNum(T x) : Num(x) { }
T getval() { return val * val; }
};
int main()

Chapter 22:

Run-Time Type ID and the Casting Operators

{
Num *bp, numInt_ob(2);
SqrNum *dp, sqrInt_ob(3);
Num numDouble_ob(3
...
\n";
cout << "Value is " << bp->getval() << endl;
} else
cout << "Error\n";
cout << endl;
dp = dynamic_cast *> (&numInt_ob);
if(dp)
cout << "Error\n";
else {
cout << "Cast from Num* to SqrNum* not OK
...
\n";
}
cout << endl;
bp = dynamic_cast *> (&numDouble_ob);
if(bp)
cout << "Error\n";
else
cout << "Can't cast from Num* to Num*
...
\n";
return 0;
}

The output from this program is shown here:
Cast from SqrNum* to Num* OK
...

Can't cast a pointer to a base object into

587

588

C++: The Complete Reference

a pointer to a derived object
...

These are two different types
...
Remember, the precise type of an object of a template class is determined by
the type of data used to create an instance of the template
...


const_cast
The const_cast operator is used to explicitly override const and/or volatile in a cast
...
The most common use of const_cast is to remove const-ness
...

const_cast (expr)
Here, type specifies the target type of the cast, and expr is the expression being cast into
the new type
...

// Demonstrate const_cast
...

p = const_cast (val);
*p = *val * *val; // now, modify object through v
}
int main()
{

Chapter 22:

Run-Time Type ID and the Casting Operators

int x = 10;
cout << "x before call: " << x << endl;
sqrval(&x);
cout << "x after call: " << x << endl;
return 0;
}

The output produced by this program is shown here:
x before call: 10
x after call: 100

As you can see, x was modified by sqrval( ) even though the parameter to sqrval( ) was
specified as a const pointer
...
For
example, here is the preceding program reworked so that the value being squared is
passed as a const reference
...

#include
using namespace std;
void sqrval(const int &val)
{
// cast away const on val
const_cast (val) = val * val;
}
int main()
{
int x = 10;
cout << "x before call: " << x << endl;
sqrval(x);
cout << "x after call: " << x << endl;
return 0;
}

589

590

C++: The Complete Reference

This program produces the same output as before
...

It must be stressed that the use of const_cast to cast way const-ness is a potentially
dangerous feature
...

One other point: Only const_cast can cast away const-ness
...


static_cast
The static_cast operator performs a nonpolymorphic cast
...
No run-time checks are performed
...

The static_cast operator is essentially a substitute for the original cast operator
...
For example, the following casts an int value
into a double
...

#include
using namespace std;
int main()
{
int i;
for(i=0; i<10; i++)
cout << static_cast (i) / 3 << " ";
return 0;
}

reinterpret_cast
The reinterpret_cast operator converts one type into a fundamentally different type
...
It can
also be used for casting inherently incompatible pointer types
...

The following program demonstrates the use of reinterpret_cast:
// An example that uses reinterpret_cast
...
This conversion
represents a fundamental type change and is a good use of reinterpret_cast
...


C++

Chapter 23
Namespaces,
Conversion Functions,
and Other Advanced Topics

593

594

C++: The Complete Reference

his chapter describes namespaces and several other advanced features, including
conversion functions, explicit constructors, const and volatile member functions,
the asm keyword, and linkage specifications
...


T

Namespaces
Namespaces were briefly introduced earlier in this book
...
Their purpose is to localize the names of identifiers to avoid name
collisions
...
Prior to the invention of namespaces, all of these names
competed for slots in the global namespace and many conflicts arose
...
Name collisions were compounded when two or more
third-party libraries were used by the same program
...
The situation can be particularly troublesome for class
names
...

The creation of the namespace keyword was a response to these problems
...
Perhaps the most
noticeable beneficiary of namespace is the C++ standard library
...
Since the addition of namespace, the C++ library is now defined
within its own namespace, called std, which reduces the chance of name collisions
...
This is especially important if you are
creating class or function libraries
...
In essence, a namespace defines a scope
...

Here is an example of a namespace
...
In the namespace are defined the counter class, which
implements the counter, and the variables upperbound and lowerbound, which
contain the upper and lower bounds that apply to all counters
...

Inside a namespace, identifiers declared within that namespace can be referred to
directly, without any namespace qualification
...


595

596

C++: The Complete Reference

For example, to assign the value 10 to upperbound from code outside
CounterNameSpace, you must use this statement:
CounterNameSpace::upperbound = 10;

Or to declare an object of type counter from outside CounterNameSpace, you will use
a statement like this:
CounterNameSpace::counter ob;

In general, to access a member of a namespace from outside its namespace, precede the
member's name with the name of the namespace followed by the scope resolution
operator
...

// Demonstrate a namespace
...
run();
cout << i << " ";
} while(i > CounterNameSpace::lowerbound);
cout << endl;
CounterNameSpace::counter ob2(20);
do {
i = ob2
...
reset(100);
CounterNameSpace::lowerbound = 90;
do {
i = ob2
...
However, once an object of type
counter has been declared, it is not necessary to further qualify it or any of its
members
...
run( ) can be called directly; the namespace has already
been resolved
...
The using statement
was invented to alleviate this problem
...
All of
the members defined within the specified namespace are brought into view (i
...
, they
become part of the current namespace) and may be used without qualification
...
For example,
assuming CounterNameSpace as shown above, the following using statements and
assignments are valid
...

// Demonstrate using
...

CounterNameSpace::lowerbound = 0;
CounterNameSpace::counter ob1(10);
int i;
do {
i = ob1
...
run();
cout << i << " ";
} while(i > lowerbound);
cout << endl;
ob2
...
run();
cout << i << " ";
} while(i > lowerbound);
return 0;
}

The program illustrates one other important point: using one namespace does not
override another
...
Thus, by the end of the program,
both std and CounterNameSpace have been added to the global namespace
...
Unnamed namespaces are also called
anonymous namespaces
...
That is, within the file that contains the unnamed
namespace, the members of that namespace may be used directly, without
qualification
...

Unnamed namespaces eliminate the need for certain uses of the static storage class
modifier
...
For example, consider the following two
files that are part of the same program
...
In File Two, k is specified
as extern, which means that its name and type are known but that k itself is not

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

actually defined
...
By preceding k with static in File
One, its scope is restricted to that file and it is not available to File Two
...
For example:

File One

File Two

namespace {
int k;
}
void f1() {
k = 99; // OK
}

extern int k;
void f2() {
k = 10; // error
}

Here, k is also restricted to File One
...


Some Namespace Options
There may be more than one namespace declaration of the same name
...

For example:
#include
using namespace std;
namespace NS {
int i;
}
//
...
However, the contents of each piece are still within
the same namespace, that is, NS
...
This means that you
cannot declare namespaces that are localized to a function, for example
...
Consider
this program:
#include
using namespace std;
namespace NS1 {
int i;
namespace NS2 { // a nested namespace
int j;
}
}
int main()
{
NS1::i = 19;
// NS2::j = 10; Error, NS2 is not in view
NS1::NS2::j = 10; // this is right
cout << NS1::i << " "<<
// use NS1

NS1::NS2::j << "\n";

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

using namespace NS1;
/* Now that NS1 is in view, NS2 can be used to
refer to j
...
Thus, when the program begins, to
refer to j, you must qualify it with both the NS1 and NS2 namespaces
...
After the statement
using namespace NS1;

executes, you can refer directly to NS2 since the using statement brings NS1 into view
...
However, if you will be creating libraries of reusable code or it you want to
ensure the widest portability, then consider wrapping your code within a namespace
...
This is the
reason that most of the programs in this book include the following statement:
using namespace std;

This causes the std namespace to be brought into the current namespace, which gives
you direct access to the names of the functions and classes defined within the library
without having to qualify each one with std::
...
For example,
the following program does not bring the library into the global namespace
...


603

604

C++: The Complete Reference

#include
int main()
{
int val;
std::cout << "Enter a number: ";
std::cin >> val;
std::cout << "This is your number: ";
std::cout << std::hex << val;
return 0;
}

Here, cout, cin, and the manipulator hex are explicitly qualified by their namespace
...

You may not want to bring the standard C++ library into the global namespace if
your program will be making only limited use of it
...

If you are using only a few names from the standard library, it may make more
sense to specify a using statement for each individually
...
For example:
// Bring only a few names into the global namespace
...

As explained, the original C++ library was defined in the global namespace
...
This
is especially important if you are replacing old
...
Remember, the old
...


Creating Conversion Functions
In some situations, you will want to use an object of a class in an expression involving
other types of data
...
However, in other cases, what you want is a simple type conversion from
the class type to the target type
...
A conversion function converts your class into a type
compatible with that of the rest of the expression
...
Conversion functions return data of type type, and no
other return type specifier is allowed
...
A
conversion function must be a member of the class for which it is defined
...

The following illustration of a conversion function uses the stack class first
developed in Chapter 11
...
Further, suppose that the value of a stack object used in
an integer expression is the number of values currently on the stack
...
) One way to approach this is to
convert an object of type stack into an integer that represents the number of items
on the stack
...
\n";
return;
}
stck[tos] = i;
tos++;
}
int stack::pop()
{
if(tos==0) {
cout << "Stack underflow
...
push(i);

j = stck; // convert to integer
cout << j << " items on stack
...
\n";
return 0;
}

This program displays this output:
20 items on stack
...


As the program illustrates, when a stack object is used in an integer expression,
such as j = stck, the conversion function is applied to the object
...
Also, when stck is subtracted from SIZE,
the conversion function is also called
...
This program creates a class
called pwr( ) that stores and computes the outcome of some number raised to some
power
...
By supplying a conversion function to type
double and returning the result, you can use objects of type pwr in expressions
involving other double values
...
b;
exp = e + o
...
0, 2);
double a;
a = x; // convert to double
cout << x + 100
...
2
cout << "\n";
pwr y(3
...

116
...
7

As you can see, when x is used in the expression x + 100
...
Notice also that in the expression x + y, no
conversion is applied because the expression involves only objects of type pwr
...
Often, conversion functions
provide a more natural syntax to be used when class objects are mixed with the built-in
types
...

You can create different conversion functions to meet different needs
...
Each will be applied
automatically as determined by the type of each expression
...
Thus, that function cannot modify the object that invokes it
...
However, a const member
function can be called by either const or non-const objects
...

class X {
int some_var;
public:
int f1() const; // const member function
};

As you can see, the const follows the function's parameter declaration
...
For example, consider the following program
...


609

610

C++: The Complete Reference

This program won't compile
...
seti(1900);
cout << ob
...
This means that it is
not allowed to modify the invoking object
...
In contrast, since geti( ) does not attempt to modify i, it is perfectly acceptable
...
You can accomplish this through the use of mutable
...
That is, a mutable member can be modified by a const member
function
...

#include
using namespace std;
class Demo {

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

mutable int i;
int j;
public:
int geti() const {
return i; // ok
}
void seti(int x) const {
i = x; // now, OK
...

void setj(int x) const {
j = x; // Still Wrong!
}
*/
};
int main()
{
Demo ob;
ob
...
geti();
return 0;
}

Here, i is specified as mutable, so it may be changed by the seti( ) function
...


Volatile Member Functions
Class member functions may be declared as volatile, which causes this to be treated as
a volatile pointer
...
The reason for this is
that whenever you create a constructor that takes one argument, you are also implicitly
creating a conversion from the type of that argument to the type of the class
...
For this
purpose, C++ defines the keyword explicit
...

#include
using namespace std;
class myclass {
int a;
public:
myclass(int x) { a = x; }
int geta() { return a; }
};
int main()
{
myclass ob = 4; // automatically converted into myclass(4)
cout << ob
...
Pay special attention to how
ob is declared in main( )
...
That is, the preceding statement is handled by the compiler as if it were
written like this:
myclass ob(4);

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

If you do not want this implicit conversion to be made, you can prevent it by using
explicit
...
A constructor specified as
explicit will only be used when an initialization uses the normal constructor syntax
...
For example, by declaring the myclass
constructor as explicit, the automatic conversion will not be supplied
...

#include
using namespace std;
class myclass {
int a;
public:
explicit myclass(int x) { a = x; }
int geta() { return a; }
};

Now, only constructors of the form
myclass ob(4);

will be allowed and a statement like
myclass ob = 4; // now in error

will be invalid
...
(For example, there is no C++
statement that disables interrupts
...
This "trap door" is the asm statement
...
This assembly code is compiled
without any modification, and it becomes part of your program's code at the point at
which the asm statement occurs
...
However, several compilers also allow the following forms of asm:
asm instruction ;
asm instruction newline
asm {
instruction sequence
}
Here, instruction is any valid assembly language instruction
...

At the time of this writing, Microsoft's Visual C++ uses _ _asm for embedding
assembly code
...

Here is a simple (and fairly "safe") example that uses the asm keyword:
#include
using namespace std;
int main()
{
asm int 5; // generate intertupt 5
return 0;
}

When run under DOS, this program generates an INT 5 instruction, which invokes the
print-screen function
...
If you are not proficient with assembly language, it is best
to avoid using asm because very nasty errors may result
...
By default,
functions are linked as C++ functions
...
The general form of a
linkage specifier is
extern "language" function-prototype

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

where language denotes the desired language
...
Some will also allow linkage specifiers for Fortran, Pascal, or BASIC
...
)
This program causes myCfunc( ) to be linked as a C function
...

void myCfunc()
{
cout << "This links as a C function
...
Further, the
linkage specification must be global; it cannot be used inside of a function
...
Array-based I/O uses a character array as either the input device, the output
device, or both
...
In fact,
everything you already know about C++ I/O is applicable to array-based I/O
...
Streams that are linked to character arrays are commonly

615

616

C++: The Complete Reference

referred to as char * streams
...


Note

The character-based stream classes described in this section are deprecated by
Standard C++
...
This brief discussion is included because they are presently in wide use
...
These classes are
used to create input, output, and input/output streams, respectively
...
Therefore, all array-based classes are
indirectly derived from ios and have access to the same member functions that the
"normal" I/O classes do
...
The size of the array is passed in the size parameter
...
For example, you might include ios::app to cause output to be
written at the end of any information already contained in the array
...

Once you have opened an array-based output stream, all output to that stream is
put into the array
...

Attempting to do so will result in an error
...

#include
#include
using namespace std;
int main()
{
char str[80];
ostrstream outs(str, sizeof(str));

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

outs << "C++ array-based I/O
...
setf(ios::showbase);
outs << 100 << ' ' << 99
...
1024 0x64 99
...
The only difference is that the
device that it is linked to is a character array
...
ostream member functions, such as setf( ), are
also available for use
...

Whether the array will be automatically null terminated or not depends on the
implementation, so it is best to perform null termination manually if it is important to
your application
...
It has this prototype:
streamsize pcount( );
The number returned by pcount( ) also includes the null terminator, if it exists
...
It reports that outs contains 18
characters: 17 characters plus the null terminator
...
23;
outs << ends; // null terminate
cout << outs
...
The contents of the array pointed to by buf must
be null terminated
...

Here is a sample program that uses a string as input
...
73 OK";
istrstream ins(s);
int i;
char str[80];
float f;
// reading: 10 Hello
ins >> i;
ins >> str;
cout << i << " " << str << endl;

Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

// reading 0x75 42
...
This string
need not be null terminated, since it is the value of size that determines the size of
the string
...
For
example, the following program demonstrates how the contents of any text array can
be read
...

/* This program shows how to read the contents of any
array that contains text
...
23 this is a test <<>><istrstream ins(s);
char ch;
/* This will read and display the contents
of any text array
...
unsetf(ios::skipws); // don't skip spaces
while (ins) { // false when end of array is reached

619

620

C++: The Complete Reference

ins >> ch;
cout << ch;
}
return 0;
}

Input/Output Array-Based Streams
To create an array-based stream that can perform both input and output, use this
strstream constructor function:
strstream iostr(char *buf, streamsize size, openmode mode = ios::in | ios::out);
Here, buf points to the string that will be used for I/O operations
...
The value of mode determines how the stream iostr
operates
...
For input,
the array must be null terminated
...

// Perform both input and output
...


Chapter 23:

Namespaces, Conversion Functions, and Other Advanced Topics

Using Dynamic Arrays
In the preceding examples, when you linked a stream to an output array, the array and
its size were passed to the ostrstream constructor
...

However, what if you don't know how large the output array needs to be? The solution
to this problem is to use a second form of the ostrstream constructor, shown here:
ostrstream( );
When this constructor is used, ostrstream creates and maintains a dynamically
allocated array, which automatically grows in length to accommodate the output that it
must store
...
You use the pointer
returned by str( ) to access the dynamic array as a string
...
Therefore,
you will not want to freeze the array until you are through outputting characters to it
...

#include
#include
using namespace std;
int main()
{
char *p;
ostrstream outs;

// dynamically allocate array

outs << "C++ array-based I/O ";
outs << -10 << hex << " ";
outs
...
str(); // Freeze dynamic buffer and return
// pointer to it
...

It is possible to freeze or unfreeze a dynamic array by calling the freeze( ) function
...
If action is false, the array is unfrozen
...
Therefore, arrays linked to array-based streams can also contain binary
information
...
For example, the following
program shows how to read the contents of any array—binary or text—using the
function get( )
...
eof()) {
ins
...

To output binary characters, use the put( ) function
...
To write buffers of binary data,
use the write( ) function
...
However, a few differences do exist, and these have
been discussed throughout Parts One and Two of this book
...

In C++, local variables can be declared anywhere within a block
...

In C, a function declared like
int f();

says nothing about any parameters to that function
...
It
might have parameters, or it might not
...
That is, in C++, these two
declarations are equivalent:
int f();
int f(void);

In C++, void in a parameter list is optional
...

In C++, all functions must be prototyped
...

A small but potentially important difference between C and C++ is that in C, a
character constant is automatically elevated to an integer
...


623

624

C++: The Complete Reference

In C, it is not an error to declare a global variable several times, even though this is
bad programming practice
...

In C, an identifier will have at least 31 significant characters
...
However, from a practical point of view, extremely long identifiers are
unwieldy and seldom needed
...
This is
not allowed by C++
...
In C++, this is allowed
...
This "default-to-int" rule no longer applies to C++
...
)

C++

Chapter 24
Introducing the Standard
Template Library

625

626

C++: The Complete Reference

his chapter explores what is considered by many to be the most important new
feature added to C++ in recent years: the standard template library (STL)
...
It provides general-purpose, templatized classes and functions
that implement many popular and commonly used algorithms and data structures,
including, for example, support for vectors, lists, queues, and stacks
...
Because the STL is constructed from template
classes, the algorithms and data structures can be applied to nearly any type of data
...
To understand and use the STL, you must have a complete
understanding of the C++ language, including pointers, references, and templates
...
While there is nothing in this
chapter that is any more difficult than the material in the rest of this book, don't be
surprised or dismayed if you find the STL confusing at first
...

The purpose of this chapter is to present an overview of the STL, including its
design philosophy, organization, constituents, and the programming techniques
needed to use it
...
However, a complete reference to the STL is provided in Part Four
...
The
string class defines a string data type that allows you to work with character strings
much as you do other data types: using operators
...


T

An Overview of the STL
Although the standard template library is large and its syntax can be intimidating, it is
actually quite easy to use once you understand how it is constructed and what
elements it employs
...

At the core of the standard template library are three foundational items: containers,
algorithms, and iterators
...


Containers
Containers are objects that hold other objects, and there are several different types
...
These containers are called sequence containers
because in STL terminology, a sequence is a linear list
...
For example, a map provides access to values with unique keys
...

Each container class defines a set of functions that may be applied to the container
...

A stack includes functions that push and pop values
...
They provide the means by which you will manipulate
the contents of containers
...
Many algorithms operate on a range of
elements within a container
...
They give you the ability to cycle
through the contents of a container in much the same way that you would use a
pointer to cycle through an array
...
Elements may be accessed randomly
...
Forward and backward moving
...
Forward moving only
...
Forward moving only
...
Forward moving only
...
For example, a forward iterator can be used in place of an
input iterator
...
You can increment and decrement them
...
Iterators are declared using the iterator type
defined by the various containers
...
Reverse iterators are either bidirectional or
random-access iterators that move through a sequence in the reverse direction
...

When referring to the various iterator types in template descriptions, this book will
use the following terms:

627

628

C++: The Complete Reference

Term

Represents

BiIter

Bidirectional iterator

ForIter

Forward iterator

InIter

Input iterator

OutIter

Output iterator

RandIter

Random access iterator

Other STL Elements
In addition to containers, algorithms, and iterators, the STL relies upon several other
standard components for support
...

Each container has defined for it an allocator
...
The default allocator is an object of class allocator, but you can define
your own allocators if needed by specialized applications
...

Several of the algorithms and containers use a special type of function called a
predicate
...
A unary predicate
takes one argument, while a binary predicate has two
...
But the precise conditions that make them return true or false are defined by
you
...
When a binary predicate is required, the type BinPred
will be used
...

For both unary and binary predicates, the arguments will contain values of the type of
objects being stored by the container
...
Comparison functions return true if their first argument is less than their
second
...

In addition to the headers required by the various STL classes, the C++ standard
library includes the and headers, which provide support for the
STL
...
We will make use of pair later in this chapter
...

These are called function objects and they may be used in place of function pointers in
many places
...
They are shown here:
plus

minus

multiplies

divides

modulus

negate

equal_to

not_equal_to

greater

greater_equal

less

less_equal

logical_and

logical_or

logical_not

Chapter 24:

Introducing the Standard Template Library

Perhaps the most widely used function object is less, which determines when one
object is less than another
...
Using function objects rather than
function pointers allows the STL to generate more efficient code
...
A binder binds an
argument to a function object
...

One final term to know is adaptor
...
For example, the container queue (which creates a standard queue) is an
adaptor for the deque container
...
The containers
defined by the STL are shown in Table 24-1
...
The string class, which manages character strings, is also a
container, but it is discussed later in this chapter
...




deque

A double-ended queue
...




map

Stores key/value pairs in which each key is
associated with only one value
...




multiset

A set in which each element is not
necessarily unique
...




queue

A queue
...




stack

A stack
...




Table 24-1
...
This makes
the type names concrete
...
First, you must decide on the type of container that you wish
to use
...
For example, a vector is very
good when a random-access, array-like object is required and not too many insertions
or deletions are needed
...
A map provides an associative container, but of course incurs additional
overhead
...
Except
for bitset, a container will automatically grow as needed when elements are added to it
and shrink when elements are removed
...
For example, both the sequence containers (vector, list, and deque) and the
associative containers (map, multimap, set, and multiset) provide a member function
called insert( ), which inserts elements into a container, and erase( ), which removes
elements from a container
...
These functions are probably the most common way that individual
elements are added to a sequence container
...

One of the most common ways to access the elements within a container is through
an iterator
...
These iterators are very useful when accessing the contents of a container
...

The associative containers provide the function find( ), which is used to locate an
element in an associative container given its key
...

Since a vector is a dynamic array, it also supports the standard array-indexing
syntax for accessing its elements
...
The algorithms not only allow you to alter the contents of a
container in some prescribed fashion, but they also let you transform one type of
sequence into another
...
Once you understand how these
containers work, you will have no trouble using the others
...
The vector class supports
a dynamic array
...
As you know, in C++ the
size of an array is fixed at compile time
...
A vector solves
this problem by allocating memory as needed
...

The template specification for vector is shown here:
template > class vector
Here, T is the type of data being stored and Allocator specifies the allocator, which
defaults to the standard allocator
...
The second form constructs a vector that
has num elements with the value val
...
The
third form constructs a vector that contains the same elements as ob
...

Any object that will be stored in a vector must define a default constructor
...
Some compilers may require that other
comparison operators be defined
...
) All of the built-in types automatically satisfy
these requirements
...
Here are some examples:
vector iv;
vector cv(5);
vector cv(5, 'x');
vector iv2(iv);

//
//
//
//

create zero-length int vector
create 5-element char vector
initialize a 5-element char vector
create int vector from an int vector

The following comparison operators are defined for vector:
==, <, <=, !=, >, >=
The subscripting operator [ ] is also defined for vector
...

Several of the member functions defined by vector are shown in Table 24-2
...
) Some of the
most commonly used member functions are size( ), begin( ), end( ), push_back( ),
insert( ), and erase( )
...
This
function is quite useful because it allows you to determine the size of a vector at run
time
...

The begin( ) function returns an iterator to the start of the vector
...
As explained, iterators are similar
to pointers, and it is through the use of the begin( ) and end( ) functions that you
obtain an iterator to the beginning and end of a vector
...
If necessary, the
vector is increased in length to accommodate the new element
...
A vector can also be initialized
...
You can remove elements from a vector using erase( )
...


iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the vector
...


bool empty( ) const;

Returns true if the invoking vector is
empty and false otherwise
...


iterator erase(iterator i);

Removes the element pointed to by i
...


iterator erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


reference front( );
const_reference front( ) const;

Returns a reference to the first element
in the vector
...
An iterator to
the element is returned
...


template
void insert(iterator i, InIter start,
InIter end);

Inserts the sequence defined by start
and end immediately before the
element specified by i
...


void pop_back( );

Removes the last element in the vector
...


size_type size( ) const;

Returns the number of elements
currently in the vector
...


Some Commonly Used Member Functions Defined by vector

633

634

C++: The Complete Reference

Here is a short example that illustrates the basic operation of a vector
...

#include
#include
#include
using namespace std;
int main()
{
vector v(10); // create a vector of length 10
int i;
// display original size of v
cout << "Size = " << v
...
size(); i++) cout << v[i] << " ";
cout << "\n\n";
cout << "Expanding vector\n";
/* put more values onto the end of the vector,
it will grow as needed */
for(i=0; i<10; i++) v
...
size() << endl;
// display contents of vector
cout << "Current contents:\n";
for(i=0; i ...
size(); i++) v[i] = toupper(v[i]);
cout << "Modified Contents:\n";
for(i=0; i ...
In main( ), a character vector called v is created
with an initial capacity of 10
...
This is confirmed
by calling the size( ) member function
...
Notice that the standard
array subscripting notation is employed
...
This causes v to grow in order to accommodate
the new elements
...
Finally, the
values of v's elements are altered using standard subscripting notation
...
Notice that the loops that
display the contents of v use as their target value v
...
One of the advantages that
vectors have over arrays is that it is possible to find the current size of a vector
...


Accessing a Vector Through an Iterator
As you know, arrays and pointers are tightly linked in C++
...
The parallel to this in the STL is the
link between vectors and iterators
...
The following example shows how
...

#include
#include
#include

635

636

C++: The Complete Reference

using namespace std;
int main()
{
vector v(10); // create a vector of length 10
vector::iterator p; // create an iterator
int i;
// assign elements in vector a value
p = v
...
end()) {
*p = i + 'a';
p++;
i++;
}
// display contents of vector
cout << "Original contents:\n";
p = v
...
end()) {
cout << *p << " ";
p++;
}
cout << "\n\n";
// change contents of vector
p = v
...
end()) {
*p = toupper(*p);
p++;
}
// display contents of vector
cout << "Modified Contents:\n";
p = v
...
end()) {
cout << *p << " ";
p++;
}
cout << endl;

Chapter 24:

Introducing the Standard Template Library

return 0;
}

The output from this program is
Original contents:
a b c d e f g h i j
Modified Contents:
A B C D E F G H I J

In the program, notice how the iterator p is declared
...
Thus, to obtain an iterator for a particular container, you will use
a declaration similar to that shown in the example: simply qualify iterator with the
name of the container
...
This function returns an iterator to the start of
the vector
...
This process is directly parallel to the way a pointer can be
used to access the elements of an array
...
This function returns an
iterator to the location that is one past the last element in the vector
...
end( ), the end of the vector has been reached
...
You can also remove elements using erase( )
...

// Demonstrate insert and erase
...
push_back(str[i]);
// display original contents of vector
cout << "Original contents of v:\n";
for(i=0; i ...
begin();
p += 2; // point to 3rd element
// insert 10 X's into v
v
...
size() << endl;
cout << "Contents after insert:\n";
for(i=0; i ...
begin();
p += 2; // point to 3rd element
v
...
size() << endl;
cout << "Contents after erase:\n";
for(i=0; i ...
insert(p, v2
...
end());
cout << "Size after v2's insertion = ";
cout << v
...
size(); i++) cout << v[i] << " ";
cout << endl;

Chapter 24:

Introducing the Standard Template Library

return 0;
}

This program produces the following output:
Original contents of v:
a b c d e f g h i j
Size after inserting X's = 20
Contents after insert:
a b X X X X X X X X X X c d e f g h i j
Size after erase = 10
Contents after erase:
a b c d e f g h i j
Size after v2's insertion = 18
Contents after insert:
a b < V e c t o r > c d e f g h i j

This program demonstrates two forms of insert( )
...
The second time, it inserts the contents of a second vector, v2, into v
...
It takes three iterator arguments
...
The last two
point to the beginning and ending of the sequence to be inserted
...
They can store any type of objects, including
those of classes that you create
...
Notice that DailyTemp defines the
default constructor and that overloaded versions of < and == are provided
...

// Store a class object in a vector
...
get_temp() < b
...
get_temp() == b
...
push_back(DailyTemp(60 + rand()%30));
cout << "Farenheit temperatures:\n";
for(i=0; i ...
get_temp() << " ";
cout << endl;
// convert from Farenheit to Centigrade
for(i=0; i ...
get_temp()-32) * 5/9 ;
cout << "Centigrade temperatures:\n";
for(i=0; i ...
get_temp() << " ";
return 0;
}

The output from this program is shown here:
Farenheit temperatures:
71 77 64 70 89 64 78
Centigrade temperatures:
21 25 17 21 31 17 25

Vectors offer great power, safety, and flexibility, but they are less efficient than
normal arrays
...
But watch for situations in which the benefits of using a vector outweigh
its costs
...
Unlike a vector, which supports
random access, a list can be accessed sequentially only
...

A list has this template specification:
template > class list
Here, T is the type of data stored in the list
...
It has the following constructors:
explicit list(const Allocator &a = Allocator( ) );
explicit list(size_type num, const T &val = T ( ),
const Allocator &a = Allocator( ));
list(const list &ob);
template list(InIter start, InIter end,
const Allocator &a = Allocator( ));
The first form constructs an empty list
...
The third form constructs
a list that contains the same elements as ob
...


641

642

C++: The Complete Reference

The following comparison operators are defined for list:
==, <, <=, !=, >, >=
Some of the commonly used list member functions are shown in Table 24-3
...
You can

Member

Description

reference back( );
const_reference back( ) const;

Returns a reference to the last element in
the list
...


void clear( );

Removes all elements from the list
...


iterator end( );
const_iterator end( ) const;

Returns an iterator to the end of the list
...

Returns an iterator to the element after
the one removed
...
Returns an iterator to the element
after the last element removed
...


iterator insert(iterator i,
const T &val);

Inserts val immediately before the
element specified by i
...


void insert(iterator i, size_type num,
const T &val)

Inserts num copies of val immediately
before the element specified by i
...


Table 24-3
...
The result
is ordered
...
In the second
form, a comparison function can be
specified that determines when one
element is less than another
...


void pop_front( );

Removes the first element in the list
...


void push_front(const T &val);

Adds an element with the value specified
by val to the front of the list
...


void reverse( );

Reverses the invoking list
...


void sort( );
template
void sort(Comp cmpfn);

Sorts the list
...


void splice(iterator i,
list &ob);

The contents of ob are inserted into the
invoking list at the location pointed to by
i
...


void splice(iterator i,
list &ob,
iterator el);

The element pointed to by el is removed
from the list ob and stored in the invoking
list at the location pointed to by i
...


Table 24-3
...
An element can also be
inserted into the middle of a list by using insert( )
...
One list may be merged into another using merge( )
...
It must
also define the various comparison operators
...

Here is a simple example of a list
...

#include
#include
using namespace std;
int main()
{
list lst; // create an empty list
int i;
for(i=0; i<10; i++) lst
...
size() << endl;
cout << "Contents: ";
list::iterator p = lst
...
end()) {
cout << *p << " ";
p++;
}
cout << "\n\n";
// change contents of list
p = lst
...
end()) {
*p = *p + 100;
p++;
}
cout << "Contents modified: ";
p = lst
...
end()) {
cout << *p << " ";

Chapter 24:

Introducing the Standard Template Library

p++;
}
return 0;
}

The output produced by this program is shown here:
Size = 10
Contents: 0 1 2 3 4 5 6 7 8 9
Contents modified: 100 101 102 103 104 105 106 107 108 109

This program creates a list of integers
...
Next, 10
integers are put into the list
...
Next, the size of the list and
the list itself is displayed
...
begin();
while(p != lst
...
Each time through the
loop, p is incremented, causing it to point to the next element
...
This code is essentially the same as was used to cycle
through a vector using an iterator
...


Understanding end( )
Now is a good time to emphasize a somewhat unexpected attribute of the end( )
container function
...

Instead, it returns a pointer one past the last element
...
This feature allows us to write very efficient
algorithms that cycle through all of the elements of a container, including the last one,
using an iterator
...
However, you must keep this feature in
mind since it may seem a bit counterintuitive
...


645

646

C++: The Complete Reference

// Understanding end()
...
push_back(i);
cout << "List printed forwards:\n";
list::iterator p = lst
...
end()) {
cout << *p << " ";
p++;
}
cout << "\n\n";
cout << "List printed backwards:\n";
p = lst
...
begin()) {
p--; // decrement pointer before using
cout << *p << " ";
}
return 0;
}

The output produced by this program is shown here:
List printed forwards:
0 1 2 3 4 5 6 7 8 9
List printed backwards:
9 8 7 6 5 4 3 2 1 0

The code that displays the list in the forward direction is the same as we have been
using
...
The
iterator p is initially set to the end of the list through the use of the end( ) function
...
This is why p is decremented
before the cout statement inside the loop, rather than after
...


push_front( ) vs push_back( )
You can build a list by adding elements to either the end or the start of the list
...
To add elements to
the start, use push_front( )
...
*/
#include
#include
using namespace std;
int main()
{
list lst1, lst2;
int i;
for(i=0; i<10; i++) lst1
...
push_front(i);
list::iterator p;
cout << "Contents of lst1:\n";
p = lst1
...
end()) {
cout << *p << " ";
p++;
}
cout << "\n\n";
cout << "Contents of lst2:\n";
p = lst2
...
end()) {
cout << *p << " ";
p++;
}

647

648

C++: The Complete Reference

return 0;
}

The output produced by this program is shown here:
Contents of lst1:
0 1 2 3 4 5 6 7 8 9
Contents of lst2:
9 8 7 6 5 4 3 2 1 0

Since lst2 is built by putting elements onto its front, the resulting list is in the reverse
order of lst1, which is built by putting elements onto its end
...
The following program
creates a list of random integers and then puts the list into sorted order
...

#include
#include
#include
using namespace std;
int main()
{
list lst;
int i;
// create a list of random integers
for(i=0; i<10; i++)
lst
...
begin();
while(p != lst
...
sort();
cout << "Sorted contents:\n";
p = lst
...
end()) {
cout << *p << " ";
p++;
}
return 0;
}

Here is sample output produced by the program:
Original contents:
41 18467 6334 26500 19169 15724 11478 29358 26962 24464
Sorted contents:
41 6334 11478 15724 18467 19169 24464 26500 26962 29358

Merging One List with Another
One ordered list may be merged with another
...
The new list is left in the invoking list,
and the second list is left empty
...
The first contains
the even numbers between 0 and 9
...
These lists
are then merged to produce the sequence 0 1 2 3 4 5 6 7 8 9
...

#include
#include
using namespace std;
int main()
{
list lst1, lst2;
int i;

649

650

C++: The Complete Reference

for(i=0; i<10; i+=2) lst1
...
push_back(i);
cout << "Contents of lst1:\n";
list::iterator p = lst1
...
end()) {
cout << *p << " ";
p++;
}
cout << endl << endl;
cout << "Contents of lst2:\n";
p = lst2
...
end()) {
cout << *p << " ";
p++;
}
cout << endl << endl;
// now, merge the two lists
lst1
...
empty())
cout << "lst2 is now empty\n";
cout << "Contents of lst1 after merge:\n";
p = lst1
...
end()) {
cout << *p << " ";
p++;
}
return 0;
}

The output produced by this program is shown here:
Contents of lst1:
0 2 4 6 8
Contents of lst2:
1 3 5 7 9

Chapter 24:

Introducing the Standard Template Library

lst2 is now empty
Contents of lst1 after merge:
0 1 2 3 4 5 6 7 8 9

One other thing to notice about this example is the use of the empty( ) function
...
Since merge( ) removes all of the
elements from the list being merged, it will be empty after the merge is completed, as
the program output confirms
...
Notice that the <, >,
!=, and == are overloaded for objects of type myclass
...
) Other compilers may require additional ones
...
Even though
a list is not an ordered container, it still needs a way to compare elements when
searching, sorting, or merging
...

#include
#include
#include
using namespace std;
class myclass {
int a, b;
int sum;
public:
myclass() { a = b = 0; }
myclass(int i, int j) {
a = i;
b = j;
sum = a + b;
}
int getsum() { return sum; }
friend bool operator<(const
const
friend bool operator>(const
const

myclass
myclass
myclass
myclass

&o1,
&o2);
&o1,
&o2);

651

652

C++: The Complete Reference

friend bool operator==(const myclass &o1,
const myclass &o2);
friend bool operator!=(const myclass &o1,
const myclass &o2);
};
bool operator<(const myclass &o1, const myclass &o2)
{
return o1
...
sum;
}
bool operator>(const myclass &o1, const myclass &o2)
{
return o1
...
sum;
}
bool operator==(const myclass &o1, const myclass &o2)
{
return o1
...
sum;
}
bool operator!=(const myclass &o1, const myclass &o2)
{
return o1
...
sum;
}
int main()
{
int i;
// create first list
list lst1;
for(i=0; i<10; i++) lst1
...
begin();
while(p != lst1
...
push_back(myclass(i*2, i*3));
cout << "Second list: ";
p = lst2
...
end()) {
cout << p->getsum() << " ";
p++;
}
cout << endl;
// now, merget lst1 and lst2
lst1
...
begin();
while(p != lst1
...
It then merges the two lists and displays the result
...
In essence, a key is simply a name that you give to a value
...
Thus, in its most general sense, a
map is a list of key/value pairs
...
For example, you could define a map that uses a person's name as its key
and stores that person's telephone number as its value
...

As mentioned, a map can hold only unique keys
...

To create a map that allows nonunique keys, use multimap
...
This defaults to the
standard less( ) utility function object
...

A map has the following constructors:
explicit map(const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ) );
map(const map &ob);
template map(InIter start, InIter end,
const Comp &cmpfn = Comp( ), const Allocator &a = Allocator( ));
The first form constructs an empty map
...
The third form constructs a map that contains the
elements in the range specified by the iterators start and end
...

In general, any object used as a key must define a default constructor and overload
any necessary comparison operators
...

==, <, <=, !=, >, >=
Several of the map member functions are shown in Table 24-4
...


Chapter 24:

Introducing the Standard Template Library

Member

Description

iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the map
...


size_type count(const key_type &k) const;

Returns the number of times k occurs
in the map (1 or zero)
...


iterator end( );
const_iterator end( ) const;

Returns an iterator to the end of
the list
...


void erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


iterator find(const key_type &k);
const_iterator find(const key_type &k)
const;

Returns an iterator to the specified
key
...


iterator insert(iterator i,
const value_type &val);

Inserts val at or after the element
specified by i
...


template
void insert(InIter start, InIter end)

Inserts a range of elements
...
An
iterator to the element is returned
...
If the element was
inserted, pair is
returned
...


Table 24-4
...
If this element does not
exist, it is inserted
...


Table 24-4
...

template struct pair {
typedef Ktype first_type; // type of key
typedef Vtype second_type; // type of value
Ktype first; // contains the key
Vtype second; // contains the value
// constructors
pair();
pair(const Ktype &k, const Vtype &v);
template pair(const &ob);
}

As the comments suggest, the value in first contains the key and the value in second
contains the value associated with that key
...
make_pair( ) is a generic function that has this prototype
...
The advantage of make_pair( ) is that the types of the objects being

Chapter 24:

Introducing the Standard Template Library

stored are determined automatically by the compiler rather than being explicitly
specified by you
...
It stores key/value
pairs that show the mapping between the uppercase letters and their ASCII character
codes
...
The key/value pairs
stored are
A
B
C

65
66
67

and so on
...
e
...

// A simple map demonstration
...
insert(pair('A'+i, 65+i));
}
char ch;
cout << "Enter key: ";
cin >> ch;
map::iterator p;
// find value given key
p = m
...
end())
cout << "Its ASCII value is " << p->second;
else
cout << "Key not in map
...
The data
types specified by pair must match those of the map into which the pairs are being
inserted
...
find( ) returns an iterator to the matching
element or to the end of the map if the key is not found
...

In the preceding example, key/value pairs were constructed explicitly, using
pair
...
For example, assuming the previous program, this line of code will also
insert key/value pairs into m
...
insert(make_pair((char)('A'+i), 65+i));

Here, the cast to char is needed to override the automatic conversion to int when i is
added to 'A
...


Storing Class Objects In a Map
As with all of the containers, you can use a map to store objects of types that you
create
...
That is, it
creates a map of names with their numbers
...
Since a map maintains a sorted list of keys, the program also
defines the < operator for objects of type name
...
(Some compilers may require that
additional comparison operators be defined
...

#include
#include
#include
using namespace std;
class name {
char str[40];
public:
name() { strcpy(str, ""); }

Chapter 24:

Introducing the Standard Template Library

name(char *s) { strcpy(str, s); }
char *get() { return str; }
};
// Must define less than relative to name objects
...
get(), b
...
insert(pair(name("Tom"),
phoneNum("555-4533")));
directory
...
insert(pair(name("John"),
phoneNum("555-8195")));
directory
...
find(name(str));

659

660

C++: The Complete Reference

if(p != directory
...
get();
else
cout << "Name not in directory
...


In the program, each entry in the map is a character array that holds a
null-terminated string
...


Algorithms
As explained, algorithms act on containers
...
They also allow you to work with two different types of containers at
the same time
...

The STL defines a large number of algorithms, which are summarized in Table 24-5
...
This means that they can be applied to
any type of container
...
The
following sections demonstrate a representative sample
...
To do this, you can use either count( ) or count_if( )
...


binary_search

Performs a binary search on an ordered sequence
...


copy_backward

Same as copy( ) except that it moves the elements from
the end of the sequence first
...


count_if

Returns the number of elements in the sequence that
satisfy some predicate
...


equal_range

Returns a range in which an element can be inserted into
a sequence without disrupting the ordering of
the sequence
...


find

Searches a range for a value and returns an iterator to the
first occurrence of the element
...
It returns an iterator
to the end of the subsequence within the range
...


find_if

Searches a range for an element for which a user-defined
unary predicate returns true
...


generate and generate_n

Assign elements in a range the values returned by a
generator function
...


inplace_merge

Merges a range with another range
...
The resulting sequence is sorted
...


lexicographical_compare

Alphabetically compares one sequence with another
...


The STL Algorithms

661

662

C++: The Complete Reference

Algorithm

Purpose

lower_bound

Finds the first point in the sequence that is not less than a
specified value
...


max

Returns the maximum of two values
...


merge

Merges two ordered sequences, placing the result into a
third sequence
...


min_element

Returns an iterator to the minimum element within a range
...
Iterators to the two elements are returned
...


nth_element

Arranges a sequence such that all elements less than a
specified element E come before that element and all
elements greater than E come after it
...


partial_sort_copy

Sorts a range and then copies as many elements as will
fit into a resulting sequence
...


pop_heap

Exchanges the first and last −1 elements and then
rebuilds the heap
...


push_heap

Pushes an element onto the end of a heap
...


remove, remove_if,
remove_copy, and
remove_copy_if

Removes elements from a specified range
...


Table 24-5
...

rotate and rotate_copy

Left-rotates the elements in a range
...


search_n

Searches for a sequence of a specified number of similar
elements
...


set_intersection

Produces a sequence that contains the intersection of the
two ordered sets
...

set_union

Produces a sequence that contains the union of the two
ordered sets
...


sort_heap

Sorts a heap within a specified range
...
The partitioning is stable
...


stable_sort

Sorts a range
...
This means that equal
elements are not rearranged
...


swap_ranges

Exchanges elements in a range
...


unique and unique_copy

Eliminates duplicate elements from a range
...


Table 24-5
...
The count_if( ) algorithm returns the number of
elements in the sequence beginning at start and ending at end for which the unary
predicate pfn returns true
...

// Demonstrate count()
...
push_back(true);
else v
...
size(); i++)
cout << boolalpha << v[i] << " ";
cout << endl;
i = count(v
...
end(), true);
cout << i << " elements are true
...


Chapter 24:

Introducing the Standard Template Library

The program begins by creating a vector comprised of randomly generated true and
false values
...

This next program demonstrates count_if( )
...
It then counts those that are evenly divisible by 3
...

// Demonstrate count_if()
...
*/
bool dividesBy3(int i)
{
if((i%3) == 0) return true;
return false;
}
int main()
{
vector v;
int i;
for(i=1; i < 20; i++) v
...
size(); i++)
cout << v[i] << " ";
cout << endl;
i = count_if(v
...
end(), dividesBy3);
cout << i << " numbers are divisible by 3
...


Notice how the unary predicate dividesBy3( ) is coded
...
The predicate must then return a true or false result
based upon this object
...
One algorithm that does this is remove_copy( )
...
It puts the result into the sequence pointed to by result and
returns an iterator to the end of the result
...

To replace one element in a sequence with another when a copy is made, use
replace_copy( )
...
It puts the result into the sequence pointed to by result
and returns an iterator to the end of the result
...

The following program demonstrates remove_copy( ) and replace_copy( )
...
It then removes all of the spaces from the sequence
...

// Demonstrate remove_copy and replace_copy
...
";
vector v, v2(30);
int i;
for(i=0; str[i]; i++) v
...
size(); i++) cout << v[i];
cout << endl;
// remove all spaces
remove_copy(v
...
end(), v2
...
size(); i++) cout << v2[i];
cout << endl << endl;
// **** now, demonstrate replace_copy ****
cout << "Input sequence:\n";
for(i=0; i ...
begin(), v
...
begin(), ' ', ':');

cout << "Result after replacing spaces with colons:\n";
for(i=0; i ...

Result after removing spaces:
TheSTLispowerprogramming
...

Result after replacing spaces with colons:
The:STL:is:power:programming
...
Its general form is
template void reverse(BiIter start, BiIter end);
The reverse( ) algorithm reverses the order of the range specified by start and end
...

// Demonstrate reverse
...
push_back(i);
cout << "Initial: ";
for(i=0; i ...
begin(), v
...
size(); i++) cout << v[i] << " ";
return 0;
}

Chapter 24:

Introducing the Standard Template Library

The output from this program is shown here:
Initial: 0 1 2 3 4 5 6 7 8 9
Reversed: 9 8 7 6 5 4 3 2 1 0

Transforming a Sequence
One of the more interesting algorithms is transform( ) because it modifies each element
in a range according to a function that you provide
...
In the first form, the range is specified by start and end
...
This function receives the value of an
element in its parameter, and it must return its transformation
...
Both versions return an iterator to the
end of the resulting sequence
...
Notice that the
resulting sequence is stored in the same list that provided the original sequence
...

#include
#include
#include
using namespace std;
// A simple transformation function
...
0/i; // return reciprocal
}
int main()

669

670

C++: The Complete Reference

{
list vals;
int i;
// put values into list
for(i=1; i<10; i++) vals
...
begin();
while(p != vals
...
begin(), vals
...
begin(), reciprocal);
cout << "Transformed contents of vals:\n";
p = vals
...
end()) {
cout << *p << " ";
p++;
}
return 0;
}

The output produced by the program is shown here:
Original contents of vals:
1 2 3 4 5 6 7 8 9
Transformed contents of vals:
1 0
...
333333 0
...
2 0
...
142857 0
...
111111

As you can see, each element in vals has been transformed into its reciprocal
...
Recall that function objects are simply classes that define operator( )
...
It also allows
you to define your own function objects
...

Fortunately, as the preceding examples have shown, you can make significant use of
the STL without ever creating a function object
...


Unary and Binary Function Objects
Just as there are unary and binary predicates, there are unary and binary function
objects
...
You must use the type of object required
...


Using the Built-in Function Objects
The STL provides a rich assortment of built-in function objects
...
The only one that
may not be self-evident is negate( ), which reverses the sign of its argument
...
For
example, to invoke the binary function object plus( ), use this syntax:
plus()

The built-in function objects use the header
...
The following program uses the transform( )
algorithm (described in the preceding section) and the negate( ) function object to
reverse the sign of a list of values
...

#include
#include
#include
#include
using namespace std;
int main()
{
list vals;
int i;
// put values into list
for(i=1; i<10; i++) vals
...
begin();
while(p != vals
...
begin(), vals
...
begin(),
negate()); // call function object
cout << "Negated contents of vals:\n";
p = vals
...
end()) {
cout << *p << " ";
p++;
}
return 0;
}

Chapter 24:

Introducing the Standard Template Library

This program produces the following output:
Original contents of vals:
1 2 3 4 5 6 7 8 9
Negated contents of vals:
-1 -2 -3 -4 -5 -6 -7 -8 -9

In the program, notice how negate( ) is invoked
...
The transform( ) algorithm automatically
calls negate( ) for each element in the sequence
...

The next program demonstrates the use of the binary function object divides( )
...
This program uses the
binary form of the transform( ) algorithm
...

#include
#include
#include
#include
using namespace std;
int main()
{
list vals;
list divisors;
int i;
// put values into list
for(i=10; i<100; i+=10) vals
...
push_back(3
...
begin();
while(p != vals
...
begin(), vals
...
begin(), vals
...
begin();
while(p != vals
...
33333 6
...
3333 16
...
3333 26
...
Thus, divides( )
receives arguments in this order:
divides(first, second)
This order can be generalized
...


Creating a Function Object
In addition to using the built-in function objects, you can create your own
...
However, for the
greatest flexibility, you will want to use one of the following classes defined by the STL
as a base class for your function objects
...
Although they are technically a convenience, they are almost
always used when creating function objects
...
It converts the
reciprocal( ) function (used to demonstrate the transform( ) algorithm earlier) into a
function object
...

#include
#include
#include
#include
using namespace std;
// A simple function object
...
0/i; // return reciprocal
}
};
int main()
{
list vals;
int i;
// put values into list
for(i=1; i<10; i++) vals
...
begin();
while(p != vals
...
begin(), vals
...
begin(),
reciprocal()); // call function object
cout << "Transformed contents of vals:\n";
p = vals
...
end()) {
cout << *p << " ";
p++;
}
return 0;
}

Notice two important aspects of reciprocal( )
...
This gives it access to the argument_type and result_type types
...
In
general, to create a function object, simply inherit the proper base class and overload
operator( ) as required
...


Using Binders
When using a binary function object, it is possible to bind a value to one of the
arguments
...
For example, you may wish to
remove all elements from a sequence that are greater than some value, such as 8
...
That is, you want greater( ) to perform the comparison
val > 8
for each element of the sequence
...

There are two binders: bind2nd( ) and bind1st( )
...
bind1st( ) returns a unary function object
that has binfunc_obj's left-hand operand bound to value
...
The bind2nd( )
binder is by far the most commonly used
...

To demonstrate the use of a binder, we will use the remove_if( ) algorithm
...
It has
this prototype:
template
ForIter remove_if(ForIter start, ForIter end, UnPred func);
The algorithm removes elements from the sequence defined by start and end if the
unary predicate defined by func is true
...

The following program removes all values from a sequence that are greater than
the value 8
...
Instead, we
must bind the value 8 to the second argument of greater( ) using the bind2nd( ) binder,
as shown in the program
...

#include
#include
#include
#include
using namespace std;
int main()
{
list lst;
list::iterator p, endp;
int i;
for(i=1; i < 20; i++) lst
...
begin();
while(p != lst
...
begin(), lst
...
begin();
while(p != endp) {
cout << *p << " ";
p++;
}
return 0;
}

The output produced by the program is shown here:
Original sequence:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Resulting sequence:
1 2 3 4 5 6 7 8

You might want to experiment with this program, trying different function objects and
binding different values
...

One last point: There is an object related to a binder called a negator
...
They return the negation (i
...
, the complement of) whatever
predicate they modify
...
begin(), lst
...


Chapter 24:

Introducing the Standard Template Library

The string Class
As you know, C++ does not support a built-in string type per se
...
First, you may use the traditional,
null-terminated character array with which you are already familiar
...
The second way is as a class object of type string; this is the
approach examined here
...
In fact, there are two specializations of basic_string: string, which
supports 8-bit character strings, and wstring, which supports wide-character strings
...

Before looking at the string class, it is important to understand why it is part of the
C++ library
...
In fact, a
significant amount of thought and debate has accompanied each new addition
...

However, this is actually far from the truth
...
Nor can they take part in
normal C++ expressions
...
These operations must be written using library
functions, as shown here:
strcpy(s1,
strcpy(s2,
strcpy(s3,
strcat(s3,

"Alpha");
"Beta");
s1);
s2);

Since null-terminated character arrays are not technically data types in their own
right, the C++ operators cannot be applied to them
...
More than anything else, it is the inability to
operate on null-terminated strings using the standard C++ operators that has driven
the development of a standard string class
...
This, of course, means that the operators can be overloaded relative to
the new class
...

There is, however, one other reason for the standard string class: safety
...
For example, consider the standard string
copy function strcpy( )
...
If the source array contains more characters than the target array
can hold, then a program error or system crash is possible (likely)
...

In the final analysis, there are three reasons for the inclusion of the standard string
class: consistency (a string now defines a data type), convenience (you may use the
standard C++ operators), and safety (array boundaries will not be overrun)
...
They are still the most efficient way in which to implement strings
...

Although not traditionally thought of as part of the STL, string is another container
class defined by C++
...
However, strings have additional capabilities
...

The string class is very large, with many constructors and member functions
...
For this reason, it is not
possible to look at the entire contents of string in this chapter
...
Once you have a general understanding of
how string works, you can easily explore the rest of it on your own
...
The prototypes for three of its most
commonly used ones are shown here:
string( );
string(const char *str);
string(const string &str);
The first form creates an empty string object
...
This form provides a conversion
from null-terminated strings to string objects
...

A number of operators that apply to strings are defined for string objects,
including:

Chapter 24:

Introducing the Standard Template Library

Operator

Meaning

=

Assignment

+

Concatenation

+=

Concatenation assignment

==

Equality

!=

Inequality

<

Less than

<=

Less than or equal

>

Greater than

>=

Greater than or equal

[]

Subscripting

<<

Output

>>

Input

These operators allow the use of string objects in normal expressions and eliminate
the need for calls to functions such as strcpy( ) or strcat( ), for example
...
For
example, a string object can be assigned a null-terminated string
...
That is, the following variations are supported:
string + string
string + C-string
C-string + string
The + operator can also be used to concatenate a character onto the end of a string
...
This constant represents the
length of the longest possible string
...
For example,
using string objects you can use the assignment operator to assign a quoted string to a
string, the + operator to concatenate strings, and the comparison operators to compare
strings
...


681

682

C++: The Complete Reference

// A short string demonstration
...
*/
str1 = "This is a null-terminated string
...

This is a null-terminated string
...
For example, the +
is used to concatenate strings and the > is used to compare two strings
...
Because C++ string objects can be
freely mixed with C-style null-terminated strings, there is no disadvantage to using
them in your program—and there are considerable benefits to be gained
...
string objects are automatically sized to hold the string that they are
given
...
It is not possible to overrun the end
of the string
...


Some string Member Functions
Although most simple string operations can be accomplished using the string
operators, more complex or subtle ones are accomplished using string member
functions
...


Basic String Manipulations
To assign one string to another, use the assign( ) function
...
In the second form, the first num characters of the
null-terminated string str are assigned to the invoking object
...
Of course, it is much easier to use the = to assign one
entire string to another
...

You can append part of one string to another using the append( ) member function
...
In the second form, the first num characters of the
null-terminated string str are appended to the invoking object
...
Of course, it is much easier to use the + to append
one entire string to another
...

You can insert or replace characters within a string using insert( ) and replace( )
...
The second form of insert( ) function inserts num characters from strob
beginning at insStart into the invoking string at the index specified by start
...
The second form replaces orgNum characters, beginning
at start, in the invoking string with the replaceNum characters from the string specified
by strob beginning at replaceStart
...

You can remove characters from a string using erase( )
...
A reference to
the invoking string is returned
...

// Demonstrate insert(), erase(), and replace()
...
");
string str2("STL Power");
cout << "Initial strings:\n";
cout << "str1: " << str1 << endl;
cout << "str2: " << str2 << "\n\n";
// demonstrate insert()
cout << "Insert str2 into str1:\n";
str1
...
erase(6, 9);
cout << str1 <<"\n\n";
// demonstrate replace
cout << "Replace 8 characters in str1 with str2:\n";
str1
...

str2: STL Power
Insert str2 into str1:
StringSTL Power handling C++ style
...

Replace 8 characters in str1 with str2:
String STL Power C++ style
...
Here are the prototypes for the most common versions of
these functions:
size_type find(const string &strob, size_type start=0) const;
size_type rfind(const string &strob, size_type start=npos) const;
Beginning at start, find( ) searches the invoking string for the first occurrence of
the string contained in strob
...
If no match is found, then npos is returned
...
Beginning at start, it searches the invoking string in the
reverse direction for the first occurrence of the string contained in strob (i
...
If found, rfind( ) returns the
index at which the match occurs within the invoking string
...

Here is a short example that uses find( ) and rfind( )
...
find("Quick");
if(i!=string::npos) {
cout << "Match found at " << i << endl;
cout << "Remaining string is:\n";
s2
...
size());
cout << s2;
}
cout << "\n\n";
i = s1
...
assign(s1, i, s1
...
find("Pure");
if(i!=string::npos) {
cout << "Match found at " << i << endl;
cout << "Remaining string is:\n";
s2
...
size());
cout << s2;
}
cout << "\n\n";
// find list "of"
i = s1
...
assign(s1, i, s1
...
However, if you want to compare
a portion of one string to another, you will need to use the compare( ) member
function, shown here:
int compare(size_type start, size_type num, const string &strob) const;
Here, num characters in strob, beginning at start, will be compared against the invoking
string
...
If
the invoking string is greater than strob, it will return greater than zero
...


Obtaining a Null-Terminated String
Although string objects are useful in their own right, there will be times when you will
need to obtain a null-terminated character-array version of the string
...
However, when opening a file, you
will need to specify a pointer to a standard, null-terminated string
...
Its prototype is shown here:
const char *c_str( ) const;
This function returns a pointer to a null-terminated version of the string contained in
the invoking string object
...
It is also not
guaranteed to be valid after any other operations have taken place on the string object
...
Thus, it
supports the common container functions, such as begin( ), end( ), and size( )
...
Therefore, a string object can also be manipulated by the STL
algorithms
...

#include
#include
#include
using namespace std;
int main()
{
string str1("Strings handling is easy in C++");
string::iterator p;
int i;
// use size()
for(i=0; i ...
begin();
while(p != str1
...
begin(), str1
...
begin(), str1
...
begin(),
toupper);
p = str1
...
end())
cout << *p++;
cout << endl;

689

690

C++: The Complete Reference

return 0;
}

Output from the program is shown here:
Strings handling is easy in C++
Strings handling is easy in C++
There are 4 i's in str1
STRINGS HANDLING IS EASY IN C++

Putting Strings into Other Containers
Even though string is a container, objects of type string are commonly held in other
STL containers, such as maps or lists
...
It uses a map of string objects, rather than
null-terminated strings, to hold the names and telephone numbers
...

#include
#include
#include
using namespace std;
int main()
{
map directory;
directory
...
insert(pairdirectory
...
insert(pair
string>("Tom", "555-4533"));
string>("Chris", "555-9678"));
string>("John", "555-8195"));
string>("Rachel", "555-0809"));

string s;
cout << "Enter name: ";
cin >> s;
map::iterator p;
p = directory
...
end())
cout << "Phone number: " << p->second;

Chapter 24:

Introducing the Standard Template Library

else
cout << "Name not in directory
...
Many programming
tasks can (and will) be framed in terms of it
...
No C++
programmer can afford to neglect the STL because it will play an important role in the
way future programs are written
...


Part III
The Standard Function Library

C

++ defines two types of libraries
...
This library consists of general-purpose,
stand-alone functions that are not part of any class
...
The second library is the objectoriented class library
...
Part Four describes the class
library
...
It provides wide-character (wchar_t) equivalents to several of the library
functions
...

One last point: All compilers supply more functions than are defined by Standard
C/C++
...
You will want to check your compiler's
documentation
...
These functions are defined by
Standard C and Standard C++
...
The
functions in this chapter were first specified by the ANSI C standard, and they are
commonly referred to collectively as the ANSI C I/O system
...
(A C
program must use the header file stdio
...
) This header defines several macros and
types used by the file system
...
Two other types are size_t and fpos_t
...
The fpos_t type defines an object
that can hold all information needed to uniquely specify every position within a file
...

Many of the I/O functions set the built-in global integer variable errno when an
error occurs
...
The values that errno may take are implementation
dependent
...


T

Note

This chapter describes the character-based I/O functions
...
In 1995, several wide-character (wchar_t) functions were added, and they are
briefly described in Chapter 31
...
e
...
The end-of-file indicator is also reset
...

Once an error has occurred, the flags stay set until an explicit call to either clearerr( ) or
rewind( ) is made
...
The exact nature of the error can be determined by calling perror( ), which
displays what error has occurred (see perror( ))
...


Chapter 25:

The C-Based I/O Functions

fclose
#include
int fclose(FILE *stream);

The fclose( ) function closes the file associated with stream and flushes its buffer
...

If fclose( ) is successful, zero is returned; otherwise EOF is returned
...
Removing the storage media before
closing a file will also generate an error, as will lack of sufficient free disk space
...


feof
#include
int feof(FILE *stream);

The feof( ) function checks the file position indicator to determine if the end of the
file associated with stream has been reached
...

Once the end of the file has been reached, subsequent read operations will return
EOF until either rewind( ) is called or the file position indicator is moved using fseek( )
...
Explicit calls must be made to
feof( ) rather than simply testing the return value of getc( ), for example, to determine
when the end of a binary file has been reached
...


ferror
#include
int ferror(FILE *stream);

The ferror( ) function checks for a file error on the given stream
...

The error flags associated with stream will stay set until either the file is closed, or
rewind( ) or clearerr( ) is called
...

Related functions are clearerr( ), feof( ), and perror( )
...
If stream points
to an input file, the contents of the input buffer are cleared
...

A return value of zero indicates success; EOF indicates that a write error
has occurred
...
Also, closing a file flushes its buffer
...


fgetc
#include
int fgetc(FILE *stream);

The fgetc( ) function returns the next character from the input stream from the
current position and increments the file position indicator
...

If the end of the file is reached, fgetc( ) returns EOF
...
If fgetc( ) encounters an error, EOF is also returned
...

Related functions are fputc( ), getc( ), putc( ), and fopen( )
...
The object pointed to by position must be of type fpos_t
...

If an error occurs, fgetpos( ) returns nonzero; otherwise it returns zero
...


fgets
#include
char *fgets(char *str, int num, FILE *stream);

The fgets( ) function reads up to num-1 characters from stream and places them into
the character array pointed to by str
...
After the characters have been
read, a null is placed in the array immediately after the last character read
...

If successful, fgets( ) returns str; a null pointer is returned upon failure
...
Because
a null pointer will be returned when either an error has occurred or when the end
of the file is reached, you should use feof( ) or ferror( ) to determine what has
actually happened
...


fopen
#include
FILE *fopen(const char *fname, const char *mode);

The fopen( ) function opens a file whose name is pointed to by fname and returns
the stream that is associated with it
...
The legal values for mode are shown in
Table 25-1
...

If fopen( ) is successful in opening the specified file, a FILE pointer is returned
...


699

700

C++: The Complete Reference

Mode

Meaning

"r"

Open text file for reading
...


"a"

Append to text file
...


"wb"

Create binary file for writing
...


"r+"

Open text file for read/write
...


"a+"

Open text file for read/write
...


"wb+" or "w+b"

Create binary file for read/write
...


Table 25-1
...
In text
mode, some character translations may occur
...
No such translations occur on binary files
...
\n");
exit(1);
}

This method detects any error in opening a file, such as a write-protected or a full disk,
before attempting to write to it
...

If you use fopen( ) to open a file for output, any preexisting file by that name will
be erased and a new file started
...


Chapter 25:

The C-Based I/O Functions

Opening a file for read operations requires that the file exists
...
If you want to add to the end of the file, you must use mode "a
...

When accessing a file opened for read/write operations, you may not follow an
output operation with an input operation without an intervening call to either fflush( ),
fseek( ), fsetpos( ), or rewind( )
...

Related functions are fclose( ), fread( ), fwrite( ), putc( ), and getc( )
...
);

The fprintf( ) function outputs the values of the arguments that comprise the
argument list as specified in the format string to the stream pointed to by stream
...
If an error occurs, a negative
number is returned
...

The operations of the format control string and commands are identical to those in
printf( ); see printf( ) for a complete description
...


fputc
#include
int fputc(int ch, FILE *stream);

The fputc( ) function writes the character ch to the specified stream at the current
file position and then advances the file position indicator
...

Because all character arguments are elevated to integers at the time of the call, you will
generally see character values used as arguments
...

The value returned by fputc( ) is the value of the character written
...
For files opened for binary operations, an EOF may be a valid
character, and the function ferror( ) will need to be used to determine whether an error
has actually occurred
...


fputs
#include
int fputs(const char *str, FILE *stream);

The fputs( ) function writes the contents of the string pointed to by str to the
specified stream
...

The fputs( ) function returns nonnegative on success and EOF on failure
...

This means that there may not be a one-to-one mapping of the string onto the file
...

Related functions are fgets( ), gets( ), puts( ), fprintf( ), and fscanf( )
...
The file position indicator is advanced by the number of characters read
...
If fewer items are
read than are requested in the call, either an error has occurred or the end of the file has
been reached
...

If the stream is opened for text operations, certain character translations, such as
carriage return/linefeed sequences being transformed into newlines, may occur
...


freopen
#include
FILE *freopen(const char *fname, const char *mode,
FILE *stream);

Chapter 25:

The C-Based I/O Functions

The freopen( ) function associates an existing stream with a different file
...
The string mode uses the same format as
fopen( ); a complete discussion is found in the fopen( ) description
...
However, if the attempt to close the file fails, the freopen( ) function still
continues to open the other file
...

The main use of freopen( ) is to redirect the system defined files stdin, stdout, and
stderr to some other file
...


fscanf
#include
int fscanf(FILE *stream, const char *format,
...
See scanf( )
for details
...

This number does not include skipped fields
...

Related functions are scanf( ) and fprintf( )
...
Its purpose is to support random-access I/O
operations
...
The values for origin
must be one of these macros (defined in ):

703

704

C++: The Complete Reference

Name

Meaning

SEEK_SET

Seek from start of file

SEEK_CUR

Seek from current location

SEEK_END

Seek from end of file

A return value of zero means that fseek( ) succeeded
...

You may use fseek( ) to move the position indicator anywhere in the file, even
beyond the end
...

The fseek( ) function clears the end-of-file flag associated with the specified stream
...

Related functions are ftell( ), rewind( ), fopen( ), fgetpos( ), and fsetpos( )
...
This value must have been previously obtained
through a call to fgetpos( )
...
Also, any previous call to ungetc( ) is nullified
...
If it is successful, it returns zero
...


ftell
#include
long ftell(FILE *stream);

The ftell( ) function returns the current value of the file position indicator for the
specified stream
...
For text streams, the return value may not be
meaningful except as an argument to fseek( ) because of possible character
translations, such as carriage return/linefeeds being substituted for newlines, which
affect the apparent size of the file
...
If the stream is incapable of
random seeks—if it is a modem, for instance—the return value is undefined
...


fwrite
#include
size_t fwrite(const void *buf, size_t size,
size_t count, FILE *stream);

The fwrite( ) function writes count number of objects, each object being size bytes in
length, to the stream pointed to by stream from the character array pointed to by buf
...

The fwrite( ) function returns the number of items actually written, which, if the
function is successful, will equal the number requested
...
For text streams, various character translations
may take place but will have no effect upon the return value
...


getc
#include
int getc(FILE *stream);

The getc( ) function returns the next character from the input stream and
increments the file position indicator
...

If the end of the file is reached, getc( ) returns EOF
...
If getc( ) encounters an error, EOF is also returned
...

The functions getc( ) and fgetc( ) are identical, and in most implementations getc( )
is simply defined as the macro shown here
...

Related functions are fputc( ), fgetc( ), putc( ), and fopen( )
...
The character is read
as an unsigned char that is converted to an integer
...
However, since EOF is a
valid integer value, when working with binary files you must use feof( ) to check for
end-of-file
...
If working with
binary files, you must use ferror( ) to check for file errors
...

Related functions are fputc( ), fgetc( ), putc( ), and fopen( )
...
Characters are read until a newline or an EOF is received
...

If successful, gets( ) returns str; a null pointer is returned upon failure
...
Because a
null pointer will be returned when either an error has occurred or when the end
of the file is reached, you should use feof( ) or ferror( ) to determine what has
actually happened
...

Related functions are fputs( ), fgetc( ), fgets( ), and puts( )
...
If the value of str is not null, it is written first, followed by a
colon, and then the implementation-defined error message
...
);

The printf( ) function writes to stdout the arguments that comprise the argument
list as specified by the string pointed to by format
...
The first type is made
up of characters that will be printed on the screen
...
A format specifier begins
with a percent sign and is followed by the format code
...
For example, the following printf( ) call displays "Hi c
10 there!"
...
If there are more arguments than format specifiers, the remaining
arguments are discarded
...

The printf( ) function returns the number of characters actually printed
...

The format codes may have modifiers that specify the field width, precision, and a
left-justification flag
...
This pads the output with spaces or 0's to ensure that it
is at least a certain minimum length
...
The default
padding is done with spaces
...
For example, %05d will pad a number of less than five digits with
0's so that its total length is 5
...
To add a precision modifier, place a decimal point followed by the precision
after the field-width specifier
...


The printf( ) Format Specifiers

determines the number of decimal places printed
...
4f will display a
number at least 10 characters wide with four decimal places
...
When applied to integers, the precision modifier
specifies the minimum number of digits that will be displayed
...

When the precision modifier is applied to strings, the number following the period
specifies the maximum field length
...
7s will display a string that will

Chapter 25:

The C-Based I/O Functions

be at least five characters long and will not exceed seven
...

By default, all output is right-justified: if the field width is larger than the data
printed, the data will be placed on the right edge of the field
...
For
example, %−10
...

There are two format modifiers that allow printf( ) to display short and long
integers
...
The l
modifier tells printf( ) that a long data type follows
...
The h modifier tells printf( ) to display a short integer
...

If you are using a modern compiler that supports the wide-character features
added in 1995, then you may use the l modifier with the c specifier to indicate a
wide-character of type wchar_t
...

An L modifier may prefix the floating-point commands of e, f, and g and indicates
that a long double follows
...
For example, this code fragment displays the number 14
after the line "This is a test":
int i;
printf("This is a test%n", &i);
printf("%d", i);

The # has a special meaning when used with some printf( ) format codes
...
If you precede the x or X format code with a #, the
hexadecimal number will be printed with a 0x prefix
...
The # cannot be applied to any other
format specifiers
...
To accomplish this, use an * as a placeholder
...

Related functions are scanf( ) and fprintf( )
...
Because character arguments are elevated to
integer at the time of the call, you may use character values as arguments to putc( )
...
If the output stream has been opened in binary mode, EOF is a valid value for
ch
...

Related functions are fgetc( ), fputc( ), getchar( ), and putchar( )
...
It is functionally equivalent to putc(ch, stdout)
...

The putchar( ) function returns the character written on success or EOF if an error
occurs
...
This means that you must use ferror( ) to determine if an error has occurred
...


puts
#include
int puts(const char *str);

The puts( ) function writes the string pointed to by str to the standard output
device
...

The puts( ) function returns a nonnegative value if successful and an EOF
upon failure
...


Chapter 25:

The C-Based I/O Functions

remove
#include
int remove(const char *fname);

The remove( ) function erases the file specified by fname
...

A related function is rename( )
...
The newfname must not match any existing directory entry
...

A related function is remove( )
...
It also clears the end-of-file and error flags associated with stream
...

A related function is fseek( )
...
);

711

712

C++: The Complete Reference

The scanf( ) function is a general-purpose input routine that reads the stream stdin
and stores the information in the variables pointed to in its argument list
...

The control string pointed to by format consists of three classifications of characters:
Format specifiers
White-space characters
Non–white-space characters
The input format specifiers begin with a % sign and tell scanf( ) what type of data is to
be read next
...
For example, %s reads a
string while %d reads an integer
...


Code

Meaning

%c

Reads a single character
...


%i

Reads an integer
...


%f

Reads a floating-point number
...


%o

Reads an octal number
...


%x

Reads a hexadecimal number
...


%n

Receives an integer value equal to the number of characters read so far
...


%[ ]

Scans for a set of characters
...


Table 25-3
...
To read a short
integer, put an h in front of the format specifier
...

By default, the f, e, and g specifiers instruct scanf( ) to assign data to a float
...

Using an L tells scanf( ) that the variable receiving the data is a long double
...
You may also use the l modifier with the s format code to
indicate a pointer to a wide-character string
...

A white-space character in the format string causes scanf( ) to skip over one or
more white-space characters in the input stream
...
In essence, one white-space character in the control
string will cause scanf( ) to read, but not store, any number (including zero) of
white-space characters up to the first non–white-space character
...
For example, %d,%d causes scanf( ) to first read an
integer, then read and discard a comma, and finally read another integer
...

All the variables used to receive values through scanf( ) must be passed by their
addresses
...

The input data items must be separated by spaces, tabs, or newlines
...
This means that
scanf("%d%d", &r, &c);

will accept an input of 10 20 but fail with 10,20
...
Thus, the command
scanf("%d%*c%d", &x, &y);

given the input 10/20, will place the value 10 into x, discard the divide sign, and give y
the value 20
...
This is an
integer number placed between the % and the format code that limits the number of
characters read for any field
...
Input for a field may terminate before the maximum field
length is reached if a white space is encountered
...

Although spaces, tabs, and newlines are used as field separators, when reading a
single character, these are read like any other character
...

Beware: Any other characters in the control string—including spaces, tabs, and
newlines—will be used to match and discard characters from the input stream
...
For example, given the input stream 10t20,
scanf("%dt%d", &x, &y);

will place 10 into x and 20 into y
...

Another feature of scanf( ) is called a scanset
...
A
scanset is defined by putting the characters you want to scan for inside square brackets
...
For example, this
scanset tells scanf( ) to read only the characters A, B, and C:
%[ABC]

When a scanset is used, scanf( ) continues to read characters and put them into
the corresponding character array until a character that is not in the scanset is
encountered
...
Upon
return from scanf( ), the array will contain a null-terminated string comprised of the
characters read
...
When the ^ is
present, it instructs scanf( ) to accept any character that is not defined by the scanset
...
For example, this tells scanf( ) to accept
the characters A through Z
...
Therefore, if
you want to scan for both upper- and lowercase letters, they must be specified
individually
...
This number will not include fields that were read but not
assigned because the * modifier was used to suppress the assignment
...

Related functions are printf( ) and fscanf( )
...
If a programmer-defined
buffer is to be specified, it must be BUFSIZ characters long
...

The setbuf( ) function returns no value
...


setvbuf
#include
int setvbuf(FILE *stream, char *buf, int mode, size_t size);

The setvbuf( ) function allows the programmer to specify the buffer, its size, and
its mode for the specified stream
...
The size of the buffer is set by size, and mode
determines how buffering will be handled
...

The legal values of mode are _IOFBF, _IONBF, and _IOLBF
...
When mode is set to _IOFBF, full buffering will take place
...
In either case, the buffer is also flushed when full
...

The value of size must be greater than zero
...

A related function is setbuf( )
...
);

The sprintf( ) function is identical to printf( ) except that the output is put into the
array pointed to by buf instead of being written to the console
...

The return value is equal to the number of characters actually placed into the array
...


sscanf
#include
int sscanf(const char *buf, const char *format,
...
See scanf( ) for details
...
This number does not include fields that were skipped through the use of the *
format command modifier
...

Related functions are scanf( ) and fscanf( )
...
The function automatically uses a unique filename to avoid conflicts with
existing files
...


Chapter 25:

The C-Based I/O Functions

The temporary file created by tmpfile( ) is automatically removed when the file is
closed or when the program terminates
...


tmpnam
#include
char *tmpnam(char *name);

The tmpnam( ) function generates a unique filename and stores it in the array
pointed to by name
...

The function may be called up to TMP_MAX times
...
Each time tmpnam( ) is called, it will generate a new
temporary filename
...

A related function is tmpfile( )
...
This character will then be obtained by the next read operation
on stream
...

A one-character pushback is guaranteed; however, some implementations will
accept more
...

A call to ungetc( ) clears the end-of-file flag associated with the specified stream
...
For binary streams, each ungetc( ) call decrements the file
position indicator
...

A related function is getc( )
...
This pointer must be of type va_list, which
is defined in the header (or the C header file stdarg
...

Related functions are va_arg( ), va_start( ), and va_end( )
...
The string functions operate on null-terminated arrays of
characters and require the header
...
C programs must use the header files string
...
h
...
Neglecting to do so may cause your
program to crash
...
These are
usually the characters between a space (0x20) and tilde (0xFE)
...

For historical reasons, the parameters to the character functions are integers, but
only the low-order byte is used; the character functions automatically convert their
arguments to unsigned char
...

The header defines the size_t type, which is essentially the same as
unsigned
...

These are the functions originally defined by Standard C and C++, and they are by far
the most widely used and supported
...


T

isalnum
#include
int isalnum(int ch);

The isalnum( ) function returns nonzero if its argument is either a letter of the
alphabet or a digit
...

Related functions are isalpha( ), iscntrl( ), isdigit( ), isgraph( ), isprint( ), ispunct( ),
and isspace( )
...
What constitutes a letter of the alphabet may vary from language to
language
...


Chapter 26:

The String and Character Functions

Related functions are isalnum( ), iscntrl( ), isdigit( ), isgraph( ), isprint( ), ispunct( ),
and isspace( )
...

Related functions are isalnum( ), isalpha( ), isdigit( ), isgraph( ), isprint( ), ispunct( ),
and isspace( )
...
Otherwise
zero is returned
...


isgraph
#include
int isgraph(int ch);

The isgraph( ) function returns nonzero if ch is any printable character other than a
space; otherwise zero is returned
...

Related functions are isalnum( ), isalpha( ), iscntrl( ), isdigit( ), isprint( ), ispunct( ),
and isspace( )
...

A related function is isupper( )
...
Printable characters are often in the range 0x20
through 0x7E
...


ispunct
#include
int ispunct(int ch);

The ispunct( ) function returns nonzero if ch is a punctuation character; otherwise
zero is returned
...

Related functions are isalnum( ), isalpha( ), iscntrl( ), isdigit( ), isgraph( ), and
isspace( )
...

Related functions are isalnum( ), isalpha( ), iscntrl( ), isdigit( ), isgraph( ), and
ispunct( )
...

A related function is islower( )
...
A hexadecimal digit will be in one of these ranges: A–F, a–f, or 0–9
...


memchr
#include
void *memchr(const void *buffer, int ch, size_t count);

The memchr( ) function searches the array pointed to by buffer for the first
occurrence of ch in the first count characters
...

Related functions are memcpy( ) and isspace( )
...

The memcmp( ) function returns an integer that is interpreted as indicated here:

Value

Meaning

Less than zero

buf1 is less than buf2
...


Greater than zero

buf1 is greater than buf2
...


memcpy
#include
void *memcpy(void *to, const void *from, size_t count);

The memcpy( ) function copies count characters from the array pointed to by
from into the array pointed to by to
...

The memcpy( ) function returns a pointer to to
...


memmove
#include
void *memmove(void *to, const void *from, size_t count);

The memmove( ) function copies count characters from the array pointed to by from
into the array pointed to by to
...

The memmove( ) function returns a pointer to to
...


Chapter 26:

The String and Character Functions

memset
#include
void *memset(void *buf, int ch, size_t count);

The memset( ) function copies the low-order byte of ch into the first count characters
of the array pointed to by buf
...

The most common use of memset( ) is to initialize a region of memory to some
known value
...


strcat
#include
char *strcat(char *str1, const char *str2);

The strcat( ) function concatenates a copy of str2 to str1 and terminates str1 with a
null
...
The string str2 is untouched by the operation
...

The strcat( ) function returns str1
...

Related functions are strchr( ), strcmp( ), and strcpy( )
...
If no match is found, a null pointer is returned
...


725

726

C++: The Complete Reference

strcmp
#include
int strcmp(const char *str1, const char *str2);

The strcmp( ) function lexicographically compares two strings and returns an
integer based on the outcome as shown here:

Value

Meaning

Less than zero

str1 is less than str2
...


Greater than zero

str1 is greater than str2
...


strcoll
#include
int strcoll(const char *str1, const char *str2);

The strcoll( ) function compares the string pointed to by str1 with the one pointed
to by str2
...

The strcoll( ) function returns an integer that is interpreted as indicated here:

Value

Meaning

Less than zero

str1 is less than str2
...


Greater than zero

str1 is greater than str2
...


Chapter 26:

The String and Character Functions

strcpy
#include
char *strcpy(char *str1, const char *str2);

The strcpy( ) function is used to copy the contents of str2 into str1
...
The strcpy( ) function returns a pointer to str1
...

Related functions are memcpy( ), strchr( ), strcmp( ), and strncmp( )
...
Stated differently, strcspn( ) returns the index of the first character
in the string pointed to by str1 that matches any of the characters in the string pointed
to by str2
...


strerror
#include
char *strerror(int errnum);

The strerror( ) function returns a pointer to an implementation-defined string
associated with the value of errnum
...


strlen
#include
size_t strlen(const char *str);

727

728

C++: The Complete Reference

The strlen( ) function returns the length of the null-terminated string pointed to
by str
...

Related functions are memcpy( ), strchr( ), strcmp( ), and strncmp( )
...
The null
terminator originally ending str1 is overwritten by the first character of str2
...
If the strings overlap, the behavior is undefined
...

Remember that no bounds checking takes place, so it is the programmer's
responsibility to ensure that str1 is large enough to hold both its original contents and
also those of str2
...


strncmp
#include
int strncmp(const char *str1, const char *str2, size_t count);

The strncmp( ) function lexicographically compares not more than count characters
from the two null-terminated strings and returns an integer based on the outcome, as
shown here:

Value

Meaning

Less than zero

str1 is less than str2
...


Greater than zero

str1 is greater than str2
...

Related functions are strcmp( ), strnchr( ), and strncpy( )
...
str2 must be a pointer to a
null-terminated string
...

If the string pointed to by str2 has less than count characters, nulls will be appended
to the end of str1 until count characters have been copied
...

The strncpy( ) function returns a pointer to str1
...


strpbrk
#include
char *strpbrk(const char *str1, const char *str2);

The strpbrk( ) function returns a pointer to the first character in the string pointed
to by str1 that matches any character in the string pointed to by str2
...
If there are no matches, a null pointer is returned
...


strrchr
#include
char *strrchr(const char *str, int ch);

The strrchr( ) function returns a pointer to the last occurrence of the low-order byte
of ch in the string pointed to by str
...

Related functions are strpbrk( ), strspn( ), strstr( ), and strtok( )
...
Stated differently, strspn( ) returns the index of the first character in the string
pointed to by str1 that does not match any of the characters in the string pointed to
by str2
...


strstr
#include
char *strstr(const char *str1, const char *str2);

The strstr( ) function returns a pointer to the first occurrence in the string pointed to
by str1 of the string pointed to by str2
...

Related functions are strchr( ), strcspn( ), strpbrk( ), strspn( ), strtok( ), and strrchr( )
...
The characters making up the string pointed to by str2 are the delimiters that
determine the token
...

To tokenize a string, the first call to strtok( ) must have str1 point to the string being
tokenized
...
In this way, the entire
string can be reduced to its tokens
...

Related functions are strchr( ), strcspn( ), strpbrk( ), strrchr( ), and strspn( )
...
After the transformation, the outcome of a strcmp( ) using str1
and a strcoll( ) using the original string pointed to by str2 will be the same
...

A related function is strcoll( )
...

A related function is toupper( )
...

A related function is tolower( )
...


C++

Chapter 27
The Mathematical Functions

733

734

C++: The Complete Reference

T

he standard function library contains several mathematical functions, which fall
into the following categories:

s Trigonometric functions
s Hyperbolic functions
s Exponential and logarithmic functions
s Miscellaneous functions

All the math functions require the header
...
h
...
The macros EDOM and ERANGE are also used by the
math functions
...
h)
...
If a routine produces a result that is too large to be
represented, an overflow occurs
...
If an underflow happens, the
function returns zero and sets errno to ERANGE
...

Originally, the mathematical functions were specified as operating on values of type
double, but Standard C++ added overloaded versions to explicitly accommodate values
of type float and long double
...


acos
#include
float acos(float arg);
double acos(double arg);
long double acos(long double arg);

The acos( ) function returns the arc cosine of arg
...

Related functions are asin( ), atan( ), atan2( ), sin( ), cos( ), tan( ), sinh( ), cosh( ),
and tanh( )
...
The argument to asin( ) must be in
the range –1 to 1; otherwise a domain error will occur
...


atan
#include
float atan(float arg);
double atan(double arg);
long double atan(long double arg);

The atan( ) function returns the arc tangent of arg
...


atan2
#include
float atan2(float y, float x);
double atan2(double y, double x);
long double atan2(long double y, long double x);

The atan2( ) function returns the arc tangent of y/x
...

Related functions are asin( ), acos( ), atan( ), tan( ), cos( ), sin( ), sinh( ), cosh( ),
and tanh( )
...
For example, given 1
...
0
...
02,
ceil( ) would return –1
...


cos
#include
float cos(float arg);
double cos(double arg);
long double cos(long double arg);

The cos( ) function returns the cosine of arg
...

Related functions are asin( ), acos( ), atan2( ), atan( ), tan( ), sin( ), sinh( ), cos( ),
and tanh( )
...

Related functions are asin( ), acos( ), atan2( ), atan( ), tan( ), sin( ), cosh( ), and
tanh( )
...

A related function is log( )
...

A related function is abs( )
...
For example, given 1
...
0
...
02, floor( ) would return –2
...

Related functions are fceil( ) and fmod( )
...

Related functions are ceil( ), floor( ), and fabs( )
...
5
exp
to less than 1, and an integer exponent such that num = mantissa * 2
...

A related function is ldexp( )
...


The ldexp( ) returns the value of num * 2
If overflow occurs, HUGE_VAL
is returned
...


log
#include
float log(float num);
double log(double num);
long double log(long double num);

The log( ) function returns the natural logarithm for num
...

A related function is log10( )
...
A domain error occurs
if num is negative, and a range error occurs if the argument is zero
...


modf
#include
float modf(float num, float *i);
double modf(double num, double *i);
long double modf(long double num, long double *i);

The modf( ) function decomposes num into its integer and fractional parts
...

Related functions are frexp( ) and ldexp( )
...
A domain error
may occur if base is zero and exp is less than or equal to zero
...
An overflow produces a range error
...


sin
#include
float sin(float arg);
double sin(double arg);
long double sin(long double arg);

The sin( ) function returns the sine of arg
...


739

740

C++: The Complete Reference

Related functions are asin( ), acos( ), atan2( ), atan( ), tan( ), cos( ), sinh( ), cosh( ),
and tanh( )
...

Related functions are asin( ), acos( ), atan2( ), atan( ), tan( ), cos( ), tanh( ), cosh( ),
and sin( )
...
If it is called with a negative
argument, a domain error will occur
...


tan
#include
float tan(float arg);
double tan(double arg);
long double tan(long double arg);

The tan( ) function returns the tangent of arg
...

Related functions are acos( ), asin( ), atan( ), atan2( ), cos( ), sin( ), sinh( ), cosh( ),
and tanh( )
...

Related functions are acos( ), asin( ), atan( ), atan2( ), cos( ), sin( ), cosh( ), sinh( ),
and tan( )
...


C++

Chapter 28
Time, Date, and
Localization Functions

743

744

C++: The Complete Reference

he standard function library defines several functions that deal with the date and
time
...
These functions are described here
...
(A C program must use the
header file time
...
) This header defines three time-related types: clock_t, time_t, and tm
...
This is called the calendar time
...
The tm structure is defined as shown here:

T

struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst

/*
/*
/*
/*
/*
/*
/*
/*
/*

seconds, 0-61 */
minutes, 0-59 */
hours, 0-23 */
day of the month, 1-31 */
months since Jan, 0-11 */
years from 1900 */
days since Sunday, 0-6 */
days since Jan 1, 0-365 */
Daylight Saving Time
indicator */

}

The value of tm_isdst will be positive if daylight saving time is in effect, zero if it is
not in effect, and negative if there is no information available
...

In addition, defines the macro CLOCKS_PER_SEC, which is the number
of system clock ticks per second
...
(A C
program must use the header file locale
...
)

asctime
#include
char *asctime(const struct tm *ptr);

The asctime( ) function returns a pointer to a string that contains the information
stored in the structure pointed to by ptr converted into the following form:
day month date hours:minutes:seconds year\n\0

Chapter 28:

Time, Date, and Localization Functions

For example:
Wed Jun 19 12:05:34 1999

The structure pointer passed to asctime( ) is usually obtained from either localtime( )
or gmtime( )
...
If you wish
to save the contents of the string, you must copy it elsewhere
...


clock
#include
clock_t clock(void);

The clock( ) function returns a value that approximates the amount of time the
calling program has been running
...
A value of –1 is returned if the time is not available
...


ctime
#include
char *ctime(const time_t *time);

The ctime( ) function returns a pointer to a string of the form
day month year hours:minutes:seconds year\n\0
given a pointer to the calendar time
...

The buffer used by ctime( ) to hold the formatted output string is a statically
allocated character array and is overwritten each time the function is called
...

Related functions are localtime( ), gmtime( ), time( ), and asctime( )
...

That is, time2 –time1
...


gmtime
#include
struct tm *gmtime(const time_t *time);

The gmtime( ) function returns a pointer to the broken-down form of time in the
form of a tm structure
...
The time value is usually obtained through
a call to time( )
...

The structure used by gmtime( ) to hold the broken-down time is statically
allocated and is overwritten each time the function is called
...

Related functions are localtime( ), time( ), and asctime( )
...
The lconv structure is organized as shown here:
struct lconv {
char *decimal_point;
char *thousands_sep;
char *grouping;

/* decimal point character
for nonmonetary values */
/* thousands separator
for nonmonetary values */
/* specifies grouping for

Chapter 28:

Time, Date, and Localization Functions

nonmonetary values */
char *int_curr_symbol;
/* international currency symbol */
char *currency_symbol;
/* local currency symbol */
char *mon_decimal_point; /* decimal point character for
monetary values */
char *mon_thousands_sep; /* thousands separator for
monetary values */
char *mon_grouping;
/* specifies grouping for
monetary values */
char *positive_sign;
/* positive value indicator for
monetary values */
char *negative_sign;
/* negative value indicator for
monetary values */
char int_frac_digits;
/* number of digits displayed to the
right of the decimal point for
monetary values displayed using
international format */
char frac_digits;
/* number of digits displayed to the
right of the decimal point for
monetary values displayed using
local format */
char p_cs_precedes;
/* 1 if currency symbol precedes
positive value, 0 if currency
symbol follows value */
char p_sep_by_space;
/* 1 if currency symbol is
separated from value by a space,
0 otherwise */
char n_cs_precedes;
/* 1 if currency symbol precedes
a negative value, 0 if currency
symbol follows value */
char n_sep_by_space;
/* 1 if currency symbol is
separated from a negative
value by a space, 0 if
currency symbol follows value */
char p_sign_posn;
/* indicates position of
positive value symbol */
char n_sign_posn;
/* indicates position of
negative value symbol */
}

The localeconv( ) function returns a pointer to the lconv structure
...
Refer to your compiler's documentation for
implementation-specific information relating to the lconv structure
...


localtime
#include
struct tm *localtime(const time_t *time);

The localtime( ) function returns a pointer to the broken-down form of time in the
form of a tm structure
...
The time value is usually
obtained through a call to time( )
...
If you wish to save the
contents of the structure, you must copy it elsewhere
...


mktime
#include
time_t mktime(struct tm *time);

The mktime( ) function returns the calendar-time equivalent of the broken-down
time found in the structure pointed to by time
...

If mktime( ) cannot represent the information as a valid calendar time, –1 is returned
...


setlocale
#include
char *setlocale(int type, const char *locale);

The setlocale( ) function allows certain parameters that are sensitive to the
geopolitical environment of a program's execution to be queried or set
...
Otherwise, setlocale( )
attempts to use the string specified by locale to set the locale parameters as specified by
type
...

At the time of the call, type must be one of the following macros:

Chapter 28:

Time, Date, and Localization Functions

LC_ALL
LC_COLLATE
LC_CTYPE
LC_MONETARY
LC_NUMERIC
LC_TIME
LC_ALL refers to all localization categories
...
LC_CTYPE alters the way the character functions work
...
LC_NUMERIC changes the
decimal-point character for formatted input/output functions
...

The setlocale( ) function returns a pointer to a string associated with the type
parameter
...


strftime
#include
size_t strftime(char *str, size_t maxsize, const char *fmt,
const struct tm *time);

The strftime( ) function places time and date information, along with other
information, into the string pointed to by str according to the format commands found
in the string pointed to by fmt and using the broken-down time time
...

The strftime( ) function works a little like sprintf( ) in that it recognizes a set of
format commands that begin with the percent sign (%) and places its formatted output
into a string
...
Any other characters found in the format string
are placed into str unchanged
...
The
format commands are shown in the table below
...

The strftime( ) function returns the number of characters placed in the string
pointed to by str or zero if an error occurs
...


time
#include
time_t time(time_t *time);

The time( ) function returns the current calendar time of the system
...


Chapter 28:

Time, Date, and Localization Functions

The time( ) function can be called either with a null pointer or with a pointer to a
variable of type time_t
...

Related functions are localtime( ), gmtime( ), strftime( ), and ctime( )
...


C++

Chapter 29
The Dynamic
Allocation Functions

753

754

C++: The Complete Reference

his chapter describes the dynamic allocation functions, which were inherited
from the C language
...
Each
time malloc( ) is called, a portion of the remaining free memory is allocated
...
The region of free memory
from which memory is allocated is called the heap
...
A C program must use the header file stdlib
...

All C++ compilers will include at least these four dynamic allocation functions:
calloc( ), malloc( ), free( ), realloc( )
...
You will want to refer to your compiler's documentation
...
The reason for this is that C++ provides the
dynamic allocation operators new and delete
...
First, new automatically allocates the correct amount
of memory for the type of data being allocated
...
Third, both new and delete can be overloaded
...


T

calloc
#include
void *calloc(size_t num, size_t size);

The calloc( ) function allocates memory the size of which is equal to num * size
...

The calloc( ) function returns a pointer to the first byte of the allocated region
...
It is
always important to verify that the return value is not null before attempting to use it
...


free
#include
void free(void *ptr);

The free( ) function returns the memory pointed to by ptr to the heap
...

It is imperative that free( ) only be called with a pointer that was previously
allocated using one of the dynamic allocation system's functions (either malloc( ) or

Chapter 29:

The Dynamic Allocation Functions

calloc( ))
...

Related functions are calloc( ), malloc( ), and realloc( )
...
If there is insufficient memory in the
heap to satisfy the request, malloc( ) returns a null pointer
...
Attempting to use a
null pointer will usually result in a system crash
...


realloc
#include
void *realloc(void *ptr, size_t size);

The realloc( ) function changes the size of the previously allocated memory pointed
to by ptr to that specified by size
...
A pointer to the memory block is returned because it may be necessary for
realloc( ) to move the block in order to increase its size
...

If ptr is null, realloc( ) simply allocates size bytes of memory and returns a pointer
to it
...

If there is not enough free memory in the heap to allocate size bytes, a null pointer is
returned, and the original block is left unchanged
...


755

This page intentionally left blank
...
They include a number of conversions, variable-length
argument processing, sorting and searching, and random number generation
...
(A C
program must use the header file stdlib
...
) In this header are defined div_t and ldiv_t,
which are the types of values returned by div( ) and ldiv( ), respectively
...
The following
macros are defined:

T

Macro

Meaning

NULL

A null pointer
...


EXIT_FAILURE

The value returned to the calling process if program
termination is unsuccessful
...


If a function requires a different header than , that function description
will discuss it
...

Generally, no files are flushed
...

Related functions are exit( ) and atexit( )
...
The long version of abs( ) is
the same as labs( )
...

A related function is labs( )
...

Otherwise, assert( ) does nothing
...

It is not necessary to remove the assert( ) statements from the source code once a
program is debugged because if the macro NDEBUG is defined (as anything), the
assert( ) macros will be ignored
...


atexit
#include
int atexit(void (*func)(void));

The atexit( ) function causes the function pointed to by func to be called upon
normal program termination
...

At least 32 termination functions may be established, and they will be called in the
reverse order of their establishment
...


atof
#include
double atof(const char *str);

759

760

C++: The Complete Reference

The atof( ) function converts the string pointed to by str into a double value
...
If this is not the case, the returned
value is undefined
...
This includes white space, punctuation (other than periods), and
characters other than E or e
...
00HELLO", the
value 100
...

Related functions are atoi( ) and atol( )
...
The string
must contain a valid integer number
...

The number may be terminated by any character that cannot be part of an integer
number
...
This means that if atoi( )
is called with "123
...
23" is ignored
...


atol
#include
long atol(const char *str);

The atol( ) function converts the string pointed to by str into a long value
...
If this is not the case, the returned
value is undefined; however, most implementations will return zero
...
This includes white space, punctuation, and characters
...
23", the long integer value 123L will be returned, and the "
...

Related functions are atof( ) and atoi( )
...

The number of elements in the array is specified by num, and the size (in bytes) of each
element is described by size
...
The form of the compare function must be as follows:
int func_name(const void *arg1, const void *arg2);
It must return values as described in the following table:

Comparison

Value Returned

arg1 is less than arg2

Less than zero

Arg1 is equal to arg2

Zero

Arg1 is greater than arg2

Greater than zero

The array must be sorted in ascending order with the lowest address containing the
lowest element
...

A related function is qsort( )
...
The long version of div( )
returns the quotient and remainder in a structure of type ldiv_t
...

The structure type div_t will have at least these two fields:
int quot; /* quotient */
int rem; /* remainder */

761

762

C++: The Complete Reference

The structure type ldiv_t will have at least these two fields:
long quot; /* quotient */
long rem; /* remainder */

A related function is ldiv( )
...

The value of exit_code is passed to the calling process, usually the operating system,
if the environment supports it
...
A nonzero value, or
EXIT_FAILURE, is used to indicate an implementation-defined error
...


getenv
#include
char *getenv(const char *name);

The getenv( ) function returns a pointer to environmental information associated
with the string pointed to by name in the implementation-defined environmental
information table
...

The environment of a program may include such things as path names and devices
online
...
You will need to refer
to your compiler's documentation for details
...

A related function is system( )
...

A related function is abs( )
...

The structure type ldiv_t will have at least these two fields:
long quot; /* quotient */
long rem; /* remainder */

A related function is div( )
...
These two functions provide a means of jumping between functions
...

The longjmp( ) function operates by resetting the stack to the state as described in
envbuf, which must have been set by a prior call to setjmp( )
...
That is, the
computer is "tricked" into thinking that it never left the function that called setjmp( )
...
)
The buffer evnbuf is of type jmp_buf, which is defined in the header
...

The value of status becomes the return value of setjmp( ) and may be interrogated to
determine where the long jump came from
...

By far the most common use of longjmp( ) is to return from a deeply nested set of
routines when an error occurs
...


mblen
#include
int mblen(const char *str, size_t size);

763

764

C++: The Complete Reference

The mblen( ) function returns the length (in bytes) of a multibyte character pointed
to by str
...
It returns –1 on error
...
If they do not, zero is returned
...


mbstowcs
#include
size_t mbstowcs(wchar_t *out, const char *in, size_t size);

The mbstowcs( ) function converts the multibyte string pointed to by in into a wide
character string and puts that result in the array pointed to by out
...

The mbstowcs( ) function returns the number of multibyte characters that are
converted
...

Related functions are wcstombs( ), mbtowc( )
...

Only size number of characters will be examined
...
–1 is returned if an
error occurs
...
If they do not, zero is returned
...


qsort
#include
void qsort(void *buf, size_t num, size_t size,
int (*compare) (const void *, const void *));

Chapter 30:

Utility Functions

The qsort( ) function sorts the array pointed to by buf using a Quicksort (developed
by C
...
R
...
The Quicksort is the best general-purpose sorting algorithm
...
The number of elements in the array is specified by
num, and the size (in bytes) of each element is described by size
...
The form of the compare function must be as follows:
int func_name(const void *arg1, const void *arg2);
It must return values as described here:

Comparison

Value Returned

arg1 is less than arg2

Less than zero

arg1 is equal to arg2

Zero

arg1 is greater than arg2

Greater than zero

The array is sorted into ascending order with the lowest address containing the
lowest element
...


raise
#include
int raise(int signal);

The raise( ) function sends the signal specified by signal to the executing program
...
It uses the header
...
Of course, your compiler is free
to provide additional signals
...


rand
#include
int rand(void);

The rand( ) function generates a sequence of pseudorandom numbers
...

A related function is srand( )
...
It uses the header
...
However, longjmp( ) passes
an argument to setjmp( ) when it executes, and it is this value (always nonzero) that
will appear to be the value of setjmp( ) after a call to longjmp( ) has occurred
...

A related function is longjmp( )
...
That is, the function pointed to by func will be called when
signal is received by your program
...

On success, signal( ) returns the address of the previously defined function for the
specified signal
...

A related function is raise( )
...
(The rand( ) function returns pseudorandom numbers
...
Conversely, you can
also use srand( ) to generate the same pseudorandom sequence over and over again by
calling it with the same seed before starting the sequence each time
...


strtod
#include
double strtod(const char *start, char **end);

767

768

C++: The Complete Reference

The strtod( ) function converts the string representation of a number stored in the
string pointed to by start into a double and returns the result
...
First, any white space in the string pointed
to by start is stripped
...
Any
character that cannot be part of a floating-point number will cause this process to stop
...
Finally, end is set to point to the remainder, if any, of the original string
...
00 Pliers", the value 100
...

If no conversion takes place, zero is returned
...
If underflow occurs,
then zero is returned and the global variable errno is set to ERANGE
...


strtol
#include
long strtol(const char *start, char **end,
int radix);

The strtol( ) function converts the string representation of a number stored in the
string pointed to by start into a long and returns the result
...
If radix is zero, the base is determined by rules that govern
constant specification
...

The strtol( ) function works as follows
...
Next, each character that comprises the number is read
...

This includes white space, punctuation, and characters
...
This means that if strtol( ) is called with
"100 Pliers", the value 100L will be returned, and end will point to the space that
precedes "Pliers"
...
If no conversion takes place, zero is returned
...


strtoul
#include
unsigned long strtoul(const char *start, char **end,
int radix);

Chapter 30:

Utility Functions

The strtoul( ) function converts the string representation of a number stored in
the string pointed to by start into an unsigned long and returns the result
...
If radix is zero, the base is determined by rules
that govern constant specification
...

The strtoul( ) function works as follows
...
Next, each character that comprises the number is read
...
This includes white space, punctuation, and characters
...
This means that if strtoul( ) is
called with " 100 Pliers", the value 100L will be returned, and end will point to the space
that precedes "Pliers"
...
If no conversion takes place, zero is returned
...


system
#include
int system(const char *str);

The system( ) function passes the string pointed to by str as a command to the
command processor of the operating system
...
(Some C++ code will be executed in dedicated
systems that do not have operating systems and command processors, so you may not
be able to assume that a command processor is present
...
However, generally it will return zero if the command was
successfully executed, and nonzero otherwise
...


va_arg, va_start, and va_end
#include
type va_arg(va_list argptr, type);
void va_end(va_list argptr);
void va_start(va_list argptr, last_parm);

The va_arg( ), va_start( ), and va_end( ) macros work together to allow a variable
number of arguments to be passed to a function
...
The type va_list is
defined by
...
The function must have at least one known parameter, but
may have more, prior to the variable parameter list
...
The name of last_parm is used as the second parameter in a call to
va_start( )
...
After that, parameters are
returned via calls to va_arg( ), with type being the type of the next parameter
...
If va_end( ) is
not called, a program crash is very likely
...


wcstombs
#include
size_t wcstombs(char *out, const wchar_t *in, size_t size);

The wcstombs( ) converts the wide-character array pointed to by in into its
multibyte equivalent and puts the result in the array pointed to by out
...
Conversion stops before that if the null terminator is
encountered
...
On failure, –1
is returned
...


wctomb
#include
int wctomb(char *out,

wchar_t in);

The wctomb( ) converts the wide character in in into its multibyte equivalent and
puts the result in the object pointed to by out
...

If successful, wctomb( ) returns the number of bytes contained in the multibyte
character
...

If out is null, then wctomb( ) returns nonzero if the multibyte character has state
dependencies and zero if it is not
...


C++

Chapter 31
The Wide-Character Functions

771

772

C++: The Complete Reference

n 1995, a number of wide-character functions were added to Standard C and
subsequently adopted by Standard C++
...
For the most part these functions
parallel their char equivalents
...
In general, the wide-character functions use the
same names as their char equivalents, except that a "w" is added
...
The C
header files wchar
...
h are also supported
...
Many of
the wide-character functions receive a wide character as a parameter
...
It is capable of holding a wide character
...

wctrans_t and wctype_t are the types of objects used to represent a character mapping
(i
...
, character translation) and the classification of a character, respectively
...

In addition to defining win_t, the header defines the type mstate_t
...
The header also defines the macros NULL, WEOF,
WCHAR_MAX, and WCHAR_MIN
...

Although the standard function library's support for wide characters is quite
extensive, these functions are not frequently used
...
Also, interest in wide-character-compliant
programs has been less than expected
...

Since most of the wide-character functions simply parallel their char equivalents
and are not frequently used by most C++ programmers, only a brief description of
these functions is provided
...
These functions categorize wide characters as to their
type or convert the case of a character
...

In addition to the functions shown in Table 31-1, defines the following
ones, which provide an open-ended means of classifying characters
...
The string pointed to by attr specifies a property that a character must

Chapter 31:

The Wide-Character Functions

Function

char Equivalent

int iswalnum(wint_t ch)

isalnum( )

int iswalpha(wint_t ch)

isalpha( )

int iswcntrl(wint_t ch)

iscntrl( )

int iswdigit(wint_t ch)

isdigit( )

int iswgraph(wint_t ch)

isgraph( )

int iswlower(wint_t ch)

islower( )

int iswprint(wint_t ch)

isprint( )

int iswpunct(wint_t c)

ispunct( )

int iswspace(wint_t ch)

isspace( )

int iswupper(wint_t ch)

isupper( )

int iswxdigit(wint_t ch)

isxdigit( )

wint_t tolower(wint_t ch)

tolower( )

wint_t toupper(wint_t ch)

toupper( )

Table 31-1
...
The value in attr_ob used to determine if ch is a character that has that property
...
Otherwise, it returns zero
...

alnum

alpha

cntrl

digit

graph

lower

print

punct

space

upper

xdigit

The following program demonstrates the wctype( ) and iswctype( ) functions
...
\n";
return 0;
}

This program displays "Is a space
...
They are
shown here:
wctrans_t wctrans(const char *mapping);
wint_t towctrans(wint_t ch, wctrans_t mapping_ob);
The function wctrans( ) returns a value that can be passed to the mapping_ob parameter
to towctrans( )
...
This value can then be used by iswctrans( ) to map ch
...
The following mapping strings are supported in all
execution environments
...

#include
#include
using namespace std;
int main()
{
wctrans_t x;
x = wctrans("tolower");

Chapter 31:

The Wide-Character Functions

wchar_t ch = towctrans(L'W', x);
cout << (char) ch;
return 0;
}

This program displays a lowercase "w"
...
These functions are shown in Table 31-2
...
Notice that swprintf( ) and vswprintf( ) require an
additional parameter not needed by their char equivalents
...
If how is negative,
fwide( ) makes stream into a char stream
...
If the
stream has already been oriented to either wide or normal characters, it will not be
changed
...
A stream's orientation is determined by its
first use
...
These are shown in Table 31-3
...
Note that
wcstok( ) requires an additional parameter not used by its char equivalent
...
These functions use the header
...
)

fprintf( )

int fwscanf(FILE *stream, const wchar_t fmt,
...
)

sprintf( )
Note the addition of the
parameter num, which limits
the number of characters
written to str
...
)

sscanf( )

wint_t ungetwc(wint_t ch, FILE *stream)

ungetc( )

int vfwprintf(FILE *stream,
const wchar_t fmt, va_list arg)

vfprintf( )

int vswprintf(wchar_t *str, size_t num,
const wchar_t *fmt, va_list arg)

vsprintf( )
Note the addition of the
parameter num, which limits
the number of characters
written to str
...
)

printf( )

int wscanf(const wchar_t *fmt,
...


The Wide-Character I/O Functions

Chapter 31:

The Wide-Character Functions

Function

char Equivalent

wchar_t *wcscat(wchar_t *str1, const wchar_t *str2)

strcat( )

wchar_t *wcschr(const wchar_t *str, wchar_t ch)

strchr( )

int wcscmp(const wchar_t *str1, const wchar_t *str2)

strcmp( )

int wcscoll(const wchar_t *str1, const wchar_t *str2)

strcoll( )

size_t wcscspn(const wchar_t *str1,
const wchar_t *str2)

strcspn( )

wchar_t *wcscpy(wchar_t *str1, const wchar_t *str2)

strcpy( )

size_t wcslen(const wchar_t *str)

strlen( )

wchar_t *wcsncpy(wchar_t *str1, const wchar_t str2,
size_t num)

strncpy( )

wchar_t *wcsncat(wchar_t *str1, const wchar_t str2,
size_t num)

strncat( )

int wcsncmp(const wchar_t *str1,
const wchar_t *str2, size_t num)

strncmp( )

wchar_t *wcspbrk(const wchar_t *str1,
const wchar_t *str2)

strpbrk( )

wchar_t *wcsrchr(const wchar_t *str, wchar_t ch)

strrchr( )

size_t wcsspn(const wchar_t *str1,
const wchar_t str2)

strspn( )

wchar_t *wcstok(wchar_t *str1, const wchar_t *str2,
wchar_t **endptr)

strtok( )
Here, endptr is a pointer
that holds information
necessary to continue the
tokenizing process
...


The Wide-Character String Functions

777

778

C++: The Complete Reference

Function

char Equivalent

size_t wcsftime(wchar_t *str, size_t max,
const wchar_t *fmt,
const struct tm *ptr)

strftime( )

double wcstod(const wchar_t *start,
wchar_t **end);

strtod( )

long wcstol(const wchar_t *start, wchar_t **end,
int radix)

strtol( )

unsigned long wcstoul(const wchar_t *start,
wchar_t **end, int radix)

strtoul( )

Table 31-4
...
They are shown in Table 31-5
...


Function

char Equivalent

wchar_t *wmemchr(const wchar_t *str,
wchar_t ch, size_t num)

memchr( )

int wmemcmp(const wchar_t *str1,
const wchar_t *str2, size_t num)

memcmp( )

wchar_t *wmemcpy(wchar_t *str1,
const wchar_t *str2,
size_t num)

memcpy( )

wchar_t *wmemmove(wchar_t *str1,
const wchar_t *str2,
size_t num)

memmove( )

wchar_t *wmemset(wchar_t *str, wchar_t ch,
size_t num)

memset( )

Table 31-5
...
These functions, shown in Table 31-6, use the
header
...
The restartable version utilizes the state information passed to it in a
parameter of type mbstate_t
...


Function

Description

win_t btowc(int ch)

Converts ch into its wide-character
equivalent and returns the result
...


size_t mbrlen(const char *str, size_t num,
mbstate_t *state)

Restartable version of mblen( ) as
described by state
...
Zero is
returned if the next character is null
...


size_t mbrtowc(wchar_t *out,
const char *in, size_t num,
mbstate_t *state)

Restartable version of mbtowc( ) as
described by state
...
Zero is
returned if the next character is null
...
If an error occurs, the
macro EILSEQ is assigned to errno
...


size_t mbsrtowcs(wchar_t *out,
const char **in,
size_t num,
mbstate_t state)

Restartable version of mbstowcs( ) as
described by state
...
If an error occurs, the macro
EILSEQ is assigned to errno
...


Wide-Character/Multibyte Conversion Functions

779

780

C++: The Complete Reference

Function

Description

size_t wcrtomb(char *out, wchar_t ch,
mbstate_t *state)

Restartable version of wctomb( ) as
described by state
...


size_t wcsrtombs(char *out,
const wchar_t **in,
size_t num,
mbstate_t *state)

Restartable version of wcstombs( ) as
described by state
...

If an error occurs, the macro EILSEQ
is assigned to errno
...
It returns EOF
on failure
...


Wide-Character/Multibyte Conversion Functions (continued)

Part IV
The Standard C++ Class Library

S

tandard C++ defines an extensive set of classes that provide
support for a number of common activities, including I/O,
strings, and numeric processing
...
The class library
forms a major portion of the C++ language and defines much of
its character
...


781

782

C++: The Complete Reference

The Standard C++ library is quite large and an in-depth description of all of its
classes, features, attributes, and implementation details is beyond the scope of this
book
...
Therefore, this section describes only those parts of the class library that
are used in an application
...


C++

Chapter 32
The Standard C++ I/O Classes

783

784

C++: The Complete Reference

his chapter describes the Standard C++ I/O class library
...
The
first is the old-style library, which is not defined by Standard C++
...
Since the modern I/O library is
essentially a superset of the old-style one, its is the only one described here
...


T

Note

For an overview of C++ I/O, see Chapters 20 and 21
...
These classes are shown here
...
It provides
definitions for various elements of the I/O system
...
The first is derived from the low-level I/O class called basic_streambuf
...
The classes basic_filebuf and
basic_stringbuf are derived from basic_streambuf
...

The class hierarchy that you will most commonly be working with is derived from
basic_ios
...
basic_ios is used as a base for several derived
classes, including basic_istream, basic_ostream, and basic_iostream
...

Specifically, from basic_istream are derived the classes basic_ifstream and
basic_istringstream, from basic_ostream are derived basic_ofstream and
basic_ostringstream, and from basic_iostream are derived basic_fstream and
basic_stringstream
...
Thus, any class derived from
basic_ios has access to the members of ios_base
...
For example, here is the template
specification for basic_ios:
template >
class basic_ios: public ios_base
Here, CharType specifies the type of character (such as char or wchar_t) and Attr
specifies a type that describes its attributes
...

As explained in Chapter 20, the I/O library creates two specializations of the
template class hierarchies just described: one for 8-bit characters and one for wide
characters
...


Template
Class

Character-Based
Class

Wide-Character-Based
Class

basic_ios

ios

wios

basic_istream

istream

wistream

basic_ostream

ostream

wostream

basic_iostream

iostream

wiostream

785

786

C++: The Complete Reference

Template
Class

Character-Based
Class

Wide-Character-Based
Class

basic_ifstream

ifstream

wifstream

basic_ofstream

ofstream

wofstream

basic_fstream

fstream

wfstream

basic_istringstream

istringstream

wistringstream

basic_ostringstream

ostringstream

wostringstream

basic_stringstream

stringstream

wstringstream

basic_streambuf

streambuf

wstreambuf

basic_filebuf

filebuf

wfilebuf

basic_stringbuf

stringbuf

wstringbuf

Since the vast majority of programmers will be using character-based I/O, those
are the names used by this chapter
...

For instance, this chapter will use the name ios rather than basic_ios, istream rather
than basic_istream, and fstream rather than basic_fstream
...


The I/O Headers
The Standard C++ I/O system relies upon several headers
...


Header

For



File I/O



Parameterized I/O manipulators



Basic I/O support



Forward declarations used by the I/O system



General I/O



Basic input support

Chapter 32:

Header

For



Basic output support



String-based streams



The Standard C++ I/O Classes

Low-level I/O support

Several of these headers are used internally by the I/O system
...


The Format Flags and I/O Manipulators
Each stream has associated with it a set of format flags that control the way
information is formatted
...

adjustfield

basefield

boolalpha

dec

fixed

floatfield

hex

internal

left

oct

right

scientific

showbase

showpoint

showpos

skipws

unitbuf

uppercase

These values are used to set or clear the format flags, using functions such as setf( )
and unsetf( )
...

In addition to setting or clearing the format flags directly, you may alter the format
parameters of a stream through the use of special functions called manipulators, which
can be included in an I/O expression
...


Input/Output

dec

Turns on dec flag
...


Output

ends

Output a null
...


Output

flush

Flush a stream
...


Input/Output

internal

Turns on internal flag
...


Output

noboolalpha

Turns off boolalpha flag
...


Output

noshowpoint

Turns off showpoint flag
...


Output

noskipws

Turns off skipws flag
...


Output

nouppercase

Turns off uppercase flag
...


Input/Output

resetiosflags (fmtflags f )

Turn off the flags
specified in f
...


Output

scientific

Turns on scientific flag
...


Input/Output

setfill(int ch)

Set the fill character to ch
...


Input/output

setprecision (int p)

Set the number of digits of
precision
...


Output

showbase

Turns on showbase flag
...


Output

showpos

Turns on showpos flag
...


Input

unitbuf

Turns on unitbuf flag
...


Output

ws

Skip leading white space
...


Chapter 32:

The Standard C++ I/O Classes

Several Data Types
In addition to the fmtflags type just described, the Standard C++ I/O system defines
several other types
...
It is typically some form of integer
...
It is typically some form of integer
...


The streampos and wstreampos Types
An object of type streampos is capable of holding a value that represents a position
within a char stream
...
These are defined in , which is
automatically included by the I/O system
...

These types are defined by ios (and other classes) and are essentially the same as
streamoff and streampos (or their wide-character equivalents)
...
It
will be one or more of these values
...


ate

Seek to end of file on creation
...


in

Open file for input
...


trunc

Erase previously existing file
...


789

790

C++: The Complete Reference

The iostate Type
The current status of an I/O stream is described by an object of type iostate, which is
an enumeration defined by ios_base that includes these members
...


eofbit

End-of-file is encountered
...


badbit

A fatal I/O error has occurred
...
It is
defined within ios_base
...

beg

Beginning-of-file

cur

Current location

end

End-of-file

The failure Class
In ios_base is defined the exception type failure
...
It inherits exception (the standard
exception class)
...
This message can be obtained from a
failure object by calling its what( ) function, shown here:
virtual const char *what( ) const throw( );

Overload << and >> Operators
The following classes overload the << and/or >> operators relative to all of the built-in
data types
...


The General-Purpose I/O Functions
The remainder of this chapter describes the general-purpose I/O functions supplied by
Standard C++
...
Many of the members of the low-level classes are not
used for application programming
...


bad
#include
bool bad() const;

The bad( ) function is a member of ios
...

A related function is good( )
...

The clear( ) function clears the status flags associated with a stream
...
Otherwise,
the status flags will be set to whatever value is specified in flags
...


eof
#include
bool eof() const;

791

792

C++: The Complete Reference

The eof( ) function is a member of ios
...

Related functions are bad( ), fail( ), good( ), rdstate( ), and clear( )
...

The first form returns an iostate object that indicates which flags cause an
exception
...

A related function is rdstate( )
...

The fail( ) function returns true if an I/O error has occurred in the associated
stream
...

Related functions are good( ), eof( ), bad( ), clear( ), and rdstate( )
...

By default, when a field needs to be filled, it is filled with spaces
...
The old fill character is returned
...


Chapter 32:

The Standard C++ I/O Classes

Related functions are precision( ) and width( )
...

The first form of flags( ) simply returns the current format flags settings of the
associated stream
...
When you use this version, the bit pattern found in f is copied into the
format flags associated with the stream
...

Related functions are unsetf( ) and setf( )
...

The flush( ) function causes the buffer connected to the associated output
stream to be physically written to the device
...

Related functions are put( ) and write( )
...

The versions of fstream( ), ifstream( ), and ofstream( ) that take no parameters
create a stream that is not associated with any file
...

The versions of fstream( ), ifstream( ), and ofstream( ) that take a filename for their
first parameters are the most commonly used in application programs
...
The constructor functions have
the same parameters and defaults as the open( ) function
...
) For
instance, this is the most common way you will see a file opened:
ifstream mystream("myfile");

If for some reason the file cannot be opened, the value of the associated stream
variable will be false
...

Related functions are close( ) and open( )
...

The gcount( ) function returns the number of characters read by the last
input operation
...


get
#include
int get();
istream &get(char &ch):
istream &get(char *buf, streamsize num);
istream &get(char *buf, streamsize num, char delim);
istream &get(streambuf &buf);
istream &get(streambuf &buf, char delim);

Chapter 32:

The Standard C++ I/O Classes

The get( ) function is a member of istream
...
The parameterless form of
get( ) reads a single character from the associated stream and returns that value
...
It returns a reference to the stream
...
The array pointed to by buf will be null terminated by get( )
...
Instead, it
remains in the stream until the next input operation
...

get(char *buf, streamsize num, char delim) reads characters into the array pointed
to by buf until either num−1 characters have been read, the character specified by delim
has been found, or the end of the file has been encountered
...
If the delimiter character is encountered in the input
stream, it is not extracted
...
This function returns a reference to the stream
...
Characters are read until a newline is found or the end of the file is encountered
...
If the newline character is encountered in the input
stream, it is not extracted
...
Characters are read until the character specified by delim is found or
the end of the file is encountered
...
If the delimiter
character is encountered in the input stream, it is not extracted
...


getline
#include
istream &getline(char *buf, streamsize num);
istream &getline(char *buf, streamsize num, char delim);

The getline( ) function is a member of istream
...
The array pointed to by buf will be null
terminated by getline( )
...
This function returns a reference to the stream
...
The array pointed

795

796

C++: The Complete Reference

to by buf will be null terminated by getline( )
...
This function returns a
reference to the stream
...


good
#include
bool good() const;

The good( ) function is a member of ios
...

Related functions are bad( ), fail( ), eof( ), clear( ), and rdstate( )
...

You can use the ignore( ) member function to read and discard characters from the
input stream
...
If the delimiting character is encountered, it is removed from the input stream
...

Related functions are get( ) and getline( )
...

A file is associated with a stream by using the open( ) function
...
The value of mode determines how
the file is opened
...

Including ios::app causes all output to that file to be appended to the end
...
Including ios::ate causes a seek to
the end of the file to occur when the file is opened
...

The ios::binary value causes the file to be opened for binary I/O operations
...

The ios::in value specifies that the file is capable of input
...
However, creating an ifstream stream
implies input, and creating an ofstream stream implies output, and opening a file
using fstream implies both input and output
...

In all cases, if open( ) fails, the stream will be false
...

Related functions are close( ), fstream( ), ifstream( ), and ofstream( )
...

The peek( ) function returns the next character in the stream or EOF if the end of
the file is encountered
...

A related function is get( )
...

By default, six digits of precision are displayed when floating-point values are
output
...
The original value is returned
...

Related functions are width( ) and fill( )
...

The put( ) function writes ch to the associated output stream
...

Related functions are write( ) and get( )
...

The putback( ) function returns ch to the associated input stream
...


rdstate
#include
iostate rdstate() const;

Chapter 32:

The Standard C++ I/O Classes

The rdstate( ) function is a member of ios
...
The C++ I/O
system maintains status information about the outcome of each I/O operation relative
to each active stream
...


eofbit

End-of-file is encountered
...


badbit

A fatal I/O error has occurred
...

rdstate( ) returns goodbit when no error has occurred; otherwise, an error bit has
been set
...


read
#include
istream &read(char *buf, streamsize num);

The read( ) function is a member of istream
...
If the end of the file is reached before num
characters have been read, read( ) simply stops, sets failbit, and the buffer contains
as many characters as were available
...
) read( ) returns a reference
to the stream
...


readsome
#include
streamsize readsome(char *buf, streamsize num);

The readsome( ) function is a member of istream
...
If the stream contains less than num

799

800

C++: The Complete Reference

characters, that number of characters are read
...
The difference between read( ) and readsome( ) is that readsome( )
does not set the failbit if there are less than num characters available
...


seekg and seekp
#include
istream &seekg(off_type offset, ios::seekdir origin)
istream &seekg(pos_type position);
ostream &seekp(off_type offset, ios::seekdir origin);
ostream &seekp(pos_type position);

The seekg( ) function is a member of istream, and the seekp( ) function is a member
of ostream
...
To this end, the C++ I/O system manages two pointers associated with a
file
...
The other is the put pointer, which specifies where in the file the next output
operation will occur
...
However, using the seekg( )
and seekp( ) functions, it is possible to access the file in a nonsequential fashion
...
The two-parameter version of seekp( ) moves the
put pointer offset number of bytes from the location specified by origin
...

The origin parameter is of type seekdir and is an enumeration that has these values:
ios::beg

Seek from beginning

ios::cur

Seek from current position

ios::end

Seek from end

The single-parameter versions of seekg( ) and seekp( ) move the file pointers to the
location specified by position
...
pos_type is a type that is capable of
containing the largest valid value that position can have
...

Related functions are tellg( ) and tellp( )
...

The setf( ) function sets the format flags associated with a stream
...

The first version of setf( ) turns on the format flags specified by flags
...
) For example, to turn on the showpos flag for cout, you can use
this statement:
cout
...

It is important to understand that a call to setf( ) is done relative to a specific
stream
...
Put differently, there is no
concept in C++ of global format status
...

The second version of setf( ) affects only the flags that are set in flags2
...
Even if flags1 contains other set flags, only those specified by flags2
will be affected
...

Related functions are unsetf( ) and flags( )
...

The setstate( ) function sets the status of the associated stream as described by flags
...

Related functions are clear( ) and rdstate( )
...

The first form of the str( ) function returns a string object that contains the current
contents of the string-based stream
...

Related functions are get( ) and put( )
...
These construct streams that are tied to strings
...
The versions that take a string
parameter initialize the string stream
...

// Demonstrate string streams
...
");
// get string
string str = s
...
2;
int i;
double d;
s >> str >> i >> d;
cout << str << " " << i << " " << d;
return 0;
}

The output produced by this program is shown here:
This is initial string
...
2

A related function is str( )
...

Calling sync_with_stdio( ) allows the standard C-like I/O system to be safely used
concurrently with the C++ class-based I/O system
...
The previous setting is returned: true for
synchronized; false for no synchronization
...
This function is reliable only if called prior to any other I/O operations
...

The C++ I/O system manages two pointers associated with a file
...
The other
is the put pointer, which specifies where in the file the next output operation will occur
...
You can determine the current position of the get
pointer using tellg( ) and of the put pointer using tellp( )
...

The values returned by tellg( ) and tellp( ) can be used as parameters to seekg( )
and seekp( ), respectively
...


unsetf
#include
void unsetf(fmtflags flags);

The unsetf( ) function is a member of ios (inherited from ios_base)
...

The flags specified by flags are cleared
...
)
Related functions are setf( ) and flags( )
...


Chapter 32:

The Standard C++ I/O Classes

To obtain the current field width, use the first form of width( )
...
To set the field width, use the second form
...

Related functions are precision( ) and fill( )
...

The write( ) function writes num bytes to the associated output stream from the
buffer pointed to by buf
...

Related functions are read( ) and put( )
...


C++

Chapter 33
The STL Container Classes

807

808

C++: The Complete Reference

his chapter describes the classes that implement the containers defined by the
standard template library (STL)
...
In addition to supplying the memory necessary to store
objects, they define the mechanisms by which the objects in the container may be
accessed
...


T

For an overview and tutorial to the STL, refer to Chapter 24
...
When
referring to the various iterator types generically, this book will use the terms listed here
...
When a binary predicate is required, the type BinPred will be used
...
For both unary and binary predicates, the arguments
will contain values of the type of objects being stored by the container
...

One other point: In the descriptions that follow, when an iterator is said to point to
the end of a container, this means that the iterator points just beyond the last object in
the container
...


Container

Description

Required Header

bitset

A set of bits
...




list

A linear list
...




multimap

Stores key/value pairs in
which one key may be
associated with two or
more values
...




priority_queue

A priority queue
...




set

A set in which each
element is unique
...




vector

A dynamic array
...
Since the containers
are implemented using template classes, various placeholder data types are used
...

Since the names of the placeholder types in a template class are arbitrary, the
container classes declare typedefed versions of these types
...
Here are the typedef names used by the container classes
...


reference

A reference to an element
...


difference_type

Can represent the difference between two addresses
...


const_iterator

A const iterator
...


const_reverse_iterator

A const reverse iterator
...
(Same as the
generic type T
...


key_type

The type of a key
...


mapped_type

The type of value stored in a map
...
)

value_compare

The type of a function that compares two values
...
(Same as
the generic type T
...


const_pointer

The type of a const pointer
...


bitset
The bitset class supports operations on a set of bits
...
It has the following constructors:
bitset( );
bitset(unsigned long bits);
explicit bitset(const string &s, size_t i = 0, size_t num = npos);
The first form constructs an empty bitset
...
The third form constructs a bitset using
the string s, beginning at i
...
Only num or
s
...
The constant npos is a value that is
sufficiently large to describe the maximum length of s
...

bitset contains the following member functions
...


size_type count( ) const;

Returns the number of 1 bits
...


bitset &flip(size_t i);

Reverses the bit in position i in the
invoking bitset and returns *this
...


bool operator !=(const bitset &op2)
const;

Returns true if the invoking bitset differs
from the one specified by right-hand
operator, op2
...


bitset
&operator &=(const bitset &op2);

ANDs each bit in the invoking bitset
with the corresponding bit in op2 and
leaves the result in the invoking bitset
...


bitset
&operator ^=(const bitset &op2);

XORs each bit in the invoking bitset
with the corresponding bit in op2 and
leaves the result in the invoking bitset
...


bitset
&operator |=(const bitset &op2);

ORs each bit in the invoking bitset with
the corresponding bit in op2 and leaves
the result in the invoking bitset
...


bitset &operator ~=( ) const;

Reverses the state of all bits in the
invoking bitset and returns the result
...
It returns *this
...
It returns
*this
...


bitset &reset( );

Clears all bits in the invoking bitset and
returns *this
...


bitset &set( );

Sets all bits in the invoking bitset and
returns *this
...
Any nonzero value
for val is assumed to be 1
...


bool test(size_t i) const;

Returns the state of the bit in position i
...


unsigned long to_ulong( ) const;

Converts the invoking bitset into an
unsigned long integer
...
Its template specification is
template > class deque
Here, T is the type of data stored in the deque
...
The second form constructs a deque that has
num elements with the value val
...
The fourth form constructs a deque that contains the elements in
the range specified by start and end
...


Member

Description

template
void assign(InIter start, InIter end);

Assigns the deque the sequence
defined by start and end
...


reference at(size_type i);
const_reference at(size_type i) const;

Returns a reference to the element
specified by i
...


iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the deque
...


bool empty( ) const;

Returns true if the invoking deque is
empty and false otherwise
...


iterator erase(iterator i);

Removes the element pointed to by i
...


iterator erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


reference front( );
const_reference front( ) const;

Returns a reference to the first element
in the deque
...


iterator insert(iterator i,
const T &val);

Inserts val immediately before the
element specified by i
...


void insert(iterator i, size_type num,
const T &val);

Inserts num copies of val immediately
before the element specified by i
...


size_type max_size( ) const;

Returns the maximum number of
elements that the deque can hold
...


void pop_back( );

Removes the last element in the deque
...


void push_back(const T &val);

Adds an element with the value
specified by val to the end of the deque
...


reverse_iterator rbegin( );
const_reverse_iterator rbegin( ) const;

Returns a reverse iterator to the end of
the deque
...


void resize(size_type num, T val = T ( ));

Changes the size of the deque to that
specified by num
...


size_type size( ) const;

Returns the number of elements
currently in the deque
...


Chapter 33:

The STL Container Classes

list
The list class supports a list
...
It has the following constructors:
explicit list(const Allocator &a = Allocator( ) );
explicit list(size_type num, const T &val = T ( ),
const Allocator &a = Allocator( ));
list(const list &ob);
template list(InIter start, InIter end,
const Allocator &a = Allocator( ));
The first form constructs an empty list
...
The third form constructs a list that contains the same
elements as ob
...

The following comparison operators are defined for list:
==, <, <=, !=, >, >=
list contains the following member functions
...


Void assign(size_type num, const T &val);

Assigns the list num elements of value
val
...


iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the list
...


bool empty( ) const;

Returns true if the invoking list is
empty and false otherwise
...


iterator erase(iterator i);

Removes the element pointed to by i
...


iterator erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


reference front( );
const_reference front( ) const;

Returns a reference to the first element
in the list
...


iterator insert(iterator i,
const T &val = T( ));

Inserts val immediately before the
element specified by i
...


void insert(iterator i, size_type num,
const T & val);

Inserts num copies of val immediately
before the element specified by i
...


size_type max_size( ) const;

Returns the maximum number of
elements that the list can hold
...
The
result is ordered
...
In the
second form, a comparison function
can be specified that determines when
one element is less than another
...


void pop_front( );

Removes the first element in the list
...


void push_front(const T &val);

Adds an element with the value
specified by val to the front of the list
...


void remove(const T &val);

Removes elements with the value val
from the list
...


reverse_iterator rend( );
const_reverse_iterator rend( ) const;

Returns a reverse iterator to the start
of the list
...
If the list must be
lengthened, then elements with the value
specified by val are added to the end
...


size_type size( ) const;

Returns the number of elements
currently in the list
...
The second form sorts
the list using the comparison function
cmpfn to determine when one element
is less than another
...
After the operation, ob is empty
...


void splice(iterator i,
list &ob,
iterator start, iterator end);

The range defined by start and end is
removed from ob and stored in the
invoking list beginning at the location
pointed to by i
...


void unique( );
template
void unique(BinPred pr);

Removes duplicate elements from the
invoking list
...


map
The map class supports an associative container in which unique keys are mapped
with values
...
It has the following
constructors:
explicit map(const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ) );
map(const map &ob);
template map(InIter start, InIter end,
const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ));
The first form constructs an empty map
...
The third form constructs a map that contains the
elements in the range specified by start and end
...

The following comparison operators are defined for map
...
In the descriptions,
key_type is the type of the key, and value_type represents pair
...


void clear( );

Removes all elements from the map
...


bool empty( ) const;

Returns true if the invoking map is
empty and false otherwise
...


pair
equal_range(const key_type &k);
pair
equal_range(const key_type &k) const;

Returns a pair of iterators that point
to the first and last elements in the
map that contain the specified key
...


void erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


iterator find(const key_type &k);
const_iterator find(const key_type &k)
const;

Returns an iterator to the specified
key
...


allocator_type get_allocator( ) const;

Returns map's allocator
...
An iterator to the
element is returned
...


pair
insert(const value_type &val);

Inserts val into the invoking map
...

The element is only inserted if it does
not already exist
...
Otherwise, pairfalse> is returned
...


iterator lower_bound(const key_type &k);
const_iterator
lower_bound(const key_type &k) const;

Returns an iterator to the first
element in the map with the key
equal to or greater than k
...


reference operator[ ](const key_type &i);

Returns a reference to the element
specified by i
...


reverse_iterator rbegin( );
const_reverse_iterator rbegin( ) const;

Returns a reverse iterator to the end
of the map
...


size_type size( ) const;

Returns the number of elements
currently in the map
...


iterator upper_bound(const key_type &k);
const_iterator
upper_bound(const key_type &k) const;

Returns an iterator to the first
element in the map with the key
greater than k
...


multimap
The multimap class supports an associative container in which possibly nonunique
keys are mapped with values
...
It has the following
constructors:

Chapter 33:

The STL Container Classes

explicit multimap(const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ) );
multimap(const multimap &ob);
template multimap(InIter start, InIter end,
const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ));
The first form constructs an empty multimap
...
The third form constructs a multimap that
contains the elements in the range specified by start and end
...

The following comparison operators are defined by multimap:
==, <, <=, !=, >, >=
The member functions contained by multimap are shown here
...


Member

Description

iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the multimap
...


size_type count(const key_type &k) const;

Returns the number of times k occurs
in the multimap
...


iterator end( );
const_iterator end( ) const;

Returns an iterator to the end of the
list
...


void erase(iterator i);

Removes the element pointed to by i
...


821

822

C++: The Complete Reference

Member

Description

size_type erase(const key_type &k);

Removes from the multimap
elements that have keys with the
value k
...
If the key is not found, then an
iterator to the end of the multimap is
returned
...


iterator insert(iterator i,
const value_type &val);

Inserts val at or after the element
specified by i
...


template
void insert(InIter start, InIter end);

Inserts a range of elements
...


key_compare key_comp( ) const;

Returns the function object that
compares keys
...


size_type max_size( ) const;

Returns the maximum number of
elements that the multimap can hold
...


reverse_iterator rend( );
const_reverse_iterator rend( ) const;

Returns a reverse iterator to the start
of the multimap
...


void swap(multimapAllocator> &ob);

Exchanges the elements stored in the
invoking multimap with those in ob
...


value_compare value_comp( ) const;

Returns the function object that
compares values
...
Its template
specification is shown here:
template ,
class Allocator = allocator> class multiset
Here, Key is the data of the keys and Comp is a function that compares two keys
...
The second form constructs a multiset that
contains the same elements as ob
...
The function specified by cmpfn, if
present, determines the ordering of the set
...

==, <, <=, !=, >, >=
The member functions contained by multiset are shown here
...


Member

Description

iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the multiset
...


size_type count(const key_type &k) const;

Returns the number of times k occurs
in the multiset
...


823

824

C++: The Complete Reference

Member

Description

iterator end( );
const_iterator end( ) const;

Returns an iterator to the end of the
multiset
...


void erase(iterator i);

Removes the element pointed to by i
...


size_type erase(const key_type &k);

Removes from the multiset elements
that have keys with the value k
...
If the key is not found, then an
iterator to the end of the multiset is
returned
...


iterator insert(iterator i,
const value_type &val);

Inserts val at or after the element
specified by i
...


template
void insert(InIter start, InIter end);

Inserts a range of elements
...

An iterator to the element is returned
...


iterator lower_bound(const key_type &k)
const;

Returns an iterator to the first element
in the multiset with the key equal to
or greater than k
...


reverse_iterator rbegin( );
const_reverse_iterator rbegin( ) const;

Returns a reverse iterator to the end
of the multiset
...


Chapter 33:

The STL Container Classes

Member

Description

size_type size( ) const;

Returns the number of elements
currently in the multiset
...


iterator upper_bound(const key_type &k)
const;

Returns an iterator to the first element
in the multiset with the key greater
than k
...


queue
The queue class supports a single-ended queue
...
It has the following constructor:
explicit queue(const Container &cnt = Container( ));
The queue( ) constructor creates an empty queue
...
You can also
use a list as a container for a queue
...

The following comparison operators are defined for queue:
==, <, <=, !=, >, >=
queue contains the following member functions
...


bool empty( ) const;

Returns true if the invoking queue is empty
and false otherwise
...


void pop( );

Removes the first element in the queue
...


size_type size( ) const;

Returns the number of elements current in
the queue
...
Its template
specification is shown here:
template ,
class Comp = less>
class priority_queue
Here, T is the type of data being stored
...
It has the following
constructors:
explicit priority_queue(const Comp &cmpfn = Comp( ),
Container &cnt = Container( ));
template priority_queue(InIter start, InIter end,
const Comp &cmpfn = Comp( ),
Container &cnt = Container( ));
The first priority_queue( ) constructor creates an empty priority queue
...

By default it uses a vector as a container
...
The container is held in a protected object called c of type Container
...


Chapter 33:

The STL Container Classes

Member

Description

bool empty( ) const;

Returns true if the invoking priority queue is
empty and false otherwise
...


void push(const T &val);

Adds an element to the priority queue
...


const value_type &top( ) const;

Returns a reference to the element with the
highest priority
...


set
The set class supports a set containing unique keys
...
It has
the following constructors:
explicit set(const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ) );
set(const set &ob);
template set(InIter start, InIter end,
const Comp &cmpfn = Comp( ),
const Allocator &a = Allocator( ));
The first form constructs an empty set
...
The third form constructs a set that contains the elements in
the range specified by start and end
...

The following comparison operators are defined for set:
==, <, <=, !=, >, >=

827

828

C++: The Complete Reference

The member functions contained by set are shown here
...


void clear( );

Removes all elements from the set
...


bool empty( ) const;

Returns true if the invoking set is
empty and false otherwise
...


pair
equal_range(const key_type &k) const;

Returns a pair of iterators that point
to the first and last elements in the set
that contain the specified key
...


void erase(iterator start, iterator end);

Removes the elements in the range
start to end
...
The
number of elements removed is
returned
...
If the key is not found, then an
iterator to the end of the set is
returned
...


iterator insert(iterator i,
const value_type &val);

Inserts val at or after the element
specified by i
...
An iterator to the
element is returned
...
Duplicate
elements are not inserted
...
An
iterator to the element is returned
...
If the element was
inserted, pair is
returned
...


iterator lower_bound(const key_type &k)
const;

Returns an iterator to the first
element in the set with the key equal
to or greater than k
...


size_type max_size( ) const;

Returns the maximum number of
elements that the set can hold
...


reverse_iterator rend( );
const_reverse_iterator rend( ) const;

Returns a reverse iterator to the start
of the set
...


void swap(set &ob); Exchanges the elements stored in the
invoking set with those in ob
...


value_compare value_comp( ) const;

Returns the function object that
compares values
...
Its template specification is shown here:
template > class stack

829

830

C++: The Complete Reference

Here, T is the type of data being stored and Container is the type of container used to
hold the queue
...
By default it uses a deque as a
container, but a stack can only be accessed in a last-in, first-out manner
...
The container is held in a protected
member called c of type Container
...


Member

Description

bool empty( ) const;

Returns true if the invoking stack is
empty and false otherwise
...


void push(const T &val);

Pushes an element onto the end of the
stack
...


size_type size( ) const;

Returns the number of elements
currently in the stack
...
The element is not removed
...
Its template specification is shown here
...
It has the
following constructors
...
The second form constructs a vector that has
num elements with the value val
...
The fourth form constructs a vector that contains the elements in
the range specified by start and end
...


Member

Description

template
void assign(InIter start, InIter end);

Assigns the vector the sequence
defined by start and end
...


reference at(size_type i);
const_reference at(size_type i) const;

Returns a reference to an element
specified by i
...


iterator begin( );
const_iterator begin( ) const;

Returns an iterator to the first element
in the vector
...
This is the number of elements
it can hold before it will need to
allocate more memory
...


bool empty( ) const;

Returns true if the invoking vector is
empty and false otherwise
...


iterator erase(iterator i);

Removes the element pointed to by i
...


iterator erase(iterator start, iterator end);

Removes the elements in the range
start to end
...


reference front( );
const_reference front( ) const;

Returns a reference to the first
element in the vector
...


iterator insert(iterator i, const T &val);

Inserts val immediately before the
element specified by i
...


void insert(iterator i, size_type num,
const T & val);

Inserts num copies of val immediately
before the element specified by i
...


size_type max_size( ) const;

Returns the maximum number of
elements that the vector can hold
...


void pop_back( );

Removes the last element in the
vector
...


reverse_iterator rbegin( );
const_reverse_iterator rbegin( ) const;

Returns a reverse iterator to the end of
the vector
...


Chapter 33:

The STL Container Classes

Member

Description

void reserve(size_type num);

Sets the capacity of the vector so that
it is equal to at least num
...
If the vector must
be lengthened, then elements with
the value specified by val are added to
the end
...


void swap(vector &ob);

Exchanges the elements stored in the
invoking vector with those in ob
...
It includes all of
the functionality of vector and adds these two members
...


static void swap(reference i, reference j);

Exchanges the bits specified by i
and j
...


C++

Chapter 34
The STL Algorithms

835

836

C++: The Complete Reference

he algorithms defined by the standard template library are described here
...
All of the algorithms are
template functions
...


T

Generic Name

Represents

BiIter

Bidirectional iterator

ForIter

Forward iterator

InIter

Input iterator

OutIter

Output iterator

RandIter

Random access iterator

T

Some type of data

Size

Some type of integer

Func

Some type of function

Generator

A function that generates objects

BinPred

Binary predicate

UnPred

Unary predicate

Comp

Comparison function

adjacent_find
template
ForIter adjacent_find(ForIter start, ForIter end);
template
ForIter adjacent_find(ForIter start, ForIter end, BinPred pfn);

The adjacent_find( ) algorithm searches for adjacent matching elements within a
sequence specified by start and end and returns an iterator to the first element
...
The first version looks for equivalent elements
...


binary_search
template

Chapter 34:

The STL Algorithms

bool binary_search(ForIter start, ForIter end, const T &val);
template
bool binary_search(ForIter start, ForIter end, const T &val,
Comp cmpfn);

The binary_search( ) algorithm performs a binary search on an ordered sequence
beginning at start and ending with end for the value specified by val
...
The first version compares the elements in the
specified sequence for equality
...


copy
template
OutIter copy(InIter start, InIter end, OutIter result);

The copy( ) algorithm copies a sequence beginning at start and ending with end,
putting the result into the sequence pointed to by result
...
The range to be copied must not overlap with result
...


count
template
size_t count(InIter start, InIter end, const T &val);

The count( ) algorithm returns the number of elements in the sequence beginning
at start and ending at end that match val
...


equal
template
bool equal(InIter1 start1, InIter1 end1, InIter2 start2);
template
bool equal(InIter1 start1, InIter1 end1, InIter2 start2,
BinPred pfn);

The equal( ) algorithm determines if two ranges are the same
...
If the
ranges are the same, true is returned
...

The second form allows you to specify a binary predicate that determines when
two elements are equal
...
The region in which
to search for such a range is specified by start and end
...
To
specify your own search criteria, specify the comparison function cmpfn
...


Chapter 34:

The STL Algorithms

fill and fill_n
template
void fill(ForIter start, ForIter end, const T &val);
template
void fill_n(ForIter start, Size num, const T &val);

The fill( ) and fill_n( ) algorithms fill a range with the value specified by val
...
For fill_n( ), the range begins at start and
runs for num elements
...
It
returns an iterator to the first occurrence of the element or to end if the value is not in
the sequence
...
If the sequence is found, an iterator to
the last element in the sequence is returned
...

The second form allows you to specify a binary predicate that determines when
elements match
...
If no
matching element is found, the iterator end1 is returned
...


find_if
template
InIter find_if(InIter start, InIter end, UnPred pfn);

The find_if( ) algorithm searches the range start to end for an element for which the
unary predicate pfn returns true
...


for_each
template
Func for_each(InIter start, InIter end, Func fn);

The for_each( ) algorithm applies the function fn to the range of elements specified
by start and end
...


generate and generate_n
template
void generate(ForIter start, ForIter end, Generator fngen);
template
void generate_n(OutIter start, Size num, Generator fngen);

The algorithms generate( ) and generate_n( ) assign to elements in a range the
values returned by a generator function
...
For generate_n( ), the range begins at start and runs for num
elements
...
It has no parameters
...
It returns true
if the elements are all found and false otherwise
...


inplace_merge
template
void inplace_merge(BiIter start, BiIter mid, BiIter end);
template
void inplace_merge(BiIter start, BiIter mid, BiIter end, Comp cmpfn);

Within a single sequence, the inplace_merge( ) algorithm merges the range defined
by start and mid with the range defined by mid and end
...
After executing, the resulting sequence is sorted in increasing order
...


iter_swap
template
void iter_swap(ForIter1 i, ForIter2 j)

The iter_swap( ) algorithm exchanges the values pointed to by its two iterator
arguments
...
It returns true
if the first sequence is lexicographically less than the second (that is, if the first
sequence would come before the second using dictionary order)
...


lower_bound
template
ForIter lower_bound(ForIter start, ForIter end, const T &val);
template
ForIter lower_bound(ForIter start, ForIter end, const T &val,
Comp cmpfn);

The lower_bound( ) algorithm finds the first point in the sequence defined by
start and end that is not less than val
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...


Chapter 34:

The STL Algorithms

The second form allows you to specify a comparison function that determines when
one element is less than another
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...
The sequences to be merged are defined by start1, end1 and start2, end2
...
An iterator to the end of the
resulting sequence is returned
...


min
template
const T &min(const T &i, const T &j);
template
const T &min(const T &i, const T &j, Comp cmpfn);

The min( ) algorithm returns the minimum of two values
...


min_element
template
ForIter min_element(ForIter start, ForIter last);
template
ForIter min_element(ForIter start, ForIter last, Comp cmpfn);

The min_element( ) algorithm returns an iterator to the minimum element within
the range start and last
...


mismatch
template
pair mismatch(InIter1 start1, InIter1 end1,
InIter2 start2);
template
pair mismatch(InIter1 start1, InIter1 end1,
InIter2 start2, BinPred pfn);

The mismatch( ) algorithm finds the first mismatch between the elements in two
sequences
...
If no mismatch is found, iterators
to the last element in each sequence are returned
...

The pair template class contains two data members called first and second that
hold the pair of values
...

The permutations are generated assuming a sorted sequence: from low to high
represents the first permutation
...

Otherwise, it returns true
...


nth_element
template
void nth_element(RandIter start, RandIter element, RandIter end);
template
void nth_element(RandIter start, RandIter element,
RandIter end, Comp cmpfn);

The nth_element( ) algorithm arranges the sequence specified by start and end such
that all elements less than element come before that element and all elements greater
than element come after it
...


partial_sort
template
void partial_sort(RandIter start, RandIter mid, RandIter end);
template
void partial_sort(RandIter start, RandIter mid,
RandIter end, Comp cmpfn);

845

846

C++: The Complete Reference

The partial_sort( ) algorithm sorts the range start to end
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...
It
returns an iterator to the last element copied into the resulting sequence
...


partition
template
BiIter partition(BiIter start, BiIter end, UnPred pfn);

The partition( ) algorithm arranges the sequence defined by start and end such that
all elements for which the predicate specified by pfn returns true come before those for
which the predicate returns false
...


pop_heap
template
void pop_heap(RandIter start, RandIter end);
template
void pop_heap(RandIter start, RandIter end, Comp cmpfn);

The pop_heap( ) exchanges the first and last−1 elements and then rebuilds the heap
...


prev_permutation
template
bool prev_permutation(BiIter start, BiIter end);
template
bool prev_permutation(BiIter start, BiIter end, Comp cmpfn);

The prev_permutation( ) algorithm constructs the previous permutation of a
sequence
...
If the next permutation does not exist,
prev_permutation( ) sorts the sequence as its final permutation and returns false
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...
The range
specified by start and end is assumed to represent a valid heap
...


random_shuffle
template
void random_shuffle(RandIter start, RandIter end);
template
void random_shuffle(RandIter start, RandIter end, Generator rand_gen);

The random_shuffle( ) algorithm randomizes the sequence defined by start and end
...
This function must
have the following general form:
rand_gen(num);
It must return a random number between zero and num
...
It returns an iterator to the end of the remaining elements
...
It returns an iterator to the end of the remaining elements
...
It returns an
iterator to the end of the result
...
It returns an iterator to the end of the result
...

Within the specified range, the replace_if( ) algorithm replaces those elements for
which the predicate pfn is true with elements that have the value new
...

In the process it replaces elements that have the value old with elements that have the
value new
...
An iterator to the end of result is returned
...
In the process it replaces elements for which the predicate pfn returns true with
elements that have the value new
...
An iterator to the
end of result is returned
...

The reverse_copy( ) algorithm copies in reverse order the range specified by start
and end and stores the result in result
...


rotate and rotate_copy
template
void rotate(ForIter start, ForIter mid, ForIter end);
template
OutIter rotate_copy(ForIter start, ForIter mid, ForIter end,
OutIter result);

The rotate( ) algorithm left-rotates the elements in the range specified by start and
end so that the element specified by mid becomes the new first element
...
In the process it left-rotates the elements so that the element specified
by mid becomes the new first element
...


search
template
ForIter1 search(ForIter1 start1, ForIter1 end1,
ForIter2 start2, ForIter2 end2);
template
ForIter1 search(ForIter1 start1, ForIter1 end1,
ForIter2 start2, ForIter2 end2, BinPred pfn);

The search( ) algorithm searches for a subsequence within a sequence
...
The subsequence being searched
is specified by start2 and end2
...
Otherwise, end1 is returned
...


search_n
template
ForIter search_n(ForIter start, ForIter end,
Size num, const T &val);
template
ForIter search_n(ForIter start, ForIter end,
Size num, const T &val, BinPred pfn);

The search_n( ) algorithm searches for a sequence of num elements equal to val
within a sequence
...
If the
subsequence is found, an iterator to its beginning is returned
...

The second form allows you to specify a binary predicate that determines when one
element is equal to another
...
That is, the set
defined by start2, end2 is subtracted from the set defined by start1, end1
...
It returns an iterator to the end of the result
...


set_intersection
template
OutIter set_intersection(InIter1 start1, InIter1 end1,
InIter2 start2, InIter2 last2, OutIter result);
template
OutIter set_intersection(InIter1 start1, InIter1 end1,
InIter2 start2, InIter2 last2,
OutIter result, Comp cmpfn);

The set_intersection( ) algorithm produces a sequence that contains the intersection
of the two ordered sets defined by start1, end1 and start2, end2
...
The result is ordered and put into result
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...
That is, the resultant set contains only those elements that are not common to
both sets
...
It returns an iterator to the end of
the result
...


set_union
template
OutIter set_union(InIter1 start1, InIter1 end1,
InIter2 start2, InIter2 last2, OutIter result);
template
OutIter set_union(InIter1 start1, InIter1 end1,
InIter2 start2, InIter2 last2, OutIter result,
Comp cmpfn);

The set_union( ) algorithm produces a sequence that contains the union of the two
ordered sets defined by start1, end1 and start2, end2
...
The result is ordered and put into result
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...
The partitioning is stable
...
It returns an iterator to the
beginning of the elements for which the predicate is false
...
The sort is stable
...

The second form allows you to specify a comparison function that determines when
one element is less than another
...


853

854

C++: The Complete Reference

swap_ranges
template
ForIter2 swap_ranges(ForIter1 start1, ForIter1 end1,
ForIter2 start2);

The swap_ranges( ) algorithm exchanges elements in the range specified by start1
and end1 with elements in the sequence beginning at start2
...


transform
template
OutIter transform(InIter start, InIter end,
OutIter result, Func unaryfunc);
template
OutIter transform(InIter1 start1, InIter1 end1,
InIter2 start2, OutIter result,
Func binaryfunc);

The transform( ) algorithm applies a function to a range of elements and stores the
outcome in result
...
The function
to be applied is specified by unaryfunc
...

In the second form, the transformation is applied using a binary operator function
that receives the value of an element from the sequence to be transformed in its first
parameter and an element from the second sequence as its second parameter
...


unique and unique_copy
template
ForIter unique(ForIter start, ForIter end);
template
ForIter unique(ForIter start, ForIter end, BinPred pfn);
template
OutIter unique_copy(ForIter start, ForIter end, OutIter result);
template
OutIter unique_copy(ForIter start, ForIter end, OutIter result,
BinPred pfn);

Chapter 34:

The STL Algorithms

The unique( ) algorithm eliminates duplicate elements from the specified range
...
unique( ) returns an iterator to the end of the range
...
The outcome is put into result
...
unique_copy( ) returns an iterator to the end of the range
...
It returns an iterator to this point
...


855

This page intentionally left blank
...
These components are part of the standard template library
...


Iterators
While containers and algorithms form the foundation of the standard template library,
iterators are the glue that holds it together
...
Iterators are handled in your program like
pointers, and they implement the standard pointer operators
...

Standard C++ defines a set of classes and functions that support iterators
...
Instead, you will use the iterators provided by the various containers
in the STL, manipulating them like you would any other pointer
...
For example, it is possible to create your own iterators that
accommodate special situations
...

Iterators use the header
...
Elements may be accessed
randomly
...
Forward and backward moving
...
Forward moving only
...
Forward moving only
...
Forward moving only
...
For example, a forward iterator can be used in place of an
input iterator
...
Reverse iterators are either bidirectional or
random-access iterators that move through a sequence in the reverse direction
...

Stream-based iterators are available that allow you to operate on streams through
iterators
...

All iterators must support the pointer operations allowed by their type
...
Further, the * operator cannot
be used to assign a value
...
Also, the * must allow assignment
...
As explained in Chapter 24, each of the STL containers
defines its own iterator type, which is typedefed as iterator
...
But you can use the classes described here to derive your
own iterators
...
This type is capable of
representing the difference between two pointers
...
It is shown here:
template class Pointer = T *, class Ref = T &>
struct iterator {
typedef T value_type;
typedef Dist difference_type;
typedef Pointer pointer;
typedef Ref reference;
typedef Cat iterator_category;
};

Here, difference_type is a type that can hold the difference between two addresses,
value_type is the type of value operated upon, pointer is the type of a pointer to a
value, reference is the type of a reference to a value, and iterator_category describes
the type of the iterator (such as input, random-access, etc
...
Their tag names can be stored in
iterator_category
...
It is defined like this:
template struct iterator_traits {
typedef Iterator::difference_type difference_type;
typedef Iterator::value_type value_type;
typedef Iterator::pointer pointer;
typedef Iterator::reference reference;
typedef Iterator::iterator_category iterator_category;
}

The Predefined Iterators
The header contains several predefined iterators that may be used directly
by your program or to help create other iterators
...

Notice that there are four iterators that operate on streams
...
Also notice the
insert iterators
...

Each of the predefined iterators is examined here
...

Its template definition is shown here:
template class insert_iterator:
public iterator
Here, Cont is the type of container that the iterator operates upon
...


Chapter 35:

STL Iterators, Allocators, and Function Objects

Class

Description

insert_iterator

An output iterator that inserts anywhere in the container
...


front_insert_iterator

An output iterator that inserts at the front of a container
...


istream_iterator

An input stream iterator
...


ostream_iterator

An output stream iterator
...


Table 35-1
...
A pointer to the container is
stored in a protected variable called container
...

Also defined is the function inserter( ), which creates an insert_iterator
...
To
fully understand the effects of an insert iterator, consider the following program
...

// Demonstrate insert_iterator
...
push_back(i);
cout << "Original array: ";
itr = v
...
end())
cout << *itr++ << " ";
cout << endl;
itr = v
...
begin();
while(itr != v
...
The same
basic process applies to back_insert_iterator and front_insert_iterator as well
...
Its template definition is shown here:
template class back_insert_iterator:
public iterator
Here, Cont is the type of container that the iterator operates upon
...
All insertions will occur at the end
...
A pointer to the
container is stored in a protected variable called container
...

It is shown here:
template back_insert_iterator back_inserter(Cont &cnt);

front_insert_iterator
The front_insert_iterator class supports output iterators that insert objects on the front
of a container using push_front( )
...
front_insert_iterator
has the following constructor:
explicit front_insert_iterator(Cont &cnt);
Here, cnt is the container being operated upon
...

front_insert_iterator defines the following operators: =, *, ++
...

Also defined is the function front_inserter( ), which creates a front_insert_iterator
...
A reverse iterator
operates the opposite of a normal iterator
...
Its template definition is shown here:
template class reverse_iterator:
public iterator::iterator_category,
iterator_traits::value_type,
iterator_traits::difference_type,
iterator_traits::pointer,
iterator_traits::reference>
Here, Iter is either a random-access iterator or a bidirectional iterator
...

If Iter is a random-access iterator, then the following operators are available: –>, +,
++, –, −−, *, <, >, <=, >=, –=, +=, ==, !=, and [ ]
...

The reverse_iterator class defines a protected member called current, which is an
iterator to the current location
...
Its prototype is shown here:
Iter base( ) const;
It returns an iterator to the current location
...
Its template
definition is shown here:
template ,
class Dist = ptrdiff_t> class istream_iterator:
public iterator
Here, T is the type of data being transferred, and CharType is the character type (char
or wchar_t) that the stream is operating upon
...
istream_iterator has the following constructors:

Chapter 35:

STL Iterators, Allocators, and Function Objects

istream_iterator( );
istream_iterator(istream_type &stream);
istream_iterator(const istream_iterator &ob);
The first constructor creates an iterator to an empty stream
...
The type istream_type is a typedef that
specifies the type of the input stream
...

The istream_iterator class defines the following operators: –>, *, ++
...

Here is a short program that demonstrates istream_iterator
...

// Use istream_iterator
#include
#include
using namespace std;
int main()
{
istream_iterator in_it(cin);
do {
cout << *in_it++;
} while (*in_it != '
...

Its template definition is shown here:
template >
class istreambuf_iterator:
public iteratorCharType *, CharType &>
Here, CharType is the character type (char or wchar_t) that the stream is operating
upon
...
The second creates an
iterator to the stream specified by stream
...
The third form creates an iterator using the
stream buffer specified by streambuf
...
The operators
== and != are also defined for objects of type istreambuf_iterator
...
It returns true if the invoking iterator and ob both
point to the end of the stream
...
There is no requirement that what they point to be the same
...
The == and != operators work in the same fashion
...
Its template
definition is shown here:
template >
class ostream_iterator:
public iterator
Here, T is the type of data being transferred, CharType is the character type (char or
wchar_t) that the stream is operating upon
...
The type ostream_type is
a typedef that specifies the type of the output stream
...
The
delimiters are written to the stream after every output operation
...

The ostream_iterator class defines the following operators: =, *, ++
...


Chapter 35:

STL Iterators, Allocators, and Function Objects

// Use ostream_iterator
#include
#include
using namespace std;
int main()
{
ostream_iterator out_it(cout);
*out_it = 'X';
out_it++;
*out_it = 'Y';
out_it++;
*out_it = ' ';
char str[] = "C++ Iterators are powerful
...
23;
out_double_it++;
*out_double_it = -102
...

187
...
7

ostreambuf_iterator
The ostreambuf_iterator class supports character output iterator operations on a
stream
...
ostreambuf_iterator has the following constructors:
ostreambuf_iterator(ostream_type &stream) throw( );
ostreambuf_iterator(streambuf_type *streambuf) throw( );
The first creates an iterator to the stream specified by stream
...
The second form creates an
iterator using the stream buffer specified by streambuf
...

The ostreambuf_iterator class defines the following operators: =, *, ++
...


Two Iterator Functions
There are two special functions defined for iterators: advance( ) and distance( )
...
The distance( )
function returns the number of elements between start and end
...
The advance( ) and distance( )
functions overcome this restriction
...


Function Objects
Function objects are classes that define operator( )
...
You can also define your own function
objects
...
Also defined in
are several entities that support function objects
...
Each is described here
...


Chapter 35:

STL Iterators, Allocators, and Function Objects

Function Objects
Function objects come in two varieties: binary and unary
...

logical_not

negate

The general form for invoking a function object is shown here:
func_ob( )
For example,
less()

invokes less( ) relative to operands of type int
...
Although they are technically a convenience, they are almost
always used when creating function objects
...
Here are examples of each:
template struct plus : binary_function
{
T operator() (const T &arg1, const T&arg2) const;
};
template struct negate : unary_function
{
T operator() (const T &arg) const;
};

Each operator( ) function returns the specified result
...
There are two binders: bind2nd( ) and bind1st( )
...
bind1st( ) returns a unary
function object that has op's left-hand operand bound to value
...
The bind2nd( )
binder is by far the most commonly used
...

The binder1st and binder2nd classes are shown here:
template class binder1st:
public unary_function(typename BinFunc::second_argument_type,
typename BinFunc::result_type>
{
protected:
BinFunc op;
typename BinFunc::first_argument_type value;
public:

Chapter 35:

STL Iterators, Allocators, and Function Objects

binder1st(const BinFunc &op,
const typename BinFunc::first_argument_type &v);
result_type operator()(const argument_type &v) const;
};
template class binder2nd:
public unary_function(typename BinFunc::first_argument_type,
typename BinFunc::result_type>
{
protected:
BinFunc op;
typename BinFunc::second_argument_type value;
public:
binder2nd(const BinFunc &op,
const typename BinFunc::second_argument_type &v);
result_type operator()(const argument_type &v) const;
};

Here, BinFunc is the type of a binary function object
...
This is why the resulting object of bind1st( ) and bind2nd( ) can be
used anywhere that a unary function can be
...

The negators are not1( ) and not2( )
...


Adaptors
The header defines several classes called adaptors that allow you to adapt
a function pointer to a form that can be used by the STL
...
Adaptors also
exist for member pointers
...
These classes are shown here:
template
class pointer_to_unary_function:
public unary_function
{
public:
explicit pointer_to_unary_function(Result (*func)(Argument));
Result operator()(Argument arg) const;
};

Chapter 35:

STL Iterators, Allocators, and Function Objects

template
class pointer_to_binary_function:
public binary_function
{
public:
explicit pointer_to_binary_function(
Result (*func)(Argument1, Argument2));
Result operator()(Argument1 arg1, Argument2 arg2) const;
};

For unary functions, operator( ) returns
func(arg)
...


The Pointer-to-Member Function Adaptors
The pointer-to-member function adaptors are shown here:
template
mem_fun_t mem_fun(Result (T::*func)( ));
template
mem_fun1_t
mem_fun1(Result (T::*func)(Argument));
Here, mem_fun( ) returns an object of type mem_fun_t, and mem_fun1 returns an
object of type mem_fun1_t
...

The mem_fun1_t constructor calls the member function specified as its first parameter,
passing a value of type Argument as its second parameter
...
The
general form of the functions is shown here:
template
mem_fun_t mem_fun_ref(Result (T::*func)( ));
template
mem_fun1_t
mem_fun1_ref(Result (T::*func)(Argument));
The classes mem_fun_ref and mem_fun1_ref are shown here:
template class mem_fun_ref_t:
public unary_function
{
public:
explicit mem_fun_ref_t(Result (T::*func)());
Result operator()(T &func) const;
};
template
class mem_fun1_ref_t:
public binary_function
{
public:
explicit mem_fun1_ref_t(Result (T::*func)(Argument));
Result operator()(T &func, Argument arg) const;
};

Chapter 35:

STL Iterators, Allocators, and Function Objects

Allocators
An allocator manages memory allocation for a container
...
However, these
details are useful if you are creating your own library classes, etc
...
First, they must define the
following types:
const_pointer

A const pointer to an object of type value_type
...


difference_type

Can represent the difference between two addresses
...


reference

A reference to an object of type value_type
...


value_type

The type of object being allocated
...

address

Returns a pointer given a reference
...


deallocate

Frees memory
...


construct

Constructs an object
...


The operations == and != must also be defined
...
Its
template specification is shown here:
template class allocator
Here, T is the type of objects that allocator will be allocating
...
The second creates a copy of ob
...
The member functions defined
by allocator are shown in Table 35-2
...


Function

Description

pointer address(reference ob) const;
const_pointer address(const_reference ob) const;

Returns the address of ob
...
The value
of h is a hint to the function
that can be used to help
satisfy the request or
ignored
...


void deallocate(pointer ptr, size_type num);

Deallocates num objects of
type T starting at ptr
...


void destroy(pointer ptr);

Destroys the object at ptr
...


size_type max_size( ) const throw( );

Returns the maximum
number of objects of type T
that can be allocated
...


Member Functions of allocator

C++

Chapter 36
The String Class

877

878

C++: The Complete Reference

his chapter describes the Standard C++ string class
...
The first is as a null-terminated character array
...
The second way is as a class object of type
basic_string
...
Most often, you
will use string objects of type string
...
This means that iterators and the
STL algorithms can operate on strings
...

A class used by basic_string is char_traits, which defines several attributes of the
characters that comprise a string
...
Both basic_string
and char_traits are described here
...


The basic_string Class
The template specification for basic_string is
template ,
class Allocator = allocator > class basic_string
Here, CharType is the type of character being used, Attr is the class that describes the
character's traits, and Allocator specifies the allocator
...
The second form constructs a string that has
len characters of value ch
...
The fourth form constructs a string that contains a substring of str that
begins at zero and is len characters long
...
The sixth
form constructs a string that contains the elements in the range specified by start and end
...

The + operator can be used to concatenate a string object with another string object
or a string object with a C-style string
...

The basic_string class defines the constant npos, which is usually –1
...

In the descriptions, the generic type CharType represents the type of character
stored by a string
...
This makes the type
names concrete
...


reference

A reference to a character within a string
...


iterator

An iterator
...


reverse_iterator

A reverse iterator
...


value_type

The type of character stored in a string
...


pointer

A pointer to a character within a string
...


traits_type

A typedef for char_traits

difference_type

A type that can store the difference between two
addresses
...
Since the
vast majority of programmers will be using char strings (and to keep the descriptions
easy-to-understand), the table uses the type string, but the functions also apply to
objects of type wstring (or any other type of basic_string)
...
Returns *this
...
The
substring being appended begins at
indx and runs for len characters
...


string &append(const CharType *str);

Appends str onto the end of the
invoking string
...


string &append(const CharType *str,
size_type num);

Appends the first num characters
from str onto the end of the invoking
string
...


string &append(size_type len, CharType ch); Appends len characters specified by
ch onto the end of the invoking
string
...

template
string &append(InIter start,
InIter end);

Appends the sequence specified by
start and end onto the end of the
invoking string
...


string &assign(const string &str);

Assigns str to the invoking string
...


string &assign(const string &str,
size_type indx,
size_type len);

Assigns a substring of str to the
invoking string
...
Returns *this
...

Returns *this
...


The String Member Functions

Chapter 36:

The String Class

Member

Description

string &assign(const CharType *str,
size_type len);

Assigns the first len character
from str to the invoking string
...


string &assign(size_type len, CharType ch);

Assigns len characters specified by ch
to the end of the invoking string
...


template
string &assign(InIter start, InIter end);

Assigns the sequence specified by
start and end to the invoking string
...


reference at(size_type indx);
const_reference at(size_type indx) const;

Returns a reference to the character
specified by indx
...


const CharType *c_str( ) const;

Returns a pointer to a C-style (i
...
,
null-terminated) version of the
invoking string
...
This is the number of
characters it can hold before it will
need to allocate more memory
...

It returns one of the following:
Less than zero if *this < str
Zero if *this == str
Greater than zero if *this > str

int compare(size_type indx, size_type len,
const string &str) const;

Compares str to a substring within
the invoking string
...
It returns one of the following:
Less than zero if *this < str
Zero if *this == str
Greater than zero if *this > str

Table 36-1
...

The substring in the invoking string
begins at indx and is len characters
long
...
It
returns one of the following:
Less than zero if *this < str
Zero if *this == str
Greater than zero if *this > str

int compare(const CharType *str) const;

Compares str to the invoking string
...

The substring in the invoking string
begins at indx and is len characters
long
...
It
returns one of the following:
Less than zero if *this < str
Zero if *this == str
Greater than zero if *this > str

size_type copy(CharType *str,
size_type len,
size_type indx = 0) const;

Beginning at indx, copies len
characters from the invoking string
into the character array pointed to
by str
...


const CharType *data( ) const;

Returns a pointer to the first
character in the invoking string
...


Table 36-1
...


iterator erase(iterator i);

Removes character pointed to by i
...


iterator erase(iterator start, iterator end);

Removes characters in the range start
to end
...


string &erase(size_type indx = 0,
size_type len = npos);

Beginning at indx, removes len
characters from the invoking string
...


size_type find(const string &str,
size_type indx = 0) const;

Returns the index of the first
occurrence of str within the invoking
string
...
npos is returned if no match is
found
...
The search begins at index
indx
...


size_type find(const CharType *str,
size_type indx,
size_type len) const;

Returns the index of the first
occurrence of the first len characters
of str within the invoking string
...
npos is
returned if no match is found
...
The search begins at index
indx
...


Table 36-1
...

The search begins at index indx
...


size_type find_first_of(const CharType *str,
size_type indx = 0) const;

Returns the index of the first
character within the invoking string
that matches any character in str
...
npos is
returned if no match is found
...
The search
begins at index indx
...


size_type find_first_of(CharType ch,
size_type indx = 0) const;

Returns the index of the first
occurrence of ch within the invoking
string
...
npos is returned if no match
is found
...
The search begins at index indx
...


size_type find_first_not_of(
const CharType *str,
size_type indx = 0) const;

Returns the index of the first
character within the invoking string
that does not match any character in
str
...

npos is returned if no mismatch
is found
...


The String Member Functions (continued)

Chapter 36:

The String Class

Member

Description

size_type find_first_not_of(
const CharType *str,
size_type indx,
size_type len) const;

Returns the index of the first
character within the invoking string
that does not match any character in
the first len characters of str
...
npos is
returned if no mismatch is found
...
The search
begins at index indx
...


size_type find_last_of(const string &str,
size_type indx = npos) const;

Returns the index of the last character
within the invoking string that
matches any character in str
...
npos is
returned if no match is found
...
The
search begins at index indx
...


size_type find_last_of(const CharType *str,
size_type indx,
size_type len) const;

Returns the index of the last
character within the invoking string
that matches any character in the
first len characters of str
...
npos is
returned if no match is found
...
The search begins at index
indx
...


Table 36-1
...
The
search begins at index indx
...


size_type find_last_not_of(
const CharType *str,
size_type indx = npos) const;

Returns the index of the last character
within the invoking string that does
not match any character in str
...
npos is
returned if no mismatch is found
...
The
search begins at index indx
...


size_type find_last_not_of(CharType ch,
size_type indx = npos) const;

Returns the index of the last
character within the invoking string
that does not match ch
...
npos is
returned if no mismatch is found
...


iterator insert(iterator i,
const CharType &ch );

Inserts ch immediately before the
character specified by indx
...


string &insert(size_type indx,
const string &str);

Inserts str into the invoking string at
the index specified by indx
...


string &insert(size_type indx1,
const string &str,
size_type indx2,
size_type len);

Inserts a substring of str into the
invoking string at the index specified
by indx1
...

Returns *this
...


The String Member Functions (continued)

Chapter 36:

The String Class

Member

Description

string &insert(size_type indx,
const CharType *str);

Inserts str into the invoking string at
the index specified by indx
...


string &insert(size_type indx,
const CharType *str,
size_type len);

Inserts the first len characters of str
into the invoking string at the index
specified by indx
...


string &insert(size_type indx,
size_type len,
CharType ch);

Inserts len characters of value ch into
the invoking string at the index
specified by indx
...


void insert(iterator i, size_type len,
const CharType &ch)

Inserts len copies of ch immediately
before the element specified by i
...


size_type length( ) const;

Returns the number of characters in
the string
...


reference operator[ ](size_type indx) const;
const_reference operator[ ](size_type indx)
const;

Returns a reference to the character
specified by indx
...

Returns *this
...
Returns *this
...


reverse_iterator rend( );
const_reverse_iterator rend( ) const;

Returns a reverse iterator to the start
of the string
...


The String Member Functions (continued)

887

888

C++: The Complete Reference

Member

Description

string &replace(size_type indx,
size_type len,
const string &str);

Replaces up to len characters in the
invoking string, beginning at indx
with the string in str
...


string &replace(size_type indx1,
size_type len1,
const string &str,
size_type indx2,
size_type len2);

Replaces up to len1 characters in the
invoking string beginning at indx1
with the len2 characters from the
string in str that begin at indx2
...


string &replace(size_type indx,
size_type len,
const CharType *str);

Replaces up to len characters in the
invoking string, beginning at indx
with the string in str
...


string &replace(size_type indx1,
size_type len1,
const CharType *str,
size_type len2);

Replaces up to len1 characters in the
invoking string beginning at indx1
with the len2 characters from the
string in str that begins at indx2
...


string &replace(size_type indx,
size_type len1,
size_type len2,
CharType ch);

Replaces up to len1 characters in the
invoking string beginning at indx
with len2 characters specified by ch
...


string &replace(iterator start,
iterator start,
const string &str);

Replaces the range specified by start
and end with str
...


string &replace(iterator start,
iterator start,
const CharType *str);

Replaces the range specified by start
and end with str
...


string &replace(iterator start,
iterator end,
const CharType *str,
size_type len);

Replaces the range specified by start
and end with the first len characters
from str
...


string &replace(iterator start,
interator end, size_type len,
CharType ch);

Replaces the range specified by start
and end with the len characters
specified by ch
...


Table 36-1
...

Returns *this
...


void resize(size_type num)
void resize(size_type num, CharType ch);

Changes the size of the string to that
specified by num
...


size_type rfind(const string &str,
Returns the index of the last
size_type indx = npos) const; occurrence of str within the invoking
string
...
npos is returned if no match
is found
...
The search begins at index
indx
...

size_type rfind(const CharType *str,
size_type indx,
size_type len) const;

Returns the index of the last
occurrence of the first len characters
of str within the invoking string
...
npos is
returned if no match is found
...
The search begins at index
indx
...

size_type size( ) const;

Table 36-1
...


The String Member Functions (continued)

889

890

C++: The Complete Reference

Member

Description

string substr(size_type indx = 0,
size_type len = npos) const;

Returns a substring of len characters
beginning at indx within the
invoking string
...


Table 36-1
...
Its
template specification is shown here:
template struct char_traits
Here, CharType specifies the type of the character
...
The char_traits class defines the following five
data types:
char_type

The type of the character
...


int_type

An integer type that can hold a character of type char_type or
the EOF character
...


pos_type

An integer type that can represent a position in a stream
...
(Applies to
multibyte characters
...


Chapter 36:

The String Class

Member

Description

static void assign(char_type &ch1,
const char_type &ch2);

Assigns ch2 to ch1
...
Returns str
...
Returns
zero if the strings are same
...


static char_type *copy(char_type *to,
const char_type *from,
size_t num);

Copies num characters from
from to to
...


static int_type eof( );

Returns the end-of-file
character
...


static bool eq_int_type(const int_type &ch1,
const int_type &ch2);

Returns true if ch1 equals ch2
and false otherwise
...
Only
the first num characters are
examined
...


static size_t length(const char_type *str);

Returns the length of str
...


Table 36-2
...
Returns to
...

Otherwise, the EOF character
is returned
...


static char_type to_char_type(const int_type &ch);

Converts ch into a char_type
and returns the result
...


Table 36-2
...
These classes aid in the development of numerical programs
...
The difference is that many of the numeric functions
described here operate on objects of type valarray, which is essentially an array of
values, or on objects of type complex, which represent complex numbers
...


O

The complex Class
The header defines the complex class, which represents complex numbers
...

The template specification for complex is shown here:
template class complex
Here, T specifies the type used to store the components of a complex number
...
These values default to zero if not specified
...
The third creates a complex object from ob
...
Once for operations
involving a complex object on the left and a scalar object on the right, again for
operations involving a scalar on the left and a complex object on the right, and finally
for operations involving two complex objects
...

Two member functions are defined for complex: real( ) and imag( )
...
The functions shown in Table 37-1 are also defined
for complex objects
...

// Demonstrate complex
...


template
T arg(const complex &ob);

Returns the phase angle of ob
...


template
complex cos(const complex &ob);

Returns the cosine of ob
...


template
complex
exp(const complex &ob);

Returns the eob
...


template
complex
log(const complex &ob);

Returns the natural logarithm of ob
...


template
T norm(const complex &ob);

Returns the magnitude of ob squared
...


Functions Defined for complex

Chapter 37:

The Numeric Classes

Function

Description

template
complex
polar(const T &v, const T &theta=0);

Returns a complex number that has
the magnitude specified by v and a
phase angle of theta
...


template
complex
pow(const complex &b,
const T &e);

Returns be
...


template
complex
pow(const T &b,
const complex &e);

Returns be
...


template
complex sin(const complex &ob);

Returns the sine of ob
...


template
complex
sqrt(const complex &ob);

Returns the square root of ob
...


template
complex
tanh(const complex &ob);

Returns the hyperbolic tangent of ob
...


Functions Defined for complex (continued)

897

898

C++: The Complete Reference

The valarray Class
The header defines a number of classes that support numeric arrays
...
There are a
wide variety of member operators and functions defined for it as well as a large
number of nonmember functions
...
One other point:
Although valarray is very large, most of its operations are intuitive
...
The second creates a valarray of
length num
...
The fourth
creates a valarray of length num and initializes it with the elements pointed to by ptr
...
The next four constructors create a valarray from
one of valarray's helper classes
...

The following operators are defined for valarray:
+



*

/

−=

+=

/=

*=

=

==

!=

<<

>>

<<=

>>=

^

^=

%

%=

~

!

|

|=

&

&=

[]

Chapter 37:

The Numeric Classes

These operators have several overloaded forms that are described in the accompanying
tables
...

The nonmember operator functions defined for valarray are shown in Table 37-3
...


Function

Description

valarray apply(T func(T)) const;
valarray apply(T func(const T &ob)) const;

Applies func( ) to the invoking
array and returns an array
containing the result
...
(That is, it performs
a circular shift left
...


T max( ) const;

Returns the maximum value in
the invoking array
...


valarray
&operator=(const valarray &ob);

Assigns the elements in ob to the
corresponding elements in the
invoking array
...


valarray &operator=(const T &v);

Assigns each element in the
invoking array the value v
...


valarray
&operator=(const slice_array &ob);

Assigns a subset
...


valarray
&operator=(const gslice_array &ob);

Assigns a subset
...


valarray
&operator=(const mask_array &ob);

Assigns a subset
...


Table 37-2
...
Returns a
reference to the invoking array
...

Returns the resulting array
...

Returns the resulting array
...
Returns the resulting
array
...
Returns the resulting
array
...
Returns a
reference to the invoking array
...
Returns a
reference to the invoking array
...
Returns a
reference to the invoking array
...
Returns a
reference to the invoking array
...
Returns a
reference to the invoking array
...


The Member Functions of valarray (continued)

Chapter 37:

The Numeric Classes

Function

Description

valarray &operator^=(const T &v) const;

XORs v with each element in the
invoking array
...


valarray &operator&=(const T &v) const;

ANDs v with each element in
the invoking array
...


valarray &operator|=(const T &v) const;

ORs v to each element in the
invoking array
...


valarray &operator<<=(const T &v) const;

Left-shifts each element in the
invoking array v places
...


valarray &operator>>=(const T &v) const;

Right-shifts each element in the
invoking array v places
...


valarray
&operator+=(const valarray &ob) const;

Corresponding elements of the
invoking array and ob are added
together
...


valarray
&operator−=(const valarray &ob) const;

The elements in ob are
subtracted from their
corresponding elements in the
invoking array
...


valarray
&operator/=(const valarray &ob) const;

The elements in the invoking
array are divided by their
corresponding elements in ob
...


valarray
&operator*=(const valarray &ob) const;

Corresponding elements of the
invoking array and ob are
multiplied together
...


Table 37-2
...

Returns a reference to the
invoking array
...

Returns a reference to the
invoking array
...
Returns
a reference to the invoking
array
...

Returns a reference to the
invoking array
...

Returns a reference to the
invoking array
...

Returns a reference to the
invoking array
...


The Member Functions of valarray (continued)

Chapter 37:

The Numeric Classes

Function

Description

T &operator[ ] (size_t indx) ;

Returns a reference to the
element at the specified index
...


slice_array operator[ ](slice ob);

Returns the specified subset
...


gslice_array operator[ ](const gslice &ob);

Returns the specified subset
...


mask_array
operator[ ](valarray &ob);

Returns the specified subset
...


indirect_array
operator[ ](const valarray &ob);

Returns the specified subset
...


void resize(size_t num, T v = T( ));

Resizes the invoking array
...


size_t size( ) const;

Returns the size (i
...
, the number
of elements) of the invoking
array
...
Returns an array
containing the result
...


Table 37-2
...

Returns an array containing
the result
...

Returns an array containing the
result
...

Returns an array containing
the result
...
Returns an array
containing the result
...
Returns an array
containing the result
...
Returns an array
containing the result
...
Returns an array
containing the result
...
Returns an array
containing the result
...

Returns an array containing
the result
...


The Nonmember Operator Functions Defined for valarray

Chapter 37:

The Numeric Classes

Function

Description

template valarray
operator/(const valarray ob,
const T &v);

Divides each element in ob by
v
...


template valarray
operator/(const T &v,
const valarray ob);

Divides v by each element in
ob
...


template valarray
operator/(const valarray ob1,
const valarray &ob2);

Divides each element in ob1 by
its corresponding element in
ob2
...


template valarray
operator%(const valarray ob,
const T &v);

Obtains the remainder that
results from dividing each
element in ob by v
...


template valarray
operator%(const T &v,
const valarray ob);

Obtains the remainder that
results from dividing v by each
element in ob
...


template valarray
operator%(const valarray ob1,
const valarray &ob2);

Obtains the remainder that
results from dividing each
element in ob1 by its
corresponding element in ob2
...


template valarray
operator^(const valarray ob,
const T &v);

XORs each element in ob with
v
...


template valarray
operator^(const T &v,
const valarray ob);

XORs each element in ob with
v
...


Table 37-3
...
Returns an array
containing the result
...
Returns an array containing
the result
...
Returns an array containing
the result
...
Returns an array
containing the result
...

Returns an array containing the
result
...

Returns an array containing the
result
...
Returns an array
containing the result
...
Returns an array
containing the result
...


The Nonmember Operator Functions Defined for valarray (continued)

Chapter 37:

The Numeric Classes

Function

Description

template valarray
operator<<(const T &v,
const valarray ob);

Left-shifts v the number of
places specified by the
elements in ob
...


template valarray
operator<<(const valarray ob1,
const valarray &ob2);

Left-shifts each element in ob1
the number of places specified
by its corresponding element
in ob2
...


template valarray
operator>>(const valarray ob,
const T &v);

Right-shifts each element in ob
the number of places specified
by v
...


template valarray
operator>>(const T &v,
const valarray ob);

Right-shifts v the number of
places specified by the
elements in ob
...


template valarray
operator>>(const valarray ob1,
const valarray &ob2);

Right-shifts each element in
ob1 the number of places
specified by its corresponding
element in ob2
...


template valarray
operator==(const valarray ob,
const T &v);

For every i, performs ob[i] ==
v
...


template valarray
operator==(const T &v,
const valarray ob);

For every i, performs v ==
ob[i]
...


template valarray
operator==(const valarray ob1,
const valarray &ob2);

For every i, performs ob1[i] ==
ob2[i]
...


Table 37-3
...

Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...
Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...
Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...
Returns a Boolean array
containing the result
...

Returns a Boolean array
containing the result
...


The Nonmember Operator Functions Defined for valarray (continued)

Chapter 37:

The Numeric Classes

Function

Description

template valarray
operator>(const T &v,
const valarray ob);

For every i, performs v > ob[i]
...


template valarray
operator>(const valarray ob1,
const valarray &ob2);

For every i, performs ob1[i] >
ob2[i]
...


template valarray
operator>=(const valarray ob,
const T &v);

For every i, performs ob[i] >=
v
...


template valarray
operator>=(const T &v,
const valarray ob);

For every i, performs v >=
ob[i]
...


template valarray
operator>=(const valarray ob1,
const valarray &ob2);

For every i, performs ob1[i] >=
ob2[i]
...


template valarray
operator&&(const valarray ob,
const T &v);

For every i, performs ob[i] &&
v
...


template valarray
operator&&(const T &v,
const valarray ob);

For every i, performs v &&
ob[i]
...


template valarray
operator&&(const valarray ob1,
const valarray &ob2);

For every i, performs ob1[i] &&
ob2[i]
...


template valarray
operator||(const valarray ob,
const T &v);

For every i, performs ob[i] ||
v
...


template valarray
operator||(const T &v,
const valarray ob);

For every i, performs v ||
ob[i]
...


template valarray
operator||(const valarray ob1,
const valarray &ob2);

For every i, performs ob1[i] ||
ob2[i]
...


Table 37-3
...


template valarray
acos(const valarray &ob);

Obtains the arc cosine of each
element in ob and returns an
array containing the result
...


template valarray
atan(const valarray &ob);

Obtains the arc tangent of each
element in ob and returns an
array containing the result
...


template valarray
atan2(const T &v, const valarray &ob);

For all i, obtains the arc tangent
of v / ob1[i] and returns an array
containing the result
...


template valarray
cos(const valarray &ob);

Obtains the cosine of each
element in ob and returns an
array containing the result
...


template valarray
exp(const valarray &ob);

Computes exponential function
for each element in ob and returns
an array containing the result
...


Transcendental Functions Defined for valarray

Chapter 37:

The Numeric Classes

Function

Description

template valarray
log(const valarray &ob);

Obtains the natural logarithm of
each element in ob and returns
an array containing the result
...


template valarray
pow(const valarray &ob1,
const valarray &ob2);

For all i, computes ob1[i]ob2[i]
and returns an array containing
the result
...


template valarray
pow(const valarray &ob, const T &v);

For all i, computes ob1[i]v and
returns an array containing
the result
...


template valarray
sinh(const valarray &ob);

Obtains the hyperbolic sine of
each element in ob and returns
an array containing the result
...


template valarray
tan(const valarray &ob);

Obtains the tangent of each
element in ob and returns an
array containing the result
...


Table 37-4
...

// Demonstrate valarray
#include
#include
#include
using namespace std;
int main()
{
valarray v(10);
int i;
for(i=0; i<10; i++) v[i] = i;
cout << "Original contents: ";
for(i=0; i<10; i++)
cout << v[i] << " ";
cout << endl;
v = v
...
0;
cout << "After subtracting 10 from each element:\n";
for(i=0; i<5; i++)
cout << fv[i] << " ";
cout << endl;
return 0;
}

Its output is shown here:
Original contents: 0 1 2 3 4 5 6 7 8 9
Shifted contents: 3 4 5 6 7 8 9 0 1 2
Those elements less than 5: 1 1 0 0 0 0 0 1 1 1
Original contents: 0 1 2 3 4
Square roots: 0 1 1
...
73205 2
Double the square roots: 0 2 2
...
4641 4
After subtracting 10 from each element:
-10 -8 -7
...
5359 -6

The slice and gslice Classes
The header defines two utility classes called slice and gslice
...
e
...
These classes are used with the subset
forms of valarray's operator[ ]
...
The second constructor creates a slice that
specifies the starting element, the number of elements, and the interval between
elements (that is, the stride)
...

Here is a program that demonstrates slice
...
size(); i++)
cout << result[i] << " ";
return 0;
}

Chapter 37:

The Numeric Classes

The output from the program is shown here:
Contents of v: 0 1 2 3 4 5 6 7 8 9
Contents of result: 0 2 4 6 8

As you can see, the resulting array consists of 5 elements of v, beginning at 0, that
are 2 apart
...
The second constructor creates a slice that
specifies the starting element, an array that specifies the number of elements, and an
array that specifies the intervals between elements (that is, the strides)
...
The member functions return these
parameters
...

The following program demonstrates gslice
...
size(); i++)
cout << result[i] << " ";
return 0;
}

The output is shown here:
Contents of v: 0 1 2 3 4 5 6 7 8 9 10 11
Contents of result: 0 3 6 2 5 8 4 7 10

The Helper Classes
The numeric classes rely upon these "helper" classes, which your program will never
instantiate directly: slice_array, gslice_array, indirect_array, and mask_array
...
Each is examined here
...
Its prototypes are shown here:
template T accumulate(InIter start, InIter end, T v);
template
T accumulate(InIter start, InIter end, T v, BinFunc func);
Here, T is the type of values being operated upon
...
The second version applies func to the running

Chapter 37:

The Numeric Classes

total
...
) The value of v provides an
initial value to which the running total is added
...

// Demonstrate accumulate()
#include
#include
#include
using namespace std;
int main()
{
vector v(5);
int i, total;
for(i=0; i<5; i++) v[i] = i;
total = accumulate(v
...
end(), 0);
cout << "Summation of v is: " << total;
return 0;
}

The following output is produced:
Summation of v is: 10

adjacent_difference
The adjacent_difference( ) algorithm produces a new sequence in which each element
is the difference between adjacent elements in the original sequence
...
) The prototypes for
adjacent_difference( ) are shown here:
template
outIter adjacent_difference(InIter start, InIter end, OutIter result);
template
outIter adjacent_difference(InIter start, InIter end, OutIter result,
BinFunc func);
Here, start and end are iterators to the beginning and ending of the original sequence
...
In the first form,

917

918

C++: The Complete Reference

adjacent elements are subtracted, with the element at location n being subtracted from
the element at location n+1
...
An iterator to the end of result is returned
...

// Demonstrate adjacent_difference()
#include
#include
#include
using namespace std;
int main()
{
vector v(10), r(10);
int i;
for(i=0; i<10; i++) v[i] = i*2;
cout << "Original sequence: ";
for(i=0; i<10; i++)
cout << v[i] << " ";
cout << endl;
adjacent_difference(v
...
end(), r
...


inner_product
The inner_product( ) algorithm produces a summation of the product of corresponding
elements in two sequences and returns the result
...
The
iterator start2 is an iterator to the beginning of the second sequence
...
In the second form,
func1 specifies a binary function that determines how the running total is computed,
and func2 specifies a binary function that determines how the two sequences are
multiplied together
...

// Demonstrate inner_product()
#include
#include
#include
using namespace std;
int main()
{
vector v1(5), v2(5);
int i, total;
for(i=0; i<5; i++) v1[i] = i;
for(i=0; i<5; i++) v2[i] = i+2;
total = inner_product(v1
...
end(),
v2
...
(That is, it creates a sequence that
is a running total of the original sequence
...
The prototypes for partial_sum( ) are shown
here:
template
OutIter partial_sum(InIter start, InIter end, OutIter result);
template
OutIter partial_sum(InIter start, InIter end, OutIter result,
BinFunc func);
Here, start1 and end1 are iterators to the beginning and end of the original sequence
...
In the
second form, func specifies a binary function that determines how the running total is
computed
...

Here is an example of partial_sum( )
...
begin(), v
...
begin());
cout << "Resulting sequence: ";
for(i=0; i<5; i++)
cout << r[i] << " ";
return 0;
}

Here is its output:
Original sequence: 0 1 2 3 4
Resulting sequence: 0 1 3 6 10

C++

Chapter 38
Exception Handling and
Miscellaneous Classes

921

922

C++: The Complete Reference

T

his chapter describes the exception handling classes
...


Exceptions
The standard C++ library defines two headers that relate to exceptions:
and
...
Each header is
examined here
...
The classes defined by are shown here
...

The bad_exception class is the type of exception thrown by the unexpected( ) function
...

Several important classes are derived from exception
...
Next is bad_typeid
...
Finally, bad_cast is thrown when an invalid dynamic cast is
attempted
...

The types defined by are:

Type

Meaning

terminate_handler

typedef void (*terminate_handler) ( );

unexpected_handler

typedef void (*unexpected_handler) ( );

The functions declared in are shown in Table 38-1


The header defines several standard exceptions that may be thrown by C++
library functions and/or its run-time system
...
Logic errors occur because of
mistakes made by the programmer
...


Function

Description

terminate_handler
set_terminate(terminate_handler fn)
throw( );

Sets the function specified by fn as the
terminate handler
...


unexpected_handler
set_unexpected(unexpected_handler fn)
throw( );

Sets the function specified by fn as the
unexpected handler
...


void terminate( );

Calls the terminate handler when a
fatal exception is unhandled
...


bool uncaught_exception( );

Returns true if an exception is
uncaught
...
By default, terminate( ) is
called
...


The Functions Defined Within

923

924

C++: The Complete Reference

The standard exceptions defined by C++ caused by logic errors are derived from
the base class logic_error
...


Exception

Meaning

domain_error

Domain error occurred
...


length_error

An attempt was made to create an object that was too large
...


The following run-time exceptions are derived from the base class runtime_error
...


range_error

An internal range error occurred
...


auto_ptr
A very interesting class is auto_ptr, which is declared in the header
...
Ownership of this object
can be transferred to another auto_ptr, but some auto_ptr always owns the object
...
For example, when one auto_ptr object is assigned to another, only the
target of the assignment will own the object
...

The main benefit of this approach is that dynamically allocated objects can be
destroyed when an exception is handled
...

Here are the constructors for auto_ptr:
explicit auto_ptr(T *ptr = 0) throw( );

Chapter 38:

Exception Handling and Miscellaneous Classes

auto_ptr(const auto_ptr &ob) throw( );
template auto_ptr(const auto_ptr &ob) throw( );
The first constructor creates an auto_ptr to the object specified by ptr
...
The third converts ob to type T (if possible) and transfers ownership
...
It also defines these two
member functions:
T *get( ) const throw( );
T *release( ) const throw( );
The get( ) function returns a pointer to the stored object
...
After a call to release( ), the pointed-to object is not automatically
destroyed when the auto_ptr object goes out-of-scope
...

// Demonstrate an auto_ptr
...
get();

925

926

C++: The Complete Reference

ptr->f();
return 0;
}

The output produced by this program is shown here:
constructing
Inside f()
Inside f()
destructing

Notice that X's member function f( ) can be called either through an auto_ptr or
through the "normal" pointer returned by get( )
...
It has this template specification:
template struct pair {
typedef Ktype first_type;
typedef Vtype second_type;
Ktype first;
Vtype second;
// constructors
pair();
pair(const Ktype &k, const Vtype &v);
template pair(const &ob);
}

The value in first typically contains a key, and the value in second typically contains
the value associated with that key
...

You can construct a pair using either one of pair's constructors or by using
make_pair( ), which constructs a pair object based upon the types of the data used as
parameters
...
The advantage of make_pair( ) is that the types of the objects being
stored are determined automatically by the compiler rather than being explicitly
specified by you
...


Localization
Standard C++ provides an extensive localization class library
...
Thus, it defines such things as the format of currency, time and date, and
collation order
...
The localization library uses
the header
...
All facets are derived from the class facet, which
is a nested class inside the locale class
...
A description
of its features is beyond the scope of this book
...


Other Classes of Interest
Here are a few other classes defined by the Standard C++ library that may be of
interest
...
Uses the header

...
Uses the
header
...

Uses the header
...


Part V
Applying C++

P

art Five of this book provides two sample C++ applications
...
First, the examples
help illustrate the benefits of object-oriented programming
...


929

This page intentionally left blank
...
As you know, Standard
C++ provides a full-featured, powerful string class called basic_string
...
The creation of a string class is the quintessential example of this
process
...
In this chapter, we will do the same
...
You may find this useful in
certain situations
...
Of
course, it is not as large or as sophisticated
...

s Both string objects and quoted strings may be assigned to string objects
...

s Substring deletion is performed using the – operator
...

s String objects may be initialized by using either a quoted string or another
string object
...
This implies that
storage for each string is dynamically allocated
...

Although our string class will, in general, be less powerful than the standard string
class, it does include one feature not defined by basic_string: substring deletion via the
– operator
...
Its declaration is shown here:
class StrType {
char *p;
int size;
public:
StrType();

Chapter 39:

Integrating New Classes: A Custom String Class

StrType(char *str);
StrType(const StrType &o); // copy constructor
~StrType() { delete [] p; }
friend ostream &operator<<(ostream &stream, StrType &o);
friend istream &operator>>(istream &stream, StrType &o);
StrType operator=(StrType &o); // assign a StrType object
StrType operator=(char *s); // assign a quoted string
StrType operator+(StrType &o); // concatenate a StrType object
StrType operator+(char *s); // concatenate a quoted string
friend StrType operator+(char *s, StrType &o); /* concatenate
a quoted string with a StrType object */
StrType operator-(StrType &o); // subtract a substring
StrType operator-(char *s); // subtract a quoted substring
// relational operations between StrType objects
int operator==(StrType &o) { return !strcmp(p, o
...
p); }
int operator<(StrType &o) { return strcmp(p, o
...
p) > 0; }
int operator<=(StrType &o) { return strcmp(p, o
...
p) >= 0; }
// operations between StrType objects and quoted strings
int operator==(char *s) { return !strcmp(p, s); }
int operator!=(char *s) { return strcmp(p, s); }
int operator<(char *s) { return strcmp(p, s) < 0; }
int operator>(char *s) { return strcmp(p, s) > 0; }
int operator<=(char *s) { return strcmp(p, s) <= 0; }
int operator>=(char *s) { return strcmp(p, s) >= 0; }
int strsize() { return strlen(p); } // return size of string
void makestr(char *s) { strcpy(s, p); } // make quoted string
operator char *() { return p; } // conversion to char *
};

933

934

C++: The Complete Reference

The private part of StrType contains only two items: p and size
...
The string pointed to by p will be a normal,
null-terminated character array
...
Because the string pointed to by p is a null-terminated string, it
would be possible to compute the size of the string each time it is needed
...

The next several sections detail how the StrType class works
...
The
constructors that support these three operations are shown here:
// No explicit initialization
...

StrType::StrType(char *str) {
size = strlen(str) + 1; // make room for null terminator
try {
p = new char[size];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, str);
}
// Initialize using a StrType object
...
size;
try {
p = new char[size];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, o
...

Although the string could have been left undefined, knowing that all StrType objects
contain a valid, null-terminated string simplifies several other member functions
...
This value is stored in size
...

When a StrType object is used to initialize another, the process is similar to using a
quoted string
...
This version of the StrType constructor is also the class' copy
constructor
...
This means that it is called when temporary objects are created and
when objects of type StrType are passed to functions
...
)
Given the three preceding constructors, the following declarations are allowed:
StrType x("my string"); // use quoted string
StrType y(x); // use another object
StrType z; // no explicit initialization

The StrType destructor function simply frees the memory pointed to by p
...

ostream &operator<<(ostream &stream, StrType &o)
{
stream << o
...

istream &operator>>(istream &stream, StrType &o)
{
char t[255]; // arbitrary size - change if necessary
int len;
stream
...
size) {
delete [] o
...
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
o
...
p, t);
return stream;
}

As you can see, output is very simple
...
Since StrType objects may be quite large, passing one by reference
is more efficient than passing one by value
...
(Any function you create that takes StrType parameters should
probably do the same
...
First, the
string is read using the getline( ) function
...
As the comments indicate, you can
change this if you like
...
Once the
string has been read, if the size of the new string exceeds that of the one currently held
by o, that memory is released and a larger amount is allocated
...


Chapter 39:

Integrating New Classes: A Custom String Class

The Assignment Functions
You can assign a StrType object a string in two ways
...
Second, you can assign it a quoted string
...

StrType StrType::operator=(StrType &o)
{
StrType temp(o
...
size > size) {
delete [] p; // free old memory
try {
p = new char[o
...
size;
}
strcpy(p, o
...
p, o
...

StrType StrType::operator=(char *s)
{
int len = strlen(s) + 1;
if(size < len) {
delete [] p;
try {
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);

937

938

C++: The Complete Reference

}
size = len;
}
strcpy(p, s);
return *this;
}

These two functions work by first checking to see if the memory currently pointed
to by p of the target StrType object is sufficiently large to hold what will be copied to it
...
Then the string is
copied into the object and the result is returned
...
The StrType
class allows for the following three distinct concatenation situations:
s Concatenation of a StrType object with another StrType object
s Concatenation of a StrType object with a quoted string
s Concatenation of a quoted string with a StrType object
When used in these situations, the + operator produces as its outcome a StrType object
that is the concatenation of its two operands
...


Chapter 39:

Integrating New Classes: A Custom String Class

The overloaded operator+( ) functions are shown here:
// Concatenate two StrType objects
...
p;
len = strlen(o
...
size = len;
try {
temp
...
p, p);
strcat(temp
...
p);
return temp;
}
// Concatenate a StrType object and a quoted string
...
p;
len = strlen(s) + strlen(p) + 1;
temp
...
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";

939

940

C++: The Complete Reference

exit(1);
}
strcpy(temp
...
p, s);
return temp;
}
// Concatenate a quoted string and a StrType object
...
p;
len = strlen(s) + strlen(o
...
size = len;
try {
temp
...
p, s);
strcat(temp
...
p);
return temp;
}

All three functions work basically in the same way
...
This object will contain the outcome of the concatenation,
and it is the object returned by the functions
...
p is
freed
...
Next, enough
memory is allocated to hold the concatenation of the two strings
...
p, and temp is returned
...
As
implemented by the StrType class, substring subtraction removes all occurrences
of a specified substring from another string
...

The StrType class supports two cases of substring subtraction
...
The other allows a quoted string to
be removed from a StrType object
...

StrType StrType::operator-(StrType &substr)
{
StrType temp(p);
char *s1;
int i, j;
s1 = p;
for(i=0; *s1; i++) {
if(*s1!=*substr
...
p[i] = *s1; // then copy into temp
s1++;
}
else {
for(j=0; substr
...
p[j]; j++) ;
if(!substr
...
p[i] = *s1;
s1++;
}
}
}
temp
...

StrType StrType::operator-(char *substr)
{
StrType temp(p);

941

942

C++: The Complete Reference

char *s1;
int i, j;
s1 = p;
for(i=0; *s1; i++) {
if(*s1!=*substr) { // if not first letter of substring
temp
...
p[i] = *s1;
s1++;
}
}
}
temp
...
The resulting StrType object is returned
...

The StrType class allows substring subtractions like these:
StrType x("I like C++"), y("like");
StrType z;
z = x - y; // z will contain "I C++"
z = x - "C++"; // z will contain "I like "
// multiple occurrences are removed
z = "ABCDABCD";
x = z -"A"; // x contains "BCDBCD"

Chapter 39:

Integrating New Classes: A Custom String Class

The Relational Operators
The StrType class supports the full range of relational operations to be applied to
strings
...
They are repeated here for your convenience:
// relational operations between StrType objects
int operator==(StrType &o) { return !strcmp(p, o
...
p); }
int operator<(StrType &o) { return strcmp(p, o
...
p) > 0; }
int operator<=(StrType &o) { return strcmp(p, o
...
p) >= 0; }
// operations between StrType objects and quoted strings
int operator==(char *s) { return !strcmp(p, s); }
int operator!=(char *s) { return strcmp(p, s); }
int operator<(char *s) { return strcmp(p, s) < 0; }
int operator>(char *s) { return strcmp(p, s) > 0; }
int operator<=(char *s) { return strcmp(p, s) <= 0; }
int operator>=(char *s) { return strcmp(p, s) >= 0; }

The relational operations are very straightforward; you should have no trouble
understanding their implementation
...
If you want
to be able to put the quoted string on the left and a StrType object on the right, you will
need to add additional relational functions
...
They are strsize( ), makestr( ),
and the conversion function operator char *( )
...
As you can see, the strsize( )
function returns the length of the string pointed to by p
...
The
makestr( ) function copies into a character array the string pointed to by p
...

The conversion function operator char *( ) returns p, which is, of course, a pointer
to the string contained within the object
...
For example, this is valid
code:
StrType x("Hello");
char s[20];
// copy a string object using the strcpy() function
strcpy(s, x); // automatic conversion to char *

Recall that a conversion function is automatically executed when an object is
involved in an expression for which the conversion is defined
...
This pointer is then used by
strcpy( ) to copy the string into s
...


Chapter 39:

Note

Integrating New Classes: A Custom String Class

The conversion to char * does circumvent encapsulation, because once a function
has a pointer to the object's string, it is possible for that function to modify the
string directly, bypassing the StrType member functions and without that object's
knowledge
...
The
loss of encapsulation in this case is offset by increased utility and integration with
existing library functions
...


The Entire StrType Class
Here is a listing of the entire StrType class along with a short main( ) function that
demonstrates its features:
#include
#include
#include
#include
using namespace std;
class StrType {
char *p;
int size;
public:
StrType();
StrType(char *str);
StrType(const StrType &o); // copy constructor
~StrType() { delete [] p; }
friend ostream &operator<<(ostream &stream, StrType &o);
friend istream &operator>>(istream &stream, StrType &o);
StrType operator=(StrType &o); // assign a StrType object
StrType operator=(char *s); // assign a quoted string
StrType operator+(StrType &o); // concatenate a StrType object
StrType operator+(char *s); // concatenate a quoted string
friend StrType operator+(char *s, StrType &o); /* concatenate
a quoted string with a StrType object */

945

946

C++: The Complete Reference

StrType operator-(StrType &o); // subtract a substring
StrType operator-(char *s); // subtract a quoted substring
// relational operations between StrType objects
int operator==(StrType &o) { return !strcmp(p, o
...
p); }
int operator<(StrType &o) { return strcmp(p, o
...
p) > 0; }
int operator<=(StrType &o) { return strcmp(p, o
...
p) >= 0; }
// operations between StrType objects and quoted strings
int operator==(char *s) { return !strcmp(p, s); }
int operator!=(char *s) { return strcmp(p, s); }
int operator<(char *s) { return strcmp(p, s) < 0; }
int operator>(char *s) { return strcmp(p, s) > 0; }
int operator<=(char *s) { return strcmp(p, s) <= 0; }
int operator>=(char *s) { return strcmp(p, s) >= 0; }
int strsize() { return strlen(p); } // return size of string
void makestr(char *s) { strcpy(s, p); } // null-terminated string
operator char *() { return p; } // conversion to char *
};
// No explicit initialization
...

StrType::StrType(char *str) {
size = strlen(str) + 1; // make room for null terminator
try {
p = new char[size];

Chapter 39:

Integrating New Classes: A Custom String Class

} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, str);
}
// Initialize using a StrType object
...
size;
try {
p = new char[size];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, o
...

ostream &operator<<(ostream &stream, StrType &o)
{
stream << o
...

istream &operator>>(istream &stream, StrType &o)
{
char t[255]; // arbitrary size - change if necessary
int len;
stream
...
size) {
delete [] o
...
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);

947

948

C++: The Complete Reference

}
o
...
p, t);
return stream;
}
// Assign a StrType object to a StrType object
...
p);
if(o
...
size];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
size = o
...
p);
strcpy(temp
...
p);
return temp;
}
// Assign a quoted string to a StrType object
...

StrType StrType::operator+(StrType &o)
{
int len;
StrType temp;
delete [] temp
...
p) + strlen(p) + 1;
temp
...
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(temp
...
p, o
...

StrType StrType::operator+(char *s)
{
int len;
StrType temp;
delete [] temp
...
size = len;
try {
temp
...
p, p);
strcat(temp
...

StrType operator+(char *s, StrType &o)
{
int len;
StrType temp;
delete [] temp
...
p) + 1;
temp
...
p = new char[len];
} catch (bad_alloc xa) {
cout << "Allocation error\n";
exit(1);
}
strcpy(temp
...
p, o
...

StrType StrType::operator-(StrType &substr)
{
StrType temp(p);
char *s1;
int i, j;
s1 = p;
for(i=0; *s1; i++) {
if(*s1!=*substr
...
p[i] = *s1; // then copy into temp

Chapter 39:

Integrating New Classes: A Custom String Class

s1++;
}
else {
for(j=0; substr
...
p[j]; j++) ;
if(!substr
...
p[i] = *s1;
s1++;
}
}
}
temp
...

StrType StrType::operator-(char *substr)
{
StrType temp(p);
char *s1;
int i, j;
s1 = p;
for(i=0; *s1; i++) {
if(*s1!=*substr) { // if not first letter of substring
temp
...
p[i] = *s1;
s1++;
}
}

951

952

C++: The Complete Reference

}
temp
...
\n");
StrType s2(s1);
StrType s3;
char s[80];
cout << s1 << s2;
s3 = s1;
cout << s1;
s3
...
";
cout << s2 << endl;
StrType s4(" So is this
...
\n";
if(s2!=s3) cout << "Strings are not equal
...
strsize() << " characters long
...

A sample session using string objects
...

Convert to a string: A sample session using string objects
...

This is a new string
...

Strings are not equal
...

I like C++
Aren't C++ strings fun
Aren't C++ strings fun
Aren't C++ strings fun
Bye Bye Bye

This output assumes that the string "I like C++" was entered by the user when
prompted for input
...
H
...


Using the StrType Class
To conclude this chapter, two short examples are given that illustrate the StrType class
...

That is, it can be used like any other type defined by Standard C++
...
It first
creates a two-dimensional array of StrType objects
...
The second string contains a list of
alternative or related words
...
This program is very simple, but notice how
clean and clear the string handling is because of the use of the StrType class and its
operators
...
H contains the StrType class
...
h"
#include
using namespace std;
StrType thesaurus[][2] = {
"book", "volume, tome",

Chapter 39:

Integrating New Classes: A Custom String Class

"store", "merchant, shop, warehouse",
"pistol", "gun, handgun, firearm",
"run", "jog, trot, race",
"think", "muse, contemplate, reflect",
"compute", "analyze, work out, solve"
"", ""
};
int main()
{
StrType x;
cout << "Enter word: ";
cin >> x;
int i;
for(i=0; thesaurus[i][0]!=""; i++)
if(thesaurus[i][0]==x) cout << thesaurus[i][1];
return 0;
}

The next example uses a StrType object to check if there is an executable version of
a program, given its filename
...
The program then repeatedly tries to find an
executable file by that name by adding an extension, trying to open that file, and
reporting the results
...
) After each
extension is tried, the extension is subtracted from the filename and a new extension is
added
...

#include "str
...
"; // add period
for(i=0; i<3; i++) {
fname = fname + ext[i]; // add extension
cout << "Trying " << fname << " ";
ifstream f(fname);
if(f) {
cout << "- Exists\n";
f
...
EXE exists,
the command line ISEXEC TEST produces this output:
Trying TEST
...
COM - Not found
Trying TEST
...
This works because the conversion function char *( ) is
automatically invoked
...


Chapter 39:

Integrating New Classes: A Custom String Class

Creating and Integrating New Types in General
As the StrType class has demonstrated, it is actually quite easy to create and integrate a
new data type into the C++ environment
...

1
...

2
...

3
...

Part of the power of C++ is its extensibility
...


A Challenge
Here is an interesting challenge that you might enjoy
...
That is, use a container to store the characters that comprise a string
...


957

This page intentionally left blank
...
In this chapter we will examine one of them: the expression parser
...
Expression parsers are quite useful and are applicable to a wide range of
applications
...
For various
reasons, the procedures used to create an expression parser are not widely taught or
disseminated
...

Expression parsing is actually very straightforward, and in many ways easier than
other programming tasks
...
This chapter will develop what is commonly
referred to as a recursive-descent parser and all the necessary support routines that
enable you to evaluate complex numeric expressions
...
The first two are nongeneric versions
...
However, before any parser can be developed,
a brief overview of expressions and parsing is necessary
...
Although expressions can
be made up of all types of information, this chapter deals only with numeric
expressions
...
These items can be combined in expressions
according to the rules of algebra
...

In the examples in this chapter, all variables are single letters (in other words, 26
variables, A through Z, are available)
...
For the first version of the parser, all numeric values are
elevated to double, although you could easily write the routines to handle other types
of values
...


Parsing Expressions: The Problem
If you have not thought much about the problem of expression parsing, you might
assume that it is a simple task
...
Although you could easily create
a program that would compute that specific expression, the question is how to create a
program that gives the correct answer for any arbitrary expression
...
However, if you use this basic approach, the expression 10 – 2 * 3
evaluates to 24 (that is, 8 * 3) instead of 4 because this procedure neglects the
precedence of the operators
...
Some beginners think that this problem can be easily overcome, and
sometimes, in very restricted cases, it can
...

Although there are a few ways to write a routine that evaluates expressions, the
one developed here is the one most easily written by a person
...
The method used here is called a recursive-descent parser, and in the course of
this chapter you will see how it got its name
...

These are sometimes called table-driven parsers
...
For use with a
recursive-descent parser, think of expressions as recursive data structures—that is,
expressions that are defined in terms of themselves
...
In
fact, the rules are usually called the production rules of the expression
...
Notice that the precedence of the operators is implicit in the way
an expression is defined
...
The second term contains two factors: 5 and B
...

On the other hand, the expression
14 * (7 – C)
has two factors: 14 and (7 – C)
...
The parenthesized expression contains two terms: one number and one variable
...
At each appropriate step, the parser performs the specified
operations in the algebraically correct sequence
...
Get the first term, 9/3
...
Get each factor and divide the integers
...

3
...
At this point, start recursively analyzing the
second subexpression
...
Get each term and add
...

5
...
The answer is –153
...
This is a fairly complex
concept that takes some getting used to
...
First, the precedence of the operators is implicit in
the way the production rules are defined
...

The remainder of this chapter develops three parsers
...

Next, this parser is enhanced to support the use of variables
...


963

964

C++: The Complete Reference

The Parser Class
The expression parser is built upon the parser class
...
Subsequent versions of the parser build upon it
...
The expression to be
evaluated is contained in a null-terminated string pointed to by exp_ptr
...
For example,
the following strings contain expressions that the parser can evaluate:
"10 − 5"
"2 * 3
...
1416 * 3
...
As the parser executes, it works its way through the string until the
null-terminator is encountered
...

The entry point to the parser is through eval_exp( ), which must be called with a
pointer to the expression to be analyzed
...
They implement an
enhanced set of the expression production rules discussed earlier
...


Chapter 40:

An Object-Oriented Expression Parser

The serror( ) handles syntax errors in the expression
...


Dissecting an Expression
In order to evaluate expressions, you need to be able to break an expression into its
components
...

Each component of an expression is called a token
...
Each token represents an indivisible
unit of the expression
...
The function must also be able to skip over spaces
and tabs and detect the end of the expression
...

Besides the token, itself, you will also need to know what type of token is being
returned
...
(DELIMITER is used for both operators
and parentheses
...
It obtains the next token from the
expression pointed to by exp_ptr and puts it into the member variable token
...

// Obtains the next token
...

int parser::isdelim(char c)
{
if(strchr(" +-/*%^=()", c) || c==9 || c=='\r' || c==0)
return 1;
return 0;
}

Look closely at the preceding functions
...
It does
so by checking the character pointed to by exp_ptr
...
If there are still more tokens to retrieve from the expression, get_token( ) first
skips over any leading spaces
...
If the next character is an operator, it is returned as a string in token, and
DELIMITER is placed in tok_type
...
It is returned as a string in token, and tok_type is assigned
the value VARIABLE
...
Finally, if the next character
is none of the preceding, it is assumed that the end of the expression has been reached
...

As stated earlier, to keep the code in this function clean, a certain amount of error
checking has been omitted and some assumptions have been made
...
Also, in this version, variables may be
of any length, but only the first letter is significant
...


Chapter 40:

An Object-Oriented Expression Parser

To better understand the tokenization process, study what it returns for each token
and type in the following expression:
A + 100 – (B * C) /2

Token

Token type

A

VARIABLE

+

DELIMITER

100

NUMBER



DELIMITER

(

DELIMITER

B

VARIABLE

*

DELIMITER

C

VARIABLE

)

DELIMITER

/

DELIMITER

2

NUMBER

null

null

Remember that token always holds a null-terminated string, even if it contains just
a single character
...
It can evaluate expressions that consist solely of
constants, operators, and parentheses
...

/* This module contains the recursive descent
parser that does not use variables
...

double parser::eval_exp(char *exp)
{
double result;
exp_ptr = exp;
get_token();
if(!*token) {
serror(2); // no expression present
return 0
...

void parser::eval_exp2(double &result)
{
register char op;
double temp;
eval_exp3(result);
while((op = *token) == '+' || op == '-') {
get_token();
eval_exp3(temp);
switch(op) {
case '-':
result = result - temp;
break;
case '+':
result = result + temp;
break;
}
}
}
// Multiply or divide two factors
...
0) {
result = 1
...

void parser::eval_exp5(double &result)
{
register char op;
op = 0;
if((tok_type == DELIMITER) && *token=='+' || *token == '-') {
op = *token;
get_token();
}
eval_exp6(result);
if(op=='-') result = -result;
}
// Process a parenthesized expression
...

void parser::atom(double &result)
{
switch(tok_type) {
case NUMBER:
result = atof(token);
get_token();
return;
default:
serror(0);
}
}
// Display a syntax error
...

void parser::get_token()
{
register char *temp;
tok_type = 0;
temp = token;

971

972

C++: The Complete Reference

*temp = '\0';
if(!*exp_ptr) return; // at end of expression
while(isspace(*exp_ptr)) ++exp_ptr; // skip over white space
if(strchr("+-*/%^=()", *exp_ptr)){
tok_type = DELIMITER;
// advance to next char
*temp++ = *exp_ptr++;
}
else if(isalpha(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = VARIABLE;
}
else if(isdigit(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = NUMBER;
}
*temp = '\0';
}
// Return true if c is a delimiter
...
In
addition, it can handle integer exponentiation (^) and the unary minus
...
The actual evaluation of an expression takes place
in the mutually recursive functions eval_exp2( ) through eval_exp6( ), plus the atom( )
function, which returns the value of a number
...

The simple main( ) function that follows demonstrates the use of the parser
...
\n";
parser ob; // instantiate a parser
for(;;) {
cout << "Enter expression: ";
cin
...
') break;
cout << "Answer is: " << ob
...

Enter a period to stop
...
33333
Enter expression:
...
(Assume that exp_ptr points to the start of the expression
...
If
the token is null, the function prints the message No Expression Present and returns
...
Since the first token is not
null, eval_exp2( ) is called
...
Then eval_exp5( ) checks whether
the token is a unary plus or minus, which in this case it is not, so eval_exp6( ) is called
...
Since the
token is not a left parentheses, atom( ) is executed and result is assigned the value 10
...
Since
the token is now the operator –, the functions return up to eval_exp2( )
...
Because the token is –, it is saved in op
...
As before, atom( ) is entered
...
This causes a return back up the chain to eval_exp3( ), where the final token 2 is
read
...

The result is returned to eval_exp2( ), and the subtraction is performed
...
Although the process may at first seem complicated,
work through some other examples to verify that this method functions correctly
every time
...
Before it could be used in a computer language,
database, or in a sophisticated calculator, however, it would need the ability to handle
variables
...


Adding Variables to the Parser
All programming languages, many calculators, and spreadsheets use variables to store
values for later use
...
To accomplish this, you need to add several things to
the parser
...
As stated earlier, we will use
the letters A through Z for variables
...
Each variable uses one array location in a 26-element array of doubles
...

// parser constructor
parser::parser()
{
int i;
exp_ptr = NULL;

Chapter 40:

An Object-Oriented Expression Parser

for(i=0; i ...

You will also need a function to look up the value of a given variable
...
The member function
find_var( ), shown here, accomplishes this:
// Return the value of a variable
...
0;
}
return vars[toupper(*token)-'A'];
}

As this function is written, it will actually accept long variable names, but only the first
letter is significant
...

You must also modify the atom( ) function to handle both numbers and variables
...

void parser::atom(double &result)
{
switch(tok_type) {
case VARIABLE:
result = find_var(token);
get_token();
return;
case NUMBER:
result = atof(token);
get_token();
return;
default:
serror(0);
}
}

975

976

C++: The Complete Reference

Technically, these additions are all that is needed for the parser to use variables
correctly; however, there is no way for these variables to be assigned a value
...
There are various
ways to do this
...
This function will now begin the recursive-descent chain
...

eval_exp1( ) is shown here:
// Process an assignment
...
This is because a variable name always precedes an
assignment, but a variable name alone does not guarantee that an assignment
expression follows
...
To accomplish this, eval_exp1( ) reads the next
token from the input stream
...
The putback( ) function must also be
included in the parser class
...

void parser::putback()
{
char *t;
t = token;
for(; *t; t++) exp_ptr--;
}

After making all the necessary changes, the parser will now look like this
...

*/
#include
#include
#include
#include
using namespace std;
enum types { DELIMITER = 1, VARIABLE, NUMBER};
const int NUMVARS = 26;
class parser {
char *exp_ptr; // points to the expression
char token[80]; // holds current token
char tok_type; // holds token's type
double vars[NUMVARS]; // holds variables' values
void eval_exp1(double &result);
void eval_exp2(double &result);

977

978

C++: The Complete Reference

void eval_exp3(double &result);
void eval_exp4(double &result);
void eval_exp5(double &result);
void eval_exp6(double &result);
void atom(double &result);
void get_token();
void putback();
void serror(int error);
double find_var(char *s);
int isdelim(char c);
public:
parser();
double eval_exp(char *exp);
};
// parser constructor
parser::parser()
{
int i;
exp_ptr = NULL;
for(i=0; i ...

double parser::eval_exp(char *exp)
{
double result;
exp_ptr = exp;
get_token();
if(!*token) {
serror(2); // no expression present
return 0
...

void parser::eval_exp1(double &result)
{
int slot;
char ttok_type;
char temp_token[80];
if(tok_type==VARIABLE) {
// save old token
strcpy(temp_token, token);
ttok_type = tok_type;
// compute the index of the variable
slot = toupper(*token) - 'A';
get_token();
if(*token != '=') {
putback(); // return current token
// restore old token - not assignment
strcpy(token, temp_token);
tok_type = ttok_type;
}
else {
get_token(); // get next part of exp
eval_exp2(result);
vars[slot] = result;
return;
}
}
eval_exp2(result);
}
// Add or subtract two terms
...

void parser::eval_exp3(double &result)
{
register char op;
double temp;
eval_exp4(result);
while((op = *token) == '*' || op == '/' || op == '%') {
get_token();
eval_exp4(temp);
switch(op) {
case '*':
result = result * temp;
break;
case '/':
result = result / temp;
break;
case '%':
result = (int) result % (int) temp;
break;
}
}
}
// Process an exponent
void parser::eval_exp4(double &result)
{
double temp, ex;
register int t;

Chapter 40:

An Object-Oriented Expression Parser

eval_exp5(result);
if(*token== '^') {
get_token();
eval_exp4(temp);
ex = result;
if(temp==0
...
0;
return;
}
for(t=(int)temp-1; t>0; --t) result = result * (double)ex;
}
}
// Evaluate a unary + or -
...

void parser::eval_exp6(double &result)
{
if((*token == '(')) {
get_token();
eval_exp2(result);
if(*token != ')')
serror(1);
get_token();
}
else atom(result);
}
// Get the value of a number or a variable
...

void parser::putback()
{
char *t;
t = token;
for(; *t; t++) exp_ptr--;
}
// Display a syntax error
...

void parser::get_token()
{
register char *temp;
tok_type = 0;
temp = token;

Chapter 40:

An Object-Oriented Expression Parser

*temp = '\0';
if(!*exp_ptr) return; // at end of expression
while(isspace(*exp_ptr)) ++exp_ptr; // skip over white space
if(strchr("+-*/%^=()", *exp_ptr)){
tok_type = DELIMITER;
// advance to next char
*temp++ = *exp_ptr++;
}
else if(isalpha(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = VARIABLE;
}
else if(isdigit(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = NUMBER;
}
*temp = '\0';
}
// Return true if c is a delimiter
...

double parser::find_var(char *s)
{
if(!isalpha(*s)){
serror(1);
return 0
...
With the enhanced parser, you can now enter expressions like
A = 10/4
A–B
C = A * (F – 21)

Syntax Checking in a Recursive-Descent Parser
Before moving on to the template version of the parser, let's briefly look at syntax
checking
...
Most of the time,
this is caused by human error, usually typing mistakes
...
None of these conditions is
allowed by the parsers
...

As you studied the code of the parsers, you probably noticed the serror( ) function,
which is called under certain situations
...
The only
problem with the syntax checking as it now stands is that the entire parser is not
terminated on syntax error
...

The best way to implement the serror( ) function is to have it execute some sort of
reset
...
These two functions allow a program to branch to a different
function
...

Depending upon the use you put the parser to, you might also find that C++'s
exception handling mechanism (implemented through try, catch, and throw) will be
beneficial when handling errors
...

This can be an annoyance in some situations but a blessing in others because multiple
errors may be caught
...


Chapter 40:

An Object-Oriented Expression Parser

Building a Generic Parser
The two preceding parsers operated on numeric expressions in which all values were
assumed to be of type double
...
Also, by hard-coding the type of values being evaluated, the application of
the parser is unnecessarily restricted
...
Once this has been done, the parser
can be used both with built-in types and with numeric types that you create
...

// A generic parser
...

template PType parser::eval_exp(char *exp)
{
PType result;
exp_ptr = exp;
get_token();
if(!*token) {
serror(2); // no expression present
return (PType) 0;
}
eval_exp1(result);
if(*token) serror(0); // last token must be null
return result;
}
// Process an assignment
...

template void parser::eval_exp2(PType &result)
{
register char op;
PType temp;
eval_exp3(result);
while((op = *token) == '+' || op == '-') {
get_token();
eval_exp3(temp);
switch(op) {
case '-':
result = result - temp;
break;
case '+':
result = result + temp;
break;
}
}
}

987

988

C++: The Complete Reference

// Multiply or divide two factors
...
0) {
result = (PType) 1;
return;
}
for(t=(int)temp-1; t>0; --t) result = result * ex;
}

Chapter 40:

An Object-Oriented Expression Parser

}
// Evaluate a unary + or -
...

template void parser::eval_exp6(PType &result)
{
if((*token == '(')) {
get_token();
eval_exp2(result);
if(*token != ')')
serror(1);
get_token();
}
else atom(result);
}
// Get the value of a number or a variable
...

template void parser::putback()
{
char *t;
t = token;
for(; *t; t++) exp_ptr--;
}
// Display a syntax error
...

template void parser::get_token()
{
register char *temp;
tok_type = 0;
temp = token;
*temp = '\0';
if(!*exp_ptr) return; // at end of expression
while(isspace(*exp_ptr)) ++exp_ptr; // skip over white space
if(strchr("+-*/%^=()", *exp_ptr)){
tok_type = DELIMITER;
// advance to next char
*temp++ = *exp_ptr++;
}

Chapter 40:

An Object-Oriented Expression Parser

else if(isalpha(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = VARIABLE;
}
else if(isdigit(*exp_ptr)) {
while(!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
tok_type = NUMBER;
}
*temp = '\0';
}
// Return true if c is a delimiter
...

template PType parser::find_var(char *s)
{
if(!isalpha(*s)){
serror(1);
return (PType) 0;
}
return vars[toupper(*token)-'A'];
}

As you can see, the type of data now operated upon by the parser is specified by
the generic type PType
...

int main()
{
char expstr[80];
// Demonstrate floating-point parser
...
";
cout << "Enter a period to stop\n";
for(;;) {
cout << "Enter expression: ";
cin
...
') break;
cout << "Answer is: " << ob
...

parser Iob;
cout << "Integer parser
...
getline(expstr, 79);
if(*expstr=='
...
eval_exp(expstr) << "\n\n";
}
return 0;
}

Here is a sample run
...
Enter a period to stop
Enter expression: a=10
...
1
Enter expression: b=3
...
2
Enter expression: a/b
Answer is: 3
...

Integer parser
...


As you can see, the floating-point parser uses floating-point values, and the integer
parser uses integer values
...
You might want to add detailed error reporting
...
This would allow
the user to find and correct a syntax error
...
However, with
a few additions, it is possible to enable the parser to evaluate other types of
expressions, such as strings, spatial coordinates, or complex numbers
...
Define a new token type called STRING
...
Enhance get_token( ) so that it recognizes strings
...
Add a new case inside atom( ) that handles STRING type tokens
...

Here is one good application for the parser: create a simple, pop-up mini-calculator
that accepts an expression entered by the user and then displays the result
...
If you are
programming for Windows, this would be especially easy to do
...


Index
& (bitwise operator), 42, 43-44
& (pointer operator), 48, 49,
115-116, 141, 262, 349
& (reference parameter),
342-343, 349
&&, 40, 41
< >, 242, 268, 467, 485
->, 51, 171, 175, 178, 331
overloading, 409, 415-416
->* (pointer-to-member
operator), 339, 340, 341
* (multiplication operator), 37,
38
* (pointer operator), 48-49,
115-116, 123-124, 349
* (printf( ) placeholder), 202-203
|, 42, 43, 44
||, 40, 41
[ ], 51-52, 90, 352, 353, 358
overloading, 409-413
^, 42, 43, 44, 207
:, 47, 271

::(scope resolution operator),
272, 319, 440-441
, (comma operator), 50
overloading, 416-418
{ }, 7, 18

...
* (pointer-to-member
operator), 339, 340
!, 40, 41
!=, 40, 41
=, 35
==, 40, 41
<, 40, 41
<< (left shift), 43, 44-46
<< (output operator), 262-264
overloading, 528-534,
790-791
<=, 40, 41
-, 37, 38
—, 37-39, 391-392, 395-397
( ) function operator, 138

995

overloading, 409, 413-415
( ) precedence operator, 39,
41-42, 50, 51
% (format specifier), 195
% (modulus operator), 37, 38
+, 37, 38
++, 37-39, 391-392, 395-397
# (preprocessor directive), 238
# (preprocessor operator),
248-250
# (printf( ) modifier), 202
## (preprocessor operator),
248-250
?, 47, 63-66
>, 40, 41
>> (right shift), 43, 44-46
>> (input operator), 262,
263-264
overloading, 528, 534-537,
790-791
>=, 40, 41
; (semicolon), 88, 163
/, 37, 38

996

C++: The Complete Reference

/* */, 250
//, 251, 262
~, 42, 43, 46-47, 284

A
abort( ), 491, 492, 502, 505, 506,
758
abs( ), 758-759
Access declarations, 436-439
Access modifiers, 23-25
Access specifiers, 290, 420-427
accumulate( ) algorithm,
916-917
acos( ), 734
Ada, 5
Adaptor(s), 629, 872-874
Address, memory
& operator used to return,
48, 115-116
pointer as, 47, 115
relocatable format, 12
adjacent_difference( )
algorithm, 917-918
adjacent_find( ) algorithm, 836
adjustfield format flag, 516
advance( ), 868
Aggregate data type, 162
ALGOL, 6, 8
header, 660
Algorithms, 627, 631, 660-670,
836-855
table of STL, 661-663
allocator class, 628, 875-876
member functions, table of,
876
Allocators, 628, 875-876
AND
& bitwise operator, 42, 43-44
&& logical operator, 40, 41
ANSI/ISO C standard, 2, 4
app, 789
append( ), 684
argc, 144-145, 147
Arguments, function
call by reference passing
convention, 140-141, 170,
341-345
call by value passing
convention, 139-140

command line, 144-147
default, 374-380, 382-383
passing arrays as, 92-93, 98,
102, 142-144
passing functions as, 126-129
argv, 123, 144-147
Arithmetic operators, 37-39
precedence of, 39
Array(s)
allocating with new, 352-353
bounds checking on, 5, 91,
369, 412
compacting, 472-474
definition of, 90
generating pointer to, 92
indexing versus pointer
arithmetic, 121
initialization, 105-107
multidimensional, 101-102
of objects, 328-331, 356,
366-368
to functions, passing, 92-93,
142-144
of pointers, 122-123
using pointers to access,
103-104, 121
safe, creating, 369-371,
412-413
single-dimension, 90-91
sorting, 471-472
square brackets as operator
for indexing, 51-52
of strings, 100-101
of structures, 166
within structures, 173
two-dimensional, 96-101
unsized, 106-107
vector as dynamic, 631
Array-based I/O, 615-623
and binary data, 622-623
using dynamic arrays and,
621-622
using ios member functions
with, 616
Arrow operator (->), 51, 171,
175, 178, 331
overloading, 409, 415-416
asctime( ), 744-745
asin( ), 734-735
asm statement, 613-614
Assembly language, 4, 8

using asm to embed, 613-614
C used in place of, 8
assert( ), 759
assign( ), 683-684
Assignment
functions used in, 149-150,
346-347
multiple, 36-37
object, 324-325
operation for C++ classes,
default, 391
operator, 34-35
pointer, 117, 333-334
shorthand notation for, 56
structure, 165-166
type conversion in, 35-36
atan( ), 735
atan2( ), 735
ate, 789
atexit( ), 759
atof( ), 759-760
atoi( ), 146, 760
atol( ), 760
auto keyword, 18
auto_ptr class, 924-926

B
B language, 4
back_insert_iterator class, 862,
863
Backslash character constants,
33-34
bad( ), 565, 791
bad_alloc class, 350, 922
bad_cast, 580, 923
bad_exception class, 508, 922
bad_typeid, 574, 922
badbit, 563, 565, 790, 799
Base class
access control, 420-426
constructors, passing
parameters to, 432-436
definition of, 278, 420
general form for inheriting,
279, 420
inheritance, protected,
426-427
virtual, 439-443
base( ), 864

Index

basefield format flag, 516
BASIC, 4, 5, 6, 7, 8
basic_filebuf class, 784, 785
basic_fstream class, 514, 783,
785
basic_ifstream class, 514, 784,
785
basic_ios class, 513, 514, 784,
785
basic_iostream class, 513, 514,
784, 785
basic_istream class, 513, 514,
784, 785
basic_istringstream class, 784,
785
basic_ofstream class, 514, 784,
785
basic_ostream class, 513, 514,
784, 785
basic_ostringstream class, 784,
785
basic_streambuf class, 513, 514,
784, 785
basic_string class, 679, 878-890
constructors, 878
member functions, table of,
880-890
basic_stringbuf class, 784, 785
basic_stringstream class, 784,
785
BCPL language, 4
before( ), 570-571
beg, 790
begin( ), 631, 632, 633, 637
BiIter, 628, 808
binary, 789
binary_function class, 675, 869,
870
binary_negate class, 871-872
binary_search( ) algorithm,
836-837
bind1st( ) binder, 676-677,
870-871
bind2nd( ) binder, 676-677,
870-871
Binders, 629, 676-678, 870-871
BinPred type, 628, 808
Bit shift operators (>> and <<),
43, 44-46
Bit-fields, 162, 174-176

bitset container, 629, 630, 808,
810-812
member functions, table of,
811-812
header, 629, 808
Bitwise operation, definition of,
42
Bitwise operators, 42-47
table of, 42-43
Block statements, 58, 88
bool data type, 14, 39, 58, 266
boolalpha
format flag, 516
manipulator, 524, 527-528
break statement, 67, 68, 69-70,
76, 83-85
Broken-down time, 744
bsearch( ), 760-761
BUFSIZ macro, 715

C

...
) form of, 500-502
and derived-class
exceptions, 499
general form of, 490
using multiple, 497-498
header, 720
ceil( ), 735-736
cerr, 514
header, 734
char data type, 14, 15
char_traits class, 785, 878,
890-892
member functions, table of,
891-892
Character(s)
ASCII, 14, 549
in C console I/O, 189-191
constants, 31-32, 623
constants, backslash, 33-34
control, 720
printable, 720
set, extended, 549-550
wide
...
See Base class
creating conversion
functions for, 605-609
declaration, general form
for, 271-272, 290
defining functions within,
306-307, 309
derived
...
See Generic class
instance of, 271
libraries, 460
local, 320-321
nested, 320

997

998

C++: The Complete Reference

overview of, 270-274
stream, 513-514
structures and, 293-295
unions and, 295-297
class keyword, 270, 290, 295
Class member(s)
accessing, public, 272-273,
293
definition of, 271, 292
pointers to, 339-341
restrictions on, 292-293
static, 310-317
clear( ), 791
clearerr( ), 696
header, 14
header, 744
clock( ), 745
clock_t type, 744
CLOCKS_PER_SEC, 744, 745
clog, 514
close( ), 544
header, 734
COBOL, 5, 7, 8
Code
block, 7-8, 18-19
compartmentalization of, 6,
7
Comma operator, 50
overloading, 416-418
Command line arguments,
144-147
Comments
in C, 250-251
in C++, 251-252, 262
Comp type, 628, 808
compare( ), 688
Compilation
conditional, 242-246
separate, 12, 25
Compilers
compiling C programs with
C++, 12
working with older C++, 270
complex class, 894-897
functions defined for, table
of, 896-897
header, 894
Compound data types, 162
Compound statements, 58, 88
Conditional expression, 58,
66-67

Conglomerate data type, 162
conio
...
CPP file extension, 12
header, 763, 766
header, 718, 770
header, 188, 214-215,
696, 715
header, 59, 754, 758
header, 30>9, 767
header, 94, 720
ctime( ), 745
header, 744
ctype
...
), 51, 165, 175,
178, 272, 293, 346
double data type, 14, 15
Dynamic allocation, 129-131
functions for, 130-131,
754-755
operators for, 349-359, 754
dynamic_cast, 580-588

E
Early binding, 460
EDOM, 734
#elif directive, 243, 244-245, 248
else, 59
#else directive, 243-245
empty( ), 642, 651
Encapsulation, 258, 265
class as basic unit of, 290
and global variables, 315
how to achieve, 271, 293
end, 790
end( ), 631, 632, 633, 637,
645-647
#endif, 243-245
enum keyword, 180
Enumerations, 162, 180-183
EOF macro, 189, 215, 696
eof( ), 555-557, 565, 622, 791-792
eofbit, 790, 799

equal( )
algorithm, 838
member function, 866
equal_range( ) algorithm, 838
ERANGE, 734, 768, 769
erase( ), 630, 632, 633, 637,
684-685
errno, 696, 734, 768, 769
errno
...
See XOR
exit( ), 85-86, 150, 492, 762
EXIT_FAILURE, 263, 758, 762
EXIT_SUCCESS, 263, 758, 762
exp( ), 736
explicit specifier, 612-613
export keyword, 487
Expression(s), 53-56
conditional, 58, 66-67
definition of, 14, 53
evaluation order, 53, 65
function calls used in, 64-65,
149-150
parser, 960-993
pointer, 116-120
production rules of, 962-963
statements, 58, 88
tokens, 965
type conversion in, 53-54

extern storage class specifier,
25-27, 615
Extractors, creating, 528,
534-537

F
fabs( ), 737
facet class, 927
fail( ), 565, 792
failbit, 563, 565, 790, 799
failed( ), 868
failure class, 790
false, 39, 58-59, 266
fclose( ), 150, 217, 218-220, 697
feof( ), 220-222, 697
ferror( ), 224-226, 697-698
fflush( ), 227, 698
fgetc( ), 218, 698
fgetpos( ), 698-699
fgets( ), 192, 222, 233, 699
File(s), C
in C I/O system, 213-214
closing, 216
control structure, 213
erasing, 226-227
opening, 215-217, 699-701
pointer, 215, 216
File(s), C++
closing, 544
get pointer, 559, 563
opening, 542-544
put pointer, 559, 563
reading and writing
unformatted and binary,
547-555
reading and writing text,
545-547
FILE data type, 213, 215, 696
File position indicator, 213,
698-699, 704
resetting, 223-224, 711
setting, 229-231
_ _FILE_ _ predefined macro,
248, 250
filebuf class, 542, 786
fill( )
algorithm, 839
member function, 523-524,
792

999

1000

C++: The Complete Reference

fill_n( ) algorithm, 839
find( )
algorithm, 839
member function, 631, 655,
658, 686-688
find_end( ) algorithm, 839
find_first_of( ) algorithm,
839-840
find_if( ) alogrithm, 840
fixed format flag, 516
flags( ), 520-522, 793
float data type, 14, 15
floatfield format flag, 516
Floating-point constants, 32
floor( ), 737
flush( ), 558-559, 793
fmod( ), 737
fmtflags enumeration, 515, 787
fopen( ), 215-217, 218-220,
699-701
FOPEN_MAX macro, 215, 217
for loop, 6, 70-77
declaring variable within, 81
general form of, 70-71
infinite, 76
variations of, 72-76
with no body, 77
for_each( ) algorithm, 840
ForIter, 628, 808
Formal parameters
...
See Arguments,
function
conversion, creating, 605-609
formal parameters of
...
See Friend functions
general form of, 138
general-purpose, 159
generic
...
See Inline functions
inline code versus, 35-36
main( )
...
See Member
functions
objects
...
See Virtual
functions
void, 150, 152-153
Function objects, 628-629,
671-678, 868-874
built-in, list of, 628, 671, 869
using binders with, 676-678,
870-871
creating, 674-676, 869
Function overloading, 274-277,
362-373
and ambiguity, 380-384
and constructor functions,
364-372
and function pointers,
372-373
versus default arguments,
378-380
header, 628-629,
671, 868
fwide( ), 775
fwrite( ), 179, 227-229, 705

G
gcount( ), 552-553, 794
generate( ) algorithm, 840-841
generate_n( ) algorithm,
840-841
Generated function, 464
Generic class, 474-486
creating safe array with,
479-482
default arguments and,
using, 483-485
explicit specialization of,
485-486
general form of, 475
using non-type arguments
in, 481-482
typeid and, 578-580
Generic function(s), 462-474
applying, 470-474
explicitly overloading,
465-467
general forms of, 462, 464
restrictions, 469-470
get( ), 548-549, 622-623, 794-795

Index

member function of
auto_ptr, 925-926
overloaded forms of, 553
Get pointer, 559, 800, 804
getc( ), 218-220, 705
getch( ), 190-191, 193
getchar( ), 189-191, 192, 193,
205, 706
getche( ), 190-191, 193
getenv( ), 762
getline( ), 553-555, 795-796
gets( ), 143, 144, 192-193, 205,
233, 706
gmtime( ), 746
good( ), 565, 796
goodbit, 563, 565, 790, 799
goto statement, 6, 83, 138
greater( ) function object,
676-678
gslice class, 913, 915-916
gslice_array class, 916

H
Headers and header files, 157,
242, 261, 268-269, 605
Heap, 130, 754
hex format flag, 516
Hexadecimal constants, 32-33
Hierarchical classifications, 420
Hoare, C
...
R
...
unsigned, 16
size of, 14
internal format flag, 515
int_type data type, 557
invalid_argument exception,
924
I/O, C-style, 188
functions, 696-718, 775, 776
I/O, C console, 188-209

basic functions for, table of,
193
and characters, 189-191
formatted, 195-209
and strings, 191-194
I/O, C file, 212-235
common functions for, table
of, 214
connection with console
I/O, 234
files in, 212, 213-214 See also
File(s), C
formatted, with fprintf( )
and fscanf( ), 231-232
random-access, 229-231
reading and writing a
character in, 218
reading and writing blocks
of data in, 227-229
streams in, 212-214 See also
Streams
and strings, 222-223
I/O, C++
array-based
...

character-oriented,
547-548
formatted, 515-528
functions, 791-805
headers, 786-787
manipulators
...
modern, 512
operators
...
h header file, 512
is_open( ), 544
isalnum( ), 720
isalpha( ), 720
iscntrl( ), 721
isdigit( ), 721
isgraph( ), 721
islower( ), 721-722
isprint( ), 722
ispunct( ), 722
isspace( ), 722
istream class, 514, 534, 616, 785
header, 786
istream_iterator class, 864-865
istream_type type, 865, 866
istreambuf_iterator class, 865
istringstream class, 786
istringstream( ), 802-803
istrstream class, 616, 618, 619
isupper( ), 723
iswctype( ), 772-774
isxdigit( ), 723
Iteration statements, 58, 70-81
declaring variables within,
81
Iterator(s), 627, 631, 635-637,
808, 858-868
functions, 868

predefined, 860-868
iterator
class, 859
type, 627, 637, 859
header, 858, 859
iterator_category type, 859
iterator_traits class, 860
iter_swap( ) algorithm, 841

J
jmp_buf type, 763
Jump statements, 58, 68, 82-87

K
kbnit( ), 84
Kernighan, Brian, 4
Keywords
C, 6, 8
C++, table of, 287-288
extended, common, 10
Standard C, table of, 9

L
Label
identifier for goto statement,
83
statements, 58, 67
labs( ), 762
Language(s), computer
block-structured, 6, 139
high-level, 4-6
middle-level, C as, 4-6
programmer's, C as, 8-9
structured, C as, 6-8, 257-258
Late binding, 460
LC_ALL, 749
LC_COLLATE, 749
LC_CTYPE, 749
LC_MONETRAY, 749
LC_NUMERIC, 749
LC_TIME, 749
lconv structure, 746-747
ldexp( ), 738
ldiv( ), 758, 763
ldiv_t structure, 758, 761-762,
763

left format flag, 515
length_error exception, 924
less( ) function object, 628, 629,
654
lexicographical_compare( )
algorithm, 842
Library
class, 10, 11, 460, 782-783,
894
standard function, 10-12,
694-695
standard template
...
h header file, 744
localeconv( ), 746-748
Localization
C functions for, 744, 746-747,
748-750
class library, 927
localtime( ), 748
log( ), 738
log10( ), 738-739
logic_error class, 924
Logical operators, 39-42
truth table for, 40
long modifier, 15-16
LONG_MAX, 768
LONG_MIN, 768
longjmp( ), 509, 763, 766
Loops
do-while, 6, 79-81
for
...
h header file, 734
max( ) algorithm, 843
max_element( ) algorithm, 843
MB_CUR_MAX, 770
mblen( ), 763-764
mbstate_t type, 779
mbstowcs( ), 764
mbtowc( ), 764
mem_fun( ) adaptor, 873
mem_fun_ref( ) adaptor, 874
mem_fun_ref_t class, 874
mem_fun_t class, 873-874
mem_fun1( ) adaptor, 873
mem_fun1_ref( ) adaptor, 874

mem_fun1_ref_t class, 874
mem_fun1_t class, 873-874
Member functions, 271, 272, 292
const, 609-611
and scope resolution
operator, 272
static, 315-317
and the this pointer, 334-336
volatile, 611
within class, defining,
306-307, 309
Member variables, 271, 272, 292,
293
static, 310-315
memchr( ), 723
memcmp( ), 723-724
memcpy( ), 724
memmove( ), 724
header, 875, 924,
927
memset( ), 725
merge( ), 643, 644, 649-651
algorithm, 843-844
Microsoft's Visual C++, 191
min( ) algorithm, 844
min_element( ) algorithm, 844
mismatch( ) algorithm, 844-845
mktime( ), 748
modf( ), 739
Modula-2, 5, 6, 7, 8
multimap container, 629, 630,
654, 809, 820-822
member functions, table of,
821-822
multiset container, 629, 630,
809, 823-825
member functions, table of,
823-825
mutable keyword, 25, 610-611

N
name( ), 570, 571
Namespace, 29, 261, 269,
594-605
unnamed, 600-601
namespace statement, 267, 270,
594-595
NDEBUG, 759

negate( ) function object, 671,
672-673
Negators, 629, 678, 871-872
new dynamic allocation
operator, 349-359, 754, 922
and allocating arrays,
352-353
and allocating objects,
353-358
and initializing memory,
351-352
overloading, 400-405,
408-409
overloading for arrays,
405-408
placement form of, 359
header, 350, 358
next_permutation( ) algorithm,
845
NOT
! logical operator, 40, 41
~ bitwise operator, 42, 43,
46-47
not1( ) negator, 678, 871
not2( ) negator, 678, 871
nothrow option for new, 358
overloading, 408-409
nothrow_t data type, 409
npos constant, 681, 859
nth_element( ) algorithm, 845
Null
definition of, 94
statement, 88
NULL macro, 215, 758
Numeric
classes, 894-920
constants, 32
numeric_limits class, 927

O
Object(s)
allocating, 353-358
arrays of, 328-331, 356,
366-368
assignment, 324-325
base class pointers to
derived class, 336-338
base class references to
dervied class, 348

1003

1004

C++: The Complete Reference

creating, 271
definition of, 258, 290
factory, 576
function
...
See Arrow operator
(->)
assignment, 34-35
bitwise, 42-47
casting, 55, 580-591
comma, 50
compile-time, 49-50
dot, 51, 165, 175, 178, 272,
293, 346
dynamic allocation, 349-359
pointer, 47-49, 115-116, 349
pointer-to-member (
...
See
Function overloading
Overloading operators
...
h header file, 718
_ _STDC_ _ predefined macro,
250
stderr standard stream, 232-233
header, 922,
923-924
stdin standard stream, 232-234
stdio
...
h header file, 59, 755, 758
stdout standard stream, 232-234
Stepanov, Alexander, 256
Storage class specifiers, 25-31
str( ), 621, 802
strcat( ), 94-95, 681, 725
strchr( ), 94-95, 725
strcmp( ), 74, 94-95, 726
strcoll( ), 726
strcpy( ), 94-95, 680, 681, 727
strcspn( ), 727
Stream(s)
binary, 212-213
C++, 512
for C++ array-based I/O,
616-620
for C++ file I/O, 542
classes, 512-514, 542, 616
flushing, 227
predefined (C++), 514-515
standard C, 232-234, 235
text, 212-213
streambuf class, 514, 786
header, 787
streambuf_type, 868
streamoff data type, 789
streampos data type, 789
streamsize data type, 550, 789
strerror( ), 727
strftime( ), 749-750
Stride, 914
String(s)
as arrays, 90, 94-95
arrays of, 100-101
class, creation of custom,
930-957

Index

classes, Standard C++, 94,
626, 679-691, 878-892
in console I/O, 192-194
constant, 33, 94, 125-126
in file I/O, 222-223
limitations of
null-terminated, 679-680
manipulation functions,
94-95
substring subtraction from,
930, 941-942
table, 125
string class, 94, 626, 679-691,
878, 880
and containers, 689-691
dynamic aspect of, 683
member functions, 683-688
operators defined for, 681
header, 680
string
...
See Generic
function(s)
template keyword, 462
template< > syntax, 467, 485
Templates, 462-487
advantages to using, 487
definition of, 462
See also Generic class;
Generic function(s)
terminate( ), 491, 505-507
terminate_handler type, 506,
923
Ternary operator (?), 47, 63-66
this pointer, 315, 334-336, 388,
393
Thompson, Ken, 4
throw statement, 491-492,
504-505
throw( ) clause, 502, 504
Time and date functions,
744-751
Time delay loops, 77
time
...
See Data types

U
ULONG_MAX, 769
unary_function class, 674,
869-870
unary_negate class, 871, 872
uncaught_exception( ), 507
#undef directive, 246-247
underflow_error exception, 924
unexpected( ), 502, 503, 505-506,
922
unexpected_handler type, 506,
923
ungetc( ), 717
union keyword, 177
Unions, 162, 176-180, 184
anonymous, 180, 297
and classes, 295-297
for nonstandard type
conversions, 178-180
unique( ) algorithm, 854-855

1007

1008

C++: The Complete Reference

unique_copy( ) algorithm,
854-855
unitbuf format flag, 516
Unix, 4
UnPred type, 628, 808
unsetf( ), 517-518, 804
unsigned modifier, 14-16
upper_bound( ) algorithm, 855
uppercase format flag, 516
using statement, 261, 270, 436,
439, 598-600
header, 628, 927

V
va_arg( ), 769-770
va_end( ), 769-770
va_list type, 718, 770
va_start( ), 769-770
valarray class, 898-916
member functions, table of,
899-903
nonmember operator
functions defined for,
table of, 904-909
transcendental functions
defined for, table of,
910-911
header, 898
Variables, 17-23
access modifiers for, 23-25
automatic, 17
declaration vs
...
See Member
variables
placement in memory, 133
pointer, 47-49, 115

reference
...
h header file, 772
WCHAR_MAX, 772
WCHAR_MIN, 772
wchar_t data type, 14, 32, 515,
772
wcstombs( ), 770
wctomb( ), 770
wctrans( ), 774-775
wctrans_t type, 772
wctype
Title: C PLUS PLUS PROGRAMING LANGUAGE
Description: this is notes for c ++ programing language in computer application