My Basket
You have nothing in your shopping cart yet.
Title: FULL CONCEPT FOR C++ PROGRAMMING LANGUAGE
Description: Fundamentals 1
Development and Properties of C++ 2
Object-Oriented Programming 4
Developing a C++ Program 6
A Beginner’s C++ Program 8
Structure of Simple C++ Programs 10
Exercises 12
Solutions 14
Chapter 2 Fundamental Types, Constants, and Variables 15
Fundamental Types 16
Constants 22
Escape Sequences 26
Names 28
Variables 30
The Keywords const and volatile 32
Exercises 34
Solutions 36
contents
Chapter 3 Using Functions and Classes 39
Declaring Functions 40
Function Calls 42
Type void for Functions 44
Header Files 46
Standard Header Files 48
Using Standard Classes 50
Exercises 52
Solutions 54
Chapter 4 Input and Output with Streams 57
Streams 58
Formatting and Manipulators 60
Formatted Output of Integers 62
Formatted Output of Floating-Point Numbers 64
Output in Fields 66
Output of Characters, Strings, and Boolean Values 68
Formatted Input 70
Formatted Input of Numbers 72
Unformatted Input/Output 74
Exercises 76
Solutions 78
Chapter 5 Operators for Fundamental Types 81
Binary Arithmetic Operators 82
Unary Arithmetic Operators 84
Assignments 86
Relational Operators 88
Logical Operators 90
Exercises 92
Solutions 94
Chapter 6 Control Flow 95
The while Statement 96
The for Statement 98
The do-while Statement 102
Selections with if-else 104
Else-if Chains 106
Conditional Expressions 108
Selecting with switch 110
Jumps with break, continue, and goto 112
Exercises 114
Solutions 116
xii ■ CONTENTS
Chapter 7 Symbolic Constants and Macros 119
Macros 120
Macros with Parameters 122
Working with the #define Directive 124
Conditional Inclusion 126
Standard Macros for Character Manipulation 128
Redirecting Standard Input and Output 130
Exercises 132
Solutions 134
Chapter 8 Converting Arithmetic Types 139
Implicit Type Conversions 140
Performing Usual Arithmetic Type Conversions 142
Implicit Type Conversions in Assignments 144
More Type Conversions 146
Exercises 148
Solutions 150
Chapter 9 The Standard Class string 153
Defining and Assigning Strings 154
Concatenating Strings 156
Comparing Strings 158
Inserting and Erasing in Strings 160
Searching and Replacing in Strings 162
Accessing Characters in Strings 164
Exercises 166
Solutions 168
Chapter 10 Functions 171
Significance of Functions in C++ 172
Defining Functions 174
Return Value of Functions 176
Passing Arguments 178
Inline Functions 180
Default Arguments 182
Overloading Functions 184
Recursive Functions 186
Exercises 188
Solutions 191
Chapter 11 Storage Classes and Namespaces 197
Storage Classes of Objects 198
The Storage Class extern 200
CONTENTS ■ xiii
The Storage Class static 202
The Specifiers auto and register 204
The Storage Classes of Functions 206
Namespaces 208
The Keyword using 210
Exercises 212
Solutions 216
Chapter 12 References and Pointers 221
Defining References 222
References as Parameters 224
References as Return Value 226
Expressions with Reference Type 228
Defining Pointers 230
The Indirection Operator 232
Pointers as Parameters 234
Exercises 236
Solutions 238
Chapter 13 Defining Classes 243
The Class Concept 244
Defining Classes 246
Defining Methods 248
Defining Objects 250
Using Objects 252
Pointers to Objects 254
Structs 256
Unions 258
Exercise 260
Solution 262
Chapter 14 Methods 265
Constructors 266
Constructor Calls 268
Destructors 270
Inline Methods 272
Access Methods 274
const Objects and Methods 276
Standard Methods 278
this Pointer 280
Passing Objects as Arguments 282
Returning Objects 284
Exercises 286
Solutions 290
xiv ■ CONTENTS
Chapter 15 Member Objects and Static Members 297
Member Objects 298
Member Initializers 300
Constant Member Objects 302
Static Data Members 304
Accessing Static Data Members 306
Enumeration 308
Exercises 310
Solutions 314
Chapter 16 Arrays 321
Defining Arrays 322
Initializing Arrays 324
Arrays 326
Class Arrays 328
Multidimensional Arrays 330
Member Arrays 332
Exercises 334
Solutions 338
Chapter 17 Arrays and Pointers 349
Arrays and Pointers (1) 350
Arrays and Pointers (2) 352
Pointer Arithmetic 354
Arrays as Arguments 356
Pointer Versions of Functions 358
Read-Only Pointers 360
Returning Pointers 362
Arrays of Pointers 364
Command Line Arguments 366
Exercises 368
Solutions 372
Chapter 18 Fundamentals of File Input and Output 379
Files 380
File Streams 382
Creating File Streams 384
Open Modes 386
Closing Files 388
Reading and Writing Blocks 390
Object Persistence 392
Exercises 394
Solutions 398
CONTENTS ■ xv
Chapter 19 Overloading Operators 411
Generals 412
Operator Functions (1) 414
Operator Functions (2) 416
Using Overloaded Operators 418
Global Operator Functions 420
Friend Functions 422
Friend Classes 424
Overloading Subscript Operators 426
Overloading Shift-Operators for I/O 428
Exercises 430
Solutions 432
Chapter 20 Type Conversion for Classes 441
Conversion Constructors 442
Conversion Functions 444
Ambiguities of Type Conversions 446
Exercise 448
Solution 450
Chapter 21 Dynamic Memory Allocation 453
The Operator new 454
The Operator delete 456
Dynamic Storage Allocation for Classes 458
Dynamic Storage Allocation for Arrays 460
Application: Linked Lists 462
Representing a Linked List 464
Exercises 466
Solutions 468
Chapter 22 Dynamic Members 477
Members of Varying Length 478
Classes with a Dynamic Member 480
Creating and Destroying Objects 482
Implementing Methods 484
Copy Constructor 486
Assignment 488
Exercises 490
Solutions 492
Chapter 23 Inheritance 499
Concept of Inheritance 500
Derived Classes 502
xvi ■ CONTENTS
Members of Derived Classes 504
Member Access 506
Redefining Members 508
Constructing and Destroying Derived Classes 510
Objects of Derived Classes 512
Protected Members 514
Exercises 516
Solutions 520
Chapter 24 Type Conversion in Class Hierarchies 529
Converting to Base Classes 530
Type Conversions and Assignments 532
Converting References and Pointers 534
Explicit Type Conversions 536
Exercises 538
Solutions 540
Chapter 25 Polymorphism 543
Concept of Polymorphism 544
Virtual Methods 546
Destroying Dynamically Allocated Objects 548
Virtual Method Table 550
Dynamic Casts 552
Exercises 554
Solutions 558
Chapter 26 Abstract Classes 565
Pure Virtual Methods 566
Abstract and Concrete Classes 568
Pointers and References to Abstract Classes 570
Virtual Assignment 572
Application: Inhomogeneous Lists 574
Implementing an Inhomogeneous List 576
Exercises 578
Solutions 580
Chapter 27 Multiple Inheritance 587
Multiply-Derived Classes 588
Multiple Indirect Base Classes 590
Virtual Base Classes 592
Constructor Calls 594
Initializing Virtual Base Classes 596
Exercises 598
Solutions 602
CONTENTS ■ xvii
Chapter 28 Exception Handling 607
Traditional Error Handling 608
Exception Handling 610
Exception Handlers 612
Throwing and Catching Exceptions 614
Nesting Exception Handling 616
Defining Your Own Error Classes 618
Standard Exception Classes 620
Exercises 622
Solutions 626
Chapter 29 More About Files 637
Opening a File for Random Access 638
Positioning for Random Access 640
File State 644
Exception Handling for Files 646
Persistence of Polymorphic Objects 648
Application: Index Files 652
Implementing an Index File 654
Exercises 656
Solutions 660
Chapter 30 More About Pointers 681
Pointer to Pointers 682
Variable Number of Arguments 684
Pointers to Functions 688
Complex Declarations 690
Defining Typenames 692
Application: Dynamic Matrices 694
Exercises 696
Solutions 698
Chapter 31 Manipulating Bits 705
Bitwise Operators 706
Bitwise Shift Operators 708
Bit Masks 710
Using Bit Masks 712
Bit-Fields 714
Exercises 716
Solutions 718
Chapter 32 Templates 721
Function and Class Templates 722
Defining Templates 724
xviii ■ CONTENTS
Template Instantiation 726
Template Parameters 728
Template Arguments 730
Specialization 732
Default Arguments of Templates 734
Explicit Instantiation 736
Exercises 738
Solutions 742
Chapter 33 Containers 749
Container Types 750
Sequences 752
Iterators 754
Declaring Sequences 756
Inserting in Sequences 758
Accessing Objects 760
Length and Capacity 762
Deleting in Sequences 764
List Operations 766
Associative Containers 768
Sets and Multisets 770
Maps and Multimaps 772
Bitsets 774
Exercise 778
Solution 780
Document Preview
Extracts from the notes are below, to see the PDF you'll receive please use the links above
A Complete Guide to
Programming in C++
Ulla Kirch-Prinz
Peter Prinz
JONES AND BARTLETT PUBLISHERS
A Complete Guide to
Programming in C++
Ulla Kirch-Prinz
Peter Prinz
World Headquarters
Jones and Bartlett Publishers
40 Tall Pine Drive
Sudbury, MA 01776
978-443-5000
info@jbpub
...
jbpub
...
All rights reserved
...
Cover Image: Stones on shore-line and yellow leaf, Bjorkliden, Sweden, by Peter Lilja
Library of Congress Cataloging-in-Publication Data
Prinz, Peter
...
English]
A complete guide to programming in C++ / Peter Prinz, Ulla Kirch-Prinz; translated by Ian Travis
...
cm
...
C++ (Computer program language) I
...
II
...
QA76
...
C153 P73713 2001
005
...
Jones, Jr
...
P
...
Hauck
V
...
, Design and Production: Anne Spencer
V
...
, Manufacturing and Inventory Control: Therese Bräuer
Editor-in-Chief: Michael Stranz
Development and Product Manager: Amy Rose
Marketing Manager: Nathan Schultz
Production Assistant: Tara McCormick
Cover Design: Night & Day Design
Composition: Northeast Compositors
Text Design: Mary McKeon
Printing and Binding: Courier Westford
Cover printing: John Pow Company, Inc
...
11 on a Macintosh G4
...
The first printing was printed on 50 lb
...
Printed in the United States of America
05 04 03 02 01
10 9 8 7 6 5 4 3 2 1
Dedicated to our children, Vivi and Jeany
This page intentionally left blank
preface
This book was written for readers interested in learning the C++ programming
language from scratch, and for both novice and advanced C++ programmers
wishing to enhance their knowledge of C++
...
The C++ language definition is based on the American National Standards Institute ANSI Standard X3J16
...
The C++ programming language is thus platform-independent
in the main with a majority of C++ compilers providing ANSI support
...
Visit the Jones and Bartlett web site
at www
...
com for a listing of compilers available for this text
...
The order in which these
elements are discussed reflects our goal of helping the reader to create useful
programs at every step of the way
...
This type of visual representation offered by each
spread will provide students and professionals with an unmatched guide throughout the
text
...
In addition, filter programs and case studies introduce the reader to a
wide range of application scenarios
...
Thus, each chapter includes exercises followed by sample solutions, allowing the reader to test and enhance his or her performance and understanding
of C++
...
In order to test and expand your acquired knowledge, you can download sample programs and solutions to the exercises at:
http://completecpp
...
com
Content Organization
Chapter 1 gives a thorough description of the fundamental characteristics of the objectoriented C++ programming language
...
Many examples are provided to
help enforce these steps and to demonstrate the basic structure of a C++ program
...
Integral types and constants, fundamental types, and Boolean constants
are just a few of the topics discussed
...
This chapter also
teaches students to use standard classes, including standard header files
...
Chapter 4 explains the use of streams for input and output, with a focus on formatting
techniques
...
Chapter 5 introduces operators needed for calculations and selections
...
Chapter 6 describes the statements needed to control the flow of a program
...
Chapter 7 provides a thorough introduction to the definition of symbolic constants
and macros, illustrating their significance and use
...
Chapter 8 introduces implicit type conversions, which are performed in C++ whenever different arithmetic types occur in expressions
...
PREFACE
■
vii
Chapter 9 takes an in-depth look at the standard class string, which is used to represent strings
...
These include inserting and erasing, searching and replacing, comparing, and concatenating strings
...
The basic rules are covered,
as are passing arguments, the definition of inline functions, overloading functions and
default arguments, and the principle of recursion
...
Object lifetime and scope are discussed, along with global, static, and auto objects
...
Chapter 12 explains how to define references and pointers and how to use them as
parameters and/or return values of functions
...
Chapter 13 provides a complete description of how classes are defined and how
instances of classes, or objects, are used
...
Chapter 14 describes how constructors and destructors are defined to create and
destroy objects
...
Furthermore, the chapter explains the pointer this, which is available for all methods, and what you need to pay attention to when passing objects as arguments or returning objects
...
In addition, this chapter describes constant members and enumerated types
...
Of particular interest are one-dimensional and multidimensional arrays, C strings, and class arrays
...
This includes
pointer arithmetic, pointer versions of functions, pointers as return values and read-only
pointers, and pointer arrays
...
Chapter 18 explains sequential file access using file streams
...
Chapter 19 provides a complete description of the various uses of overloaded operators
...
In addition,
the concept of friend functions, which is introduced in this context, is particularly
important for overloading operators
...
Chapter 20 discusses how implicit type conversion occurs in C++ when an expression
cannot be compiled directly but can be compiled after applying a conversion rule
...
Finally, the chapter discusses
ambiguity that occurs due to type conversion and how to avoid it
...
Dynamic memory allocation is an important factor in many C++ programs, and the following chapters contain several case studies to
help students review the subject
...
These include your own copy constructor definition and overloading
the assignment operator
...
Chapter 23 provides a thorough description of how derived classes can be constructed
from existing classes by inheritance
...
Chapter 24 discusses implicit type conversion within class hierarchies, which occurs
in the context of assignments and function calls
...
Chapter 25 gives a complete explanation of how to develop and manage polymorphic
classes
...
Chapter 26 describes how defining pure virtual methods can create abstract classes
and how you can use abstract classes at a polymorphic interface for derived classes
...
Chapter 27 describes how new classes are created by multiple inheritance and
explains their uses
...
Chapter 28 explains how a C++ program uses error-handling techniques to resolve
error conditions
...
In
addition, the use of standard exception classes is discussed
...
Exception handling for files is discussed as well
...
The applications introduced in this chapter include simple index files and hash
tables
...
These
include pointers to pointers, functions with a variable number of arguments, and pointers
to functions
...
Chapter 31 describes bitwise operators and how to use bit masks
...
Finally, the definition of bit-fields is introduced
...
In addition,
special options, such as default arguments, specialization, and explicit instantiation, are
PREFACE
■
ix
discussed
...
Thus, templates are a powerful tool for
automating program code generation
...
These include sequences, such as lists and
double ended queues; container adapters, such as stacks, queues, and priority queues;
associative containers, such as sets and maps; and bitsets
...
Additional Features
Chapter Goals A concise chapter introduction, which contains a description of the
chapter’s contents, is presented at the beginning of each chapter
...
Chapter Exercises Each chapter contains exercises, including programming problems,
designed to test students’ knowledge and understanding of the main ideas
...
Solutions are included to allow
students to check their work immediately and correct any possible mistakes
...
Notes This feature provides students with helpful tips and information useful to learning
C++
...
Hints These are informative suggestions for easier programming
...
Acknowledgements
Our thanks go out to everyone who helped produce this book, particularly to
Ian Travis, for his valuable contributions to the development of this book
...
Michael Stranz and Amy Rose at Jones and Bartlett Publishers, who managed the publishing agreement and the production process so smoothly
...
In addition, you will be introduced
to the steps necessary for creating a fully functional C++ program
...
1
2
■
CHAPTER 1
FUNDAMENTALS
■
DEVELOPMENT AND PROPERTIES OF C++
Characteristics
C++
C
-universal
-efficient
-close to the machine
-portable
OOP
-data abstraction
-data hiding
-inheritance
-polymorphism
Extensions
-exception handling
-templates
DEVELOPMENT AND PROPERTIES OF C++
■
3
ᮀ Historical Perspective
The C++ programming language was created by Bjarne Stroustrup and his team at Bell
Laboratories (AT&T, USA) to help implement simulation projects in an object-oriented and efficient way
...
As the name C++ implies, C++ was derived from the C
programming language: ++ is the increment operator in C
...
The aim was to have as many
compiler vendors and software developers as possible agree on a unified description of
the language in order to avoid the confusion caused by a variety of dialects
...
ᮀ Characteristics of C++
C++ is not a purely object-oriented language but a hybrid that contains the functionality
of the C programming language
...
The large quantities of existing C source code can also be used in C++ programs
...
Various language elements were added to C++, such as references, templates, and exception handling
...
4
■
CHAPTER 1
FUNDAMENTALS
■
OBJECT-ORIENTED PROGRAMMING
Traditional concept
function1
data1
function2
data2
function3
Object-oriented concept
object1
object2
Properties
Properties
Capacities
Capacities
OBJECT-ORIENTED PROGRAMMING
■
5
ᮀ Traditional Procedural Programming
In traditional, procedural programming, data and functions (subroutines, procedures) are
kept separate from the data they process
...
g
...
Both of these points can lead to errors and neither support low program maintenance
requirements
...
A program designed to maintain bank
accounts would work with data such as balances, credit limits, transfers, interest calculations, and so on
...
OOP objects combine data (properties) and functions (capacities)
...
Objects communicate by sending each other “messages,” which in turn activate another object’s capacities
...
More
specifically, an object can reject erroneous access attempts
easy re-use: objects maintain themselves and can therefore be used as building
blocks for other programs
low maintenance requirement: an object type can modify its own internal data
representation without requiring changes to the application
...
First, a text editor is used to save the C++ program in a text file
...
In larger projects the programmer will normally use modular programming
...
2
...
If everything works as
planned, an object file made up of machine code is created
...
3
...
These further modules contain functions from standard libraries or
parts of the program that have been compiled previously
...
Although
the file extension depends on the compiler you use, the most commonly found file extensions are
...
cc
...
Header files are text files containing information needed by various source files, for example, type definitions or declarations of variables and functions
...
h, but they may not have any file extension
...
Modern compilers normally offer an integrated software development environment, which
combines the steps mentioned previously into a single task
...
Moreover, additional tools, such as a debugger, can be launched
...
Additional error
messages may be shown if the compiler attempts to continue despite having found an error
...
In addition to error messages, the compiler will also issue warnings
...
8
■
CHAPTER 1
FUNDAMENTALS
■
A BEGINNER’S C++ PROGRAM
Sample program
#include
using namespace std; int main() { cout << "Enjoy yourself with C++!" return 0; } << endl; Screen output Enjoy yourself with C++! Structure of function main() Function name Type of function Beginning of function int main() {
...
What the program does What the program does (satements) (statements)
...
End of function } Function block A BEGINNER’S C++ PROGRAM ■ 9 A C++ program is made up of objects with their accompanying member functions and global functions, which do not belong to any single particular class
...
You can create functions yourself or use ready-made functions from the standard library
...
The short programming example on the opposite page demonstrates two of the most important elements of a C++ program
...
The first line begins with the number symbol, #, which indicates that the line is intended for the preprocessor
...
You can type #include to have the preprocessor copy the quoted file to this position in the source code
...
The header file iostream comprises conventions for input and output streams
...
Predefined names in C++ are to be found in the std (standard) namespace
...
Program execution begins with the first instruction in function main(), and this is why each C++ program must have a main function
...
Apart from the fact that the name cannot be changed, this function’s structure is not different from that of any other C++ function
...
The first statement cout << "Enjoy yourself with C++!" << endl; outputs the text string Enjoy yourself with C++! on the screen
...
The two less-than symbols, <<, indicate that characters are being “pushed” to the output stream
...
The statement return 0; terminates the function main() and also the program, returning a value of 0 as an exit code to the calling program
...
Note that statements are followed by a semicolon
...
10 ■ CHAPTER 1 FUNDAMENTALS ■ STRUCTURE OF SIMPLE C++ PROGRAMS A C++ program with several functions /****************************************************** A program with some functions and comments ******************************************************/ #include using namespace std; void line(), message(); // Prototypes int main() { cout << "Hello! The program starts in main()
...
" << endl; return 0; } void line() // To draw a line
...
{ cout << "In function message()
...
----------------------------------In function message()
...
STRUCTURE OF SIMPLE C++ PROGRAMS ■ 11 The example on the opposite page shows the structure of a C++ program containing multiple functions
...
For example, you could define the function message() first, followed by the function line(), and finally the main() function
...
In other words, main() calls functions that have yet to be defined
...
This example also introduces comments
...
*/ or starting with // are interpreted as comments
...
Comments that cover several lines are useful when troubleshooting, as you can use them to mask complete sections of your program
...
As to the layout of source files, the compiler parses each source file sequentially, breaking the contents down into tokens, such as function names and operators
...
The order of the source code is important but it is not important to adhere to a specific layout, such as organizing your code in rows and columns
...
" endl;} << might be difficult to read, but it is a correct definition of the function message()
...
The number sign, #, at the beginning of a line can be preceded only by a space or a tab character
...
In addition, make generous use of comments
...
Exercise 2 The following program contains several errors: */ Now you should not forget your glasses // #include int main { cout << "If this text", cout >> " appears on your display, "; cout << " endl;" cout << 'you can pat yourself on ' << " the back!" << endl
...
Exercise 3 What does the C++ program on the opposite page output on screen? ■ 13 ■ CHAPTER 1 ■ solutions 14 FUNDAMENTALS SOLUTIONS Exercise 1 // Let's go ! #include using namespace std; int main() { cout << cout << cout << cout << " " " " Oh what " << a happy day! Oh yes, " << what a happy endl; " << endl; endl; day! " << endl; return 0; } Exercise 2 The corrected places are underlined
...
15 16 ■ CHAPTER 2 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES ■ FUNDAMENTAL TYPES Overview* For boolean values bool char For characters wchar_t short For integers int long float For floating-point values double long double * without type void, which will be introduced later
...
Since a computer uses different methods for processing and saving data, the data type must be known
...
the internal representation of the data, and 2
...
A number such as -1000 can be stored in either 2 or 4 bytes
...
Moreover, the memory content, that is the bit sequence being read, must be interpreted correctly as a signed integer
...
) are based
...
C++ uses the bool type to represent boolean values
...
ᮀ The char and wchar_t Types These types are used for saving character codes
...
The letter A is represented by code 65, for example
...
When displaying characters on screen, the applicable character codes are transmitted and the “receiver,” that is the screen, is responsible for correctly interpreting the codes
...
This 7-bit code contains definitions for 32 control characters (codes 0 – 31) and 96 printable characters (codes 32 – 127)
...
This amount of storage is sufficient for extended character sets, for example, the ANSI character set that contains the ASCII codes and additional characters such as German umlauts
...
Unicode is a 16-bit code also used in Windows NT and containing codes for approximately 35,000 characters in 24 languages
...
4 byte Range of Values (decimal) — to +127 or 0 to 255 128 0 to 255 — to +127 128 — 32768 to +32767 resp
...
0 to 65535 resp
...
int main() { cout << "Range of types int and unsigned int" << endl << endl; cout << "Type Minimum Maximum" << endl << "--------------------------------------------" << endl; cout << "int " << << INT_MIN << " INT_MAX << endl; " cout << "unsigned int " << " 0 << UINT_MAX << endl; " return 0; } FUNDAMENTAL TYPES (CONTINUED) ■ 19 ᮀ Integral Types The types short, int, and long are available for operations with integers
...
The table on the opposite page shows the integer types, which are also referred to as integral types, with their typical storage requirements and ranges of values
...
For 16-bit computers, int is thus equivalent to short, whereas for 32-bit computers int will be equivalent to long
...
This means you can perform calculations with variables belonging to the char or wchar_t types in exactly the same way as with int type variables
...
The range of values is thus –128 to +127 or from 0 to 255, depending on whether the compiler interprets the char type as signed or unsigned
...
The wchar_t type is a further integral type and is normally defined as unsigned short
...
However, integral types can be preceded by the keyword unsigned
...
The keyword unsigned can be used as an abbreviation for unsigned int
...
Since this is merely a convention and not mandatory, the signed keyword is available
...
NOTE In ANSI C++ the size of integer types is not preset
...
The current value ranges are available in the climits header file
...
The program on the opposite page outputs the value of these constants for the int and unsigned int types
...
4E+38 1
...
7E+308 2
...
1E+4932 3
...
The table above makes use of this representation
...
e
...
FUNDAMENTAL TYPES (CONTINUED) ■ 21 ᮀ Floating-Point Types Numbers with a fraction part are indicated by a decimal point in C++ and are referred to as floating-point numbers
...
The following three types are available for calculations involving floating-point numbers: float double long double for simple accuracy for double accuracy for high accuracy The value range and accuracy of a type are derived from the amount of memory allocated and the internal representation of the type
...
This means that “six decimal places” allows a programmer to store two floating-point numbers that differ within the first six decimal places as separate numbers
...
3456 and 12
...
And remember, it is not a question of the position of the decimal point, but merely of the numerical sequence
...
Readers interested in additional material on this subject should refer to the Appendix, which contains a section on the representation of binary numbers on computers for both integers and floating-point numbers
...
For example, sizeof(int)represents a value of 2 or 4 depending on the machine
...
ᮀ Classification The fundamental types in C++ are integer types, floating-point types, and the void type
...
The void type is used for expressions that do not represent a value
...
22 ■ CHAPTER 2 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES ■ CONSTANTS Examples for integral constants Decimal Hexadecimal Type 16 020 0x10 int 255 0377 OXff int 32767 077777 0x7FFF int 32768U 0100000U 0x8000U unsigned int 100000 0303240 0x186A0 int (32 bit-) long (16 bitCPU) 10L 012L 0xAL long 27UL 033UL 0x1bUL unsigned long 2147483648 ✓ Octal 020000000000 0x80000000 unsigned long NOTE In each line of the above table, the same value is presented in a different way
...
// #include using namespace std; int main() { // cout outputs integers as decimal integers: cout << "Value of 0xFF = " << 0xFF << " decimal" << endl; // Output: 255 decimal // The manipulator hex changes output to hexadecimal // format (dec changes to decimal format): cout << "Value of 27 = " << hex << 27 <<" hexadecimal" << endl; // Output: 1b hexadecimal return 0; } CONSTANTS ■ 23 The boolean keywords true and false, a number, a character, or a character sequence (string) are all constants, which are also referred to as a literals
...
Every constant represents a value and thus a type—as does every expression in C++
...
ᮀ Boolean Constants A boolean expression can have two values that are identified by the keywords true and false
...
They can be used, for example, to set flags representing just two states
...
Hexadecimal numbers can be capitalized or noncapitalized
...
If the value of the constant is too large for the int type, a type capable of representing larger values will be applied
...
For example, 12L 12U 12UL and and and 12l 12u 12ul correspond to the type long correspond to the type unsigned int correspond to the type unsigned long 24 ■ CHAPTER 2 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES ■ CONSTANTS (CONTINUED) Examples for floating-point constants 5
...
0
...
0 0
...
OE-2 0
...
00004 0
...
75
...
4E-4 7
...
' Dot 46 '0' Digit 0 48 Terminating null character 0 '\0' Internal representation of a string literal String literal: "Hello!" Stored byte sequence: 'H' 'e' '1' '1' 'o' '!' '\0' CONSTANTS (CONTINUED) ■ 25 ᮀ Floating-Point Constants Floating-point numbers are always represented as decimals, a decimal point being used to distinguish the fraction part from the integer part
...
EXAMPLES: 27
...
8E–2 // Type: double Here, 1
...
8*10–2
...
A decimal point or E (e) must always be used to distinguish floating-point constants from integer constants
...
However, you can add F or f to designate the float type, or add L or l for the long double type
...
Character constants take the type char
...
The constant 'A' thus has a value of 65 in ASCII code
...
A string constant consists of a sequence of characters enclosed in double quotes
...
Thus, a string occupies one byte more in memory than the number of characters it contains
...
The terminating null character \0 is not the same as the number zero and has a different character code than zero
...
The terminating null character \0 is an example of an escape sequence
...
26 ■ CHAPTER 2 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES ■ ESCAPE SEQUENCES Overview Single character Meaning ASCII code (decimal) \a alert (BEL) 7 \b backspace (BS) 8 \t horizontal tab (HT) 9 \n line feed (LF) 10 \v vertical tab (VT) 11 \f form feed (FF) 12 \r carriage return (CR) 13 \" " (double quote) 34 \' ' (single quote) 39 \? ? (question mark) 63 \\ \ (backslash) 92 \0 string terminating character 0 \ooo numerical value of a character ooo (octal!) numerical value of a character hh (hexadecimal!) (up to 3 octal digits) \xhh (hexadecimal digits) Sample program #include using namespace std; int main() { cout << "\nThis is\t a string\n\t\t" " with \"many\" escape sequences!\n"; return 0; } Program output: This is a string with "many" escape sequences! ESCAPE SEQUENCES ■ 27 ᮀ Using Control and Special Characters Nongraphic characters can be expressed by means of escape sequences, for example \t, which represents a tab
...
The sequence \t, for example, depends on the setting for the tab width, which defaults to eight blanks but can be any value
...
The table on the opposite page shows the standard escape sequences, their decimal values, and effects
...
Thus, the letter A (decimal 65) in ASCII code can also be expressed as \101 (three octals) or \x41 (two hexadecimals)
...
The control sequences for screen and printer drivers are, for example, initiated by the ESC character (decimal 27), which can be represented as \33 or \x1b
...
EXAMPLES: '\t' "\tHello\n\tMike!" The characters ', ", and \ have no special significance when preceded by a backslash, i
...
they can be represented as \', \", and \\ respectively
...
This helps to avoid any subsequent numbers being evaluated as part of the escape sequence
...
The sequence of hex numbers automatically terminates with the first character that is not a valid hex number
...
The fact that a string can occupy two lines is another new feature
...
To continue a string in the next line you can also use a backslash \ as the last character in a line, and then press the Enter key to begin a new line, where you can continue typing the string
...
It is thus generally preferable to use the first method, that is, to terminate the string with " and reopen it with "
...
The following rules apply when creating names, which are also known as identifiers: ■ ■ ■ ■ a name contains a series of letters, numbers, or underscore characters ( _ )
...
C++ is case sensitive; that is, upper- and lowercase letters are different
...
The opposite page shows C++ keywords and some examples of valid and invalid names
...
To avoid confusion with these names, avoid use of the underscore at the beginning of a name
...
For this reason names of global objects, such as functions, should be chosen so that the first eight characters are significant
...
The names of some variables tend to be associated with a specific use
...
In the case of software projects, naming conventions will normally apply
...
30 ■ CHAPTER 2 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES ■ VARIABLES Sample program // Definition and use of variables #include using namespace std; int gVar1; int gVar2 = 2; // Global variables, // explicit initialization int main() { char ch('A'); // Local variable being initialized // or: char ch = 'A'; cout << "Value of gVar1: cout << "Value of gVar2: cout << "Character in ch: " << gVar1 " << gVar2 " << ch << endl; << endl; << endl; int sum, number = 3; // Local variables with // and without initialization sum = number + 5; cout << "Value of sum: " << sum << endl; return 0; } ✓ HINT Both strings and all other values of fundamental types can be output with cout
...
Screen output Value of gVar1: Value of gVar2: Character in ch: Value of sum: 0 2 A 8 VARIABLES ■ 31 Data such as numbers, characters, or even complete records are stored in variables to enable their processing by a program
...
ᮀ Defining Variables A variable must be defined before you can use it in a program
...
This memory space is addressed by reference to the name of the variable
...
]; This defines the names of the variables in the list name1 [, name2
...
The parentheses [
...
Thus, one or more variables can be stated within a single definition
...
This has the following effect: ■ ■ a variable defined outside of each function is global, i
...
it can be used by all functions a variable defined within a function is local, i
...
it can be used only in that function
...
However, they can be defined wherever a statement is permitted
...
ᮀ Initialization A variable can be initialized, i
...
a value can be assigned to the variable, during its definition
...
EXAMPLES: char c = 'a'; float x(1
...
In contrast, the initial value for any local variables that you fail to initialize will have an undefined initial value
...
5 #include using namespace std; const double pi = 3
...
5; area = pi * radius * radius; circuit = 2 * pi * radius; cout << "\nTo Evaluate a Circle\n" << endl; cout << "Radius: " << radius << "Circumference: " << circuit << "Area: " << area << endl << endl << endl; return 0; } ✓ NOTE By default cout outputs a floating-point number with a maximum of 6 decimal places without trailing zeros
...
5 9
...
06858 THE KEYWORDS CONST AND VOLATILE ■ 33 A type can be modified using the const and volatile keywords
...
As an object of this type is constant, it cannot be modified at a later stage and must be initialized during its definition
...
1415947; Thus the value of pi cannot be modified by the program
...
0; // invalid ᮀ Volatile Objects The keyword volatile, which is rarely used, creates variables that can be modified not only by the program but also by other programs and external events
...
EXAMPLE: volatile unsigned long clock_ticks; Even if the program itself does not modify the variable, the compiler must assume that the value of the variable has changed since it was last accessed
...
It is also possible to combine the keywords const and volatile when declaring a variable
...
■ CHAPTER 2 ■ exercise s 34 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES EXERCISES Screen output for exercise 2 I "RUSH" \TO\ AND /FRO/ For exercise 3 Defining and initializing variables: int a(2
...
2E+5); const long large; char c('\''); unsigned char ch = '\201'; unsigned size(40000); float val = 12345
...
For example, sizeof(short) is equivalent to 2
...
Exercise 2 Write a C++ program to generate the screen output shown on the opposite page
...
456 and 76
...
■ CHAPTER 2 ■ solutions 36 FUNDAMENTAL TYPES, CONSTANTS, AND VARIABLES SOLUTIONS Exercise 1 #include using namespace std; int main() { cout << << << cout << cout << cout << cout << cout << cout << cout << << "\nSize of Fundamental Types\n" " Type Number of Bytes\n" "----------------------------------" << endl; " char: " << sizeof(char) << endl; " short: " << sizeof(short)<< endl; " int: " << sizeof(int) << endl; " long: " << sizeof(long) << endl; " float: " << sizeof(float)<< endl; " double: " << sizeof(double)< " long double: " << sizeof(long double) endl; return 0; } Exercise 2 // Usage of escape sequences #include using namespace std; int main() { cout << "\n\n\t I" "\n\n\t\t \"RUSH\"" "\n\n\t\t\t \\TO\\" "\n\n\t\t AND" "\n\n\t /FRO/" << endl; return 0; } // // // // // Instead of tabs you can send the suited number of blanks to the output
...
5); const long large; char z(500); int big = 40000; double he's(1
...
12345; // // // // // // // // // // 2
...
456F, y = 76
...
This includes using standard header files
...
e
...
Functions and classes that you define on your own will not be introduced until later in the book
...
■ Mathematical standard functions double sin (double); // Sine double cos (double); // Cosine double tan (double); // Tangent double atan (double); // Arc tangent double cosh (double); // Hyperbolic Cosine double sqrt (double); // Square Root double pow (double, double); // Power double exp (double); // Exponential Function double log (double); // Natural Logarithm double log10 (double); // Base-ten Logarithm DECLARING FUNCTIONS ■ 41 ᮀ Declarations Each name (identifier) occurring in a program must be known to the compiler or it will cause an error message
...
e
...
Each time a variable or a function is defined it is also declared
...
If you need to use a function that has already been introduced in a library, you must declare the function but you do not need to redefine it
...
The function’s type is defined by its return value, that is, the value the function passes back to the program
...
When a function is declared, the compiler must therefore be provided with information on ■ ■ the name and type of the function and the type of each argument
...
Examples: int toupper(int); double pow(double, double); This informs the compiler that the function toupper() is of type int, i
...
its return value is of type int, and it expects an argument of type int
...
The types of the arguments may be followed by names, however, the names are viewed as a comment only
...
Both junctions are standard junctions
...
If the header file is included in the program’s source code by means of the #include directive, the function can be used immediately
...
Additional details on header files can be found later in this chapter
...
5, y; // By means of a prototype, the compiler generates // the correct call or an error message! // Computes x raised y = pow("x", 3
...
0); y = pow(x, 3
...
cout << "2
...
5) yields: " << 2
...
0, x) << endl; return 0; } Screen output 2
...
5) yields: 15
...
9017 FUNCTION CALLS ■ 43 ᮀ Function Calls A function call is an expression of the same type as the function and whose value corresponds to the return value
...
Example: y = pow( x, 3
...
0, and the result, the power x3, is assigned to y
...
Thus, the function pow() can be used to perform calculations for double values
...
0 + pow( 5
...
0 to the return value of pow(5
...
Any expression can be passed to a function as an argument, such as a constant or an arithmetical expression
...
The compiler refers to the prototype to check that the function has been called correctly
...
Example: y = pow( x, 3); // also ok! The value 3 of type int is passed to the function as a second argument
...
If a function is called with the wrong number of arguments, or if type conversion proves impossible, the compiler generates an error message
...
Example: float x = pow(3
...
7); // Error! The compiler recognizes that the number of arguments is incorrect
...
e
...
44 ■ CHAPTER 3 USING FUNCTIONS AND CLASSES ■ TYPE void FOR FUNCTIONS Sample program // Outputs three random numbers #include #include // // // // Declaration of cin and cout Prototypes of srand(), rand(): void srand( unsigned int seed ); int rand( void ); using namespace std; int main() { unsigned int seed; int z1, z2, z3; cout << " --- Random Numbers --- \n" << endl; cout << "To initialize the random number generator, " << "\n please enter an integer value: "; cin >> seed; // Input an integer srand( seed); // and use it as argument for a // new sequence of random numbers
...
cout << "\nThree random numbers: " << z1 << " " << z2 << " " << z3 << endl; return 0; } ✓ NOTE The statement cin >> seed; reads an integer from the keyboard, because seed is of the unsigned int type
...
The type void is available for functions of this type, which are also referred to as procedures in other programming languages
...
Since the function does not return a value, it is of type void
...
The value is used to create a series of random numbers
...
Example: int rand( void ); // or int rand(); The standard function rand() is called without any arguments and returns a random number between 0 and 32767
...
ᮀ Usage of srand() and rand() The function prototypes for srand() and rand() can be found in both the cstdlib and stdlib
...
Calling the function rand() without previously having called srand() creates the same sequence of numbers as if the following statement would have been proceeded: srand(1); If you want to avoid generating the same sequence of random numbers whenever the program is executed, you must call srand() with a different value for the argument whenever the program is run
...
See Chapter 6 for an example of this technique
...
h // // // // // Declaration // of cin, cout, //
...
cpp Copy #include #include "myheader
...
cin >> a; cout << myfunc (a);
...
By using an #include directive these declarations and macros can be made available to any other source file, even in other header files
...
> or double quotes "
...
If the name of the header file is enclosed by angled brackets <
...
The current directory is not searched to increase the speed when searching for header files
...
To enable the compiler to find these header files, the #include directive must state the name of the header files in double quotes
...
h" The compiler will then also search the current folder
...
h is normally used for user-defined header files
...
When a header file is included, the classes defined and any objects declared in the file are available to the program
...
cin is an object of the istream class and cout an object of the ostream class
...
h and iomanip
...
Within these header files the identifiers of iostream and iomanip are not contained in the std namespace but are declared globally
...
h limits
...
h time
...
h locale
...
h wchar
...
h math
...
h wctype
...
h setjmp
...
h iso646
...
h string
...
They are not indicated by the file extension
...
Namespaces will be introduced in a later chapter
...
If you merely stipulate the directive Example: #include the compiler would not be aware of the cin and cout streams
...
Example: #include #include using namespace std; You can then use cin and cout without any additional syntax
...
This makes the string class available and allows userfriendly string manipulations in C++
...
ᮀ Header Files in the C Programming Language The header files standardized for the C programming language were adopted for the C++ standard and, thus, the complete functionality of the standard C libraries is available to C++ programs
...
h> Mathematical functions are made available by this statement
...
This can cause name conflicts in large programs
...
h, is accompanied in C++ by a second header file, cname, which declares the same identifiers in the std namespace
...
h is thus equivalent to Example: #include using namespace std; The string
...
These header files grant access to the functionality of the C string library and are to be distinguished from the string header file that defines the string class
...
These may be graphics libraries or database interfaces
...
#include #include using namespace std; // Declaration of cin, cout // Declaration of class string int main() { // Defines four strings: string prompt("What is your name: "), name, // An empty line( 40, '-'), // string with 40 '-' total = "Hello "; // is possible! cout << prompt; getline( cin, name); total = total + name; // Request for input
...
cout << line << endl // Outputs line and name << total << endl; cout << " Your name is " // Outputs length << name
...
Strings can be printed with cout and the operator <<
...
Sample screen output What is your name: Rose Summer --------------------------------------Hello Rose Summer Your name is 11 characters long! --------------------------------------- USING STANDARD CLASSES ■ 51 Several classes are defined in the C++ standard library
...
Each class is a type with certain properties and capacities
...
Methods are functions that belong to a class and cooperate with the members to perform certain operations
...
ᮀ Creating Objects An object is a variable of a class type, also referred to as an instance of the class
...
Example: string s("I am a string"); In this example the object s, an instance of the standard class string (or simply a string), is defined and initialized with the string constant that follows
...
In general, there are several ways of initializing an object of a class
...
ᮀ Calling Methods All the methods defined as public within the corresponding class can be called for an object
...
The name of the object precedes the method and is separated from the method by a period
...
length(); // object
...
e
...
This results in a value of 13 for the string s defined above
...
These functions perform certain operations for objects passed as arguments
...
Example: getline(cin, s); The keyboard input is terminated by pressing the return key to create a new-line character, '\n', which is not stored in the string
...
25 0
...
5 0
...
5); b = rand( a ); cout << "\nRandom number: " << b << endl; return 0; } EXERCISES ■ 53 Exercise 1 Create a program to calculate the square roots of the numbers 4 12
...
0121 and output them as shown opposite
...
To calculate the square root, use the function sqrt(), which is defined by the following prototype in the math
...
Exercise 2 The program on the opposite page contains several errors! Correct the errors and ensure that the program can be executed
...
Read two lines of text from the keyboard
...
Output the new string on screen
...
0, x2 = 12
...
0121; cout << "\n cout << "\n << "\n << "\n Number \t Square Root" " << x1 << " \t " << " << x2 << " \t " << " << x3 << " \t " << << endl; sqrt(x1) sqrt(x2) sqrt(x3) << endl; cout << "\nType a number whose square root is to be" " computed
...
h> using namespace std; // Introduces all names of namespace // std into the global scope
...
// = cout << message << endl; SOLUTIONS ■ int len = message
...
srand(12); // instead of: a = srand(12
...
55 This page intentionally left blank chapter 4 Input and Output with Streams This chapter describes the use of streams for input and output, focusing on formatting techniques
...
This gave rise to the I/O stream classes, which are now available in a library of their own, the so-called iostream library
...
The class ios is the base class of all other stream classes
...
Effectively, the ios class ■ ■ manages the connection to the physical data stream that writes your program’s data to a file or outputs the data on screen contains the basic functions needed for formatting data
...
The istream and ostream classes derived from ios form a user-friendly interface for stream manipulation
...
The operator >> is defined in istream and << is defined in ostream, for example
...
Further stream classes, a file management class, for example, are derived from the classes mentioned above
...
These classes, which also contain methods for opening and closing files, will be discussed in a later chapter
...
When a program is launched these objects are automatically created to read standard input or write to standard output
...
However, standard input and output can be redirected to files
...
The other two standard streams cerr and clog are used to display messages when errors occur
...
60 ■ CHAPTER 4 INPUT AND OUTPUT WITH STREAMS ■ FORMATTING AND MANIPULATORS Example: Calling a manipulator Here the manipulator showpos is called
...
setf( ios::showpos); cout << 123; The other positive numbers are printed with their sign as well: cout << 22; // Output: +22 The output of a positive sign can be canceled by the manipulator noshowpos: cout << noshowpos << 123; // Output: 123 The last statement is equivalent to cout
...
setf(ios::showpos);, ios::showpos being the flag showpos belonging to the ios class ■ Using manipulators is easier than directly accessing flags
...
■ Old compilers only supply some of the manipulators
...
FORMATTING AND MANIPULATORS ■ 61 ᮀ Formatting When reading keyboard input, a valid input format must be used to determine how input is to be interpreted
...
The stream classes istream and ostream offer various options for performing these tasks
...
In previous chapters we have looked at the cin and cout streams in statements such as: cout << "Please enter a number: "; cin >> x; The following sections systematically describe the abilities of the stream classes
...
These operators are defined for expressions with fundamental types—that is, for characters, boolean values, numbers and strings
...
Manipulators can be used to generate formats for subsequent input/output
...
other methods for determining or modifying the state of a stream and unformatted input and output
...
In general, flags are represented by individual bits within a special integral variable
...
Each flag has a default setting
...
It is possible to modify individual formatting flags
...
However, the same effect can be achieved simply by using so-called manipulators, which are defined for all important flags
...
62 ■ CHAPTER 4 INPUT AND OUTPUT WITH STREAMS ■ FORMATTED OUTPUT OF INTEGERS Manipulators formatting integers Manipulator Effects oct Octal base hex Hexadecimal base dec Decimal base (by default) showpos Generates a + sign in non-negative numeric output
...
uppercase Generates capital letters in hexadecimal output
...
Sample program // Reads integral decimal values and // generates octal, decimal, and hexadecimal output
...
int main() { int number; cout << "Please enter an integer: "; cin >> number; cout << uppercase // for hex-digits << " octal \t decimal \t hexadecimal\n " << oct << number << " \t " << dec << number << " \t " << hex << number << endl; return 0; } FORMATTED OUTPUT OF INTEGERS ■ 63 ᮀ Formatting Options The << operator can output values of type short, int, long or a corresponding unsigned type
...
In addition, the field width can be defined for the above types
...
ᮀ Numeric System Integral numbers are displayed as decimals by default
...
Example: cout << hex << 11; // Output: b Hexadecimals are displayed in small letters by default, that is, using a, b,
...
The manipulator uppercase allows you to use capitals
...
ᮀ Negative Numbers When negative numbers are output as decimals, the output will always include a sign
...
Example: cout << dec << showpos << 11; //Output: +11 You can use noshowpos to revert to the original display mode
...
Example: cout << dec << -1 << " " << hex << -1; This statement causes the following output on a 32-bit system: -1 ffffffff 64 ■ CHAPTER 4 INPUT AND OUTPUT WITH STREAMS ■ FORMATTED OUTPUT OF FLOATING-POINT NUMBERS Manipulators formatting floating-point numbers Manipulator showpoint Effects Generates a decimal point character shown in floating-point output
...
noshowpoint Trailing zeroes after the decimal point are not printed
...
fixed Output in fixed point notation scientific Output in scientific notation setprecision (int n) Sets the precision to n
...
Returns the used precision
...
Sample program #include using namespace std; int main() { double x = 12
...
precision(2); // Precision 2 cout << " By default: " << x << endl; cout << " showpoint: " << showpoint << x << endl; cout << " fixed: " << fixed << x << endl; cout << " scientific: " << scientific << x << endl; return 0; } FORMATTED OUTPUT OF FLOATING-POINT NUMBERS ■ 65 ᮀ Standard Settings Floating-points are displayed to six digits by default
...
Trailing zeroes behind the decimal point are not printed
...
Examples: cout << 1
...
234; cout << 1
...
234 // Output: 1
...
Very large and very small numbers are displayed in exponential notation
...
8; // Output: 1
...
You can ■ ■ ■ change the precision, i
...
the number of digits to be output force output of the decimal point and trailing zeroes stipulate the display mode (fixed point or exponential)
...
Example: cout << setprecision(3); // Precision: 3 // or: cout
...
34; // Output: 12
...
This also applies to all standard manipulators called with at least one argument
...
The number of digits being output (e
...
6) equals the current precision
...
0; // Output: 1
...
In this case, you can use the fixed manipulator with the precision defining the number of decimal places
...
Example: cout << fixed << 66
...
000000 In contrast, you can use the scientific manipulator to specify that floating-point numbers are output as exponential expressions
...
Examples #include #include using namespace std; // Obligatory // declarations 1st Example: cout << '|' << setw(6) << 'X' << '|'; Output: | X| // Field width 6 2nd Example: cout << fixed << setprecision(2) Output: << setw(10) << 123
...
40 // Field width 10 1234567890 OUTPUT IN FIELDS ■ 67 The << operator can be used to generate formatted output in fields
...
ᮀ Field Width The field width is the number of characters that can be written to a field
...
The output will always contain at least the number of digits specified as the field width
...
Example: cout
...
The first example outputs the character 'X' to a field with width of 6, but does not output the '|' character
...
You can also use the width() method to get the current field width
...
Example: int fieldwidth = cout
...
You can either use the fill() method or the setfill() manipulator to specify another fill character
...
As the previous example shows, output to fields is normally right-aligned
...
The manipulator internal left-justifies the sign and rightjustifies the number within a field
...
width(6); cout
...
#include #include // Declaration of cin, cout // For manipulators being called // with arguments
...
In this case the character code is stored in an int variable and the variable is then output
...
The program on the opposite page contains further examples
...
As in the case of other types, strings can be positioned within output fields
...
The manipulator right can be used to right-justify the output within the field
...
If you need to output the strings true or false instead, the flag ios::boolalpha must be set
...
Example: bool ok = true; cout << ok << endl << boolalpha << ok << endl; // 1 // true You can revert this setting using the noboolalpha manipulator
...
// Manipulator setw() int main() { string label; double price; cout << "\nPlease enter an article label: "; // Input the label (15 characters maximum): cin >> setw(16); // or: cin
...
sync(); cin
...
The program to be continued return 0; } ✓ NOTE The input buffer is cleared and error flags are reset by calling the sync() and clear() methods
...
FORMATTED INPUT ■ 71 The >> operator, which belongs to the istream class, takes the current number base and field width flags into account when reading input: ■ ■ the number base specifies whether an integer will be read as a decimal, octal, or hexadecimal the field width specifies the maximum number of characters to be read for a string
...
Keyboard input is thus not read until confirmed by pressing the key
...
Input is displayed on screen by default
...
Any white space characters (such as blanks, tabs, and new lines) are ignored by default
...
An input field is terminated by the first white space character or by the first character that cannot be processed
...
However, the characters that follow, FF and the newline character, remain in the input buffer and will be read first during the next read operation
...
Example: string city; cin >> city; // To read just one word! If Lao Kai is input, only Lao will be written to the city string
...
For a given field width of n, a maximum of n–1 characters will be read, as one byte is required for the null character
...
The program on the opposite page illustrates this point and also shows how to clear the input buffer
...
sync(); // Clears the buffer cin
...
0, x2 = 0
...
number: "; x1; "2
...
Example: int n; cin >> oct >> n; An input value of 10 will be interpreted as an octal, which corresponds to a decimal value of 8
...
ᮀ Inputting Floating-Point Numbers The >> operator interprets any input as a decimal floating-point number if the variable is a floating-point type, i
...
float, double, or long double
...
Example: double x; cin >> x; The character input is converted to a double value in this case
...
0, or 3e10 is valid
...
The next input field begins with A
...
If, as in our example, no type conversion is performed, the variable is not written to and an internal error flag is raised
...
Chapter 6, “Control Flow,” and Chapter 28, “Exception Handling,” show how a program can react to input errors
...
#include #include using namespace std; string header = " --- Demonstrates Unformatted Input ---"; int main() { string word, rest; cout << header << "\n\nPress to go on" << endl; cin
...
cout << "\nPlease enter a sentence with several words!" << "\nEnd with and
...
A text of more than one line can be entered
...
The sample program requires that at least one word and a following white space are entered
...
The bytes read from a stream are passed to the program “as is
...
ᮀ Reading and Writing Characters You can use the methods get() and put() to read or write single characters
...
Example: char ch; cin
...
To prevent this from happening you can use cin >> ch; to read the first non-white space character
...
In this case, get() returns the character code of type int
...
get(); The put() method can be used for unformatted output of a character
...
Example: cout
...
ᮀ Reading a Line The >> operator can only be used to read one word into a string
...
Example: getline(cin, text); This statement reads characters from cin and stores them in the string variable text until a new line character occurs
...
Example: getline(cin, s, '
...
Any characters subsequent to the first period will remain in the input buffer of the stream
...
Number of Pieces Price per piece
...
Dollar Program listing for exercise 5 // A program with resistant mistakes #include using namespace std; int main() { char ch; string word; cin >> "Let's go! Press the return key: " >> ch; cout << "Enter a word containing three characters at most: "; cin >> setprecision(3) >> word; cout >> "Your input: " >> ch >> endl; return 0; } EXERCISES ■ 77 Exercise 1 What output is generated by the program on the page entitled “Formatted output of floating-point numbers” in this chapter? Exercise 2 Formulate statements to perform the following: a
...
123456 in an output field with a width of 15
...
Output the number 23
...
c
...
456 as an exponential and with four decimal spaces
...
✓ Exercise 4 Write a C++ program that reads any given character code (a positive integer) from the keyboard and displays the corresponding character and the character code as a decimal, an octal, and a hexadecimal on screen
...
Why do you think the character P is output when the number 336 is entered? Exercise 5 Correct the mistakes in the program on the opposite page
...
12
...
20e+001 Exercise 2 #include #include using namespace std; // For setw() and setprecision() int main() { double x1 = 0
...
987, x3 = -123
...
2346e+002 // A field width of 12 or more would be convenient! return 0; } Exercise 3 // Input and formatted output of article characteristics
...
0; // Input: cout << "\nPlease enter article characteristics
...
Is only // necessary, if input is > 255
...
Thus after the assignment, the variable c contains the value 80, representing the character P
...
// #include #include // Manipulator setw() #include // Class string using namespace std; int main() { string word; // To read a word
...
// cout <<
...
get(); cin >>
...
Overloading and other operators, such as those needed for bit manipulations, are introduced in later chapters
...
0 << endl; return 0; } Sample output for the program Enter two floating-point values: 4
...
3456 The average of the two numbers is: 8
...
The operations being executed will depend on the type of data — you could add, multiply, or compare numbers, for example
...
The following sections introduce you to the most important operators that can be used for arithmetic types
...
A unary operator has only one operand, whereas a binary operator has two
...
The opposite page shows an overview
...
If at least one of the operands is a floating-point number, the result will also be a floating-point number; e
...
, the division 7
...
5
...
For example, 7%2 computes to 1
...
Expressions can be used as the operands of operators to form more complex expressions
...
Each expression that is not a void type returns a value
...
Examples: int a(4); double a * 512 // 1
...
9); Type int Type double Type double, since one operand is of type double An expression can be used as an operand in another expression
...
e
...
In our example, 7*3 is first calculated before adding 2
...
Example: (2 + 7) * 3 // Multiplies 9 by 3
...
ᮀ Sign Operators The sign operator – returns the value of the operand but inverts the sign
...
ᮀ Increment / Decrement Operators The increment operator ++ modifies the operand by adding 1 to its value and cannot be used with constants for this reason
...
In both cases the operation i = i + 1 is performed
...
The difference becomes apparent when you look at the value of the expression; ++i means that the value of i has already been incremented by 1, whereas the expression i++ retains the original value of i
...
The decrement operator -- modifies the operand by reducing the value of the operand by 1
...
ᮀ Precedence How is an expression with multiple operators evaluated? Example: float val(5
...
0/2
...
e
...
As you can see from the table opposite, ++ has the highest precedence and / has a higher precedence than -
...
0/2
...
The result is 1
...
If two operators have equal precedence, the expression will be evaluated as shown in column three of the table
...
"; cout << "\n Please supply a divisor: "; cin >> y; x /= y; cout << "\n And this is " << "your current lucky number: " // without digits after // the decimal point: << fixed << setprecision(0) << x << endl; return 0; } ASSIGNMENTS ■ 87 ᮀ Simple Assignments A simple assignment uses the assignment operator = to assign the value of a variable to an expression
...
Examples: z = 7
...
0 + 4
...
In the case of the last example, the right side of the expression is first evaluated and the result is assigned to the variable on the left
...
Example: sin(x = 2
...
5 is assigned to x and then passed to the function as an argument
...
Example: i = j = 9; In this case the value 9 is first assigned to j and then to i
...
Examples
...
Compound assignment operators can be composed from any binary arithmetic operator (and, as we will see later, with bit operators)
...
You can modify a variable when evaluating a complex expression by means of an assignment or the ++, -- operators
...
Avoid use of side effects if possible, as they often lead to errors and can impair the readability of your programs
...
7 < 1
...
Example: length == circuit // false or true If the variables length and circuit contain the same number, the comparison is true and the value of the relational expression is true
...
When individual characters are compared, the character codes are compared
...
The following expression results in the value true when ASCII code is used
...
Example: bool flag = index < max – 1; In our example, max – 1 is evaluated first, then the result is compared to index, and the value of the relational expression (false or true) is assigned to the flag variable
...
Since result is an int type, a numerical value is assigned instead of false or true, i
...
0 for false and 1 for true
...
Example: (result = length + 1) == limit Our example stores the result of length + 1 in the variable result and then compares this expression with limit
...
The compiler will not generate an error message if the value on the left is a variable
...
90 ■ CHAPTER 5 OPERATORS FOR FUNDAMENTAL TYPES ■ LOGICAL OPERATORS “Truth” table for logical operators A B A && B A || B true true true true true false false true false true false true false false false false A !A true false false true Examples for logical expressions x Logical Expression Result 1 -1 x <= y || y >=0 false 0 0 x > -2 && y == 0 true -1 0 x && !y true 0 ✓ y 1 !(x+1) || y - 1 > 0 false NOTE A numeric value, such as x or x+1, is interpreted as “false” if its value is 0
...
” LOGICAL OPERATORS ■ 91 The logical operators comprise the boolean operators && (AND), || (OR), and ! (NOT)
...
A logical expression results in a value false or true, depending on whether the logical expression is correct or incorrect, just like a relational expression
...
However, operands of any type that can be converted to bool can also be used, including any arithmetic types
...
Any other value than 0 is interpreted as true
...
2) || (length > 9
...
2 or greater than 9
...
The AND operator && will return true only if both operands are true, so the logical expression Example: (index < max) && (cin >> number) is true, provided index is less than max and a number is successfully input
...
The left operand is evaluated first and if a result has already been ascertained, the right operand will not be evaluated! The NOT operator ! will return true only if its operand is false
...
ᮀ Precedence of Boolean Operators The && operator has higher precedence than ||
...
This is why it was permissible to omit the parentheses in the examples earlier on in this chapter
...
Refer to the table of precedence in the Appendix for further details
...
#include using namespace std; int main() { cout << boolalpha; // Outputs boolean values // as true or false bool res = false; int y = res = 7 cout << << cout << int 5; || (y = 0); "Result of (7 || (y = 0)): " << res endl; "Value of y: " << y << endl; a, b, c; a = b = c = 0; res = ++a || ++b cout << '\n' << " res = << ", a = << ", b = << ", c = a = b = c = 0; res = ++a && ++b cout << " res = << ", a = << ", b = << ", c = return 0; } && ++c; " " " " << << << << res a b c << endl; || ++c; " << res " << a " << b " << c << endl; EXERCISES ■ Exercise 1 What values do the following arithmetic expressions have? a
...
3 + 4 % 5 b
...
3 * 7 % 4 c
...
0 f
...
How are operands and operators in the following expression associated? x = –4 * i++ – 6 % 4; Insert parentheses to form equivalent expressions
...
What value will be assigned in part a to the variable x if the variable i has a value of –2? Exercise 3 The int variable x contains the number 7
...
x < 10 && x >= –1 b
...
x++ == 8 || x == 7 Exercise 4 What screen output does the program on the opposite page generate? 93 ■ CHAPTER 5 ■ solutions 94 OPERATORS FOR FUNDAMENTAL TYPES SOLUTIONS Exercise 1 a
...
7 b
...
1 c
...
5 f
...
x = ( ((–4) * (i++)) – (6 % 4) ) b
...
Exercise 3 a
...
false c
...
These are ■ loops with while, do-while, and for ■ selections with if-else, switch, and the conditional operator ■ jumps with goto, continue, and break
...
cpp // Computing the average of numbers #include using namespace std; int main() { int x, count = 0; float sum = 0
...
3333 THE WHILE STATEMENT ■ 97 Loops are used to perform a set of instructions repeatedly
...
C++ offers three language elements to formulate iteration statements: while, do-while, and for
...
In the case of while and for statements this expression is verified before the loop body is executed, whereas a do-while loop is performed once before testing
...
e
...
If this value is true, the loop body is then executed before the controlling expression is evaluated once more
...
e
...
It is common practice to place the loop body in a new line of the source code and to indent the statement to improve the readability of the program
...
However, the controlling expression might be any expression that can be converted to the bool type including any arithmetic expressions
...
ᮀ Building Blocks If you need to repeat more than one statement in a program loop, you must place the statements in a block marked by parentheses { }
...
The program on the opposite page calculates the average of a sequence of integers input via the keyboard
...
The controlling expression cin >> x is true provided the user inputs an integer
...
Invalid input, if the user types a letter instead of an integer, for example, terminates the loop and executes the next statement
...
cpp #include #include using namespace std; int main() { double rate = 1
...
95 1
...
85 3
...
75 THE FOR STATEMENT ■ 99 ᮀ Initializing and Reinitializing A typical loop uses a counter that is initialized, tested by the controlling expression and reinitialized at the end of the loop
...
loop" << endl; ++count; // Reinitialization } In the case of a for statement the elements that control the loop can be found in the loop header
...
loop" << endl; Any expression can be used to initialize and reinitialize the loop
...
expression2 is the controlling expression, which is always evaluated prior to executing the loop body: ■ ■ if expression2 is false, the loop is terminated if expression2 is true, the loop body is executed
...
You can also define the loop counter in expression1
...
Example: for( int i = 0; i < 10; cout << i++ ) ; As this example illustrates, the loop body can be an empty statement
...
However, to improve readability, even the empty statement should occupy a line of its own
...
cpp // Outputs a table of exchange: Euro and US-$ #include #include using namespace std; int main() { long euro, maxEuro; double rate; // Amount in Euros // Exchange rate Euro <-> $ cout << "\n* * * TABLE OF EXCHANGE " << " Euro – US-$ * * *\n\n"; cout << "\nPlease give the rate of exchange: " " one Euro in US-$: "; cin >> rate; cout << "\nPlease enter the maximum euro: "; cin >> maxEuro; // --- Outputs the table --// Titles of columns: cout << '\n' << setw(12) << "Euro" << setw(20) << "US-$" << "\t\tRate: " << rate << endl; // Formatting US-$: cout << fixed << setprecision(2) << endl; long lower, upper, step; // Lower and upper limit // Step width // The outer loop determines the actual // lower limit and the step width: for( lower=1, step=1; lower <= maxEuro; step*= 10, lower = 2*step) // The inner loop outputs a "block": for( euro = lower, upper = step*10; euro <= upper && euro <= maxEuro; euro+=step) cout << setw(12) << euro << setw(20) << euro*rate << endl; return 0; } THE FOR STATEMENT (CONTINUED) ■ 101 Any of the three expressions in a for statement can be omitted, however, you must type at least two semicolons
...
In the following Example: for( ; expression; ) the loop header is equivalent to while(expression)
...
ᮀ The Comma Operator You can use the comma operator to include several expressions where a single expression is syntactically correct
...
The following syntax applies for the comma operator Syntax: expression1, expression2 [, expression3
...
Example: int x, i, limit; for( i=0, limit=8; i < limit; i += 2) x = i * i, cout << setw(10) << x; The comma operator separates the assignments for the variables i and limit and is then used to calculate and output the value of x in a single statement
...
This means you can leave out the parentheses in the above example
...
The type and value are defined by the last expression in a statement separated by commas
...
102 ■ CHAPTER 6 CONTROL FLOW ■ THE do-while STATEMENT Structogram for do-while statement As long as the expression is true Sample program // tone
...
e
...
This results in the loop body being performed at least once
...
Only then is the controlling expression evaluated
...
✓ NOTE The do-while loop must be followed by a semicolon
...
The ANSI standard stipulates a maximum depth of 256 nested loops
...
The program contains two loops — one of which is nested in the other
...
The break is caused by the inner for loop where the variable i is incremented from 0 to the value of delay
...
The tone is generated by outputting the control character BELL (ASCII code 7), which is represented by the escape sequence \a
...
104 ■ CHAPTER 6 CONTROL FLOW ■ SELECTIONS WITH if-else Structogram for the if-else statement if (expression) true statement1 false statement2 Sample program // if_else
...
min = x; else min = y; cout << "\nThe smaller number is: " << min << endl; } else cout << "\nInvalid Input!" << endl; return 0; } Sample output for this program Enter two different numbers: 7
...
7 The smaller number is: 5
...
Syntax: if( expression ) statement1 [ else statement2 ] When the program is run, expression is first evaluated and the program control branches accordingly
...
If there is no else and expression is false, the control jumps to the statement following the if statement
...
But not every if statement has an else branch
...
Example: if( n > 0 ) if( n%2 == 1 ) cout << " Positive odd number "; else cout << "Positive even number"; In this example, the else branch belongs to the second if, as is indicated by the fact that the statement has been indented
...
Example: if( n > 0 ) { if( n%2 == 1 ) cout << " Positive odd number \n"; } else cout << " Negative number or zero\n"; ᮀ Defining Variables in if Statements You can define and initialize a variable within an if statement
...
In this case the variable is available within the if statement
...
} // Here to work with x
...
If this value is not 0, the statements in the next block are executed
...
106 ■ CHAPTER 6 CONTROL FLOW ■ Else-if CHAINS Structogram for an else-if chain if(expression) true false statement1 if(expression) true false statement2 if(expression)
...
cpp // Output the fine for driving too fast
...
Dollars" << endl; else if( toofast < 30) cout << "Fine payable: 80,-
...
An elseif chain implies a series of embedded if-else statements whose layout is normally as follows: if ( expression1 ) statement1 else if( expression2 ) statement2
...
...
are evaluated in the order in which they occur
...
If none of the expressions are true, the else branch of the last if statement is executed
...
ᮀ The Sample Program The program opposite uses an else-if chain to evaluate the penalty for driving too fast and outputs the fine on screen
...
If the user types 60 as the speed limit and 97
...
This outputs the message "Hand over your driver's license!" on a new line
...
cpp #include using namespace std; int main() { float x, y; cout << "Type two different numbers:\n"; if( !(cin >> x && cin >> y) ) // If the input was { // invalid
...
2 216
...
7 CONDITIONAL EXPRESSIONS ■ 109 ᮀ Conditional Operator The conditional operator ?: is used to form an expression that produces either of two values, depending on the value of some condition
...
In contrast to the if-else statement the selection mechanism is based on expressions: one of two possible expressions is selected
...
Syntax: expression ? expression1 : expression2 expression is evaluated first
...
The value of the conditional expression is therefore either the value of expression1 or expression2
...
If a has a positive value of 12, the number 12 is assigned to z
...
Since this sample program stores the value of the conditional expression in the variable z, the statement is equivalent to if( a > 0 ) z = a; else z = -a; ᮀ Precedence The conditional operator is the only C++ operator with three operands
...
In other words, you could omit the brackets in the first example
...
In this example, x is printed on screen if x is greater than y, and y is printed otherwise
...
110 ■ CHAPTER 6 CONTROL FLOW ■ SELECTING WITH switch Structogram for the switch statement switch(expression) case Const1: case Const2:
...
int command = menu(); // The function menu() reads // a command
...
switch( command ) { case 'a': case 'A': action1(); // Carry out 1st action
...
break; default: cout << '\a' << flush; // Beep on } // invalid input SELECTING WITH SWITCH ■ 111 ᮀ The switch Statement Just like the else-if chain, the switch statement allows you to choose between multiple alternatives
...
switch( expression ) { case const1: [ statement ] [ break; ] case const2: [ statement ] [ break; ]
...
...
It must be an integral type
...
, in the case labels
...
If the value of an expression matches one of the case constants, the program branches to the appropriate case label
...
You can use break to leave the switch statement unconditionally
...
If the value of the expression does not match any of the case constants, the program branches to the default label, if available
...
The default does not need to be the last label; it can be followed by additional case labels
...
Every selection can be programmed using an else-if chain
...
In this case (and only this case), you can use a switch statement
...
112 ■ CHAPTER 6 CONTROL FLOW ■ JUMPS WITH break, continue, AND goto Structogram for break within a while statement As long as expression is true break; statement, which follows the loop
...
cpp : To output an ASCII Code Table #include #include using namespace std; int main() { int ac = 32; // To begin with ASCII Code 32 // without control characters
...
get(answer); if( answer == 'q' || answer == 'Q' ) break; cin
...
JUMPS WITH BREAK, CONTINUE, AND GOTO ■ 113 ᮀ break The break statement exits from a switch or loop immediately
...
The program on the opposite page, which outputs a group of 20 ASCII characters and their corresponding codes, uses the break keyword in two places
...
} loop when a maximum value of 256 has been reached
...
The second break statement is used to terminate the while loop and hence the program
...
In the case of a while or do-while loop the program jumps to the test expression, whereas a for loop is reinitialized
...
// Processes all integers
...
// Process even // numbers only
...
This allows you to jump to any given point marked by a label within a function
...
Example: for(
...
) if (error) goto errorcheck;
...
// Error handling A label is a name followed by a colon
...
Any program can do without goto statements
...
■ CHAPTER 6 ■ exercise s 114 CONTROL FLOW EXERCISES Screen output for exercise 2 ****** MULTIPLICATION TABLE ****** 1 2 3 4 5 6 7 8 9 10 1 1 2 3
...
...
...
10 2 2 4 6 20 3
...
...
4
...
...
5
...
...
10 20 30 6 7 8 9 10
...
...
...
100 Note on exercise 4 Use the function time() to initialize the random number generator: #include
...
h> // Prototype of time() // Prototypes of srand() // and rand() long sec; time( &sec ); // Take the number of seconds and srand( (unsigned)sec ); // use it to initialize
...
cpp program in this chapter to replace both the for loops with while loops
...
Exercise 3 Write a C++ program that reads an integer between 0 and 65535 from the keyboard and uses it to seed a random number generator
...
Exercise 4 Write a program for the following numerical game: The computer stores a random number between 1 and 15 and the player (user) attempts to guess it
...
After each wrong guess, the computer tells the user if the number was too high or too low
...
The player wins if he or she can guess the number within three attempts
...
✓ NOTE Use the system time to seed the random number generator as shown opposite
...
The long value of the sec variable is converted to unsigned by unsigned(sec) and then passed to the srand() function
...
cpp are equivalent to the following while loops: // The outer loop sets the lower // limit and the step width used: lower=1, step=1; while( lower <= maxEuro) { // The inner loop outputs a block: euro = lower; upper = step*10; while( euro <= upper && euro <= maxEuro) { cout << setw(12) << euro << setw(20) << euro*rate << endl; euro += step; } step *= 10, lower = 2*step; } Exercise 2 // // MultTable
...
#include #include using namespace std; int main() { int factor1, factor2; cout << "\n\n << " ****** << endl; " MULTIPLICATION TABLE ******" // Outputs the first and second line: cout << "\n\n\n "; // 1
...
line << "-------------------------------------------" << endl; SOLUTIONS // Outputs the remaining lines of the table: for( factor1 = 1 ; factor1 <= 10 ; ++factor1 ) { cout << setw(6) << factor1 << " |"; for( factor2 = 1 ; factor2 <= 10 ; ++factor2 ) cout << setw(5) << factor1 * factor2; cout << endl; } cout << "\n\n\n"; // To shift up the table return 0; } Exercise 3 // random
...
#include
...
// Seeds the random // number generator
...
random number = " << setw(3) << (rand() % 100 + 1) << endl; return 0; } ■ 117 118 ■ CHAPTER 6 CONTROL FLOW Exercise 4 // NumGame
...
long sec; time( &sec); // Get the time in seconds
...
sync(); // Clear input buffer cin
...
attempt: "; cin >> attempt; if(attempt < number) cout << "too small!"<< endl; else if(attempt > number) cout <<"too big!"<< endl; else found = true; } if( !found) cout << "\nI won!" << " The number in question was: " << number << endl; else cout << "\nCongratulations! You won!" << endl; cout << "Repeat —> Finish —> \n"; do cin
...
In addition, standard macros for character handling are introduced
...
cpp // Creates a sine function table #include #include #include using namespace std; #define PI 3
...
0 // Lower limit #define END (2
...
0) // Step width #define HEADER (cout << \ " ***** Sine Function Table *****\n\n") int main() { HEADER; // Title // Table Head: cout << setw(16) << "x" << setw(20) << "sin(x)\n" << " -----------------------------------------" << fixed << endl; double x; for( x = START; x < END + STEP/2; x += STEP) cout << setw(20) << x << setw(16) << sin(x) << endl; cout << endl << endl; return 0; } Screen output ****** Table for the Sine Function ****** x sin(x) -------------------------------------------0
...
000000 0
...
382683 0
...
707107
...
...
...
MACROS ■ 121 C++ has a simple mechanism for naming constants or sequences of commands, that is for defining macros
...
Syntax: #define name substitutetext This defines a macro called name
...
For example, in the program on the opposite page, the name PI is replaced by the number 3
...
There is one exception to this general rule: substitution does not take place within strings
...
ᮀ Symbolic Constants Macros that are replaced by constants, such as the PI macro, are also known as symbolic constants
...
You can use any macros you have previously defined in subsequent #define directives
...
ᮀ More about Working with Macros Any preprocessor directive, and this includes the #define directive, must be placed in a line of its own
...
The rules that apply to naming variables also apply to naming macros
...
Using macros makes a C++ program more transparent and flexible
...
good readability: You can name a macro to indicate the use of the macro 2
...
122 ■ CHAPTER 7 SYMBOLIC CONSTANTS AND MACROS ■ MACROS WITH PARAMETERS Sample program // ball1
...
To do so, you must supply the appropriate parameters when defining the macro
...
Example: #define SQUARE(a) ((a) * (a)) This defines a macro called SQUARE() with a parameter a
...
When the macro is called, for example Example: z = SQUARE(x+1); the preprocessor inserts the substitute text with the current arguments, which will be expanded as follows, in this case z = ((x+1) * (x+1)); This example also shows that you must be careful when using brackets to indicate parameters for macros
...
The outer brackets in the definition ensure that even when the macro is used in a complex expression, the square is calculated before the result can be used for any further calculations
...
Peripheral devices, such as the screen or printers, can be controlled by special character sequences that normally begin with the ESC character (decimal 27, octal 033) and are thus known as escape sequences
...
1 See the appendix on Escape Sequences for Screen Control for an overview of the most important sequences
...
LOCATE is just one example of a macro with two parameters
...
The values z for the line and s for the column require decimal input with z = 1, s = 1 representing the top left corner of the screen or window
...
” In direction x (horizontally) the ball has a constant speed of dx = 1 or -1
...
1These escape sequences are valid for all standard UNIX terminals
...
sys must be loaded for DOS or a DOS box in Win95 or Win98
...
124 ■ CHAPTER 7 SYMBOLIC CONSTANTS AND MACROS ■ WORKING WITH THE #define DIRECTIVE Using macros in different source files Header file proj
...
h" #include "proj
...
...
...
...
...
...
h" WORKING WITH THE #DEFINE DIRECTIVE ■ 125 You can place the #define directive in any line of a program as long as it is placed prior to using the macro
...
If you need to use the same macros in different source files, it makes sense to create a header file
...
This method also lends itself to large-scale software projects
...
This concept is illustrated opposite using the header file proj
...
Macros with parameters can be called just like functions
...
The substitute text is inserted and re-compiled each time the macro is called
...
The speed of program execution will, however, improve since the program does not need to branch to sub-routines in contrast to normal function calls
...
Side effects of macros are possible if the substitute text contains multiple instances of a parameter
...
The variable x is incremented twice and the product does not represent the square of the incremented number
...
The linker then links them into the executable file
...
However, the executable file will be shorter as it contains only one instance of the function code
...
Inline functions, which are introduced in the chapter on functions, are an alternative to macros
...
h #ifndef_BASIS_H_ #define_BASIS_H_ //content of basis, //ex
...
#endif Header file Header file statist
...
h #include #include "basis
...
h"
...
Source file application
...
h" #include "graph
...
return 0; } CONDITIONAL INCLUSION ■ 127 ᮀ Redefining Macros A macro cannot simply be redefined
...
However, you do not need to supply the parameter list of a macro with parameters
...
#undef MIN ((a)<(b)? (a) : (b)) // Here MIN can be called The macro MIN cannot be used after this point, but it can be defined again, possibly with a different meaning, using the #define directive
...
Syntax: #ifdef name
...
#endif In the case of the #ifndef directive, the code block is compiled up to the next #endif only if the macro name has not been previously defined
...
See Preprocessor Directives in the appendix for further information
...
Example: #define MYHEADER A symbol without a substitute text is often used to identify header files and avoid multiple inclusion
...
h", you can identify the header by defining a symbol, such as _ARTICLE_, within that file
...
// Content of the header file #endif If you have already included the header, _ARTICLE_ will already be defined, and the contents of the header file need not be compiled
...
128 ■ CHAPTER 7 SYMBOLIC CONSTANTS AND MACROS ■ STANDARD MACROS FOR CHARACTER MANIPULATION Sample program // toupper
...
// --------------------------------------------------#include #include using namespace std; int main() { char c; long nChar = 0, // Counts all characters nConv = 0; // and converted characters while ( cin
...
if( islower(c)) // Lowercase letter? { c = toupper(c); // Converts the character ++nConv; // and counts it
...
put(c); // Outputs the character
...
When reading keyboard input, end-of-file is simulated by Ctrl+Z (DOS) or Ctrl+D (UNIX)
...
The macros are defined in the header files ctype
...
ᮀ Case Conversion You can use the macro toupper to convert lowercase letters to uppercase
...
However if c1 is not a lowercase letter, toupper(c1) returns the character “as is
...
As toupper only converts the letters of the English alphabet by default, any national characters, such as accentuated characters in other languages, must be dealt with individually
...
Refer to the next section for details
...
ᮀ Testing Characters A number of macros, all of which begin with is
...
For example, the macro islower(c) checks whether c contains a lowercase letter returning the value true, in this case, and false in all other cases
...
cout << "The character is no digit \n"; The following usage of islower() shows a possible definition of the toupper() macro: Example: #define toupper(c) \ (islower(c) ? ((c)-'a'+'A') : (c)) This example makes use of the fact that the codes of lower- and uppercase letters differ by a constant, as is the case for all commonly used character sets such as ASCII and EBCDIC
...
130 ■ CHAPTER 7 SYMBOLIC CONSTANTS AND MACROS ■ REDIRECTING STANDARD INPUT AND OUTPUT Sample program // lines
...
#include #include #include using namespace std; int main() { string line; int number = 0; while( getline( cin, line)) // As long as a line { // can be read
...
Redirecting the standard input: lines < text
...
dat with line numbers
...
2
...
dat Here the program reads from the keyboard and adds the output to the new file new
...
Please note, if the file already exists, it will be overwritten! You can use lines >> text
...
dat
...
dat does not already exist, it will be created
...
REDIRECTING STANDARD INPUT AND OUTPUT ■ 131 ᮀ Filter Programs The previous program, toupper
...
Programs of this type are known as filters
...
cpp, the loop while( cin
...
} is repeated while the test expression cin
...
The loop is terminated by end-of-file or if an error occurs since the test expression cin
...
The program on the opposite page, lines
...
But in this case standard input is read line by line
...
} The test expression getline(cin,line) is true while a line can be read
...
This allows easy data manipulation
...
dat with line numbers on screen, you can execute the program lines by typing the following command: Example: lines < text
...
In other words, the standard input is redirected
...
You can redirect input and output simultaneously: Example: lines < text
...
dat ✓ In this example the contents of text
...
dat
...
NOTE These examples assume that the compiled program lines
...
■ CHAPTER 7 ■ exercise s 132 SYMBOLIC CONSTANTS AND MACROS EXERCISES Hints for Exercise 2 You can use the function kbhit() to test whether the user has pressed a key
...
This avoids interrupting the program when reading from the keyboard
...
Both functions use operating system routines and are declared in the header file conio
...
The function kbhit() Prototype: Returns: int kbhit(); 0, if no key was pressed, otherwise != 0
...
The function getch() Prototype: Returns: int getch(); The character code
...
In contrast to cin
...
Additionally, control characters, such as return ( = 13), Ctrl+Z ( = 26), and Esc ( = 27), are passed to the program “as is
...
} ✓ // Key was pressed? // Yes -> Get character // character == Esc? NOTE When a function key, such as F1, F2,
...
was pressed, the function getch() initially returns 0
...
EXERCISES ■ 133 Exercise 1 Please write a
...
the macro MAX, which determines the greater of two numbers
...
Add these macros and other macros from this chapter to the header file myMacros
...
If your system supports screen control macros, also add some screen control macros to the header
...
Exercise 2 Modify the program ball1
...
display a white ball on a blue background, b
...
increase the speed of the ball with the + key and decrease the speed with the – key
...
Exercise 3 Write a filter program to display the text contained in any given file
...
Control characters are defined by codes 0 to 31
...
A single character, that is, a character appearing between two control characters, is not to be output! ✓ NOTE Since the program must not immediately output a single character following a control character, you will need to store the predecessor of this character
...
■ CHAPTER 7 ■ solutions 134 SYMBOLIC CONSTANTS AND MACROS SOLUTIONS Exercise 1 // -----------------------------------------------------// myMacros
...
// -----------------------------------------------------#ifndef _MYMACROS_ #define _MYMACROS_ #include using namespace std; // -----------------------------------------------------// Macro ABS // Call: ABS( val) // Returns the absolute value of val #define ABS(a) ( (a) >= 0 ? (a) : -(a)) // -----------------------------------------------------// Macro MIN // Call: MIN(x,y) // Returns the minimum of x and y #define MIN(a,b) ( (a) <= (b) ? (a) : (b)) // -----------------------------------------------------// Macro MAX // Call: MAX(x,y) // Returns the maximum of x and y #define MAX(a,b) ( (a) >= (b) ? (a) : (b)) // -----------------------------------------------------// Macros for controlling the screen // -----------------------------------------------------// Macro CLS // Call: CLS; // Clears the screen #define CLS (cout << "\033[2J") // -----------------------------------------------------// Macro LOCATE // Call: LOCATE(row, column); // Positions the cursor to (row,column)
...
#define LOCATE(r,c) (cout <<"\033["<< (r) <<';'<<(c)<<'H') SOLUTIONS // -----------------------------------------------------// Macro COLOR // Call: COLOR(foreground, background); // Sets the foreground and background color // for the following output
...
: COLOR( WHITE,BLUE); #define BLACK 0 #define RED 1 #define GREEN 2 #define YELLOW 3 #define BLUE 4 #define MAGENTA 5 #define CYAN 6 #define WHITE 7 // -----------------------------------------------------// Macro INVERS // Call: INVERS; // The following output is inverted
...
#define NORMAL (cout << "\033[0m") #endif // _MYMACROS_ Exercise 2 // --------------------------------------------------// ball2
...
h> #include "myMacros
...
cpp // Filter to ignore control characters // To call e
...
: NoCtrl < file // --------------------------------------------------#include using namespace std; #define isCtrl(c) ( c >= 0 int main() { char c, prec = 0; long nCtrl = 0, nChar = 0; && c <= 31 \ && c != '\n' && c != '\t') // // // // Character and predecessor Number of the following control characters or other characters while( cin
...
put(' '); nCtrl = 0; } switch( ++nChar) { case 1: break; case 2: cout
...
put(c); // current character } prec = c; } } return 0; } 137 This page intentionally left blank chapter 8 Converting Arithmetic Types This chapter introduces implicit type conversions, which are performed in C++ whenever different arithmetic types occur in expressions
...
139 140 ■ CHAPTER 8 ■ CONVERTING ARITHMETIC TYPES IMPLICIT TYPE CONVERSIONS Integer promotions bool int char, signed char, unsigned char short int if int equals long unsigned int if int equals short unsigned short Type hierarchy long double double float unsigned long long unsigned int not-existent, if int equals long int Example short size(512); double res, x = 1
...
The compiler automatically performs implicit type conversion, where a common type, which allows the operation in question to be performed, is assigned for the values of both operands
...
The assignment operator is an exception to this rule and will be discussed separately
...
However, comparison expressions will be bool types no matter what type of operands are involved
...
This type conversion is performed so as to preserve the original values
...
Thus, C++ will always use int type values or greater when performing calculations
...
ᮀ Usual Arithmetic Type Conversions If operands of different arithmetic types still occur after integer promotion, further implicit type conversions along the lines of the hierarchy on the opposite page will be necessary
...
These type conversions and integer promotions are collectively known as usual arithmetic type conversions
...
The interim result 50 is then converted to double and multiplied by x
...
142 ■ CHAPTER 8 ■ CONVERTING ARITHMETIC TYPES PERFORMING USUAL ARITHMETIC TYPE CONVERSIONS Converting signed integers a) Converting a positive number Sign bit(= 0 ↔ not negative) 26 Binary representaion of the integer 10 as value of type signed char (8 bits): 0 25 24 23 22 21 20 0 0 0 1 0 1 0 Extension to int (here 16 bit) The value 10 is preserved
...
Sign bit(= 1 ↔ negative) Binary representaion of the integer –10 as value of type signed char (8 bits): 26 1 25 24 23 22 21 20 1 1 1 0 1 1 0 Extension to int (here 16 bit) The value –10 is preserved
...
The bit pattern 1111 0110 of –10, for example, corresponds to the unsigned char value 246 == 0*20+ 1*21 + 1*22 + 0*23 + 1*24 + 1*25 + 1*26 + 1*27 PERFORMING USUAL ARITHMETIC TYPE CONVERSIONS ■ 143 Usual arithmetic type conversions retain the value of a number provided it can be represented by the new type
...
Conversion of an unsigned type to a larger integral type Examples: unsigned char to int or unsigned int Zero extension is performed first
...
2
...
The value is retained by performing sign extension
...
The new type is unsigned Examples: char to unsigned int, long to unsigned long In this case the value of negative numbers is not retained
...
However, the bit pattern will be interpreted differently
...
If the new type is longer, sign extension is performed first and the new bit pattern is then interpreted as unsigned
...
Conversion of an integral type to a floating-point type Examples: int to double, unsigned long to float The number is converted to an exponential floating-point type and the value retained
...
4
...
144 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES ■ IMPLICIT TYPE CONVERSIONS IN ASSIGNMENTS Example 1: int i = 100; long lg = i + 50; // Result of type int is // converted to long
...
Example 3: int i = –2; unsigned int ui = 2; i = i * ui; // First the value contained in i is converted to // unsigned int (preserving the bit pattern) and // multiplied by 2 (overflow!)
...
e
...
Example 4: double db = –4
...
i = db – 0
...
ui = db; // –4 is incompatible with ui
...
23456789012345; float f; f = d; // 1
...
IMPLICIT TYPE CONVERSIONS IN ASSIGNMENTS ■ 145 Arithmetic types can also be mixed in assignments
...
In the case of compound assignments, calculations using normal arithmetic type conversions are performed first before type conversion is performed following the rule for simple assignments
...
If the type of the variable is larger than the type of the value to be assigned, the type of the value must be promoted
...
2
...
” The following procedures are followed depending on individual circumstances: a
...
The bit pattern that remains will be interpreted as unsigned, if the new type is also unsigned, and as signed in all other cases
...
■ when converting an unsigned type to a signed type of the same scale, the bit pattern is retained and will be interpreted as signed (see Example 3)
...
Conversion of a floating-point type to an integral type The decimal part of the floating-point number is removed
...
9 converts to the integer 1
...
5 to a positive floating-point number or subtracting 0
...
This would allow for converting (1
...
5) to 2
...
This particularly applies to converting negative floatingpoint numbers to unsigned integers (see Example 4)
...
Conversion of a floating-point type to a smaller type If the floating-point number falls within the range of the new type, the value will be retained, although the accuracy may be compromised
...
146 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES ■ MORE TYPE CONVERSIONS Sample program // Ellipse
...
// The points (x,y) on an ellipse with center (0,0) // and axes A and B satisfy: // x = A*cos(t), y = B*sint(t) for 0 <= t <= 2*PI
...
1416 40 12 25 10 // // // // The point center of Length of Length of (Mx, My) is the the ellipse
...
CLS; // 0 <= t <= PI/2 is a 1/4-circle: for( double t = 0
...
03) { x = (int) (A * cos(t) + 0
...
5); DOT( x+Mx, y+My); DOT( x+Mx,-y+My); DOT(-x+Mx, y+My); DOT(-x+Mx,-y+My); } LOCATE(24,0); return 0; } MORE TYPE CONVERSIONS ■ 147 ᮀ Implicit Type Conversions in Function Calls In the case of function calls, arguments with arithmetic types are converted to the types of the corresponding parameters, similarly to conversions in assignments
...
func( size, 77); // Prototype // Call The function func() has two parameters belonging to the short and double types
...
This leads to implicit conversion of the value of size to short and the integer 77 to double
...
You can use explicit type conversion to avoid warnings during type conversion
...
Syntax: (type) expression This converts the value of an expression to the given type
...
The cast operator (type) is a unary operator and thus has a higher precedence than the arithmetic operators
...
Following the conventions of usual implicit type conversion, b is also converted to double and a floatingpoint division is performed
...
25, is assigned to the variable x
...
C++ has additional operators for explicit type conversion—the cast operator dynamic_cast<>, for example
...
■ CHAPTER 8 ■ exercise s 148 CONVERTING ARITHMETIC TYPES EXERCISES Program listing for exercise 3 // Convert
...
#include #include using namespace std; int main() { char v_char = 'A'; cout << "v_char: " << setw(10) << v_char << setw(10) << (int)v_char << endl; short v_short = –2; cout << "v_short: " << dec << setw(10) << v_short << hex << setw(10) << v_short << endl; unsigned short v_ushort = v_short; cout << "v_ushort: " << dec << setw(10) << v_ushort << hex << setw(10) << v_ushort << endl; unsigned long v_ulong = v_short; cout << "v_ulong: " << hex << setw(20) << v_ulong << endl; float v_float = –1
...
Exercise 3 What is output when the program opposite is executed? Exercise 4 Write a C++ program to output the sine curve on screen as in the graphic shown on the opposite page
...
Plot one point of the curve in columns 10, 10+1,
...
This leads to a step value of 2*PI/64 for x
...
Use the following extended ASCII code characters to draw the axes: Character Decimal Octal – 196 304 + 197 305 16 020 30 036 Example: cout << '\020'; // up arrowhead ■ CHAPTER 8 ■ solutions 150 CONVERTING ARITHMETIC TYPES SOLUTIONS Exercise 1 When called, the value –1 is converted to parameter n, i
...
to unsigned int
...
On a 32-bit system, –1 has the bit pattern 0xFFFFFFFF, which, when interpreted as unsigned, corresponds to the decimal value 4 294 967 295
...
Exercise 3 The screen output of the program v_char: v_short: v_ushort: v_ulong: v_float: (int)v_float: A -2 65534 65 fffe fffe fffffffe -1
...
cpp // Outputs a sine curve // ----------------------------------------------------#include #include using namespace std; #define #define #define #define #define // Prototypes of sin() CLS (cout << "\033[2J") LOCATE(z,s) (cout <<"\033["<<(z)<<';'<<(s)<<'H') PI 3
...
0 // Lower limit END (2
...
5); LOCATE( row, column); cout << '*'; } LOCATE(25,1); return 0; } ■ ; ++column) // Cursor to the last row 151 This page intentionally left blank chapter 9 The Standard Class string This chapter introduces the standard class string, which is used to represent strings
...
These include inserting and erasing, searching and replacing, comparing, and concatenating strings
...
Sample program // string1
...
size() << " characters long!" << endl; // Two new strings: string copy(text), // a copy and the start(text,0,10); // first 10 characters // starting with // position 0
...
During string operations the required memory space is automatically reserved or modified
...
The string class is defined in the string header file and was mentioned in Chapter 3 as an example for the use of classes
...
This allows for easy copying, concatenation, and comparison
...
ᮀ Initializing Strings A string, that is, an object belonging to the string class, can be initialized when you define it using ■ ■ ■ a predefined string constant a certain number of characters a predefined string or part of a string
...
The length of a string, that is, the current number of characters in the string, is stored internally and can be accessed using the length() method or its equivalent size()
...
length(); // Output: 13 ᮀ String Assignments When you assign a value to a string, the current contents are replaced by a new character sequence
...
The memory space required is adjusted automatically
...
In contrast, the >> operator reads only one word, ignoring any leading white space
...
156 ■ CHAPTER 9 THE STANDARD CLASS STRING ■ CONCATENATING STRINGS Sample program // string2
...
#include #include using namespace std; string prompt("Please enter some text!\n"), line( 50, '-'); int main() { prompt+="Terminate the input with an empty line
...
length() == 0) // Empty line? break; // Yes ->end of the loop text = line + '\n' + text; // Inserts a new // line at the beginning
...
--------------------------------------Babara, Bobby, and Susan will go to the movies today --------------------------------------Your lines of text in reverse order: --------------------------------------will go to the movies today Babara, Bobby, and Susan CONCATENATING STRINGS ■ 157 Within the string class the operators + and += are defined for concatenating, and the operators ==, !=, <, <=, >, and >= are defined for comparing strings
...
ᮀ Using + to Concatenate Strings You can use the + operator to concatenate strings, that is, to join those strings together
...
The result, "sunflower" is then assigned to sum
...
This expression can in turn be used as an operand in a more complex expression
...
Concatenation takes place from left to right
...
The expression "Good morning " + "mister X" would be invalid! ᮀ Using += to Concatenate Strings Strings can also be concatenated by first performing concatenation and then assigning the result
...
However, you can obtain the same result using the assignment operator += , which is far more efficient
...
// Also possible This adds the content of the second string directly to s1
...
158 ■ CHAPTER 9 THE STANDARD CLASS STRING ■ COMPARING STRINGS Sample program // string3
...
#include #include using namespace std; string prompt = "Please enter two lines of text!\n", line( 30, '-'); int main() { string line1, line2, key = "y"; while( key == "y" || key == "Y") { cout << line << '\n' << prompt << line << endl; getline( cin, line1); // Read the first getline( cin, line2); // and second line
...
length(), len2 = line2
...
See Chapter 17, Pointers and Arrays, for more information
...
This also allows you to use strings to formulate the conditions for branches and loops
...
// str1 is less than str2? ᮀ Results of Comparisons Strings are compared lexicographically, that is character by character, beginning at the first character
...
Thus, if you are using the ASCII character set, the letter 'A' (ASCII code 65) is smaller than the letter 'a' (ASCII code 97)
...
Given two strings s1 and s2: s1 == s2 s1 < s2 is true only if both strings are identical; this requires that both strings are exactly the same length
...
All other comparative operations can be deduced from the above rules
...
In an expression comparing strings, one operand can again be a string constant or a single character
...
} This example compares the string key with the single character 'y'
...
String comparisons can also be combined to form more complex expressions
...
} The controlling expression is valid if the string key contains only the letter 'Y' or 'y'
...
160 ■ CHAPTER 9 ■ THE STANDARD CLASS STRING INSERTING AND ERASING IN STRINGS ᮀ Inserting a string string s1("Miss Summer"); s1
...
erase(4,7); // Start position: 4, Quantity: 7 Effect of the statement: Position: 0 1 2 3 4 String s before 'T' 'h' 'e' ' ' String s afterwards 'T' 'e' ' ' 'h' 5 6 7 8 9 10 11 12 13 14 's' 'u' 'm' 'm' 'e' 'r' '-' 't' 'i' 'm' 'e' 't' 'i' 'm' 'e' INSERTING AND ERASING IN STRINGS ■ 161 The string class contains numerous methods for performing string manipulations
...
These methods generally allow passing a string constant instead of a second string
...
ᮀ Insertion The method insert() inserts a string at a certain position of another string
...
The first character in a string occupies position 0, the second character position 1, and so on
...
insert(5, "Ashley "); The string "Ashley " is inserted into the string s1 at position 5, that is in front of the 'S' character in "Summer"
...
If you need to insert only part of a string into another string, you can pass two additional arguments to the insert() method, the starting position and the length of the string
...
insert(12, s2, 0, 12); This example inserts the first 12 characters from the string s2 at position 13 in string s1
...
ᮀ Erasing You can use the erase() method to delete a given number of characters from a string
...
Example: string s("The summer-time"); s
...
The erase() method can also be called without specifying a length and will then delete all the characters in the string up to the end of the string
...
erase(6); // s now contains "winter" You can also call erase() without any arguments to delete all the characters in a string
...
Example “Bob and Bill” string s1("There they go again!"), s2("Bob and Bill"); s1
...
Example “my love” string s1("Here comes Mike!"), s2("my love?"); s1
...
If the string contains the required substring, the position of the substring found by the search is returned
...
Since the npos constant is defined in the string class, you can reference it as string::npos
...
The method requires the substring to be located as an argument
...
find("young"); The variable first has a value of 11 in this example
...
This initializes the variable last with a value of 21 in our example
...
rfind("young"); ᮀ Replacing When replacing in strings, a string overwrites a substring
...
You can use the replace() method to perform this operation
...
The third argument contains the replacement string
...
find("they"); if( pos != string::npos ) s1
...
After this operation s1 contains the string "There Bob and Bill go again!"
...
Example: string s1("Here comes Mike!"), s2("my love?"); s1
...
164 ■ CHAPTER 9 THE STANDARD CLASS STRING ■ ACCESSING CHARACTERS IN STRINGS Sample program // string4
...
// (A word is the maximum sequence of characters // containing no white space characters
...
'); // Reads a text up to // the first '
...
length(); ++i) { if( isspace( text[i]) ) // white space? { ++nSpace; fSpace = true; } else if( fSpace) // At the beginning of a word? { ++nWord; fSpace = false; } } cout << line // Outputs the result
...
length() << "\n words: " << nWord << "\n white spaces: " << nSpace << endl; return 0; } ACCESSING CHARACTERS IN STRINGS ■ 165 When manipulating strings it is often important to access the individual characters that form the string
...
An individual character is always identified by its index, also referred to as subscript, that is, its position in the string
...
ᮀ Subscript Operator The easiest way to access a single character in the string is to use the subscript operator []
...
length() – 1
...
Example: char c = s[0]; This statement copies the first character from s to the variable c
...
length() –1] = 'g'; overwrites the last character in the string s
...
ᮀ Invalid Indices Any integral expression can be used as an index
...
Example: cout << s[5]; // Error Your program’s reaction to an invalid index is undefined; this requires careful attention by the programmer! You can call the at() method if you need to perform range checks
...
Example: s
...
If an invalid index is found an exception occurs and the program will normally be terminated at this point
...
■ CHAPTER 9 THE STANDARD CLASS STRING ■ exercise s 166 EXERCISES For exercise 3 // timeStr
...
#include #include #include using namespace std; // For time(), ctime(),
...
string tm = ctime( &sec); // Converts the // seconds to a string
...
string greeting("Have a wonderful "); if( hr < "10") // Compares strings greeting += "Morning!"; else if( hr < "17") greeting += "Day!"; else greeting += "Evening!"; cout << greeting << endl; return 0; } EXERCISES ■ 167 Exercise 1 Write a C++ program to ■ ■ ■ ■ initialize a string s1 with the string "As time by
...
In each case, your program should determine the position of the substring
...
Exercise 2 Write a C++ program that reads a word from the keyboard, stores it in a string, and checks whether the word is a palindrome
...
The following are examples of palindromes:“OTTO, ” “deed, ” and “level
...
Modify the program to continually read and check words
...
✓ NOTE The function time() returns the current time as the number of seconds since 1/1/1970, 0:0
...
The function ctime() converts the number of seconds to a string with a date and time and returns this string
...
cpp: Insert, search, and replace in strings
...
", s2 = "goes "; int main() { int pos = 0; cout << header << endl; cout << "s1 : " << s1 << endl; // To insert: cout << "\nInserting in string \"" << s2 <<"\""<< endl; pos = s1
...
insert(pos,s2); cout << "s1 : " << s1 << endl; // Result // To erase: cout << "\nTo erase remaining characters behind \"by\":" << endl; pos = s1
...
erase(pos + 3); cout << "s1 : " << s1 << endl; // Result // To replace: cout << "\nTo replace \"time\" by \"Bill\":" << endl; pos = s1
...
replace(pos, 4, "Bill"); cout << "s1 : " << s1 << endl; return 0; } // Result SOLUTIONS ■ Exercise 2 // ----------------------------------------------------// palindrome
...
// ----------------------------------------------------#include #include using namespace std; string header = " * * * Testing palindromes * * * ", prompt = "Enter a word: ", line( 50, '-'); int main() { string word; char key = 'y'; // Empty string cout << "\n\t" << header << endl; while( key == 'y' || key == 'Y') { cout << '\n' << line << '\n' << prompt; cin >> word; // Compares the first and last character, // the second and the second to last etc
...
length() - 1; for( ; i <= j ; ++i, --j) if( word[i] != word[j] ) break; if( i > j) // All characters equal? cout << "\nThe word " << word << " is a P A L I N D R O M E !" << endl; else cout << "\nThe word " << word << " is not a palindrome" << endl; cout << "\nRepeat? (y/n) "; do cin
...
sync(); } return 0; } 169 170 ■ CHAPTER 9 THE STANDARD CLASS STRING Exercise 3 The program outputs the date and time first
...
For example: Date and time: Thu Nov 28 09:01:37 2001 Have a wonderful morning! chapter 10 Functions This chapter describes how to write functions of your own
...
171 172 ■ CHAPTER 10 ■ FUNCTIONS SIGNIFICANCE OF FUNCTIONS IN C++ Elements of a C++ program C++ program Core elements of C++ (built-in types, operators, control structures) Functions and classes of the standard library Self-defined functions and classes and other libraries SIGNIFICANCE OF FUNCTIONS IN C++ ■ 173 C++ supports efficient software development on the lines of the top-down principle
...
After identifying objects you will need to define classes that describe these objects
...
In addition, you can make use of inheritance to create specialized classes without needing to change any existing classes
...
However, not every function is a member function
...
Functions of this type do not belong to any particular class but normally represent algorithms of a more general nature, such as the search or sort functions of the standard library
...
Many useful global functions and classes are available from the C++ standard library
...
Often a compiler package will offer commercial class libraries or graphical user interfaces
...
Classes and functions that belong together are normally compounded to form separate source files, which can be compiled and tested independently
...
You can enhance the reusability of your source code by compiling your own libraries, but be sure to include comments for ease of readability
...
If you modify a source file, you may also need to recompile other files
...
An integrated developer environment will offer the functionality of this utility when you create a new project
...
174 ■ CHAPTER 10 ■ FUNCTIONS DEFINING FUNCTIONS Example of a function definition // func1
...
\n"; test( 10, -7
...
" << endl; return 0; } void test(int arg1, double arg2 ) // Definition { cout << "\nIn function test()
...
argument: " << arg1 << "\n 2
...
...
...
Chapter 13, Defining Classes, describes the steps for defining member functions
...
This makes the program easier to understand, since you start reading at the point where the program starts to execute
...
The example can be read as follows: type name declaration_list is the function type, that is, the type of the return value
...
contains the names of the parameters and declares their types
...
A list of declarations that contains only the word void is equivalent to an empty list
...
They are created when the function is called and initialized by the values of the arguments
...
5); is called, the parameter arg1 is initialized with a value of 10 and arg2 with -7
...
The left curved bracket indicates the start of a function block, which contains the statements defining what the function does
...
The only difference when a function is defined is that the name and declaration list are not followed by a semicolon but by a function code block
...
This means you can omit parameter names from the prototype, whereas compiling a function definition will produce machine code
...
cpp // Example for a simple function returning a value
...
5, y = 7
...
double area( double width, double len) { return (width * len); // Returns the result
...
50 and length 8
...
70 RETURN VALUE OF FUNCTIONS ■ 177 The program opposite shows how the function area() is defined and called
...
The prototype provides the compiler with all the information it needs to perform the following actions when a function is called: ■ ■ check the number and type of the arguments correctly process the return value of the function
...
Even though simple examples often define and call a function within a single source file, this tends to be an exception
...
When a function is called, an argument of the same type as the parameter must be passed to the function for each parameter
...
The value of the expression is always copied to the corresponding parameter
...
If the function is any type other than void, the return statement will also cause the function to return a value to the function that called it
...
If the type of this value does not correspond to the function type, the function type is converted, where possible
...
The function area() makes use of the fact that the return statement can contain any expression
...
If the expression in the return statement, or the return statement itself, is missing, the return value of the function is undefined and the function type must be void
...
178 ■ CHAPTER 10 ■ FUNCTIONS PASSING ARGUMENTS Calling function and called function long func2(int, double); //
...
1; double y;
...
} // Prototype // Call of func2()
...
2; long result;
...
// is computed
...
return result; } Stack content after calling a function On call “push” On return “pop” • • • further local objects return address first parameter • • • last parameter Stack PASSING ARGUMENTS ■ 179 ᮀ Passing by Value Passing values to a function when the function is called is referred to as passing by value
...
However, function arguments can also be passed by reference
...
An example of passing by reference was provided in the example containing the function time()
...
We will see how to create functions of this type later
...
Additional indirect memory access is unnecessary
...
ᮀ Local Objects The scope of function parameters and the objects defined within a function applies only to the function block
...
For example, the program structure opposite contains a variable a in the function func1() and in the function func2()
...
This also applies to the variables x in func1() and func2()
...
The stack is an area of memory that is managed according to the LIFO (last in first out) principle
...
The last plate you put on the stack has to be taken off first
...
180 ■ CHAPTER 10 ■ FUNCTIONS INLINE FUNCTIONS Call to a function not defined as inline Program Function Branching void func() { // 1st Call func(); // 2nd Call } func(); ✓ HINT The executable file only contains one instance of the function’s machine code
...
INLINE FUNCTIONS ■ 181 ᮀ Jumping to Sub-Routines When a function is called, the program jumps to a sub-routine, which is executed as follows: ■ ■ ■ the function parameters are placed on the stack and initialized with appropriate arguments the so-called return address, that is, the place where the function was called, is stored on the stack and the program flow branches to the function after executing the function the program uses the return address it stored previously to return to the calling function
...
All this jumping back and forth can affect the run time of your program, especially if the function contains only a few instructions and is called quite often
...
However, you can define inline functions to avoid this problem
...
The definition of an inline function is introduced by the inline keyword in the function header
...
This is why inline functions should contain no more than one or two instructions
...
An inline function must be defined in the source file in which it is called
...
The code containing the instructions must also be available to the compiler
...
This means the function will be available in several source files
...
When a macro is called, the preprocessor simply replaces a block of text
...
The compiler performs a type check, for example
...
// Formula: capital = k0 * (1
...
h> double capital( double k0, double p, double n) { return (k0 * pow(1
...
5, double n=1
...
0, 3
...
5); endcap = capital( 2222
...
8); endcap = capital( 3030
...
endcap = capital( 100
...
0); // No gap! endcap = capital( , 5
...
✓ // not ok // not ok NOTE A function defined with default arguments is always called with the full number of arguments
...
DEFAULT ARGUMENTS ■ 183 So-called default arguments can be defined for functions
...
The compiler simply uses the default values for any missing arguments
...
In other words, you need to supply them when you declare the function
...
Example: void moveTo( int = 0, int = 0); The function moveTo() can then be called with or without one or two arguments
...
It is also possible to define default arguments for only some of the parameters
...
They can also be supplied when the function is defined, if the definition occurs in the same source file and before the function is called if you define a default argument for a parameter, all following parameters must have default arguments default arguments must not be redefined within the prototype scope (the next chapter gives more details on this topic)
...
You can use default arguments to call a function with a different number of arguments without having to write a new version of the function
...
cpp // To generate and output random numbers
...
if( !setrand ) { srand((unsigned int)time(NULL)); setrand = true; } } inline double myRandom() // Returns random number x { // with 0
...
0 init_random(); return (double)rand() / (double)RAND_MAX; } inline int myRandom(int start, int end) // Returns the { // random number n with init_random(); // start <= n <= end return (rand() % (end+1 - start) + start); } // Testing myRandom() and myRandom(int,int): int main() { int i; cout << "5 random numbers between 0
...
0 :" << endl; for( i = 0; i < 5; ++i) cout << setw(10) << myRandom(); cout << endl; cout << "\nAnd now 5 integer random numbers " "between -100 and +100 :" << endl; for( i = 0; i < 5; ++i) cout << setw(10) << myRandom(-100, +100); cout << endl; return 0; } OVERLOADING FUNCTIONS ■ 185 Functions in traditional programming languages, such as C, which perform the same task but have different arguments, must have different names
...
Example: int int_max( int x, int y); double dbl_max( double x, double y); Of course this is detrimental to efficient naming and the readability of your program— but luckily, this restriction does not apply to C++
...
Example: int max( int x, int y); double max( double x, double y); In our example two different function share the same name, max
...
The compiler uses a function’s signature to differentiate between overloaded functions
...
When a function is called, the compiler compares the arguments to the signature of the overloaded functions and simply calls the appropriate function
...
9; maxvalue = max( 1
...
When overloaded functions are called, implicit type conversion takes place
...
Example: maxvalue = max( 1, value); // Error! The signature does not contain the function type, since you cannot deduce the type by calling a function
...
Example: int search(string key); string search(string name); Both functions have the same signature and cannot be overloaded
...
cpp // Demonstrates the principle of recursion by a // function, which reads a line from the keyboard // and outputs it in reverse order
...
get(c) getput(); cout
...
getput(); } 2nd Execution 3rd Execution getput() {
...
// c = 'k' getput(); cout
...
// c = '\n' // No call of // getput() cout
...
put(c); RECURSIVE FUNCTIONS ■ 187 ᮀ Recursion A function that calls itself is said to be recursive
...
But a break criterion is always necessary to avoid having the function call itself infinitely
...
Recursion requires local objects to be created each time the function is called, and these objects must not have access to any other local objects from other function calls
...
ᮀ A Sample Program Let’s look at the principle of recursion by referring to the sample program opposite
...
The function getput() is first called by main() and reads a character from the keyboard, storing it in the local variable c
...
The chain of recursive function calls is terminated by the user pressing the Return key
...
This outputs the second to last character, and so on
...
ᮀ Practical Usage The logic of various solutions to common problems results in a recursive structure, for example, browsing directory trees, using binary trees for data management, or some sorting algorithms, such as the quick sort algorithm
...
However, always make sure that sufficient memory is available for the stack
...
This ensures that all the source files will be compiled and linked automatically
...
cpp sum
...
...
...
...
Write the function sum() with four parameters that calculates the arguments provided and returns their sum
...
Returns: The sum of type long
...
Test the function sum() by calling it by all three possible methods
...
b
...
cpp and sum
...
Exercise 2 a
...
(Use Max instead of max to avoid a collision with other definitions of max
...
Can the function Max() also be called using arguments of the types char, int, or long? b
...
Can the function Max() still be called with two arguments of type int? Exercise 3 The factorial n! of a positive integer n is defined as n! = 1*2*3
...
Argument: A number n of type unsigned int
...
Formulate two versions of the function, where the factorial is a
...
calculated recursively Test both functions by outputting the factorials of the numbers 0 to 20 as shown opposite on screen
...
Arguments: The base of type double and the exponent of type int
...
For example, calling pow(2
...
53 = 2
...
5 * 2
...
625 This definition of the function pow()means overloading the standard function pow(), which is called with two double values
...
Compare the result of your function with the result of the standard function
...
The power x0 is defined as 1
...
2
...
3
...
0
...
In this case, your function should return the value HUGE_VAL
...
h and represents a large double value
...
SOLUTIONS solutions ■ ■ SOLUTIONS Exercise 1 // // // // ----------------------------------------------------sum_t
...
----------------------------------------------------- #include #include #include #include using namespace std; long sum( long a1, long a2, long a3=0, long a4=0); int main() // Several calls to function sum() { cout << " **** Computing sums ****\n" << endl; srand((unsigned int)time(NULL)); // Initializes the // random number generator
...
cpp Defines the function sum() ----------------------------------------------------- long sum( long a1, long a2, long a3, long a4) { return (a1 + a2 + a3 + a4); } 191 192 ■ CHAPTER 10 FUNCTIONS Exercise 2 // // // // ----------------------------------------------------max
...
------------------------------------------------------ // // // // As long as just one function Max() is defined, it can be called with any arguments that can be converted to double, i
...
with values of type char, int or long
...
#include #include using namespace std; inline double Max(double x, double y) { return (x < y ? y : x); } inline char Max(char x, char y) { return (x < y ? y : x); } string header( "To use the overloaded function Max()
...
0, x2 = 0
...
sync(); cin
...
SOLUTIONS cout << line << "And once more with characters!" << endl; cout << "Enter two characters:" << endl; char c1, c2; if( cin >> c1 && cin >> c2) { cout << "The greater character is " << Max(c1,c2) << endl; } else cout << "Invalid input!" << endl; cout << "Testing with int arguments
...
cpp // Computes the factorial of an integer iteratively, // i
...
using a loop, and recursively
...
get(); // --- cout << << << << Recursive computation of factorial ---- setw(10) << "n" << setw(30) << "Factorial of n" " (Recursive solution)\n" " -----------------------------------------" endl; for( n = 0; n <= N_MAX; ++n) cout << setw(10) << n << setw(30) << fact2(n) << endl; cout << endl; return 0; } long double fact1(unsigned int n) // Iterative { // solution
...
0; for( unsigned int i = 2; i <= n; ++i) result *= i; return result; } long double fact2(unsigned int n) { if( n <= 1) return 1
...
SOLUTIONS Exercise 4 // ----------------------------------------------------// power
...
// Overloads the standard function pow()
...
0; int exponent = 0; cout << " **** Computing Integer Powers ****\n" << endl; cout << "Enter test values
...
0; if( base == 0
...
0; else return HUGE_VAL; if( exp < 0) { base = 1
...
0; for( int n = 1; n <= exp; ++n) power *= base; return power; } ■ 195 This page intentionally left blank chapter 11 Storage Classes and Namespaces This chapter begins by describing storage classes for objects and functions
...
Namespaces can be used to avoid conflicts when naming global identifiers
...
The following storage class specifiers can be used extern static auto register STORAGE CLASSES OF OBJECTS ■ 199 When an object is declared, not only are the object’s type and name defined but also its storage class
...
In addition, the storage class delimits the part of the program in which the object can be accessed directly by its name, the so-called object scope
...
A translation unit, also referred to as module, comprises the source file you are compiling and any header files you have included
...
The object is no longer visible once you have left the code block
...
Only the functions within this module can reference the object
...
The object is available throughout the program, providing a common space in memory that can be referenced by any program function
...
Access to an object as defined by the object’s storage class is independent of any access controls for the elements of a class
...
ᮀ Lifetime Objects with block scope are normally created automatically within the code block that defines them
...
The memory used for these objects is freed after leaving the code block
...
However, it is possible to define objects with block scope that are available throughout the runtime of a program
...
When the program flow re-enters a code block, any pre-existing conditions will apply
...
These objects are created when a program is launched and are available until the program is terminated
...
These storage classes will be discussed individually in the following sections
...
cpp // A filter to remove white-space characters // at the ends of lines
...
Shorten the line
...
Source file 2 // // // // // // Cutline2
...
The string line has to be globally defined in another source file
...
size(); // extern declaration // Position after the // last character
...
resize(++i); } // If no blank and // no tab -> // stop the loop
...
THE STORAGE CLASS extern ■ 201 ᮀ Defining Global Objects If an object is not defined within a function, it belongs to the extern storage class
...
External objects thus allow you to exchange information between any functions without passing any arguments
...
The string line, which has a global definition, is used to exchange data
...
This also applies to objects belonging to class types, if not otherwise stipulated by the class
...
If you need to use an object before defining it or in another module, you must first declare the object
...
The declaration makes the name and type of the object known to the compiler
...
Example: extern long position; // Declaration This statement declares position as an external object of type long
...
A global object must be defined once, and once only, in a program
...
You will normally declare the object before the first function in a source file or in a header file that you can include when needed
...
Remember, if you declare the object within a code block, the object can only be used within the same block
...
If you do initialize the object, you are defining that object! ✓ NOTE Global objects affect the whole program and should be used sparingly
...
202 ■ CHAPTER 11 ■ ST0RAGE CLASSES AND NAMESPACES THE STORAGE CLASS static // Passw1
...
// ----------------------------------------------------#include #include #include #include using namespace std; long timediff(void); static string secret = "ISUS"; static long maxcount = 3, maxtime = 60; // Prototype // Password // Limits bool getPassword() // Enters and checks a password
...
bool ok_flag = false; // For return value string word; // For input int count = 0, time = 0; timediff(); // To start the stop watch while( ok_flag != true && ++count <= maxcount) // Number of attempts { cout << "\n\nInput the password: "; cin
...
static long sec = 0; // Time of last call
...
time( &sec); // Reads new time
...
} THE STORAGE CLASS static ■ 203 ᮀ Static Objects If an object definition is preceded by the static keyword, the object belongs to the static storage class
...
Static objects are not placed on the stack, but are stored in the data area of a program just like external objects
...
Two conditions apply, depending on where the object is defined: 1
...
✓ NOTE In contrast to objects with an extern definition, the name of an external static object is unknown to the linker and thus retains its private nature within a module
...
Definition within a code block This means that the object is internal static, that is, the object is only visible within a single block
...
On re-entering the block, you can continue to work with the original object
...
If the object is not initialized explicitly, a default value of 0 applies
...
Permission is refused following three unsuccessful attempts or when 60 seconds have elapsed
...
Its value is zero only when the function is first called
...
204 ■ CHAPTER 11 ■ ST0RAGE CLASSES AND NAMESPACES THE SPECIFIERS auto AND register Sample function with a register variable // // // // // // // // // // // // StrToL
...
Argument: A string
...
-------------------------------------------------The digits are interpreted with base 10
...
The conversion terminates when the end of the string is reached or when a character that cannot be converted is reached
...
for(i=0; i < str
...
size()) { if( str[i] == '+' ) { vz = 1; ++i; } if( str[i] == '-' ) { vz = ---1; ++i; } } // Sequence of digits -> convert to integer for( ; i < str
...
The parameters of a function are also auto objects
...
Example: auto float radius; // Equivalent to: // float radius; When the program flow reaches the definition, the object is created on the stack, but in contrast to a static type object, the object is destroyed on leaving the block
...
However, objects belonging to a class type are normally initialized with default values, which can be specified in the class definition
...
In this case, the register keyword is used to declare the object
...
In other words, it only makes sense to define register variables if the variable is not too large, as in the case of types such as char, short, int or pointers
...
However, the compiler can ignore the register keyword
...
If a program defines too many register variables in a code block, the superfluous variables are placed in the auto storage class
...
This is useful if you need to perform calculations with a number contained in a string
...
206 ■ CHAPTER 11 ■ ST0RAGE CLASSES AND NAMESPACES THE STORAGE CLASSES OF FUNCTIONS ᮀ Example of a Program Structure Source file 1 extern bool getPassword(void); // Prototype int main() { // The function permission(), // but not the function timediff() // can be called here
...
...
} Source file 2 static long timediff(void); // Prototype bool getPassword(void) // Definition { // timediff() can be called here
...
...
} static long timediff(void) {
...
...
Functions with block scope are invalid: you cannot define a function within another function
...
External functions have program scope, whereas static functions have file scope
...
In a similar manner to external objects, external functions can be used at any position in a program
...
Example: extern bool getPassword(void); // Prototype As previously seen, you can omit the extern keyword, since functions belong to the extern storage class by default
...
Example: static long timediff() {
...
They can only be called in the source file that defines them
...
If you need to call a static function before defining it, you must first declare the function in the source file
...
The function timediff() is an auxiliary function and not designed to be called externally
...
208 ■ CHAPTER 11 ■ ST0RAGE CLASSES AND NAMESPACES NAMESPACES Defining namespaces // namesp1
...
// ---------------------------------------------------#include // Class string defined within // namespace std namespace MySpace { std::string mess = "Within namespace MySpace"; int count = 0; // Definition: MySpace::count double f( double); // Prototype: MySpace::f() } namespace YourSpace { std::string mess = "Within namespace YourSpace"; void f( ) // Definition of { // YourSpace::f() mess += '!'; } } namespace MySpace // Back in MySpace
...
0; } } int MySpace::g( ) // Separate definition { // of MySpace::g() return ++count; } #include // cout,
...
2) << "\n---------------------" << std::endl; YourSpace::f(); std::cout << YourSpace::mess << std::endl; return 0; } NAMESPACES ■ 209 Using global names in large-scale software projects can lead to conflicts, especially when multiple class libraries are in operation
...
Within a namespace, you can use identifiers without needing to check whether they have been defined previously in an area outside of the namespace
...
A normal namespace is identified by a name preceded by the namespace keyword
...
Example: namespace myLib { int count; double calculate(double, int); //
...
Elements belonging to a namespace can be referenced directly by name within the namespace
...
To do so, place the scope resolution operator, ::, before the element name
...
You can also use the scope resolution operator :: to reference global names, that is, names declared outside of any namespaces
...
This technique is useful when you need to access a global name that is hidden by an identical name defined in the current namespace
...
You can reopen and expand a namespace you defined previously at any point in the program namespaces can be nested, that is, you can define a namespace within another namespace
...
210 ■ CHAPTER 11 ■ ST0RAGE CLASSES AND NAMESPACES THE KEYWORD using Sample program // namesp2
...
// ---------------------------------------------------#include // Namespace std void message() // Global function ::message() { std::cout << "Within function ::message()\n"; } namespace A { using namespace std; // Names of std are visible here void message() // Function A::message() { cout << "Within function A::message()\n"; } } namespace B { using std::cout; // void message(void); // } void B::message(void) // { cout << "Within function } int main() { using namespace std; using B::message; Declaring cout of std
...
cout << "\nCall of::message()" << endl; ::message(); // Global function return 0; } THE KEYWORD using ■ 211 You can simplify access to the elements of a namespace by means of a using declaration or using directive
...
Just like normal declarations, using declarations and using directives can occur at any part of the program
...
Example: using myLib::calculate; // Declaration You can then call the function calculate() from the myLib namespace
...
7, 5); This assumes that you have not previously used the name calculate in the same scope
...
Example: using namespace myLib; This statement allows you to reference the identifiers in the myLib namespace directly
...
If identical identifiers occur in the current namespace and an imported namespace, the using directive does not automatically result in a conflict
...
In this case, you should use the scope resolution operator to resolve the situation
...
The using directive was used in previous examples to import any required identifiers to the global scope: Example: #include using namespace std; When developing large-scale programs or libraries, it is useful to declare the elements of any proprietary namespaces in header files
...
■ CHAPTER 11 ■ exe rc i se s 212 ST0RAGE CLASSES AND NAMESPACES EXERCISES Program listing for exercise 1 // scope
...
// 2
...
// 4
...
// 6
...
EXERCISES ■ 213 Exercise 1 In general, you should use different names for different objects
...
The new declaration hides any object using the same name outside of the block
...
The program on the opposite page uses identical variable names in different blocks
...
The names of types, functions, macros, and so on are declared in the header files tool1
...
h for users of these libraries
...
In order to use both libraries, you will need to define namespaces
...
h
...
h
...
h
...
To resolve potential naming conflicts, define the namespaces TOOL1 and TOOL2 that include the relevant header files
...
cpp // Tests an internal static variable // --------------------------------------------------#include #include using namespace std; double x = 0
...
0 ) { x += fun(); cout << " Within main(): " << setw(5) << x << endl; } return 0; } double fun() { static double x = 0; cout << " Within fun():" << setw(5) << x++; return x; } EXERCISES ■ 215 Exercise 3 Test your knowledge of external and static variables by reference to the program on the opposite page
...
The function getPassword(), which checks password input, was introduced previously as an example of the use of static variables
...
cpp, which contains the function getPassword(), by adding the function changePassword()
...
Save the modified source file as Passw2
...
b
...
Only authorized users, that is, users that have access to the password, are allowed to perform bookings
...
cpp
...
If the user enters the correct password, he or she can change the password
...
✓ NOTE The modified password is only available during runtime as it is not stored permanently
...
h Defining first function calculate() inline
...
h Defining second function calculate() inline
...
cpp Uses two "libraries" and tests name lookup conflicts
...
h" } namespace TOOL2 { #include "tool2
...
5, y = 10
...
0; cout << "Calling function of Tool1!" << endl; res = TOOL1::calculate( x, y); cout << "Result: " << res << "\n---------------------------------" << endl; cout << "Calling function of Tool2!" << endl; res = TOOL2::calculate( x, y); cout << "Result: " << res << endl; return 0; } Exercise 3 Screen output of the program In In In In fun(): fun(): fun(): fun(): 0 1 2 3 In In In In main(): main(): main(): main(): 1
...
5 6
...
5 217 218 ■ CHAPTER 11 ST0RAGE CLASSES AND NAMESPACES Exercise 4 // ----------------------------------------------------// Passw2
...
// ----------------------------------------------------#include #include #include #include using namespace std; static long timediff(void); // Prototype static string secret = "guest"; // Password static long maxcount = 3, maxtime = 60; // Limits bool getPassword() { // As before
...
} // Read and verify a password
...
// As before
...
} bool changePassword() { string word1,word2; // Changes password
...
sync(); // Discards former input cin >> setw(20) >> word1; SOLUTIONS ■ if( word1
...
cpp // Testing the functions getPassword() and // changePassword()
...
three // attempts within 60 seconds), the user can change it
...
bool changePassword(void); // Change a password
...
sync(); cin
...
get() != '\n') ; } inline char getYesOrNo() // Read character Y or N
...
sync(); cin
...
get(c); c = toupper(c); // Permitting lower case letters also
...
get(choice); choice = toupper(choice); cls(); cout << header << endl; // Header switch( choice) { case 'B': // Booking if( !getPassword() ) { cout << "Access denied!" << endl; go_on(); } else { cout << "Welcome!\n\n" << "Do you want to change the password? (y/n)"; if( getYesOrNo() == 'Y') { if( changePassword() ) cout << "Password changed!" << endl; else cout << "Password unchanged!" << endl; go_on(); } // Place statements for booking here
...
In this context, passing by reference and read-only access to arguments are introduced
...
7, &rx = x; Object names: The object in memory
...
7
...
cpp // Demonstrates the definition and use of references
...
7F; int main() { float &rx = x; // double &ref = x; // Global // Local reference to x // Error: different type! rx *= 2; cout << " x = " << x << endl // x = 21
...
4 const float& cref = x; // Read-only reference cout << "cref = " << cref << endl; // ok! // ++cref; // Error: read-only! const string str = "I am a constant string!"; // str = "That doesn't work!"; // Error: str constant! // string& text = str; // Error: str constant! const string& text = str; // ok! cout << text << endl; // ok! Just reading
...
Defining a reference does not occupy additional memory
...
References are particularly useful as parameters and return values of functions
...
Given that T is a type, T& denotes a reference to T
...
7; float& rx = x; // or: float &rx = x; rx is thus a different way of expressing the variable x and belongs to the type “reference to float”
...
The & character, which indicates a reference, only occurs in declarations and is not related to the address operator &! The address operator returns the address of an object
...
Example: &rx // Address of x, thus is equal to &x A reference must be initialized when it is declared, and cannot be modified subsequently
...
ᮀ Read-Only References A reference that addresses a constant object must be a constant itself, that is, it must be defined using the const keyword to avoid modifying the object by reference
...
Example: int a; const int& cref = a; // ok! The reference cref can be used for read-only access to the variable a, and is said to be a read-only identifier
...
1415927; Since the constant does not take up any memory space, the compiler creates a temporary object which is then referenced
...
cpp Demonstrating functions with parameters of reference type
...
It is syntactically simpler to use references, although not always permissible
...
When a function is called, a reference parameter is initialized with the object supplied as an argument
...
Example: void test( int& a) { ++a; } Based on this definition, the statement test( var); // For an int variable var increments the variable var
...
If an object is passed as an argument when passing by reference, the object is not copied
...
ᮀ Comparison to Passing by Value In contrast to a normal pass by value an expression, such as a+b, cannot be used as an argument
...
Using references as parameters offers the following benefits: ■ ■ arguments are not copied
...
Passing by value allows only one result as a return value, unless you resort to using global variables
...
Example: void display( const string& str); The function display() contains a string as an argument
...
Instead, str is simply a reference to the argument
...
226 ■ CHAPTER 12 ■ REFERENCES AND POINTERS REFERENCES AS RETURN VALUE Sample program // // // // Ref3
...
-------------------------------------------------- #include #include using namespace std; // Returns a // reference to // the minimum
...
1, x2 = x1 + 0
...
cout << "x1 = " << x1 << " " << "x2 = " << x2 << endl; cout << "Minimum: " << y << endl; ++refMin( x1, cout << "x1 = << "x2 = ++refMin( x1, cout << "x1 << "x2 refMin( x1, cout << "x1 << "x2 refMin( x1, cout << "x1 << "x2 return 0; x2); // ++x1, as x1 is minimal " << x1 << " " // x1 = 2
...
6 x2); // ++x2, because x2 is // the minimum
...
1 = " << x2 << endl; // x2 = 2
...
1; // x1 = 10
...
= " << x1 << " " // x1 = 10
...
6 x2) += 5
...
0, because // x2 is the minimum
...
1 = " << x2 << endl; // x2 = 7
...
NOTE The expression refMin(x1,x2) represents either the object x1 or the object x2, that is, the object containing the smaller value
...
The function call then represents an object, and can be used just like an object
...
Pay attention to the following point when returning references and pointers: The object referenced by the return value must exist after leaving the function
...
This would destroy the string on leaving the function and the reference would point to an object that no longer existed
...
” Thus, calling message() represents a string type object, and the following statements are valid: message() = "Let's go to the beer garden!"; message() += " Cheers!"; cout << "Length: " << message()
...
Then a new string is appended before the length of the referenced string is output in the third statement
...
Example: const string& message(); // Read-only! References are commonly used as return types when overloading operators
...
Refer to the chapters on overloading operators later in this book for more details
...
228 ■ CHAPTER 12 ■ REFERENCES AND POINTERS EXPRESSIONS WITH REFERENCE TYPE Example: Operator << of class ostream cout << "Good morning" << '!'; cout << "Good morning" Reference to cout <<'!'; Sample assignments of class string // Ref4
...
// -------------------------------------------------#include #include #include // For toupper() using namespace std; void strToUpper( string& ); // Prototype int main() { string text("Test with assignments \n"); strToUpper(text); cout << text << endl; strToUpper( text = "Flowers"); cout << text << endl; strToUpper( text += " cheer you up!\n"); cout << text << endl; return 0; } void strToUpper( string& str) // Converts the content { // of str to uppercase
...
length(); for( int i=0; i < len; ++i) str[i] = toupper( str[i]); } EXPRESSIONS WITH REFERENCE TYPE ■ 229 Every C++ expression belongs to a certain type and also has a value, if the type is not void
...
ᮀ The Stream Class Shift Operators The << and >> operators used for stream input and output are examples of expressions that return a reference to an object
...
This allows you to repeatedly use the << on the expression: cout << "Good morning" << '!' The expression is then equivalent to (cout << " Good morning ") << '!' Expressions using the << operator are composed from left to right, as you can see from the table of precedence contained in the appendix
...
This allows repeated use of the >> operator
...
These operators return a reference to the operand on the left
...
In turn, the expression itself represents the object a
...
However, the class definition stipulates the available operators
...
Example: string name("Jonny "); name += "Depp"; //Reference to name Since an expression of this type represents an object, the expression can be passed as an argument to a function that is called by reference
...
230 ■ CHAPTER 12 ■ REFERENCES AND POINTERS DEFINING POINTERS Sample program // pointer1
...
// -------------------------------------------------#include using namespace std; int var, *ptr; // Definition of variables var and ptr int main() { var = 100; ptr = &var; // Outputs the values and addresses // of the variables var and ptr
...
var 100 456FD4 ptr 456FD4 456FD0
...
Linked lists or trees whose elements are generated dynamically at runtime are typical examples
...
Using the address operator, &, for a given object creates a pointer to that object
...
A pointer points to a memory address and simultaneously indicates by its type how the memory address can be read or written to
...
ᮀ Pointer Variables An expression such as &var is a constant pointer; however, C++ allows you to define pointer variables, that is, variables that can store the address of another object
...
ptr can thus store the address of an int variable
...
” Pointer types are derived types
...
In the above example T is an int type
...
Example: int a, *p, &r = a; // Definition of a, p, r After declaring a pointer variable, you must point the pointer at a memory address
...
ᮀ References and Pointers References are similar to pointers: both refer to an object in memory
...
A pointer has its own memory address and can be manipulated by pointing it at a new memory address and thus referencing a different object
...
3; += 4
...
Assign the value 12
...
5
...
Address and value of the variables x and px px x Address of px Address of x = value of px Value of x &px &x px x *px Notes on addresses in a program ■ ■ ■ Each pointer variable occupies the same amount of space, independent of the type of object it references
...
On a 32-bit computer, such as a PC, this is four bytes
...
This allows for efficient storage management and the swapping of currently unused memory blocks to the hard disk
...
Thus, the special value 0 is used to indicate an error
...
A pointer containing the value NULL is also called NULL pointer
...
As a programmer, you must always distinguish between the pointer ptr and the addressed object *ptr
...
// Let ptr point to a
...
The assignment b = a; would return the same result
...
The star character * used for defining pointer variables is not an operator but merely imitates the later use of the pointer in expressions
...
The indirection operator * has high precedence, just like the address operator &
...
This also helps distinguish the redirection operator from the binary multiplication operator *, which always takes two operands
...
The term L-value occurs commonly in compiler error messages and is derived from the assignment
...
Expressions other than an L-value are often referred to as R-values
...
However, a constant or an expression, such as x + 1, is an R-value
...
Given a pointer variable p, both p and *p are L-values, as *p designates the object to which p points
...
cpp // Definition and call of function swap()
...
// ---------------------------------------------------#include using namespace std; void swap( float *, float *); // Prototype of swap() int main() { float x = 11
...
2F;
...
...
...
} // p1 = &x void swap( float *p1, float *p2) { float temp; // Temporary variable temp = *p1; *p1 = *p2; *p2 = temp; } // At the above call p1 points // to x and p2 to y
...
The function that is called is then passed a copy of the object (passing by value) the parameter in question is a reference
...
In the first case, the argument passed to the function cannot be manipulated by the function
...
However, there is a third way of passing by reference—passing pointers to the function
...
If, for example, the function func() requires the address of an int value as an argument, you can use the following statement Example: long func( int *iPtr ) { // Function block } to declare the parameter iPtr as an int pointer
...
In the program on the opposite page, the function swap() swaps the values of the variables x and y in the calling function
...
The parameters p1 and p2 in swap() are thus declared as float pointers
...
When the function manipulates the expressions *p1 and *p2, it really accesses the variables x and y in the calling function and exchanges their values
...
// Find the error! void swap(float *p1, float *p2) { float *temp; // Temporary variable temp = p1; p1 = p2; p2 = temp; } Solutions of quadratic equations The quadratic equation: a*x2 + b*x + c = 0 has real solutions: x12 = (-b ± √(b2 - 4ac)) / 2a if the discriminant satisfies: b2 -4ac >= 0 If the value of (b2 - 4ac) is negative, no real solution exists
...
5, X1 = 3
...
5 = 0 x2 - 6x + 9 = 0 2x2 + 2 = 0 none x2 = -0
...
0 EXERCISES ■ 237 Exercise 1 What happens if the parameter in the sample function strToUpper() is declared as a string& instead of a string? Exercise 2 Write a void type function called circle()to calculate the circumference and area of a circle
...
NOTE Given a circle with radius r: Area = π * r * r and circumference = 2 * π * r where π = 3
...
5, 1
...
5,
...
0
...
The version of the function swap() opposite can be compiled without producing any error messages
...
What is wrong? b
...
Then write and test a version of the function swap() that uses references instead of pointers
...
The formula for calculating quadratic equations is shown opposite
...
false, if no real solution is available, otherwise true
...
■ CHAPTER 12 ■ solutions 238 REFERENCES AND POINTERS SOLUTIONS Exercise 1 The call to function strToUpper() is left unchanged
...
e
...
Thus, only a local copy of the string is changed in the function, but the string in the calling function remains unchanged
...
cpp // Defines and calls the function circle()
...
5, endRadius = 10
...
5; string header = "\n line( 50, '-'); // Start, end and // step width of // the table ***** Computing Circles ***** \n", int main() { double rad, circuit, plane; cout << header << endl; cout << setw(10) << "Radius" << setw(20) << "Circumference" << setw(20) << "Area\n" << line << endl; cout << fixed; // Floating point presentation for( rad = startRadius; rad < endRadius + step/2; rad += step) { circle( rad, circuit, plane); cout << setprecision(1)<< setw(8) << rad << setprecision(5)<< setw(22) << circuit << setw(20) << plane < } return 0; } SOLUTIONS ■ // Function circle(): Compute circumference and area
...
1415926536; u = 2 * pi * r; f = pi * r * r; } Exercise 3 // ---------------------------------------------------// swap
...
// 1
...
version: parameters with reference type
...
1F; float y = 22
...
cout << "x and y after 1
...
cout << "x and y after 2
...
239 240 ■ CHAPTER 12 REFERENCES AND POINTERS void swap(float& a, float& b) { float temp; temp = a; a = b; b = temp; // Reference version // Temporary variable // For above call // a equals x and b equals y } Exercise 4 // // // // // // // // ---------------------------------------------------quadEqu
...
---------------------------------------------------- #include #include #include #include using namespace std; // For the square root sqrt() string header = " *** Solutions of Quadratic Equations ***\n", line( 50, '-'); // ----- Prototypes ----// Computing solutions: bool quadEquation( double a, double b, double c, double* x1Ptr, double* x2Ptr); // Printing the equation and its solutions: void printQuadEquation( double a, double b, double c); int main() { cout << header << endl; printQuadEquation( 2
...
0, -1
...
0, -6
...
0); printQuadEquation( 2
...
0, 2
...
0, x2 = 0
...
\n\n"; cin
...
// Returns: true, if a solution exists, // otherwise false
...
*x1Ptr = (-b + help) / (2*a); *x2Ptr = (-b - help) / (2*a); return_flag = true; } return return_flag; } 241 This page intentionally left blank chapter 13 Defining Classes This chapter describes how classes are defined and how instances of classes, that is, objects, are used
...
243 244 ■ CHAPTER 13 ■ DEFINING CLASSES THE CLASS CONCEPT Real World A Car Abstraction Class CAR Properties (Data Members): Date when built Capacity (PS) Serial number
...
Instantiation Objects car1 car2 Properties: Date when built = 1990 Capacity = 100 Chassis number = 11111
...
Methods
...
••• THE CLASS CONCEPT ■ 245 Classes are the language element in C++ most important to the support object-oriented programming (OOP)
...
ᮀ Data Abstraction Humans use abstraction in order to manage complex situations
...
Classes allow more direct use of the results of this type of abstraction in software development
...
In object-oriented programming, analysis comprises identifying and describing objects and recognizing their mutual relationships
...
In C++, a class is a user-defined type
...
Classes are simply patterns used to instantiate, or create, objects of the class type
...
ᮀ Data Encapsulation When you define a class, you also specify the private members, that is, the members that are not available for external access, and the public members of that class
...
Access to object data is rarely direct, that is, object data is normally declared as private and then read or modified by methods with public declarations to ensure correct access to the data
...
If needed, the internal structure of the program data can even be modified
...
This allows you to enhance an application by programming an improved class version without changing a single byte of the application
...
This describes the concept of data encapsulation concisely
...
h // Defining the class Account
...
#define _ACCOUNT_ #include #include using namespace std; class Account { private: string name; unsigned long nr; double balance; // Sheltered members: // Account holder // Account number // Account balance public: //Public interface: bool init( const string&, unsigned long, double); void display(); }; #endif // _ACCOUNT_ DEFINING CLASSES ■ 247 A class definition specifies the name of the class and the names and types of the class members
...
The data members and methods are then declared in the subsequent code block
...
At the same time, the class members are divided into: ■ ■ private members, which cannot be accessed externally public members, which are available for external access
...
The opposite page shows a schematic definition of a class
...
This provides for data encapsulation
...
The data members, such as the name of the account holder, the account number, and the account balance, are declared as private
...
The labels private: and public: can be used at the programmer’s discretion within a class: ■ ■ you can use the labels as often as needed, or not at all, and in any order
...
If you omit both the private and public labels, all the class members are assumed to be private
...
These rules often reflect the target platform and the class libraries used
...
Class names begin with an uppercase letter and member names with a lowercase letter
...
A member of another class could therefore also be named display()
...
cpp // Defines methods init() and display()
...
h" // Class definition #include #include using namespace std; // The method init() copies the given arguments // into the private members of the class
...
size() < 1) // No empty name return false; name = i_name; nr = i_nr; balance = i_balance; return true; } // The method display() outputs private data
...
Only then can the objects of the class be used
...
Syntax: type class_name::function_name(parameter_list) {
...
Within a method, all the members of a class can be designated directly using their names
...
In particular, methods belonging to the same class can call each other directly
...
Thus, private members are completely controlled by the class
...
To allocate memory, you must define an object
...
ᮀ Modular Programming A class is normally defined in several source files
...
If you place the definition of the class Account in the file Account
...
Methods must always be defined within a source file
...
cpp, for example
...
Separating classes from application programs facilitates re-use of classes
...
When the project is compiled and linked, modified source files are automatically re-compiled and linked to the application program
...
22 current name "Dylan, Bob" nr 87654321 balance –1300
...
An object is also referred to as an instance of a class
...
Syntax: class_name object_name1 [, object_name2,
...
Memory is now allocated for the data members of the current object
...
ᮀ Objects in Memory If multiple objects of the same class type are declared, as in Example: Account current, savings; each object has its own data members
...
However, these data members occupy a different position in memory than the data members belonging to current
...
Only one instance of the machine code for a method exists in memory—this applies even if no objects have been defined for the class
...
This results in the memory content as shown on the opposite page, when the method init() is called for each object with the values shown
...
Each member object is thus defined but not explicitly initialized
...
The initial values of the members nr and balance are unknown, however
...
You can define exactly how an object is created and destroyed
...
Constructors are specifically responsible for initializing objects—more details are given later
...
cpp // Uses objects of class Account
...
h" int main() { Account current1, current2; current1
...
99); current1
...
balance += 100; // Error: private member current2 = current1; current2
...
// ok // New values for current2 current2
...
40); current2
...
display(); return 0; } // // // // // To use a reference: mtr is an alias name for object current1
...
USING OBJECTS ■ 253 ᮀ Class Member Access Operator An application program that manipulates the objects of a class can access only the public members of those objects
...
Syntax: object
...
Example: Account current; current
...
99); The expression current
...
This method is called with three arguments for current
...
Example: current
...
nr = 1234567; current
...
99; // Error: // private // members Access to the private members of an object is not permissible outside the class
...
Example: cout << current
...
display(); // Error // ok The method display() displays all the data members of current
...
The statement display(); would result in an error message, since there is no global function called display()
...
However, the source and target objects must both belong to the same class
...
Example: Account current1, current2; current2
...
0); current1 = current2; This copies the data members of current2 to the corresponding members of current1
...
cpp // Uses pointers to objects of class Account
...
h" // Includes , bool getAccount( Account *pAccount); // Prototype int main() { Account current1, current2, *ptr = ¤t1; ptr->init("Cheer, Mary", 3512345, 99
...
init(
...
display() Let ptr point to current2 Input and output a new account
...
bool getAccount( Account *pAccount ) { string name, line(50,'-'); // Local variables unsigned long nr; double startcapital; cout << line << '\n' << "Enter data for a new account: \n" << "Account holder: "; if( !getline(cin,name) || name
...
You can assign this address to a suitable pointer
...
5); Account *ptrAccount = &savings; This defines the object savings and a pointer variable called ptrAccount
...
This makes *ptrAccount the object savings itself
...
display(); to call the method display() for the object savings
...
has higher precedence than the * operator
...
Syntax: objectPointer->member This expression is equivalent to (*objectPointer)
...
Example: ptrAccount->display(); This statement calls the method display() for the object referenced by ptrAccount, that is, for the object savings
...
The difference between the class member access operators
...
ᮀ The Sample Program Pointers to objects are often used as function parameters
...
The example on the opposite page illustrates this point
...
When called, the address of the account is passed: getAccount(ptr) // or: getAccount(¤t1) The function can then use the pointer ptr and the init() method to write new data to the referenced object
...
cpp // Defines and uses a struct
...
double sales; // Sales per month
...
name << right << setw(10) << v
...
name = "Strom, Rita"; rita
...
37; john
...
sales = 23001
...
sales += 1700
...
sales + john
...
// Who gets the if( john
...
sales) // most sales? ptr = &rita; cout << "\nSalesman of the month: " << ptr->name << endl; // Representative's name // pointed to by ptr
...
Extensive data such as the data for the articles in an automobile manufacturer’s stocks can be organized for ease of viewing and stored in files
...
Thus, you can use the class keyword to define the structure of a record in C++
...
The above definition of Date with the members day, month, and year is thus equivalent to: Example: struct Date { short month, day, year; }; ᮀ The Keywords class and struct You can also use the keyword struct to define a class, such as the class Account
...
...
In contrast to a class defined using the class keyword, all the class members are public unless a private label is used
...
Example: Date future; future
...
Example: Date birthday = { 1, 29, 1987}; The first element in the list initializes the first data member of the object, and so on
...
cpp // Defines and uses a union
...
word() = 256; cout << "\nWord: " << (int)wb
...
lowByte() << "\nHigh-byte: " << (int)wb
...
However, a union is a class whose members are stored in the same memory space
...
Of course, a union cannot store various data members at the same address simultaneously
...
ᮀ Definition Syntactically speaking, a union is distinguished from a class defined as a class or struct only by the keyword union
...
The union Number can be used to store either integral or floating-point numbers
...
This is similar to the default setting for structures
...
Example: number1
...
n *= 3; number2
...
77; // and multiply by 3
...
This is normally achieved using an additional type field that identifies the current content
...
If we look at our example, the union Number, this size is defined by the double member, which defaults to 8 == sizeof(double) byte
...
■ CHAPTER 13 ■ exercise 260 DEFINING CLASSES EXERCISE Struct tm in header file ctime 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; }; // // // // // // // // // 0 - 59(60) 0 - 59 0 - 23 Day of month: 1 - 31 Month: 0 - 11 (January == 0) Years since 1900 (Year - 1900) Weekday: 0 - 6 (Sunday == 0) Day of year: 0 - 365 Flag for summer-time Sample calls to functions time( ) and localtime( ) #include #include using namespace std; struct tm *ptr; time_t sec;
...
// For seconds
...
To initialize a struct of type tm and return a pointer to it
...
day of the year " << ptr->tm_year << endl;
...
■ Define the class Date for this purpose using three integral data members for day, month, and year
...
Implement the methods for the class Date in a separate source file: 1
...
2
...
A range check is not required at this stage, but will be added later
...
The method init() without parameters writes the current date to the corresponding members
...
The type time_t is defined as long in ctime
...
This value can be passed to the function localtime() that converts the number of seconds to the local type tm date and returns a pointer to this structure
...
To this end, define two objects for the class and display the current date
...
■ CHAPTER 13 DEFINING CLASSES ■ solution 262 SOLUTION // // // // ---------------------------------------------------date
...
---------------------------------------------------- #ifndef _DATE_ #define _DATE_ class Date { private: short month, day, year; // Avoid multiple inclusion
...
cpp Implementing the methods of class Date
...
h" #include #include using namespace std; // --------------------------------------------------void Date::init(void) // Get the present date and { // assign it to data members
...
time_t sec; // For seconds
...
// Initialize a struct of // type tm and return a // pointer to it
...
cpp // Using objects of class Date
...
h" #include using namespace std; int main() { Date today, birthday, aDate; today
...
init( 12, 11, 1997); cout << "Today's date: "; today
...
print(); cout << "----------------------------------\n" "Some testing outputs:" << endl; aDate = today; aDate
...
init( 1, 5, 2000); aDate
...
// Writing to aDate
...
print(); return 0; } ■ 263 This page intentionally left blank chapter 14 Methods This chapter describes ■ how constructors and destructors are defined to create and destroy objects ■ how inline methods, access methods, and read-only methods can be used ■ the pointer this, which is available for all methods, and ■ what you need to pay attention to when passing objects as arguments or returning objects
...
h // Defining class Account with two constructors
...
cpp: Account::Account( const string& a_name, unsigned long a_nr, double a_state) { nr = a_nr; name = a_name; state = a_state; } Account::Account( const string& a_name ) { name = a_name; nr = 1111111; state = 0
...
The programmer must ensure that the variable is initialized with suitable values
...
Non-initialized objects can lead to serious runtime errors in your programs
...
This ensures that objects will always have valid data to work on
...
ᮀ Declaration Constructors can be identified by their names
...
Constructors are normally declared in the public section of a class
...
Constructors can be overloaded, just like other functions
...
This allows for different methods of object initialization
...
The class now has two constructors
...
If the number of arguments is smaller than the number of data members, the remaining members can be initialized using default values
...
268 ■ CHAPTER 14 ■ METHODS CONSTRUCTOR CALLS Sample program // account2_t
...
// --------------------------------------------------#include "account
...
99 ), save("Lucky, Luke"); Account depot; giro
...
display(); // Error: no default constructor // defined
...
0); save = temp; // ok: Assignment of // objects possible
...
display(); // Or by the presently available method init(): save
...
0); save
...
For this reason, a constructor does not have a return type
...
ᮀ Initialization When an object is defined, initial values can follow the object name in parentheses
...
After allocating sufficient memory for the object, the constructor is called
...
Example: account nomoney("Poor, Charles"); This statement calls the constructor with one parameter for the name
...
If the compiler is unable to locate a constructor with a suitable signature, it will not create the object but issue an error message
...
0); // Error! The class Account does not contain a constructor with two parameters
...
Example: account nomoney = "Poor, Charles"; This statement is equivalent to the definition in the example before last
...
For example, int i(0); is equivalent to int i =0;
...
The default constructor is only called if an object definition does not explicitly initialize the object
...
If a class does not contain a constructor definition, the compiler will create a minimal version of the default constructor as a public member
...
By contrast, if a class contains at least one constructor, a default constructor must be defined explicitly, if it is needed
...
270 ■ CHAPTER 14 ■ METHODS DESTRUCTORS Sample program // demo
...
// --------------------------------------------------#include #include using namespace std; int count = 0; // Number of objects
...
object!\n" } Demo:: ~Demo() // Defining the destructor { cout << "I am the destructor of " << name << '\n' << "The " << count << "
...
" << endl; Demo firstLocalObject("the 1
...
local object"); static Demo staticObject("the static object"); cout << "\nLast statement within the inner block" << endl; } cout << "Last statement in main()
...
The tasks involved in cleaning up include releasing memory and closing files
...
ᮀ Declaration and Definition Destructors are declared in the public section and follow this syntax: Syntax: ∼class_name(void); Just like the constructor, a destructor does not have a return type
...
Each class thus has one destructor only
...
It is important to define a destructor if certain actions performed by the constructor need to be undone
...
The destructor in the Account class has no specific tasks to perform
...
The first data member to be created is therefore cleaned up last
...
ᮀ Calling Destructors A destructor is called automatically at the end of an object’s lifetime: ■ ■ for local objects except objects that belong to the static storage class, at the end of the code block defining the object for global or static objects, at the end of the program
...
272 ■ CHAPTER 14 ■ METHODS INLINE METHODS Sample class Account // account
...
0) { name = a_name; nr = a_nr; state = a_state; } ~Account(){ } // Dummy destructor: implicit inline void display(); }; // display() outputs data of class Account
...
This is the only way to ensure data encapsulation and class functionality
...
In fact, saving a re-entry address and jumping to the called function and back into the calling function can take more time than executing the function itself
...
ᮀ Explicit and Implicit inline Methods Methods can be explicitly or implicitly defined as inline
...
You simply need to place the inline keyword before the method name in the function header when defining the method
...
} Since the compiler must have access to the code block of an inline function, the inline function should be defined in the header containing the class definition
...
Methods of this type are known as implicit inline methods, although the inline keyword is not used
...
This point is illustrated by the new definition of the Account class opposite
...
The constructor has a default value for each argument, which means that we also have a default constructor
...
Example: Account temp; Although we did not explicitly supply values here, the object temp was correctly initialized by the default constructor we defined
...
h // Class Account with set- and get-methods
...
0) { name = a_name; nr = a_nr; state = a_state; } ∼Account(){ } // Access methods: const string& getName() { return name; } bool setName( const string& s) { if( s
...
#endif // _ACCOUNT_ ACCESS METHODS ■ 275 ᮀ Accessing Private Data Members An object’s data members are normally found in the private section of a class
...
Access methods offer a far more useful way of accessing the private data members
...
If the access methods were defined as inline, access is just as efficient as direct access to the public members
...
You can now use the getName(), getNr(), getState() methods to read the individual data members
...
Direct access for write operations could be possible otherwise
...
This allows you to define a new balance, as follows: Example: save
...
0); ᮀ Access Method Benefits Defining access methods for reading and writing to each data member may seem like a lot of work—all that typing, reams of source code, and the programmer has to remember the names and tasks performed by all those methods
...
There are two important issues: ■ ■ Access methods can prevent invalid access attempts at the onset by performing sanity checks
...
Access methods also hide the actual implementation of a class
...
If you detect that a new data structure will allow more efficient data handling, you can add this modification to a new version of the class
...
You simply recompile the application program
...
h // Account class with read-only methods
...
// as before Sheltered members before Public interface destructor // Get-methods: const string& getName() const { return name; } unsigned long getNr() const { return nr; } double getState() const { return state; } // Set-methods:
...
inline void Account::display() const { cout << fixed << setprecision(2) << "--------------------------------------\n" << "Account holder: " << name << '\n' << "Account number: " << nr << '\n' << "Account state: " << state << '\n' << "--------------------------------------\n" << endl; } #endif // _ACCOUNT_ const OBJECTS AND METHODS ■ 277 ᮀ Accessing const Objects If you define an object as const, the program can only read the object
...
Example: const Account inv("YMCA, FL", 5555, 5000
...
This also means that methods such as setName() cannot be called for this object
...
The reason for this is that the compiler cannot decide whether a method performs write operations or only read operations with data members unless additional information is supplied
...
To identify a method as read-only, append the const keyword in the method declaration and in the function header for the method definition
...
Example: cout << "Account number: " << inv
...
The compiler issues an error message if a read-only method tries to modify a data member
...
ᮀ const and Non-const Versions of a Method Since the const keyword is part of the method’s signature, you can define two versions of the method: a read-only version, which will be called for constant objects by default, and a normal version, which will be called for non-const objects
...
cpp // Using standard methods
...
cd4 = cd2; // Assignment! string line( 70,'-'); line += '\n'; cout << line << left << setw(20) << "Interpreter" << setw(30) << "Title" << "Length (Min:Sec)\n" << line << endl; printLine(cd3); // Call by value ==> printLine(cd4); // Copy constructor! return 0; } void printLine( CD cd) { cout << left << setw(20) << cd
...
getTitle() << right << setw(5) << cd
...
getSeconds() % 60 << endl; } STANDARD METHODS ■ 279 Every class automatically contains four standard methods: ■ ■ ■ ■ the default constructor the destructor the copy constructor and the assignment
...
As illustrated by the sample class Account, the compiler only uses the pre-defined default constructor if no other constructor is available
...
ᮀ Copy Constructor The copy constructor initializes an object with another object of the same type
...
Example: Account myAccount("Li, Ed", 2345, 124
...
Each member is copied individually, that is, the following initialization process takes place: yourAccount
...
name; yourAccount
...
nr; yourAccount
...
state; The copy constructor is also called when an object is passed to a function by value
...
ᮀ Assignment Assignment has been used in several previous examples
...
Example: hisAccount = yourAccount; The data members of the yourAccount object are copied to the corresponding members of hisAccount in this case also
...
Later in the book, you will be introduced to situations where you need to define the copy constructor or an assignment yourself, and the necessary techniques will be discussed
...
h // The class DayTime represents the time in // hours, minutes and seconds
...
) hour = minute = second = 0; // hour is equivalent } // to this->hour etc
...
asSeconds(); } // this->asSeconds() < t
...
A method will always reference the object with which it was called
...
The address of the current object is available to the method via the constant pointer this
...
As this is a constant pointer, it cannot be redirected
...
ᮀ Using the this Pointer You can use the this pointer within a method to address an object member as follows: Example: this->data // Data member: data // Calling member function this->func() The compiler implicitly creates an expression of this type if only a member of the current object is supplied
...
However, the above statement would be invalid for a readonly method
...
This point is illustrated by the sample method setTime() on the opposite page
...
This situation often occurs when the current object needs to be returned as a copy or by reference
...
h"
...
depart2
...
isLess( depart2) ) cout << "\nThe 1st plane takes off earlier" << endl;
...
h" // Defines the global function swap(): void swap( DayTime& t1, DayTime& t2) // Two { // parameters! DayTime temp(t1); t1 = t2; t2 = temp; // To swap } // t1 and t2
...
g
...
swap( arrival1, arrival2); // To swap
...
public: void swap( DayTime& t) // One parameter! { // To swap *this and t: DayTime temp(t); t = *this; *this = temp; } }; // A call (e
...
in function main()): #include "DayTime
...
arrival1
...
PASSING OBJECTS AS ARGUMENTS ■ 283 ᮀ Passing by Value As you already know, passing by value copies an object that was passed as an argument to the corresponding parameter of a function being called
...
Example: bool isLess( DayTime t) const; When the method isLess() is called, the copy constructor executes and initializes the created object, t, with the argument
...
isLess( depart2) // Copy constructor The function uses a copy of the object depart2
...
ᮀ Passing by Reference The overhead caused by creating and cleaning up objects can be avoided by passing arguments by reference
...
Example: bool isLess( const DayTime& t) const; This new declaration of the isLess() method is preferable to the previous declaration
...
However, isLess() no longer creates an internal copy, but accesses directly the object being passed
...
ᮀ Methods Versus Global Functions Of course, it is possible to write a global function that expects one object as an argument
...
Instead, you would normally define a method for the class and the method would perform the task in hand
...
A different situation occurs where operations with at least two objects need to be performed, such as comparing or swapping
...
However, the function could only access the public interface of the objects
...
The major advantage of a globally defined function is its symmetry
...
This means that conversion rules are applied to both arguments when the function is called
...
h" #include using namespace std; // Functions time(), localtime() const DayTime& currentTime() // Returns the { // present time
...
// Initializes the struct struct tm *time = localtime(&sec); // tm with it
...
setTime( time->tm_hour, time->tm_min, time->tm_sec ); return curTime; } Sample program // DayTim_t
...
h" // Class definition #include using namespace std; const DayTime& currentTime(); // The current time
...
print(); DayTime now(currentTime()); // Copy constructor cout << "\nThe current time is "; now
...
isLess( now) ) cout << "already begun!\n" << endl; else cout << "not yet begun!\n" << endl; return 0; } RETURNING OBJECTS ■ 285 A function can use the following ways to return an object as a return value: It can create a copy of the object, or it can return a reference or pointer to that object
...
Example: DayTime startMeeting() { DayTime start;
...
setTime( 14, 30); return( start); } On exiting the function, the local object start is destroyed
...
ᮀ Returning a Reference Of course, it is more efficient to return a reference to an object
...
If this is the case, the object is destroyed on exiting the function and the returned reference becomes invalid
...
The global function currentTime() on the opposite page exploits this option by returning a reference to the current time that it reads from the system each time the function is called
...
In order to output the time, an additional method, print(), was added to the class
...
In this case too, you must ensure that the object still exists after exiting the function
...
// Unchanged return &curTime; } ■ CHAPTER 14 ■ exercise s 286 METHODS EXERCISES Class Article Private members: Type Article number: Article name: Sales price: long string double Public members: Article(long, const string&, double); ~Article(); void print(); // Formatted output set- and get-methods for any data member Output from constructor An object of type Article
...
This is the
...
Output from destructor The object of type Article
...
There are still
...
EXERCISES ■ 287 Exercise 1 A warehouse management program needs a class to represent the articles in stock
...
Store the class definition for Article in a separate header file
...
Access methods for the data members are to be defined as inline
...
If a negative price is passed as an argument, the price must be stored as 0
...
Implement the constructor, the destructor, and the method print() in a separate source file
...
The constructor must use the arguments passed to it to initialize the data members, additionally increment the global counter, and issue the message shown opposite
...
The method print() displays a formatted object on screen
...
■ The application program (again use a separate source file) tests the Article class
...
A global object and a local object in the main function
...
Two local objects in a function test() that is called twice by main()
...
The function test() displays these objects and outputs a message when it is terminated
...
Additionally, call the access methods to modify individual data members and display the objects on screen
...
Note the order in which constructors and destructors are called
...
The counter for the number of objects is negative after running the program
...
Thus, the operators >> and << , just as all manipulators, are available
...
#include // Class stringstream #include // Manipulators double x = 12
...
Add to the stream
...
Notices for exercise 3 ■ A year is a leap year if it is divisible by 4 but not by 100
...
February has 29 days in a leap year
...
EXERCISES ■ 289 Exercise 2 In the exercises for chapter 13, an initial version of the Date class containing members for day, month, and year was defined
...
The methods are shown on the opposite page
...
The default constructor uses default values, for example, 1
...
1, to initialize the objects in question
...
The constructor and the setDate() method with three parameters do not need to perform range checking
...
The methods isEqual() and isLess() enable comparisons with a date passed to them
...
g
...
You will therefore need to convert any numerical values into their corresponding decimal strings
...
In addition to the cin and cout streams, with which you are already familiar, so-called string streams with the same functionality also exist
...
Instead, the target, or source, is a buffer in main memory
...
Use an application program that calls all the methods defined in the class to test the Date class
...
To avoid this issue, add range checking functionality to the class
...
■ ■ ■ First, write a function called isLeapYear() that belongs to the bool type and checks whether the year passed to it is a leap year
...
h
...
The constructor can call setDate()
...
To do so, and to test setDate(
...
■ CHAPTER 14 ■ solutions 290 METHODS SOLUTIONS Exercise 1 // ---------------------------------------------------// article
...
// ---------------------------------------------------#ifndef _ARTICLE_ #define _ARTICLE_ #include using namespace std; class Article { private: long nr; string name; double sp; // Article number // Article name // Selling price public: Article( long nr=0, const string& name="noname", double sp=0
...
size() < 1) return false; name = s; return true; } void setNr( long n) { nr = n; } void setSP(double v) { sp = v > 0
...
0; } }; #endif // _ARTICLE_ // No empty name // No negative price SOLUTIONS // -----------------------------------------------------// article
...
// Screen output for constructor and destructor calls
...
h" // Definition of the class #include #include using namespace std; // Global counter for the objects: int count = 0; // -----------------------------------------------------// Define constructor and destructor: Article::Article( long nr, const string& name, double sp) { setNr(nr); setName(name); setSP(sp); ++count; cout << "Created object for the article " << name << "
...
article!\n" } Article::~Article() { cout << "Cleaned up object for the article " << name << "
...
void Article::print() { long savedFlags = cout
...
cout << fixed << setprecision(2) << "-----------------------------------------\n" << "Article data:\n" << " Number
...
: " << name << '\n' << " Sales price: " << sp << '\n' << "-----------------------------------------" << endl; cout
...
cout << " --- Go on with return --- "; cin
...
cpp // Tests the Article class
...
h" // Definition of the class #include #include using namespace std; void test(); // -- Creates and destroys objects of Article class -Article Article1( 1111,"volley ball", 59
...
\n" << endl; Article Article2( 2222,"gym-shoes", 199
...
print(); Article2
...
setNr( 2233); shoes
...
setSP( shoes
...
0); cout << "\nThe shoes
...
" << endl; second call to test()
...
\n" << endl; } void test() { Article shirt( 3333, "T-Shirt", 29
...
print(); static Article net( 4444, "volley ball net", 99
...
print(); cout << "\nLast statement in function test()" << endl; } SOLUTIONS ■ 293 Answer to the supplementary question: The copy constructor is called on each “passing by value,” although this constructor has not been defined explicitly
...
However, the explicitly defined destructor, which decrements the counter, is still called for each object
...
h // Defining class Date with optimized // functionality, e
...
range check
...
#define _DATE_ #include using namespace std; class Date { private: short month, day, year; public: Date() { month = day = year = 1; // Default constructor } Date( int month, int day, int year) { if( !setDate( month, day, year) ) month = day = year = 1; // If date is invalid } void setDate(); // Sets the current date bool setDate( int month, int day, int year); int getMonth() const { return month; } int getDay() const { return day; } int getYear() const { return year; } bool isEqual( const Date& d) const { return month == d
...
day && year == d
...
year) return else if( month != d
...
year; month < d
...
day; inline bool isLeapYear( int year) { return (year%4 == 0 && year%100 != 0) || year%400 == 0; } #endif // _DATE_ // -----------------------------------------------------// Date
...
// -----------------------------------------------------#include "Date
...
struct tm *dur; // Pointer to struct tm
...
time(&sec); dur = localtime(&sec); // Get the present time
...
day = (short) dur->tm_mday; month = (short) dur->tm_mon + 1; year = (short) dur->tm_year + 1900; } SOLUTIONS ■ // ----------------------------------------------------bool Date::setDate( int mn, int da, int yr) { if( mn < 1 || mn > 12 ) return false; if( da < 1 || da > 31 ) return false; switch(mn) // Month with less than 31 days { case 2: if( isLeapYear(yr)) { if( da > 29) return false; } else if( da > 28) return false; break; case 4: case 6: case 9: case 11: if( da > 30) return false; } month = (short) mn; day = (short) da; year = (short) yr; return true; } // ----------------------------------------------------void Date::print() const // Output a date { cout << asString() << endl; } // ----------------------------------------------------const string& Date::asString() const // Return a date { // as string
...
<< setw(2) << month << '-' << setw(2) << day << '-' << year; iostream >> dateString; return dateString; } 295 296 ■ CHAPTER 14 METHODS // -----------------------------------------------------// date_t
...
// -----------------------------------------------------#include "Date
...
asString() << endl; today
...
asString() << endl;; if( today
...
setDate( month, day, year)) cerr << "Invalid date!\n" << endl; else { cout << "\nYour first vacation: "; holiday
...
getYear() < holiday
...
In addition, this chapter describes constant members and enumerated types
...
h // Class Result to represent a measurement // and the time of measurement
...
h" // Class DayTime class Result { private: double val; DayTime time; public: Result(); // Default constructor Result(double w, const DayTime& z = currentTime()); Result(double w, int hr, int min, int sec); double getVal() const { return val; } void setVal( double w ) { val = w; } const DayTime& getTime() const { return time; } void setTime( const DayTime& z) { time = z; } bool setTime(int hr, int min, int sec) { return time
...
}; #endif // _RESULT_ A first implementation of constructors #include "result
...
0; } Result::Result( double w, const DayTime& z) { val = w; time = z; } Result::Result( double w, int hr, int min, int sec) { val = w; time = DayTime(hr, min, sec); // Assign a temporary // object of type } // DayTime to time
...
In our example, the Account class, we already made use of this feature
...
An object of the Account class therefore has a string type member sub-object, or member object for short
...
ᮀ Calling Constructors When an object containing member objects is initialized, multiple constructor calls are to be executed
...
The order of the constructor calls is significant in this case
...
Unless otherwise stated, the default constructor will be called for each member object
...
In addition to a double type measurement, the time of each measurement is recorded
...
The default constructor only sets the value of the measurement to 0
...
Example: Result current; The default constructor for the member object time first sets the hours, minutes and seconds to 0
...
0 is assigned to val
...
Example: Result temperature1(15
...
7, 14, 30, 35); Since the compiler has no information on the relation of initial values and member objects, it first calls the default constructor for the member object time
...
300 ■ CHAPTER 15 ■ MEMBER OBJECTS AND STATIC MEMBERS MEMBER INITIALIZERS New implementation of constructors #include "result
...
0) { /*
...
*/ } Result::Result( double w, int hr, int min, int sec) : val(w), time(hr, min, sec) { /*
...
*/ with statements, if needed
...
Sample program // result_t
...
h" #include using namespace std; int main() // Some air temperature measurements { DayTime morning(6,30); Result t1, // Default constructor t2( 12
...
2, 12,0,0), t4(17
...
print(); cout << "\n Temperature Time \n" << "-------------------------" << endl; t2
...
print(); t4
...
Correct values are assigned later
...
Constant objects or references cannot be declared as member objects since it is impossible to assign values to them later
...
When defining a constructor, you can use member initializers to ensure general and efficient use of member objects
...
Example: time(hr,min,sec) // Member initializer Multiple member initializers are separated by commas
...
Example: Result::Result( /* Parameters */ ) : val(w), time(hr, min, sec) { /* Function block */ } This ensures that a suitable constructor will be called for data members with member initializers and avoids calls to the default constructor with subsequent assignments
...
The argument names of the member initializers are normally constructor parameters
...
✓ NOTE Member initializers can only be stated in a constructor definition
...
302 ■ CHAPTER 15 ■ MEMBER OBJECTS AND STATIC MEMBERS CONSTANT MEMBER OBJECTS New version of class Result // result2
...
// --------------------------------------------------#ifndef _RESULT_ #define _RESULT_ #include "DayTime
...
cpp : Tests the new class Result
...
h" #include using namespace std; int main() { DayTime start(10,15); Result m1( 101
...
9); // At current time
...
m2
...
9); // Corrected value for m2 cout << "\n Result Time \n" << "-------------------------" << endl; m1
...
print(); m3
...
For example, you could set the time for a measurement once and not change this time subsequently
...
In this case, the member object time can be declared as follows: Example: const DayTime time; Since the const member object time cannot be modified by a later assignment, the correct constructor must be called to initialize the object
...
ᮀ The Sample Class Result If the member object time is const, the first version of the constructors are invalid since they modify time by means of a later assignment
...
The member initializer ensures that the desired initial values are used to create the member object time
...
) methods can no longer be applied
...
This means that a programmer cannot accidentally overwrite a member declared as a const
...
ᮀ Example with Fundamental Type Data members with fundamental types can also be defined as const
...
Since the client number never changes, it makes sense to define the number as const
...
*/ ) : nr(++id) { /*
...
304 ■ CHAPTER 15 ■ MEMBER OBJECTS AND STATIC MEMBERS STATIC DATA MEMBERS Class Result with static members // result3
...
// --------------------------------------------------#ifndef _RESULT_ #define _RESULT_ #include "DayTime
...
void setMinMax(double w); // private function public: Result(double w, const DayTime& z = currentTime()); Result(double w, int hr, int min, int sec); //
...
cpp // Defining static data members and // methods, which are not defined inline
...
h" double Result::min = 0
...
0; bool Result::first = true; void Result::setMinMax(double w) // Help function { if(first) { min = max = w; first = false; } else if( w < min) min = w; else if( w > max) max = w; } // Constructors with member initializer
...
STATIC DATA MEMBERS ■ 305 ᮀ Class-Specific Data Every object has its own characteristics
...
However, sometimes it is useful to keep some common data that can be accessed by all the objects belonging to a class, for example: ■ ■ figures such as exchange rates, interest rates or time limits which have the same value for every object status information, such as the number of objects, current minimum or maximum threshold values, or pointers to some objects; for example, a pointer to an active window in a window class
...
Since a programmer will also need to manage the data from within the class, it should be represented within the class rather than globally
...
In contrast to normal data members, static data members occur only once in memory
...
On the opposite page, the following statement Example: static double min, max; // Declaration defines two static data members called min and max that record the minimum and maximum values for the measurements
...
Just like member functions, which occur only once, static data members must be defined and initialized in an external source file
...
Example: double Result::min = 0
...
Static data members and member functions belonging to the same class are normally defined in one source file
...
Member functions as before, plus: static double getMin() { return min; } static double getMax() { return max; } }; Application program // result3_t
...
// --------------------------------------------------#include "result3
...
45, morning), temp2( 11
...
0; cout << "\nWhat is the air temperature now? "; cin >> temp; Result temp3(temp); // At current time
...
print(); temp2
...
print(); cout << "\n Minimum Temperature: " << Result::getMin() << "\n Maximum Temperature: " << Result::getMax() << endl; return 0; } ACCESSING STATIC DATA MEMBERS ■ 307 ᮀ Static Data Members and Encapsulation The normal rules for data encapsulation also apply to static data members
...
If the static data members min and max in the Result class are declared public rather than private, and given that temperature is an object belonging to the class, the following statement Example: cout << temperature
...
You can also use the range operator: Example: cout << Result::max; This syntax is preferable to the previous example, since it clearly shows that a static data member exists independently of any objects
...
However, normal methods can be used for class objects only
...
Static member functions are used for this purpose
...
The static keyword is used to define static member functions
...
As the Result class, which was modified to include the static member functions getMin(), setMin(), etc
...
Definitions outside of the class do not need to repeat the static keyword
...
Example: temperature
...
4); Result::setMax(42
...
Calling a static member function does not bind the function to any class object
...
This also means that static member functions cannot access data members and methods that are not static themselves
...
cpp // Uses enum-constants within a class
...
getState(); if( as == Lights::red) { A1
...
setState( Lights::amber); } cout << endl; return 0; } Lights break; break; break; break; ENUMERATION ■ 309 ᮀ Definition An enumeration is a user-definable, integral type
...
A range of values and a name for these values are also defined at the same time
...
The names quoted in the list identify integral constants
...
The first constant has a value of 0, and each subsequent constant has a value that is one higher than its predecessor
...
A Shape type variable can only assume one of these values
...
switch(shape) { case Line: // Variable shape // To evaluate shape //
...
However, you can also define the values of the constants explicitly
...
Example: enum { OFF, OUT=0, ON, IN=1 }; This statement defines the constants OFF and OUT, setting their value to 0, and the constants ON and IN with a value of 1
...
ᮀ Class-Specific Constants Enumeration can be used to define integral symbolic constants in a simple way
...
This allows you to define constants that are visible within a namespace or class only
...
This means that the type and enum constant are only available for direct use within the class
...
Example: if(Lights
...
■ CHAPTER 15 ■ exercise s 310 MEMBER OBJECTS AND STATIC MEMBERS EXERCISES Copy constructor of class Article The copy constructor creates a copy of an existing object
...
The copy constructor in the Article class is thus declared as follows: Declaration of copy constructor: Article( const Article& ); The default copy constructor simply transfers the data members to the new object
...
Public Methods Constructor with one parameter for each data member Access methods for each data member
...
A method for formatted screen output of all data members EXERCISES ■ 311 Exercise 1 In the first exercise of the last chapter you defined a simple class called Article
...
Improve and extend the Article class as follows: ■ ■ ■ ■ Use a static data member instead of a global variable to count the current number of objects
...
The method returns the current number of objects
...
This ensures that the counter will always be accurate
...
Test the new version of the class
...
Exercise 2 A sports club needs a program to manage its members
...
■ ■ ■ ■ ■ ■ Define the Member class using the data members shown opposite
...
Since a member’s birthday will not change, the data member for birthdays must be defined as a const
...
Implement the necessary methods
...
Add a static member called ptrBoss to the class
...
If no chairperson has been appointed, the pointer should point to NULL
...
Use a pointer to set and return the object in question
...
312 ■ CHAPTER 15 MEMBER OBJECTS AND STATIC MEMBERS Sample output Simulation of two traffic lights! Terminate this program with +! 1
...
Light --------------------------RED AMBER GREEN AMBER AMBER RED GREEN AMBER RED AMBER GREEN //
...
The function time() is declared in the header file ctime
...
1
...
The type time_t is defined as long
...
Instead of calling the function time() in a loop, you can use the function Sleep() for Windows or the function sleep() for Unix
...
EXERCISES ■ 313 Exercise 3 Create a program to simulate the signal positions for two sets of traffic lights at a junction
...
■ ■ ■ Each set of lights is switched through the phases red, amber, green, amber, red, and so on
...
The lights operate in an infinite loop that can be terminated by interrupting the program
...
e
...
The status of the lights is constant for a certain number of seconds
...
These values can be different for each set of lights
...
To do so, you can call the standard function time() in a loop
...
■ CHAPTER 15 ■ solutions 314 MEMBER OBJECTS AND STATIC MEMBERS SOLUTIONS Exercise 1 // ---------------------------------------------------// article
...
// ---------------------------------------------------#ifndef _ARTICLE_H_ #define _ARTICLE_H_ #include using namespace std; class Article { private: long nr; string name; double sp; // Static data member: static int countObj; // Article number // Article name // Sales price // Number of objects public: Article( long nr=0, const string& name="noname", double sp=0
...
size() < 1) // No empty Name return false; name = s; return true; } void setNr( long n) { nr = n; } void setSP(double v) { // No negative price sp = v > 0
...
0; } }; #endif // _ARTICLE_ SOLUTIONS // // // // // ■ --------------------------------------------------article
...
Constructor and destructor output when called
...
h" // Definition of the class #include #include using namespace std; // Defining the static data member: int Article::countObj = 0; // Number of objects // Defining the constructor and destructor: Article::Article( long nr, const string& name, double sp) { setNr(nr); setName(name); setSP(sp); ++countObj; cout << "An article \"" << name << "\" is created
...
article!" << endl; } // Defining the copy constructor: Article::Article( const Article& art) :nr(art
...
name), sp(art
...
\n" << "This is the " << countObj << "
...
\n" << "There are still " << --countObj << " articles!" << endl; } // The method print() outputs an article
...
} 315 316 ■ CHAPTER 15 // // // // MEMBER OBJECTS AND STATIC MEMBERS -----------------------------------------------------article_t
...
------------------------------------------------------ #include "article
...
9); // Global int main() { cout << "\nThe first statement in main()
...
99); cout << "\nThe first call of test()
...
" << endl; test(article2); // Passing by Value cout << "\nThe last statement in main()
...
print(); static Article bike( 3333, "bicycle", 999
...
print(); cout << "\nThe last statement in function test()" << endl; } SOLUTIONS ■ 317 Exercise 2 The Date class from the last chapter ( see files Date
...
cpp ) can be left unchanged
...
The other files: // -----------------------------------------------------// member
...
// -----------------------------------------------------#ifndef _MEMBER_H_ #define _MEMBER_H_ #include "Date
...
more data static Member *ptrBoss; // Member number // Name // Birthday // Pointer to boss, // NULL = no boss
...
size() < 1) // No empty name return false; name = s; return true; } void display() const; // static methods: static Member* getBoss() { return ptrBoss; } static void setBoss( Member* ptrMem) { ptrBoss = ptrMem; } }; #endif // // // // // _MEMBER_H_ --------------------------------------------------member
...
--------------------------------------------------- #include "member
...
asString() << '\n' << line << endl; } SOLUTIONS // ---------------------------------------------------// member_t
...
// ---------------------------------------------------#include "member
...
setDate(); cout << "Date: " << today
...
setNr(1111); cout << "\nTwo members of the sports club:\n" << endl; fran
...
display(); cout << "\nSomething changed!" << endl; fran
...
display(); Member benny( 1122,"Rush, Benny", 1,1,2000); cout << "The youngest member of the sports club: \n"; benny
...
getNr()) ptr = &fran; else if( nr == kurt
...
// ----------------------------------------------------// Lights_t
...
// ----------------------------------------------------#include "lights
...
{ time_t end = time(NULL) + sec; while( time(NULL) < end) ; } // Alternative for Windows: // #include
...
greenTime2 = 14 , amberTime2 = 2 }; int main() { cout << "Simulating two traffic lights!\n\n" << "Terminate this program with +!\n" << endl; cout << " 1
...
Light\n" << "---------------------------" << endl; while(true) { A1
...
setState( Lights::amber); cout << endl; wait( amberTime2); cout << " "; A2
...
setState( Lights::amber); cout << endl; wait(amberTime2); A1
...
setState( Lights::red); cout << endl; wait(amberTime1); A1
...
setState( Lights::amber); cout << endl; wait(amberTime1); } return 0; } chapter 16 Arrays This chapter describes how to define and use arrays, illustrating onedimensional and multidimensional arrays, C strings and class arrays
...
...
...
...
cpp // To input numbers into an array and output after
...
variable // Index, quantity cout << "Enter up to 10 numbers \n" << "(Quit with a letter):" << endl; for( i = 0; i < MAXCNT && cin >> x; ++i) arr[i] = x; cnt = i; cout << "The given numbers:\n" << endl; for( i = 0; i < cnt; ++i) cout << setw(10) << arr[i]; cout << endl; return 0; } DEFINING ARRAYS ■ 323 An array contains multiple objects of identical types stored sequentially in memory
...
An array is also referred to as a vector
...
The definition includes the array name and the type and number of array elements
...
Example: float arr[10]; // Array arr This statement defines the array arr with 10 elements of float type
...
” An array always occupies a contiguous memory space
...
ᮀ Index for Array Elements The subscript operator [] is used to access individual array elements
...
The elements belonging to the array arr are thus arr[0], arr[1] , arr[2],
...
Any int expression can be used as an index
...
and ->
...
As a programmer, you need to be particularly careful to avoid this error! However, you can define a class to perform range checking for indices
...
Class arrays are discussed later
...
, 190 to the elements
...
cpp // The program computes the first 20 Fibonacci // numbers and the corresponding Fibonacci quotients
...
0 + sqrt(5
...
0; // Limit // Title and the first two Fibonacci numbers: cout << header << endl; cout << setw(5) << 0 << setw(15) << fib[0] << endl; cout << setw(5) << 1 << setw(15) << fib[1] << endl; // Rest of the table: for( i=2; i <= COUNT; i++ ) { // Quotient: q = (double)fib[i] / (double)fib[i-1]; cout << setw(5) << i << setw(15) << fib[i] << setw(20) << fixed << setprecision(10) << q << setw(20) << scientific << setprecision(3) << lim - q << endl; } return 0; } INITIALIZING ARRAYS ■ 325 ᮀ Initialization List Arrays can be initialized when you define them
...
If you initialize an array when you define it, you do not need to state its length
...
If the array length is explicitly stated in the definition and is larger than the number of initial values, any remaining array elements are set to zero
...
Locally defined arrays are created on the stack at program runtime
...
g
...
Unless they are initialized, the elements of a local array will not necessarily have a definite value
...
You cannot assign a vector to another vector
...
This topic will be discussed in depth later
...
Fibonacci numbers are useful for representing natural growth
...
Their definition is as follows: ■ ■ the first Fibonacci number is 0, the second is 1 each subsequent Fibonacci number is the sum of its two immediate predecessors
...
The quotient of a Fibonacci number and its predecessor is referred to as a Fibonacci quotient
...
, converges towards the threshold value (1 + √5)/2
...
...
Sample program // C-string
...
// ----------------------------------------------------#include #include #include using namespace std; char header[] = "\n *** C Strings ***\n\n"; int main() { char hello[30] = "Hello ", name[20], message[80]; cout << header << "Your first name: "; cin >> setw(20) >> name; // Enter a word
...
cout << hello << endl; cin
...
cout << "\nWhat is the message for today?" << endl; cin
...
if( strlen( message) > 0) // If string length is { // longer than 0
...
} return 0; } C STRINGS ■ 327 ᮀ char Arrays Arrays whose elements are of char type are often used as data communication buffers
...
One way of representing a string is to store the string and the terminating null character '\0' in a char array
...
Example: char name[] = "Hugo"; This definition is equivalent to char name[] = { 'H','u','g','o','\0' }; As you can see, the string name occupies five bytes, including an additional byte for the null character
...
In the C language, strings are usually represented as char vectors with a terminating null character
...
ᮀ C Strings and the string Class C strings are simple char arrays, which means that the functionality of the string class is not available for them
...
Example: char str1[20], str2[20] = "A string"; str1 = str2; strcpy( str1, str2); // Error! // ok! The standard functions of the C language, such as strlen(), strcpy(), strcmp(), and others, are available for C strings
...
As the program on the opposite page shows, I/O streams are overloaded for char arrays, too
...
However, the program must make sure not to overrun the end of the char array when reading data into the array
...
Example: cin >> setw(20) >> name; // 19 characters C strings are preferable to the string class if only a few operations are needed and you want to avoid unnecessary overheads
...
cpp // An array containing objects of class Account
...
h" #include using namespace std; // Definition of class Account Account giro("Lucky, Peter", 1234567, -1200
...
0), Account("Smith, John", 543001), Account(), // Default constructor "Li, Zhang", // Account("Li, Zhang"), giro // Account(giro) }; int cnt = sizeof(accountTab) / sizeof(Account); int main() { // To set some values: accountTab[1]
...
00); // Assignment ok: accountTab[2] = Account("Pit, Dave", 727003, 200
...
display(); if( i % 3 == 2) { cout << "Press return to go on!\n"; cin
...
The array is known as a class array in this case
...
Example: Result temperatureTab[24]; This statement defines the class array temperatureTab that stores 24 objects of type Result
...
As the statement does not initialize the array explicitly, the default constructor is automatically called for each array element
...
Thus, the previous example is only valid for the first version of the Result class as this class contains a default constructor
...
The list contains a constructor call for each array element
...
5, 0,30,30), Result( 3
...
5, // Just so Result( temp1), // Copy constructor temp2 // Just so }; The first five array elements are initialized by the constructor calls implicitly contained in these statements
...
The default constructor is then called for the remaining elements
...
The public interface of the objects in the array is available for use as usual
...
setTime( 2,30,21); No additional parentheses are needed in this statement since the subscript operator [] and the class member operator
...
330 ■ CHAPTER 16 ■ ARRAYS MULTIDIMENSIONAL ARRAYS Sample program // multidim
...
// ---------------------------------------------------#include #include using namespace std; char representative[2][20] = {"Armstrong, Wendy", "Beauty, Eve"}; // Each representative has five different // articles available, having sold the following: int articleCount[2][5] = { { 20, 51, 30, 17, 44}, {150, 120, 90, 110, 88} }; int main() { for( int i=0; i < 2; i++ ) { cout <<"\nRepresentative: " << representative[i]; cout << "\nNumber of items sold: "; for( int j = 0; j < 5; j++ ) cout << setw(6) << articleCount[i][j]; cout << endl; } return 0; } Screen output: Representative: Armstrong, Wendy Items sold: 20 51 30 17 Representative: Beauty, Eve Items sold: 150 120 90 110 44 88 MULTIDIMENSIONAL ARRAYS ■ 331 ᮀ Defining Multidimensional Arrays In C++ you can define multidimensional arrays with any number of dimensions
...
The most common multidimensional array type is the two-dimensional array, the socalled matrix
...
Each of the 30 (3 ϫ 10) elements is a float type
...
2; // Row 0, column 9 stores the value 7
...
ᮀ Arrays as Array Elements C++ does not need any special syntax to define multidimensional arrays
...
The array number thus contains the following three elements: number[0] number[1] number[2]
...
This means that the same rules apply to multidimensional arrays as to one-dimensional arrays
...
Examples: int arr[2][3] = { {5, 0, 0}, {7, 0, 0} }; int arr[][3] = { {5}, {7} }; These two definitions are equivalent
...
It is necessary to define any other dimensions since they define the size of array elements
...
The representative[i] rows are char arrays used for storing the names of the representatives
...
Example: string representative[2] = {"La
...
"}; 332 ■ CHAPTER 16 ■ ARRAYS MEMBER ARRAYS Class TelList // telList
...
// ---------------------------------------------------#ifndef _TelList_ #define _TelList_ #include using namespace std; #define PSEUDO -1 #define MAX 100 // Pseudo position // Maximal number of elements // Type of a list element: struct Element { string name, telNr; }; class TelList { private: Element v[MAX]; int count; // The array and the current // number of elements public: TelList(){ count = 0;} int getCount() const { return count; } Element *retrieve( int i ) { return (i >= 0 && i < count)? &v[i] : NULL; } bool append( const Element& el ) { return append( el
...
telNr); } bool append( const string& name, const string& telNr); bool erase( const string& name); int search( const string& name); void print(); int print( const string& name); int getNewEntries(); }; #endif // _TelList_ MEMBER ARRAYS ■ 333 ᮀ Encapsulating Arrays A programmer often needs to handle objects of the same type, such as company employees, bank accounts, or the articles in stock
...
An array allows you to access individual objects directly and perform searches
...
When you design a class of this type, one aim will be to perform automatic range checking
...
The resulting class will contain a comfortable and safe interface for object data management
...
Each entry in the list contains a dataset containing a name and a phone number
...
The array v can store up to MAX entries of the Element type
...
When a phone list is created, this number will initially be 0
...
The TelList class uses a single default constructor that sets the counter, count, to zero
...
The tasks performed by the other methods are easily deduced from their names
...
Using a pointer makes it possible to return a NULL pointer if the index is invalid
...
The data passed to a method is copied to the next free array element and the counter is incremented
...
In this case, the method returns false instead of true
...
You can implement the methods for the TelList yourself and go on to test them
...
To eliminate a number n you simply set the nth element in the array to false
...
...
The screen control characters make it possible to locate the cursor, and that independent of the current compiler (see appendix)
...
Input can be terminated by any invalid input, such as a letter
...
This algorithm repeatedly accesses the array, comparing neighboring array elements and swapping them if needed
...
You use a flag to indicate that no elements have been swapped
...
Define and initialize an array with four DayTime class objects
...
Finally, find the largest and smallest elements and output them on screen
...
The program should also count the number of prime numbers less than 1000
...
Use the Sieve of Eratosthenes: To find primary numbers simply eliminate multiples of any primary numbers you have already found, i
...
: first eliminate any multiples of 2 ( 4, 6, 8,
...
), then eliminate any multiples of 5 ( 10, 15, 20,
...
Exercise 4 Write a C++ program to create the screen output shown opposite
...
You can scroll the banner by beginning string output with the first character, then the second, and so on
...
You can use a wait loop to modify the speed of the banner after each string is output
...
■ Implement the TelList class methods shown opposite
...
This means the append() method can only be used to append an entry provided the name is neither blank nor already in use
...
The position of the element to be deleted is first located using the search() method
...
In any other case, the last element in the array is used to overwrite the element that is to be deleted and the counter count is decremented
...
If the search operation is unsuccessful, the value PSEUDO is returned
...
You can pass the first letter or letters of a name to the second method to output any entries beginning with these letters
...
Example: ■ ✓ str1
...
The getNewEntries() method is used to read new phone list entries from the keyboard
...
Reading should be terminated if the user types an empty string
...
Write an application program that creates a phone list of type TelList and displays the menu shown on the opposite page
...
The menu must be called in the main loop of the program
...
If the menu item “Erase” or “Search” is chosen, you must also read a name or the first letters of a name from the keyboard
...
This is just one of the enhancements (another would be variable length) that will be added at a later stage
...
cpp // Inputs integers into an array, // sorts in ascending order, and outputs them
...
long help; // Swap
...
while( !sorted) // { // sorted = true; --end; for( i = 0; i < end; ++i) // { // if( number[i] > number[i+1]) { sorted = false; // help = number[i]; // number[i] = number[i+1]; number[i+1]= help; } } } As long as not yet sorted
...
Not yet sorted
...
SOLUTIONS ■ // Outputs the numbers cout << "The sorted numbers:\n" << endl; for( i = 0; i < cnt; ++i) cout << setw(10) << number[i]; cout << endl; return 0; } Exercise 2 // ---------------------------------------------------// DayTime
...
// ---------------------------------------------------#ifndef _DAYTIME_ #define _DAYTIME_ #include #include using namespace std; class DayTime { private: short hour, minute, second; bool overflow; public: DayTime( int h = 0, int m = 0, int s = 0) { overflow = false; if( !setTime( h, m, s)) // this->setTime(
...
{ } return asSeconds() < t
...
asSeconds(); void print() const { cout << setfill('0') << setw(2) << hour << << setw(2) << minute << << setw(2) << second << cout << setfill(' '); } void swap( DayTime& t) // { // DayTime temp(t); t = *this; } ':' ':' " Uhr" << endl; Just one parameter! Swaps *this and t: *this = temp; }; #endif // _DAYTIME_ // ----------------------------------------------------// TimeTab
...
// ----------------------------------------------------#include "DayTime
...
setTime( 8,40,50); // Last element
...
print(); cout << endl; } // To compute shortest and longest time: int i_min = 0, i_max = 0; // Indices for shortest // and longest elements
...
isLess( timeTab[i_min]) ) i_min = i; if( timeTab[i_max]
...
print(); cout << "\nLongest time: "; timeTab[i_max]
...
cpp // Identifies prime numbers using the Sieve of // Eratosthenes
...
for( j = i+i; j < LIMIT; j += i) flags[j] = false; } } // To count: int count = 0; // Counter for( i = 2; i < LIMIT; ++i) if(flags[i]) // If i is a prime number ++count; // -> count // Output: cout << "There are"<< count <<" prime numbers less than" << LIMIT << endl; cout << "\nTo output prime numbers? (y/n) "; char reply; cin
...
cout
...
cpp // Scrolling a message
...
h The class TelList representing a list with names and telephone numbers
...
// ---------------------------------------------------// telList
...
// ----------------------------------------------------#include "telList
...
length() > 1 // 2 characters at least && search(name) == PSEUDO) // not yet existing { v[count]
...
telNr = telNr; ++count; return true; } return false; } bool TelList::erase( const string& key ) { int i = search(key); if( i != PSEUDO ) { // Copies the last v[i] = v[count-1]; --count; // element to position i return true; } return false; } SOLUTIONS int TelList::search(const string& key ) { for( int i = 0; i < count; i++ ) if( v[i]
...
// Found return PSEUDO; // Not found } // Functions to support the output: inline void tabHeader() // Title of the table { cout << "\n Name Telephone #\n" "----------------------------------------------" << endl; } inline void printline( const Element& el) { cout << left << setw(30) << el
...
c_str() << left << setw(20) << el
...
c_str() << endl; } void TelList::print() // Outputs all entries { if( count == 0) cout << "\nThe telephone list is empty!" << endl; else { tabHeader(); for( int i = 0; i < count; ++i) printline( v[i]); } } int TelList::print( const string& name) const // Entries { // beginning with name
...
length(); for( int i = 0; i < count; ++i) { if( v[i]
...
compare(0, len, name) == 0) { if( matches == 0) tabHeader(); // Title before // first output
...
sync(); getline( cin, el
...
name
...
sync(); getline( cin, el
...
name) != PSEUDO) cout << "Name already exists!" << endl; } else { ++inputCount; cout << "A new element has been inserted!" << endl; } } return inputCount; } // -------------------------------------------------------// telList_t
...
// -------------------------------------------------------#include "telList
...
SOLUTIONS ■ inline void go_on() { cout << "\n\nGo on with return! "; cin
...
clear(); while( cin
...
append("Lucky, Peter", "0203-1234567"); while( action != 'B') { action = menu(); cls(); cout << header << endl; switch( action) { case 'D': myFriends
...
empty()) { myFriends
...
getNewEntries(); break; 347 348 ■ CHAPTER 16 ARRAYS case 'E': // Delete cout << "\n--- To delete a telephone entry
...
empty()) { if( !myFriends
...
sync(); cin
...
get(choice)) choice = 'B'; else choice = toupper(choice); cin
...
This includes: ■ pointer arithmetic ■ pointer version of functions ■ pointers as return values and read-only pointers ■ pointer arrays Operations that use C strings illustrate how to use pointers for efficient programming
...
349 350 ■ CHAPTER 17 ■ ARRAYS AND POINTERS ARRAYS AND POINTERS (1) Sample program // textPtr
...
\n" << endl; char text[] = "Good morning!", name[] = "Bill!"; char *cPtr = "Hello "; // Let cPtr point // to "Hello "
...
e
...
Hello Bill! Good morning! The text "Good morning!" starts at address 00451E40 morning! This is the B of Bill! Bill can not kill! ARRAYS AND POINTERS (1) ■ 351 ᮀ Name and Address of an Array In C++ the name of an array is also the starting address for that array
...
Example: char town[] = "Beijing"; In this case, town is a char pointer to town[0], that is, a pointer to the memory address that stores the 'B' character
...
Example: cout << town; // or: cout << &town[0]; A pointer to the first character of the string town is passed
...
ᮀ Pointer Variables and Arrays An array name is not a pointer variable but a constant that cannot be modified
...
Example: char *cPtr; cPtr = town; cout << cPtr; // or: cPtr = &town[0]; // To output "Beijing" Now cPtr points to the array element town[0] just like town
...
Example: cPtr = "Hello!"; After this statement, cPtr points to the ‘H' character
...
ᮀ Typeless Pointers If you need to display the address rather than the string, you should pass a void* type pointer rather than a char pointer
...
The << operator belongs to the ostream class and is overloaded for void * types for this purpose
...
void * pointers are also referred to as typeless pointers for this reason
...
352 ■ CHAPTER 17 ■ ARRAYS AND POINTERS ARRAYS AND POINTERS (2) Sample program // arrPtr
...
// --------------------------------------------------#include using namespace std; int arr[4] = { 0, 10, 20, 30 }; int main() { cout << "\nAddress and value of array elements:\n" << endl; for( int i cout << << << = 0; i < 4; i++ ) "Address: " << (void*)(arr+i) " Value: " << *(arr+i) endl; // &arr[i] // arr[i] return 0; } Interrelation between pointers and array elements arr 0 arr[0] arr + 1 10 arr[1] arr + 2 20 arr[0] arr + 3 30 arr[3] ARRAYS AND POINTERS (2) ■ 353 ᮀ Addressing Array Elements Access to individual array elements in C++ is very closely related to pointer arithmetic
...
Example: int arr[4] = { 0, 10, 20, 30 }; As you already know, the name of the array arr is an int pointer to arr[0]
...
The size of the object referenced by the pointer is automatically taken into consideration
...
e
...
The memory space between the two entries will be two or four bytes, depending on the size of the type int
...
Thus, arr - 1 addresses the word that precedes arr[0]
...
ᮀ Addressing with Pointer Variables Array elements can also be addressed using pointer variables
...
Thus, ptr + 1, ptr + 2,
...
For any given integer, i, the following expressions are thus equivalent: &arr[i] arr + i ptr + i The following thus represent equivalent values: arr[i] *(arr + i) *(ptr + i) ptr[i] At first it might seem surprising that you can use the array notation ptr[i] for pointers
...
354 ■ CHAPTER 17 ■ ARRAYS AND POINTERS POINTER ARITHMETIC Examples for arithmetic with pointers float v[6] = { 0
...
1, 0
...
3, 0
...
5 }, *pv, x; pv = v + 4; *pv = 1
...
Assign 1
...
Reset pv to v[2]
...
x = *pv++; // // // // // Assign v[3] to x and increment pv
...
Reset pv to v[2]
...
// -------------------------------------------------#include "account
...
Account accountTab[100]; // Table containing accounts
...
Account *aPtr; // Pointer to Account-objects
...
// To search for the account number 1234567: bool found = false; for( aPtr = accountTab; aPtr < accountTab+cnt;++aPtr) if( aPtr->getNr() == 1234567 ) { found = true; break; } if( found) // Found? aPtr->display(); // Yes -> display
...
This primarily means that the pointer must always point to the elements of an array
...
You can use a statement such as pv = pv + i; to store the pointer in the variable pv
...
You can also use the operators ++, --, and += or -= with pointer variables
...
Please note that the indirection operator, *, and the operators ++ and -- have the same precedence
...
Operations of this type are not possible using the pointer v since v is a constant
...
However, it does make sense to perform a subtraction with two pointers, resulting in an int value that represents the number of array elements between the pointers
...
To do so, you simply subtract the starting address of the array
...
ᮀ Comparing Pointers Finally, comparisons can be performed with two pointers of the same type
...
In the example on the opposite page, the pointer aPtr walks through the first cnt elements of the array accountTab, as long as aPtr < accountTab + cnt
...
cpp Defines and calls the function reverse()
...
----------------------------------------------------- #include using namespace std; #include
...
void reverse( char str[], char umstr[]); // Prototype int main() // Read a word and { // output in reversed order
...
width(CNT); // maximal CNT-1 characters cin >> word; reverse( word, revword); // Call cout << "\nThe \"reversed\" word: << endl ; " << revword return 0; } void reverse( char s1[], char s2[]) // Copies the { // reversed C string s1 to s2 int j = 0; for( int i = strlen(s1)-1; i >= 0; i--, j++) s2[j] = s1[i]; s2[j] = '\0'; // Terminating character } Sample output: Enter a word: REGAL The "reversed" word: LAGER ARRAYS AS ARGUMENTS ■ 357 If an array name is passed as an argument when calling a function, the function actually receives the address of the first array element
...
ᮀ Declaring Parameters If the argument is an array, there are two equivalent methods of declaring parameters
...
For example, calling strlen("REGAL") returns a value of 5
...
You can declare the parameter as an array
...
str[i] != '\0'; ++i) } 2
...
Example: int strlen( char *str) { /* as above */ } In both cases the parameter str is a pointer that stores the starting address of the array
...
Calling strlen("REGAL"); leads to the following situation: 'R' 'E' 'G' 'A' 'L' '\0' str[0] str[1] str[2] str[3] str[4] str[5] As you can see, the length of a C string is equal to the index of the element containing the terminating null character
...
ᮀ Array Length A function to which an array is passed initially knows only the starting address of the array but not its length
...
In most other cases the length must be supplied explicitly
...
Example: char dest[30], source[] = "A string"; strcpy( dest, source); Here the string source is copied to dest “from left to right” just like an assignment
...
Index Version of strcpy() void strcpy( char s1[], char s2[]) // Copies s2 to s1 { int i; // Index for( i = 0; s2[i] != '\0'; ++i) // Copy
...
Pointer version 1 of strcpy() void strcpy( char *s1, char *s2) // Copies s2 to s1 { for( ; *s2 != '\0'; ++s1, ++s2) // Copy *s1 = *s2; *s1 = '\0'; // Append terminating } // character
...
// Copy and append // terminating // character
...
When declaring parameters for a given type T: T name[] is always equivalent to T *name
...
However, it is possible to use pointers instead of indices
...
char* p = str; for( p = str; *p != '\0'; ++p) // Search ; // for \0 return (p - str); } In this case, the difference between two pointers results in the string length
...
Both versions produce the same results: the string s2 is copied to s1
...
As the parameters s1 and s2 are pointer variables, they can be shifted
...
Generally, pointer versions are preferable to index versions as they are quicker
...
ᮀ Multidimensional Arrays as Parameters In a parameter declaration for multidimensional arrays, you need to state every dimension with the exception of the first
...
Example: long func( int num[][10] ); long func( int *num[10] ); // ok
...
360 ■ CHAPTER 17 ■ ARRAYS AND POINTERS READ-ONLY POINTERS Sample program // accountFct
...
// -------------------------------------------------#include "account
...
Account accountTab[] = // Table with Account-objects
...
30), Account("Crusoe, Robinson", 200000, 0
...
70), Account("Valentin, Carl", 543002, -1111
...
0; cout << "Output the overdrawn accounts!\n" << "These are the accounts, which fell below \n" << "the limit, ex
...
00
...
++count; } return count; } READ-ONLY POINTERS ■ 361 ᮀ Pointers to const Objects You can use a normal pointer for both read and write access to an object
...
In fact, a read-only pointer is obligatory if you need to point to a constant object
...
Example: const int a = 5, b = 10, *p = &a; This statement defines the constants a and b, and a pointer p to a constant object of type int
...
Example: cout << *p; *p = 1; // To read is ok
...
In other words, a read-only pointer can also point to a non-constant object
...
90); const Account* ptr = &depo; ptr->display(); prt->setState( 7777
...
ᮀ Read-Only Pointers as Parameters Read-only pointers are most commonly found in parameter lists
...
Example: int strlen( const char *s); In this example, the parameter s is a read-only pointer
...
You cannot remove the “write protection” by assigning the read-only pointer s to a normal pointer
...
362 ■ CHAPTER 17 ■ ARRAYS AND POINTERS RETURNING POINTERS Sample program // search1
...
The function strstr() is called
...
dat ] // ---------------------------------------------------#include using namespace std; #define MAXL 200 // Maximum length of line namespace MyScope { // Self-defined version of function strstr(): char *strstr( const char *str, const char *patt); } char line[500], // For a line of text
...
int main() { int lineNr = 0; // As long as a line is left over: while( cin
...
width(3); cout << lineNr << ": " // Output the line << line << endl; // number and the line } } return 0; } // strstr
...
h> // For strlen() and strncmp() namespace MyScope { char *strstr( const char *s1, const char *s2) { // To search for the string s2 within s1
...
} } RETURNING POINTERS ■ 363 A function can return a pointer to an object
...
Such a function will return either a pointer to the required object or a NULL pointer if the object cannot be found
...
For example, the functions strcpy(), strcat(), and strstr() each return a pointer to the first character in a C string
...
The function returns its first argument, that is, a pointer to the target string and leads to the following: Prototype: char* strcpy( char* s1, const char* s2); The second parameter is a read-only pointer, since the source string is read-only
...
When you call this function, make sure that the char array for the first string is large enough to store both strings
...
The following example shows one possible implementation
...
This version was placed in the MyScope namespace to distinguish it from the standard function
...
The standard function strncmp() is used to compare two strings
...
The program uses the strstr() function to display all the lines in the text containing the letters “is" with line numbers
...
cpp where you can supply a search pattern
...
", accPtr[1] "Davis,
...
// -------------------------------------------------#include using namespace std; void displayError ( int errorNr) { static char* errorMsg[] = { "Invalid error number", "Error 1: Too much data ", "Error 2: Not enough memory ", "Error 3: No data available " }; if( errorNr < 1 || errorNr > 3) errorNr = 0; cerr << errorMsg[errorNr] << endl; } ✓ NOTE A string literal, such as “Error
...
Thus, such a pointer can be used to initialize another char pointer
...
ARRAYS OF POINTERS ■ 365 Pointers offer various possibilities for simple and efficient handling of large amounts of data
...
ᮀ Defining Arrays of Pointers Whenever you need a large number of pointers, you can define an array whose elements are pointers
...
Example: Account* accPtr[5]; The array accPtr contains five Account pointers accPtr[0], accPtr[1],
...
The individual pointers in the array can now be assigned object addresses
...
Example: Account save("Novack, Kim", 1111, 9999
...
); accPtr[0] = &save; accPtr[1] = &depo; for( int i=2; i<5; ++i) accPtr[i] = NULL; ᮀ Initialization As usual, an initialization list is used to initialize the array
...
Example: Account* accPtr[5] = { &depo, &save, NULL}; The value NULL is automatically assigned to any objects for which the list does not contain a value
...
ᮀ Usage The individual objects addressed by the pointers in an array do not need to occupy a contiguous memory space
...
This allows for extremely flexible object handling
...
Example: for( int i=0; i<5; ++i) if( accPtr[i] != NULL) accPtr[i]->display(); // To output The function displayError() opposite displays the error message for a corresponding error number, using an array of char pointers to the error messages
...
cpp // Demonstrates the command line arguments
...
\HELLO
...
These command line arguments are typically used to govern how a program is executed or to supply the data a program will work with
...
The individual arguments are separated by spaces
...
If an argument contains space or redirection characters, you must place it in double quotes
...
However, if you intend to process command line arguments, you must define parameters for main()
...
// Function block } argc contains the number of arguments passed via the command line
...
The parameter argv is an array of char pointers: argv[0] argv[1] argv[2] points to the program name (and path) points to the first real argument, that is, the word after the program name points to the second argument
...
Various operating systems, for example WINDOWS 98/00/NT and UNIX, allow you to declare a third parameter for main()
...
The exercises for this chapter contain a program that displays the program environment
...
// Return value: < 0, if str1 < str2 // = 0, if str1 == str2 // > 0, if str1 > str2
...
This procedure is repeated while i > 0 for the remainder of an array containing array elements with an initial index of i
...
for( pv = v; pv <= v + 3; cout << " *pv = " b
...
for( pv = v + 3; cout << " << pv[i] = " c
...
The line is then output in reverse order
...
Exercise 3 The standard function strcmp() performs a lexicographical comparison of two C strings
...
The return value is the difference between two character codes
...
Call this function str_cmp() to distinguish it from the standard function
...
The loop should terminate when both strings are empty
...
The principle of the selection sort algorithm is shown opposite
...
Test the functions with random numbers between -10000 and +10000
...
COMSPEC=C:\COMMAND
...
Frequency table for exercise 7 Bloodpressure <120 120–129 130–139 140–149 >= 160 Age 20–29 25 34 26 12 8 30–39 19 27 24 11 4 40–49 6 15 35 36 18 EXERCISES ■ 371 Exercise 5 a
...
b
...
The environment is a memory area containing strings in the format NAME=String A third parameter for the function main() allows access to the environment
...
The array elements are char pointers to the environment strings, the last element being a NULL pointer
...
Modify the program to produce a useful tool called search, to which you can pass any search pattern via the command line
...
Use the standard function strstr()
...
txt Exercise 7 The following frequency was observed during an examination of the relationship between age and blood pressure for 300 males
...
Store the sums of the rows and columns separately in a one-dimensional row or column array
...
The sum of all the matrix elements
...
■ CHAPTER 17 ■ solutions 372 ARRAYS AND POINTERS SOLUTIONS Exercise 1 Screen Output: a
...
c
...
*pv = 10 *pv = 20 *pv = 30 pv[i] = 20 pv[i] = 30 pv[i] = 40 *(pv+i) = 10 *(pv+i) = 30 v[3] = 40 v[2] = 30 v[1] = 20 *pv = 40 v[0] = 10 Exercise 2 // ------------------------------------------------------// reverse
...
// ------------------------------------------------------#include using namespace std; #define MAXLEN 80 int main() { char line[MAXLEN], *p; cout << "Enter a line of text: " << endl; // Input for( p = p < ++p ; a line: line; line+MAXLEN ) && cin
...
cpp // Define and test the pointer version str_cmp() // of the standard function strcmp()
...
\n" << endl; cout << "1
...
sync(); cin
...
get(text1,MAXLEN); cout << "2
...
sync(); cin
...
get(text2,MAXLEN); if( text1[0] == '\0' && text2[0] == '\0') break; // Both lines empty
...
// -----------------------------------------------------int str_cmp( const char* str1, const char* str2) { for( ; *str1 == *str2 && *str1 != '\0'; ++str1, ++str2) ; return (*str1 - *str2); } 373 374 ■ CHAPTER 17 ARRAYS AND POINTERS Exercise 4 // // // // // ------------------------------------------------------selSort
...
------------------------------------------------------- #include #include #include #include using namespace std; // For srand(), rand() // For time() // Prototype: void selectionSort( int arr[], int len); const int len = 200; int intArr[len]; int main() { cout << "\n << endl; *** // int-array Selection Sort Algorithm ***\n" // To initialize an int-array with random numbers: srand( (unsigned int)time(NULL)); // Initialize the // random number generator
...
if( arr[mini] > arr[j]) mini = j; swap( arr[i], arr[mini]); // Swap
...
375 376 ■ CHAPTER 17 ARRAYS AND POINTERS Exercise 5 // // // // // ------------------------------------------------------args
...
------------------------------------------------------- #include using namespace std; int main( int argc, char *argv[], char *env[]) { cout << "Program: " << argv[0] << endl; cout << "\nCommand line arguments:" << endl; int i; for( i = 1; i < argc; ++i) cout << argv[i] << endl; // Arguments cout << "Type to go on"; cin
...
cpp // A filter that outputs all lines containing a certain // pattern
...
// // Call: search pattern [ < text
...
In this case end input with +
...
SOLUTIONS ■ int main( int argc, char *argv[]) { if( argc != 2) { cerr << "Call: search pattern [ < text
...
getline( line, MAXL)) { ++lineNr; if( strstr( line, argv[1]) != NULL) { // If the pattern was found: cout
...
cpp To compute the sums of rows and columns in a matrix
...
\n" << endl; // Compute sums: int totalsum = matrixsum( matrix, 3, rowsum, colsum); // Output matrix and sums: cout << "The matrix with the sums " << "of rows and columns:\n" << endl; int i,j; for( i = 0 ; i < 3 ; ++i) // Output rows of the { // matrix with row sums
...
chapter 18 Fundamentals of File Input and Output This chapter describes sequential file access using file streams
...
379 380 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT FILES File operations Main Memory File Buffer External Memory Write File Read FILES ■ 381 When a program is terminated, the program data stored in main memory is lost
...
ᮀ File Operations Single characters or character strings can be written to text files just like they can be output on screen
...
A record contains data that forms a logical unit, such as the human resource information for a person
...
When you read a record, this record is taken from the file and copied to the data structure of a program
...
However, this normally involves more than just storing an object’s data
...
External mass storage media, such as hard disks, are normally block-oriented—that is, data is transferred in blocks whose size is a multiple of 512 bytes
...
ᮀ File Positions From the viewpoint of a C++ program, a file is simply a long byte array
...
Every character in a file occupies a byte position
...
The current file position is the position of the byte that will be read or written next
...
In the case of sequential access, the data is read or written byte by byte in a fixed order
...
If you need access to some piece of information in a file, you must read the file content from start to finish
...
Easy access to given data in a file implies being able to set the current file position as required
...
382 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT FILE STREAMS Stream classes for file access ios istream ostream iostream ifstream ofstream fstream FILE STREAMS ■ 383 C++ provides various standard classes for file management
...
As a programmer you will not need to concern yourself with file buffer management or system specifics
...
One program can thus process files on a Windows NT or UNIX platform
...
ᮀ The File Stream Classes in the iostream Library The class hierarchy on the opposite page shows that the file stream classes contain the stream classes, with which you are already familiar, as base classes: ■ ■ ■ the ifstream class derives from the istream class and allows file reading the ofstream class derives from the ostream stream class and supports writing to files the fstream class derives from the iostream stream class
...
The file stream classes are declared in the fstream header file
...
ᮀ Functionality The file stream classes inherit the functionality of their base classes
...
Thus every file stream has: ■ ■ ■ ■ methods for non-formatted writing and reading of single characters and/or data blocks the operators << or >> for formatted reading and writing from or to files methods and manipulators for formatting character sequences methods for state queries
...
384 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT CREATING FILE STREAMS Sample program // showfile
...
e
...
// Call: showfile filename // ---------------------------------------------------#include #include using namespace std; int main( int argc, char *argv[]) { if( argc != 2 ) // File declared? { cerr << "Use: showfile filename" << endl; return 1; } ifstream file( argv[1]); // Create a file stream // and open for reading
...
{ cerr << "An error occurred when opening the file " << argv[1] << endl; return 2; } char line[80]; int cnt = 0; while( file
...
if( ++cnt == 20) { cnt = 0; cout << "\n\t ---- to continue ---- " << endl; cin
...
get(); } } if( !file
...
To do so, you can ■ ■ state the file name, which can also contain a path define a so-called file access mode
...
The file access mode specifically defines whether read and/or write access to the file is permitted
...
ᮀ File Stream Definition You can open a file when you create a file stream—you simply state the file name to do so
...
Example: ifstream myfile("test
...
fle is passed to the constructor of the ifstream class, which opens the file for reading
...
When a file is opened, the current file position is the beginning of the file
...
In this case a new file is created
...
fle"); This statement creates a new file called new
...
But be careful! If the file already exists, it will be truncated to a length of zero bytes, or in other words deleted
...
Example: ofstream yourfile; yourfile
...
fle"); This example has the same effect as the previous example
...
It rarely makes sense to use fixed file names
...
If no file name is supplied, the program issues an error message and terminates
...
386 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT OPEN MODES Flags for the open mode of a file Flag Effects ios::in ios::out Opens a file for output
...
ios::app Opens a file for output at the end-of-file
...
ios::ate Open and seek to end immediately after opening
...
ios::binary ✓ Opens an existing file for input
...
NOTE 1
...
2
...
When you read from or write to a text file, control characters to indicate newlines or the end-of-file are interpreted separately and adapted to the current platform (so-called “cooked mode”)
...
Default settings when opening a file The constructor and the method open() of all stream classes use the following default values: Class Flags ifstream ios::in ofstream ios::out | ios::trunc fstream ios::in | ios::out OPEN MODES ■ 387 To open a file in any but the default mode, you must supply both the file name and the open mode
...
ᮀ Open Mode Flags In addition to the file name, you can pass a second argument for the open mode to the constructors and the open() method
...
A flag represents a single bit in a computer word
...
You can use the bit operator, |, to combine various flags
...
If the flag ios::in is raised, the file must already exist
...
Example: fstream addresses("Address
...
The file is created, if it does not already exist
...
You can use the default mode for the fstream class, that is, ios::in | ios::out, to open an existing file for reading and writing
...
ᮀ Error Handling Errors can occur when opening a file
...
The state flag failbit of the ios base class is raised in this case
...
Example: if( !myfile) // or: if( myfile
...
If a read operation fails, the end of the current file may have been reached
...
eof()) // At end-of-file? The eof bit is set if you try to carry on reading at the end of a file
...
388 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT CLOSING FILES Sample program // fcopy1
...
// Call: fcopy1 source [ destination ] // ---------------------------------------------------#include #include using namespace std; inline void openerror( const char *file) { cerr << "Error on opening the file " << file << endl; exit(1); // Ends program closing } // all opened files
...
is_open()) openerror( argv[1]); if( argc == 2) // Just one sourcefile
...
is_open() ) openerror( argv[2]); copy( infile, outfile); outfile
...
} infile
...
return 0; } void copy( istream& is, ostream& os) // Copy it to os
...
get(c) ) os
...
A program that terminates correctly will automatically close any open files before exiting
...
However, if the file is no longer in use before this point, you should close the file explicitly
...
Example: myfile
...
It is therefore possible to use the stream to open and manipulate another file
...
In the case of the myfile file stream, the test is as follows: Example: if( myfile
...
*/ } // File is open ᮀ The exit() Function Open files are also closed when you call the global function exit()
...
Prototype: void exit( int status ); The calling process, to which the status error code is passed for evaluation, will often be the command interpreter—a Unix shell, for example
...
The statement return n; is thus equivalent to the statement exit(n); when used in the main() function
...
If the user forgets to state a second (target) file, the source file is copied to standard output
...
390 ■ CHAPTER 18 ■ FUNDAMENTALS OF FILE INPUT AND OUTPUT READING AND WRITING BLOCKS Sample program // Pizza_W
...
// --------------------------------------------------#include #include using namespace std; char header[] = " * * * P I Z Z A P R O N T O * * *\n\n"; // Record structure: struct Pizza { char name[32]; float price; }; const int MAXCNT = 10; Pizza pizzaMenu[MAXCNT] = { { "Pepperoni", 9
...
90F }, { "Ham Pizza", 12
...
90F } }; int cnt = 4; char pizzaFile[256] = "pizza
...
<< endl; // To write data into the file: int exitCode = 0; ofstream outFile( pizzaFile, ios::out|ios::binary ); if( !outFile) { cerr << "Error opening the file!" << endl; exitCode = 1; } else { for( int i = 0; i < cnt; ++i) if( !outFile
...
This means you can write formatted or unformatted data to a file or read that data from the file block by block or character by character
...
Formatted input and output of numerical values, for example, requires the >> and << operators and appropriate manipulators or formatting methods
...
34; ofstream textFile("Test
...
txt will contain a line of text, such as "Price
...
Converting binary data to legible text is not practicable if you are dealing with large amounts of data
...
To do so, you simply open the file in binary mode and write the data to the file, or read it from the file, block by block
...
Prototype: ostream& write( const char *buf, int n); Since write() returns a reference to the stream, you can check to ensure that the write operation was successful
...
write("An example ", 2) ) cerr << "Error in writing!" << endl; A warning is issued if an error occurs while writing the characters "An"
...
The method transfers a data block from a file to a program buffer
...
The block that needs to be transferred can contain one or more records
...
You need to cast the address of this memory area to (char *) as shown in the example opposite
...
// Constructors, destructor, // access methods,
...
// Returns: The given stream
...
write((char*)&nr, sizeof(nr) ); os
...
// read() inputs an account from the stream is // and writes it into the members of the current object istream& Account::read(istream& is) { getline( is, name, '\0'); // Read a string is
...
read( (char*)&balance, sizeof(balance)); return is; } OBJECT PERSISTENCE ■ 393 ᮀ Storing Objects Objects are created during program runtime and cleaned up before the program terminates
...
However, you must ensure that the object can be reconstructed, as it was, when read
...
You will generally not know how to store a member object
...
However, it does not make sense to store pointer values in a file, as the memory addresses will change each time you re-launch the program
...
As string type objects are used to handle variable length strings, the object just contains a reference to the string
...
Instead, you should write the string itself to a file
...
Another solution involves providing methods to allow the objects to write their own data members to files or read them from files
...
ᮀ Storing Account Class Objects The opposite page shows the Account class, with which you are already familiar
...
A file stream that references a file opened in binary mode is passed as an argument to the methods read() and write()
...
Example: if( ! anAccount
...
Example: if( ! anAccount
...
The << operator and the function getline() are available for this task
...
If file2 already exists, it is overwritten
...
fcopy For calls without arguments, the source and destination files are entered in a user dialog
...
read(buf, 1024); transfers the next 1024 bytes from file to the buffer buf
...
In this case the fail and eof bits are set
...
The method gcount() returns the number of bytes transferred by the last read operation
...
gcount(); // Number of bytes // in last read op
...
Write a program named fcopy to enhance fcopy1 as follows: ■ ■ ■ ■ If the program is launched without any arguments, it does not issue an error message and terminate but requests user input for the names of the source and target files
...
If the command line or the user dialog contains valid source and target file names, a binary copy operation is performed
...
The default block size is 1024 bytes
...
Also refer to the notes on the opposite page
...
Modify the sample program Pizza_w
...
b
...
cpp, which displays the pizza menu, that is, outputs the contents of the pizza file
...
To do so, write a program called Account_rw
...
Use binary mode for read or write access to the file
...
New methods: const string& getFilename() const; bool setFilename( const string& fn); bool isDirty() const; bool load(); bool save(); bool saveAs(); // Read data from the file // Save data
...
Extended menu of the application program * * * * * Telephone List * * * * * S = Show all entries F = Find a telephone number A = Append an entry D = Delete an entry ----------------------------------------O = Open a file W = Save in the file U = Save as
...
To allow this, first add the data members and methods detailed on the opposite page to TelList
...
The dirty flag is raised to indicate that the phone list has been changed but not saved
...
The strings in the phone list must be saved as C strings in a binary file, allowing for entries that contain several lines
...
W = Save Save the current phone list in a file
...
Save the current phone list in a new file
...
These methods return true for a successful action and false otherwise
...
If the phone list has been modified but not saved, the user should be prompted to save the current phone list before opening another file or terminating the program
...
cpp // Copy files // Call: fcopy [ source [ destination ] ] // ---------------------------------------------------#include #include using namespace std; char usage[] = "Call: fcopy [source [destination]}"; inline void openerror( const char *file) { cerr << "Error opening the file " << file << endl; exit(1); } bool copy( istream& is, ostream& os), ok = true; // Prototype, // ok flag
...
cout << "Copying source file to " "destination file!\n" "Source file: "; cin
...
sync(); // No previous input cout << "Destination file: "; cin
...
strcpy( source, argv[1]); break; case 3: // Source and destination files are declared
...
cerr << usage << endl; return 2; // or: exit(2); } if( strlen(dest) == 0) // Only source file? { // yes ==> output to cout
...
} else // Copy source to destination { // file in binary mode
...
const int BufSize = 1024; char buf[BufSize]; do { is
...
gcount() > 0) os
...
gcount()); } while( !is
...
fail() && !os
...
eof() ) return false; else return true; } 399 400 ■ CHAPTER 18 FUNDAMENTALS OF FILE INPUT AND OUTPUT Exercise 2 // ---------------------------------------------------// Pizza
...
cpp and Pizza_R
...
// ---------------------------------------------------#include #include #include using namespace std; // Structure of a record: struct Pizza { char name[32]; float price; }; #define MAXCNT 20 // Maximum number of pizzas #define FILENAME "pizza
...
cpp // Demonstrating blockwise writing of records
...
h" Pizza pizzaMenu[MAXCNT] = { { "Pepperoni", 9
...
90F }, { "Ham Pizza", 12
...
90F } }; int cnt = 4; char pizzaFile[256] = FILENAME; int main() // Write records
...
name << setw(10) << pizzaMenu[i]
...
sync(); cin
...
getline( pizzaMenu[cnt]
...
name[0] == '\0') break; cout << "Price: "; cin >> pizzaMenu[cnt]
...
and the next pizza!\n" << "Stop with
...
write( (char*)&pizzaMenu[i], sizeof(Pizza)) ) { cerr << "Error writing to file!" << endl; exitCode = 2; } } if( exitCode == 0) cout << "\nData added to file " << pizzaFile << "
...
cpp Demonstrating block by block reading of records
...
h" char pizzaFile[256] = FILENAME; int main() { header(); // Read and display records
...
read( (char*)&onePizza, sizeof(Pizza)) ) break; else { cout << setw(20) << onePizza
...
price << endl; ++cnt; } cout << "\n------------------------------------------\n" << endl; if( !inFile
...
cpp Writes an array with objects of class Account to a file and feed the array into another array
...
h" #include #include using namespace std; // Definition of the class Account Account AccTab1[3] = { Account("Lucky, Luke", 707070, -1200
...
0), Account("Snoopy, Dog\n" // String can contain more "Cell #: 01771234567", 543001) // than one line
...
fle"; int main() { int i = 0; // --- Write accounts to file --ofstream outFile( file, ios::out | ios::binary ); if( ! outFile) { cerr << "Error opening file " << file << endl; return 1; } for( i = 0; i < cnt; ++i) if( !AccTab1[i]
...
close(); 403 404 ■ CHAPTER 18 FUNDAMENTALS OF FILE INPUT AND OUTPUT // --- Reads accounts from file --ifstream inFile( file, ios::out | ios::binary ); if( ! inFile) { cerr << "Error opening file " << file << endl; return 3; } for( i = 0; i < cnt; ++i) if( !AccTab2[i]
...
close(); // --- Displays the accounts read --cout << "The file " << file << " contains the " << "following accounts:" << endl; for( i = 0; i < cnt; ++i) AccTab2[i]
...
h A class TelList to represent a list containing names and telephone numbers
...
--------- --------------------------------------------- #ifndef _TelList_ #define _TelList_ #include using namespace std; #define PSEUDO -1 #define MAX 100 // Pseudo position // Maximum number of elements SOLUTIONS ■ // Type of a list element: struct Element { string name, telNr; }; class TelList { private: Element v[MAX]; int count; string filename; bool dirty; // The array and the actual // number of elements
...
public: TelList() : count(0), filename(""), dirty(false) {} int getCount() { return count; } Element *retrieve( int i ) { return (i >= 0 && i < count)? &v[i] : NULL; } bool append( const Element& el ) { return append( el
...
telNr); } bool append( const string& name, const string& telNr); bool erase( const string& name); int search( const string& name) const; void print() const; int print( const string& name) const; int getNewEntries(); const string& getFilename() const { return filename; } bool setFilename( const string& fn) { if( fn
...
cpp // Implements the methods of class TelList
...
h" // Definition of class TelList #include #include #include using namespace std; 405 406 ■ CHAPTER 18 FUNDAMENTALS OF FILE INPUT AND OUTPUT bool TelList::append( const string& const string& { if( count < MAX && name
...
name = name; v[count]
...
--count; dirty = true; return true; } return false; } // -------------------------------------------------// Methods search(), print(), getNewEntries() // are unchanged (refer to solutions of chapter 16)
...
bool TelList::load() { cout << "\n--- Load the telephone list " << "from a file
...
cin
...
clear(); // No previous input getline( cin, file); if( file
...
c_str(), ios::in | ios::binary); if( !infile ) { cerr << "File " << file << " could not be opened!" << endl; return false; } int i = 0; while( i < MAX) { getline( infile, v[i]
...
telNr, '\0'); if( !infile) break; else ++i; } if( i == MAX) cerr << "Max capacity " << MAX << " has been reached!" << endl; else if( !infile
...
--" << "\nFile: "; string file; // Input file name
...
sync(); cin
...
empty()) return saveAs(); if( !dirty) return true; // Save the telephone list
...
c_str(), ios::out | ios::binary); if( !outfile ) { cerr << "File " << filename << " could not be opened!" << endl; return false; } int i = 0; while( i < count) { outfile << v[i]
...
telNr << '\0'; if( !outfile) break; else ++i; } if( i < count) { cerr << "Error writing to file " << filename << endl; return false; } dirty = false; return true; } // ------------------------------------------------------// TelList_
...
// ------------------------------------------------------#include "telList
...
SOLUTIONS ■ inline void go_on() { cout << "\n\nGo on with return! "; cin
...
clear(); // No previous input while( cin
...
char header[] = "\n\n * * * * * Telephone List * * * * *\n\n"; TelList myFriends; // A telephone list int main() { int action = 0; // Command string name; // Read a name while( action != 'Q') { action = menu(); cls(); cout << header << endl; switch( action) { // --------------------------------------------------// case 'S': case 'F': case 'A': case 'D': // unchanged (refer to the solutions of chapter 16)
...
isDirty() && askForSave() == 'y') myFriends
...
load()) cout << "Telephone list read from file " << myFriends
...
if( myFriends
...
getFilename() << " !" < else cerr << "Telephone list not saved!" << endl; go_on(); break; 409 410 ■ CHAPTER 18 FUNDAMENTALS OF FILE INPUT AND OUTPUT case 'W': // Save if( myFriends
...
getFilename() << endl; else cerr << "Telephone list not saved!" << endl; go_on(); break; case 'Q': if( myFriends
...
save(); cls(); break; } } // End of while return 0; && // Quit askForSave() == 'Y') } int menu() { static char menuStr[] = //
...
" "\n -------------------------------------" "\n Q = Quit the program" "\n\n Your choice: "; // --------------------------------------------------// everything else unchanged (cf
...
get(c); c = toupper(c); }while( c != 'Y' && c != 'N'); return c; } chapter 19 Overloading Operators Overloading operators allows you to apply existing operators to objects of class type
...
This chapter describes various uses of overloaded operators
...
The concept of friend functions, which is introduced in this context, is particularly important for overloading operators
...
This meaning can be changed for classes by a definition of your own
...
The definition scope of an operator is simply extended—the characteristics of the operator remain unchanged
...
You cannot redefine the operators for fundamental types
...
A binary operator will always be binary and a unary operator will always be unary
...
GENERALS ■ 413 ᮀ Overloading An operator is said to be overloaded if it is defined for multiple types
...
Most operators are already overloaded for fundamental types
...
If both operands are integral types, an integral division is performed; in all other cases floating-point division occurs
...
ᮀ Operators for Classes In addition to defining methods, C++ offers the interesting possibility of defining the functionality of a class by means of operators
...
For the objects x and y in this class: is equivalent to x
...
Expressions using operators are often more intuitive, and thus easier to understand than expressions containing function calls
...
This applies to the string class, with which you are already familiar
...
cout << str1; str2[2] = 'i'; // // // // Operator += Operator < Operator << Operators [] and = The tables on the opposite page show those operators that can be overloaded
...
:: ?:
...
414 ■ CHAPTER 19 ■ OVERLOADING OPERATORS OPERATOR FUNCTIONS (1) Operators < and ++ for class DayTime // DayTime
...
// --------------------------------------------------#ifndef _DAYTIME_ #define _DAYTIME_ class DayTime { private: short hour, minute, second; bool overflow; public: DayTime( int h = 0, int m = 0, int s = 0); bool setTime(int hour, int minute, int second = 0); int getHour() const { return hour; } int getMinute() const { return minute; } int getSecond() const { return second; } int asSeconds() const // Daytime in seconds { return (60*60*hour + 60*minute + second); } bool operator<( const DayTime& t) const // compare { // *this and t return asSeconds() < t
...
return *this; } void print() const; }; #endif // _DAYTIME_ Calling the Operator < #include "DayTime
...
DayTime depart1( 11, 11, 11), depart2(12,0,0);
...
OPERATOR FUNCTIONS (1) ■ 415 ᮀ Naming Operator Functions To overload an operator, you just define an appropriate operator function
...
The name of an operator function must begin with the operator keyword followed by the operator symbol
...
An operator function can be defined as a global function or as a class method
...
However, it can make sense to define an operator function globally
...
ᮀ Operator Functions as Methods If you define the operator function of a binary operator as a method, the left operand will always be an object of the class in question
...
The second, right operand is passed as an argument to the method
...
Example: bool operator<( const DayTime& t) const; In this case the lesser than operator is overloaded to compare two DayTime objects
...
The prefix operator ++ has been overloaded in the example on the opposite page to illustrate overloading unary operators
...
The function is called if the object a in the expression ++a is an object of class DayTime
...
The expression is thus equivalent to depart1
...
The previous function call is therefore technically correct
...
However, you should be aware of the fact that an operator function should perform a similar operation to the corresponding operator for the fundamental type
...
416 ■ CHAPTER 19 ■ OVERLOADING OPERATORS OPERATOR FUNCTIONS(2) Class Euro // Euro1
...
// -------------------------------------------------------#ifndef _EURO_H_ #define _EURO_H_ #include // The class stringstream #include using namespace std; class Euro { private: long data; // Euros * 100 + Cents public: Euro( int euro = 0, int cents = 0) { data = 100L * (long)euro + cents; } Euro( double x) { x *= 100
...
0 ? x+0
...
5); //ex
...
7 -> 10 } long getWholePart() const { return data/100; } int getCents() const { return (int)(data%100); } double asDouble() const { return (double)data/100
...
void print( ostream& os) const // Output to stream os
...
data = -data; return temp; } Euro operator+( const Euro& e2) const // Addition
...
data = data + e2
...
{ /* Analog just as operator + */ } Euro& operator+=( const Euro& e2) // Add Euros
...
data; return *this; } Euro& operator-=( const Euro& e2); // Subtract euros
...
OPERATOR FUNCTIONS(2) ■ 417 ᮀ Notes on the Sample Class Euro The opposite page shows the Euro class, which represents the new European currency
...
Thus data/100 returns the number of euros and data%100 the number of cents
...
In addition to a constructor that is passed whole euros and cents as arguments, there is a constructor that can process a double value of euros and a standard copy constructor
...
07), e3(-e1); ᮀ Negation, Addition, and Subtraction The unary operator - does not change its operand
...
The operator function is thus a const method that creates and returns a temporary object
...
Thus, the operator functions also create temporary objects and return them with the correct values
...
operator+(e2)
...
ᮀ The += and -= Operators Although the operators + and - were overloaded for the Euro class, this does not automatically mean that the operators += and -= are overloaded
...
Of course, you should overload the operators to ensure that the statements Example: sum += e3; and sum = sum + e3; produce the same results
...
A temporary object is not required! The expression sum += e3 represents the current object after modification
...
418 ■ CHAPTER 19 ■ OVERLOADING OPERATORS USING OVERLOADED OPERATORS File Euro1
...
h // -------------------------------------------------------inline string Euro::asString() const // Euro as string { stringstream strStream; // Stream for conversion long temp = data; if( temp < 0) { strStream << '-'; temp = -temp; } strStream << temp/100 << ',' << setfill('0') << setw(2) << temp%100; return strStream
...
cpp // Tests the operators of class Euro
...
h" // Definition of the class #include using namespace std; int main() { cout << "* * * Testing the class Euro * * *\n" << endl; Euro wholesale( 20,50), retail; retail = wholesale; // Standard assignment retail += 9
...
49 cout << "Wholesale price: "; wholesale
...
print(cout); Euro discount( 2
...
print(cout); wholesale = 34
...
print(cout); Euro profit( retail - wholesale); // Subtraction and // copy constructor cout << "\nThe profit: "; profit
...
Example: Euro wholesale(15,30), retail, profit(7,50), discount(1,75); retail = wholesale + profit; // Call: wholesale
...
operator-=( discount) retail += Euro( 1
...
operator+=( Euro(1
...
However, you can also add or subtract int or double types
...
This allows a function that expects a Euro value as argument to process int or double values
...
49; is valid
...
Since there is no operator function with these characteristics, the compiler converts the double value to Euro and calls the existing operator function for euros
...
Example: retail = wholesale + 10; // ok // ok wholesale = retail - 7
...
operator+( Euro(10)); But the following statement is invalid! Example: retail = 10 + wholesale; // wrong! Since the operator function was defined as a method, the left operand must be a class object
...
However, if you want to convert both operands, you will need global definitions for the operator functions
...
The overloaded operator -> enables the use of objects in the same way as pointers
...
h // The class Euro represents a Euro with // global operator functions implemented for + and -
...
class Euro { // Without operator functions for + and -
...
}; // ---------------------------------------------------// Global operator functions (inline) // Addition: inline Euro operator+( const Euro& e1, const Euro& e2) { Euro temp(e1); temp += e2; return temp; } // Subtraction: inline Euro operator-( const Euro& e1, const Euro& e2) { Euro temp(e1); temp -= e2; return temp; } #endif // _EURO_H_ GLOBAL OPERATOR FUNCTIONS ■ 421 ᮀ Operator Functions: Global or Method? You can define an operator function as a global function instead of a method
...
+= -= *= /= %= These operators always require a so-called l-value as their left operand, that is, they require an object with an address in memory
...
g
...
g
...
ᮀ Defining Global Operator Functions The operands for a global operator function are passed as arguments to that function
...
The Euro class has been modified to provide a global definition of the operator functions for the operators + and -
...
More specifically, conversion of int or double to Euro is performed for both operands now
...
20 and 1
...
20) operator+( 1
...
The function operator+() shown opposite therefore uses the += operator, whose operator function is defined as a method
...
422 ■ CHAPTER 19 ■ OVERLOADING OPERATORS FRIEND FUNCTIONS Class Euro with friend functions // Euro
...
// --------------------------------------------------#ifndef _EURO_H_ #define _EURO_H_ //
...
// Operators -(unary), +=, -= as before
...
0/x)); } // Global friend functions friend Euro operator+( const Euro& e1, const Euro& e2); friend Euro operator-( const Euro& e1, const Euro& e2); friend Euro operator*( const Euro& e, double x) { Euro temp( ((double)e
...
0) * x) ; return temp; } friend Euro operator*( double x, const Euro& e) { return e * x; } }; // Addition: inline Euro operator+( const Euro& e1, const Euro& e2) { Euro temp; temp
...
data + e2
...
data = e1
...
data; return temp; } #endif // _EURO_H_ FRIEND FUNCTIONS ■ 423 ᮀ The Friend Concept If functions or individual classes are used in conjunction with another class, you may want to grant them access to the private members of that class
...
Imagine you need to write a global function that accesses the elements of a numerical array class
...
However, special permission to access the private data members of the class can dramatically improve the function’s response
...
This is achieved by declaring the function as a friend
...
Example: class A { //
...
This allows them direct access to the private members of class A
...
To resolve this issue, you will generally pass the object the function needs to process as an argument
...
If this were not so, data encapsulation could easily be undermined
...
In order to compute interest, it is necessary to multiply and divide euros by double values
...
As the example shows, friend functions can also be defined inline in a class
...
h // The class Result to represent a measurement // and the time the measurement was taken
...
h" // Class DayTime class Result { private: double val; DayTime time; public: // Constructor and access methods friend class ControlPoint; // All methods of }; // ControlPoint are friends
...
h class ControlPoint { private: string name; // Name of control point Result measure[100]; // Table with results //
...
// Compute static values of measurement results // (average, deviation from mean,
...
}; FRIEND CLASSES ■ 425 ᮀ Declaring Friend Classes In addition to declaring individual friend functions, you can also make entire classes “friends” of another class
...
This technique is useful if a class is used in such close conjunction with another class that all the methods in that class need access to the private members of the other class
...
Calculations with individual measurements are performed repeatedly
...
Example: class Result { //
...
The Result class itself decides who its friends are and who has access to its private members
...
However, you can regard a friend declaration as an extension of the public interface
...
ᮀ Using Friend Functions and Classes Using friend functions and friend classes helps you to create efficient programs
...
Some common uses are global operator functions declared as friend functions
...
Allowing external functions to manipulate internal data can lead to inconsistency, especially if a class is modified or extended in a later version
...
426 ■ CHAPTER 19 ■ OVERLOADING OPERATORS OVERLOADING SUBSCRIPT OPERATORS A class representing arrays // Array_t
...
// -------------------------------------------------#include #include // For exit() using namespace std; #define MAX 100 class FloatArr { private: float v[MAX]; // The array public: float& operator[](int i); static int MaxIndex(){ return MAX-1; } }; float& FloatArr::operator[]( int i ) { if( i < 0 || i >= MAX ) { cerr << "\nFloatArr: Outside of range!" << endl; exit(1); } return v[i]; // Reference to i-th element
...
int i; // An index
...
0F; cout << "\nEnter indices between 0 and " << FloatArr::MaxIndex() << "!" << "\n (Quit by entering invalid input)" << endl; while( cout << "\nIndex: " && cin >> i ) cout << i << "
...
It is a binary operator and thus has two operands
...
The subscript operator for arrays implies background pointer arithmetic, for example, v[i] is equivalent to *(v+i)
...
ᮀ Usage in Classes These restrictions do not apply if the index operator is overloaded for a class
...
The following therefore applies: ■ ■ ■ the left operand must be a class object the right operand can be any valid type the result type is not defined
...
However, your overloading should always reflect the normal use of arrays
...
Since an index can be of any valid type, the possibilities are unlimited
...
ᮀ Notes on the Sample Program Range checking is not performed when you access the elements of a normal array
...
However, you can address this issue by defining your own array classes, although this may impact the speed of your programs
...
The subscript operator [] has been overloaded to return a reference to the i-th array element
...
If an invalid index is found, the program issues an error message and terminates
...
As we will see, variable lengths are possible using dynamic memory allocation
...
h : Class Euro to represent an Euro // --------------------------------------------------#ifndef _EURO_H_ #define _EURO_H_ //
...
// The print() method is now superfluous
...
cpp // Overload the shift operators // for input/output of Euro type objects
...
h" #include using namespace std; // Output to stream os
...
asString() << " Euro"; return os; } // Input from stream is
...
x,xx): "; int euro = 0, cents = 0; char c = 0; if( !(is >> euro >> c >> cents)) // Input
...
') || cents>=100) // Error? is
...
e = Euro( euro, cents); // No => Accept return is; // value
...
However, the compiler can process the previous statement if it can locate a suitable operator function, operator<<()
...
ᮀ Overloading the << Operator In the previous example, the left operand of << is the object cout, which belongs to the ostream class
...
The right operand is a Euro class object
...
This allows for normal concatenation of operators
...
Example: cout << "Enter the price in Euros: " cin >> price; The second statement causes the following call: operator>>( cin, price); As cin is an object of the standard istream class, the first parameter of the operator function is declared as a reference to istream
...
The header file Euro
...
To allow these functions to access the private members of the Euro class, you can add a friend declaration within the class
...
■ CHAPTER 19 ■ exercise s 430 OVERLOADING OPERATORS EXERCISES Prefix and postfix increment To distinguish the postfix increment operator from the prefix increment operator, the postfix operator function has an additional parameter of type int
...
operator++() obj++ ✓ (Prefix) (Postfix) obj
...
The prefix and postfix decrement operators -- are distinguished in the same manner
...
Now modify the class as follows: ■ ■ ■ Overload the relational operators < > <= >= == and != and the shift operators >> and << for input and output using global operator functions
...
Then overload both the prefix and postfix versions of the ++ and -operators
...
The -- operator decrements the time by one second
...
Write a main function that executes all the overloaded operators and displays their results
...
■ ■ ■ Use a header file called fraction
...
The constructor has two parameters of type long: the first parameter (numerator) contains the default value 0, and the second parameter (denominator) contains the value 1
...
The operator functions of the binary operators +, -, *, / and the input / output operators <<, >> are to be declared as friend functions of the Fraction class
...
If the denominator assumes a value of 0, issue an error message and terminate the program
...
The formulae for arithmetic operations are shown opposite
...
Output both the operands and the results
...
h Class DayTime with all relational operators, the operators ++ and -- (prefix and postfix), such as the operators << and >> for input/output
...
second = 0, ++minute; if( minute >= 60) minute = 0, ++hour; if( hour >= 24) hour = 0, overflow = true; } void dec() // private function for -{ --second; if( second < 0) // handle underflow
...
asSeconds() < t2
...
asSeconds() <= t2
...
asSeconds() == t2
...
getHour() << ':' << setw(2) << t
...
getSecond() << " Time"; os << setfill(' '); return os; } istream& operator>>( istream& is, DayTime& t) { cout << "Enter daytime in hh:mm:ss format: "; int hr = 0, min = 0, sec = 0; char c1 = 0, c2 = 0; if( !(is >> hr >> c1 >> min >> c2 >> sec)) return is; if( c1 != ':' || c2 != ':' || ! t
...
setstate( ios::failbit); // Error! // => Set fail bit
...
cpp // Testing the operators of class DayTime
...
h" // Definition of the class #include using namespace std; int main() { DayTime cinema( 20,30); cout << "\nThe movie starts at " << cinema << endl; DayTime now; cout << "What time is it now?" << endl; if( !(cin >> now) ) cerr << "Invalid input!" << endl; else cout << "\nThe time is now" << now << endl; cout << "\nThe movie has "; if( cinema < now) cout << "already begun!\n" << endl; else cout << "not yet begun!\n" << endl; cout << "Now it is " << now++ << endl; cout << "After 2 seconds: " << ++now << endl; DayTime depart(16,0); cout << "Let's go at " << --depart << endl; if( depart >= now ) cout << "You can ride with us!" << endl; else cout << "We don't have room!" << endl; return 0; } ■ 435 436 ■ CHAPTER 19 OVERLOADING OPERATORS Exercise 2 // -----------------------------------------------------// Fraction
...
numerator * denominator + numerator * a
...
denominator; return *this; } Fraction& operator-=(const Fraction& a) { *this += (-a); return *this; } Fraction& operator++() { numerator += denominator; return *this; } Fraction& operator--() { numerator -= denominator; return *this; } SOLUTIONS friend friend friend friend friend friend }; #endif Fraction Fraction Fraction Fraction ostream& istream& ■ 437 operator+(const Fraction&, const Fraction&); operator-(const Fraction&, const Fraction&); operator*(const Fraction&, const Fraction&); operator/(const Fraction&, const Fraction&); operator<< (ostream& os, const Fraction& a); operator>> (istream& is, Fraction& a); // ------------------------------------------------------// Fraction
...
// ------------------------------------------------------#include "Fraction
...
denominator = a
...
denominator; temp
...
numerator*b
...
numerator * a
...
numerator = a
...
numerator; temp
...
denominator * b
...
numerator == 0) { cerr << "\nError: Division by zero!\n"; exit(1); } // To multiply a by the inverse of b: Fraction temp; temp
...
numerator * b
...
denominator = a
...
numerator; if( temp
...
numerator = -temp
...
denominator = -temp
...
numerator << "/" << a
...
numerator; cout << " Denominator != 0: "; is >> a
...
denominator == 0) { cout << "\nError: The denominator is 0\n" " New denominator != 0: "; is >> a
...
denominator == 0) { cerr << "\nError: Division by zero!\n"; exit(1); } } if( a
...
numerator = -a
...
denominator= -a
...
cpp Testing the class Fraction
...
cpp Fraction
...
h" int main() { Fraction a(1,3), b(4); cout << "\nSome test results:\n\n"; cout << " a = " << a << endl; cout << " b = " << b << endl; cout cout cout cout << << << << " " " " cout << " cout << " a a a a + * / b b b b = = = = " " " " << << << << --a = " << ++a = " << (a (a (a (a + * / b) b) b) b) << << << << endl; endl; endl; endl; --a << endl; ++a << endl; a += Fraction(1,2); cout << " a+= 1/2; a = " << a << endl; a -= Fraction(1,2); cout << " a-= 1/2; a = " << a << endl; cout << "-b = " << -b << endl; cout << "\nAnd now an input\n"; cin >> a; cout << "\nYour input: " << a << endl; return 0; } ■ 439 This page intentionally left blank chapter 20 Type Conversion for Classes Implicit type conversion occurs in C++ when an expression cannot be compiled directly but can be compiled after applying a conversion rule
...
Finally, we discuss ambiguity occurring due to type conversion and how to avoid it
...
41); // double -> Euro
...
12; // Implicit conversion: // double -> Euro your += 20; // Implicit conversion: // int -> Euro your = Euro(999
...
45; // Explicit conversion // (cast style) your = my; ✓ // No conversion NOTE When the copy constructor performs a type conversion, a temporary object is first created and this object is used in the assignment
...
CONVERSION CONSTRUCTORS ■ 443 ᮀ Possible Type Conversions Implicit and explicit type conversion is also performed for classes in C++
...
You can allow type conversion between different classes or between classes and fundamental types
...
A conversion constructor performs type conversion by converting any given type to the type of the current class
...
ᮀ Conversion Constructors A constructor with a single parameter determines how to form an object of the new class from the argument passed to it
...
The copy constructor is an exception to this rule: it creates an object of the same class and does not perform type conversion
...
The standard string class contains a constructor that creates a string object from a C string, for example
...
ᮀ Calling a Conversion Constructor Conversion constructors have already been used in several examples; for example, in the Euro class
...
Examples: Euro salary(8765
...
1; salary += 897
...
Addition is not defined for a euro and a double value
...
This object is then added to the value of the salary object
...
h : The class Euro represents a euro
...
class Euro { private: long data; // Euros * 100 + Cents public: Euro( int euro = 0, int cents = 0); Euro( double x); // For conversion from Euro to double: operator double() const { return (double)data/100
...
other methods as before
...
cpp : Testing conversions of class Euro
...
h" // Definition of the class #include using namespace std; int main() { cout << " * * * Testing Conversions * * * \n" << endl; Euro salary( 8888,80); double x(0
...
10; // implicit double -> Euro x = salary; // implicit Euro -> double x = (double)salary; // explicit Euro -> double x = salary
...
9 cout << " i = " << i << endl; // 9888 return 0; } CONVERSION FUNCTIONS ■ 445 If you need to convert an object of the current class to another type, you must define a conversion function to do so
...
Conversion functions are also automatically used by the compiler to perform implicit and explicit type conversion
...
Its name is made up of the operator keyword and the target type to convert to
...
You may have noticed that the declaration of a conversion function does not contain a return type
...
The target type can contain multiple keywords, such as unsigned short or const float*
...
The Euro shown opposite contains a conversion function with a double target type
...
Example: double x = oneEuro; // implicit ᮀ Conversion Function versus Conversion Constructor The target type of a conversion function can also be a class
...
If you do not want to modify the target class—perhaps because it is a standard class— a conversion function will perform the task well
...
In the previous example, an int variable is assigned to a euro object by this method
...
446 ■ CHAPTER 20 ■ TYPE CONVERSION FOR CLASSES AMBIGUITIES OF TYPE CONVERSIONS Explicit type conversion for class Euro // Euro
...
// ----------------------------------------------------//
...
0;} // No conversion function operator double(), // or as previously seen
...
cpp // Tests explicit conversion of class Euro
...
h" // Class definition #include using namespace std; int main() { Euro salary( 8888
...
0); /* Now impossible: salary += 1000; salary += 0
...
77; x = salary; x = (double)salary; // double constructor // implicit int -> Euro // implicit double -> Euro // implicit Euro -> double // There is no method // operator double()
...
77); // explicit double -> Euro salary += Euro(1000
...
asDouble(); // explicit by method // Euro -> double int i = salary
...
The Euro class contains a conversion constructor that converts a double value to euros
...
Example: retail = wholesale + 46
...
Since both conversion types double -> Euro and Euro -> double are defined, two possible conversions could be performed: prov2 + Euro(546
...
9; // To add values // of type double However, the compiler can only perform implicit type conversion if the technique is not ambiguous
...
ᮀ Avoiding Implicit Type Conversion You can prevent ambiguities by stating any desired conversions explicitly
...
Moreover, undesirable type conversion, which can occur when classes are extended at a later date, can be avoided
...
As the example on the opposite page shows, only explicit calls to the constructor are possible in this case
...
9) ■ // ok implicit type conversions by conversion functions can be prevented by not defining the function, of course
...
Type conversion can only be performed by calling this function explicitly
...
cpp //
...
if( numerator == 0) { denominator = 1; return; } // Calculating the greatest common divisor // using an algorithm by Euclid
...
In addition, fractions should be rounded after arithmetic operations
...
The method computes the largest common divisor of numerator and denominator
...
Add an appropriate call to the simplify() function to all operator functions (except ++ and --)
...
Example: Fraction b(0
...
The following technique should suffice for numbers below one million
...
5 for rounding
...
Set the value of the denominator to 1000
...
You now have a conversion constructor for long and double types
...
Define the appropriate conversion function inline
...
More specifically, use assignments and arithmetic functions to do so
...
Output the operands and the results on screen
...
h // A numerical class to represent fractions
...
// ------------------------------------------------------#ifndef _FRACTION_ #define _FRACTION_ #include
...
h> class Fraction { private: long numerator, denominator; public: Fraction(long z, long n); Fraction(double x); // double-constructor // Default long- and int-constructor: Fraction(long z=0) : numerator(z), denominator(1) {} Fraction(int z) : numerator(z), denominator(1) {} void simplify(); operator double() // Fraction -> double { return (double)numerator / (double)denominator; } Fraction operator-() const { return Fraction(-numerator, denominator); } Fraction& operator+=(const Fraction& a) { numerator = a
...
denominator; denominator *= a
...
}; #endif SOLUTION // // // // // ■ -------------------------------------------------------Fraction
...
-------------------------------------------------------- #include
...
h> #include "Fraction
...
} Fraction::Fraction( double x) { x *= 1000
...
0) ? 0
...
5; numerator = (long)x; denominator = 1000; simplify(); } // Round the 4th digit
...
denominator = a
...
denominator; temp
...
numerator*b
...
numerator * a
...
simplify(); return temp; } // The functions // operator-() operator<<() // are left unchanged
...
simplify() // just like the function operator+()
...
451 452 ■ CHAPTER 20 TYPE CONVERSION FOR CLASSES // ------------------------------------------------------// Fract_t
...
// ------------------------------------------------------#include
...
h" int main() { Fraction a, b(-1,5), c(2
...
5, y; a = x; // double -> Fraction cout << "\nSome test results:\n" << endl; cout << " a = " << a << endl; cout << " b = " << b << endl; cout << " c = " << c << endl; cout << "\nThe fractions as double values:\n" << endl; // Fraction -> double: cout << " a = " << (double)a << endl; cout << " b = " << (double)b << endl; cout << " c = " << (double)c << endl; cout cout cout cout cout << << << << << "\nAnd calculate with:\n" " a + b = " << (a + b) << " a - b = " << (a - b) << " a * b = " << (a * b) << " a / b = " << (a / b) << << endl; endl; endl; endl; endl; cin >> a; // Enter a fraction
...
simplify(); cout << "\nSimplified: " << a << endl; cout << "\nAs double value: " << (double)a << endl; cout << "\nEnter a floating point value: "; cin >> x; cout << "\nThis is in fraction form: " << (Fraction)x << endl; // To calculate the sum b + x : cout << " b = " << b << endl; cout << " x = " << x << endl; // a = b + x; // Error: ambiguous! a = b + Fraction(x); // ok! To compute fractions
...
cout << " b + x as fraction: " << a << endl; cout << " b + x as double: " << y << endl; return 0; } chapter 21 Dynamic Memory Allocation This chapter describes how a program can allocate and release memory dynamically in line with current memory requirements
...
453 454 ■ CHAPTER 21 ■ DYNAMIC MEMORY ALLOCATION THE OPERATOR new Sample calls to new // Dynamic objects of type long and double // -----------------------------------------------------long *ptr_long; ptr_long = new long; // No initialization // of the long object
...
9; ptr_double = new double(z); // With initialization ++(*ptr_double); *ptr_double += *ptr_long; // Increment the value // ok to add long value ptr_long = new double(2
...
9 THE OPERATOR new ■ 455 ᮀ Dynamic Memory Allocation When a program is compiled, the size of the data the program will need to handle is often an unknown factor; in other words there is no way to estimate the memory requirements of the program
...
Dynamically allocated memory can be released to continually optimize memory usage with respect to current requirements
...
Programs can access a large space of free memory known as the heap
...
C++ uses the new and delete operators to allocate and release memory, and this means that objects of any type can be created and destroyed
...
ᮀ Calling new for Fundamental Types The new operator is an operator that expects the type of object to be created as an argument
...
The new operator creates an object of the specified type and returns the address of that object
...
If the pointer belongs to a wrong type, the compiler will issue an error message
...
The previous call to new does not define an initial value for the new object, however, you can supply a value in parentheses to initialize the object
...
99); Following this statement pld points to a memory address containing a long double type with a value of 10000
...
The statement cout << *pld << endl; will output this value
...
cpp // The operators new and delete for built-in types
...
// --------------------------------------------------#include using namespace std; int main() { cout << "\nTesting dynamic storage allocation! " << endl; // To allocate storage: double width = 23
...
54); double* ptrArea = new double; // To work with ptrWidth, ptrLength, and ptrArea: *ptrArea = *ptrWidth * *ptrLength; delete ptrLength; // Error: The object is still // in use! cout << "\nWidth : " << *ptrWidth << "\nLength : " << *ptrLength << "\nArea : " << *ptrArea << endl; // To free storage: delete ptrWidth; delete ptrLength; delete ptrArea; delete ptrLength; // // // // Error: The object has not been dynamically reserved ok ok // Error: Pointer doesn't // address any object
...
45); // ok // To give a name to a dynamic object: double& length = *ptrLength; // Reference cout << "\nNew length : " << length << "\nCircumference : " << 2 * width * length << endl; return 0; // On terminating the program } // allocated storage will be freed
...
Failure to do so can impact the performance of your computer system
...
ᮀ Calling delete Memory that has been allocated by a call to new can be released using the delete operator
...
But make sure that this memory space was dynamically allocated by a call to new! Example: long *pl = new long(2000000);
...
If you do not call delete, the dynamically allocated memory space is not released until the program terminates
...
In this case nothing happens and delete just returns, so you do not need to check for NULL pointers when releasing memory
...
As the sample program illustrates, misuse of delete can be disastrous
...
ᮀ Error Handling for new If there is not enough memory available, the so-called new handler is called
...
Thus, you do not need to design your own error handling routines each time you call new
...
Exceptions can be caught by the program, allowing the error condition to be remedied (refer to Chapter 28, Exception Handling)
...
If you are working with an older compiler, please note that new returns a NULL pointer if not enough memory is available
...
cpp // The operators new and delete for classes
...
h" #include using namespace std; Account *clone( const Account* pK); // Create a copy // dynamically
...
\n" << endl; // To allocate storage: Account *ptrA1, *ptrA2, *ptrA3; ptrA1 = new Account; ptrA1->display(); // With default constructor // Show default values
...
87); ptrA1->display(); // // // // Set the other values by access methods
...
// Use the constructor with three arguments: ptrA2 = new Account("Xiang, Zhang", 7531357, 999
...
ptrA3 = clone( ptrA1); // Pointer to a dyna// mically created copy
...
delete ptrA1; delete ptrA2; delete ptrA3; // Release memory return 0; } Account *clone( const Account* pK) { return new Account(*pK); } // Create a copy // dynamically
...
In this case, in addition to allocating memory, a suitable constructor must be called
...
However, the operators new and delete ensure that this happens
...
Unless explicitly initialized, the default constructor is called for each new object, but you must make sure that a default constructor exists! Example: Euro* pEuro = new Euro; This statement allocates memory for an object of the Euro class
...
ᮀ Explicit Initialization To initialize an object explicitly, you can state its initial values in parentheses when you call new
...
If the compiler is unable to locate a suitable constructor, an error message occurs
...
The object is initialized using the supplied values
...
Example: *pE += 200; // To add 200 euros
...
Example: cout << pE->getCents() << endl; // 33 ᮀ Releasing Memory When an object that was created dynamically is destroyed, the delete operator makes sure that the object is cleaned up
...
As previously discussed in the section on fundamental types, when you call delete you must ensure that the pointer is addressing a dynamic object or that you are dealing with a NULL pointer
...
cpp // Operators new[] and delete[] for dynamic arrays
...
\n" << endl; int size = 0, cnt = 0, step = 10, i; float x, *pArr = NULL; cout << "Enter some numbers!\n" "End with q or another character " << endl; while( cin >> x) { if( cnt >= size) // Array too small? { // => enlarge it
...
0; cout << "Your input: " << endl; for( i = 0; i < cnt; i++) // To output and { // add
...
Your best option is to let the program create the array dynamically
...
ᮀ The new[ ] Operator The new[ ] operator is available for creating dynamic arrays
...
Syntax: vekPtr = new Type[cnt]; The pointer vekPtr will then reference the first of a total of cnt array elements
...
Of course, Type can also be a class
...
Those objects are pk[0], pk[1],
...
, *(pk + 255)
...
Starting values for the array elements cannot be assigned until later
...
To do so, simply call the delete[] operator
...
Example: delete[] pk; The operand for delete[]—the pointer pk in this case—must reference the place in memory that was allocated by a call to new[]! The destructor belonging to the current class is called for each array element
...
e
...
The program on the opposite page stores numbers in a dynamic array
...
To do so, a newer bigger array is created, the data is copied to the new array, and the memory occupied by the old array is released
...
A linked list is a dynamic data structure that allows easy insertion and deletion of data
...
The type of data structure you choose has a far-reaching effect on the amount of memory you need, the speed of access to the data involved, and the complexity (or simplicity) of the algorithms (data operations) you need
...
One example of this is an array whose size can be changed during runtime
...
The first element in the list has no predecessor and the last element no successor
...
ᮀ Advantages The storage used for the list elements need not be contiguous
...
When an array element is inserted or deleted, the other array elements have to be moved to make room or fill up the “gap” in the array
...
464 ■ CHAPTER 21 ■ DYNAMIC MEMORY ALLOCATION REPRESENTING A LINKED LIST Classes of header file List
...
h // Defines the classes ListEl and List
...
h" // Class Date from Chapter 14 #include #include using namespace std; class ListEl { private: Date date; double amount; ListEl* next; // A list element
...
0, ListEl* p = NULL) : date(d), amount(b), next(p) {} // Access methods: // getDate(), setDate(), getAmount(), setAmount() ListEl* getNext() const { return next; } friend class List; }; // ---------------------------------------------------// Defining the class List class List { private: ListEl* first, *last; public: List(){ first = last = NULL; } // Constructor ~List(); // Destructor // Access to the first and last elements: ListEl* front() const { return first; } ListEl* back() const { return last; } // Append a new element at the end of the list: void pushBack(const Date& d, double b); // Delete an element at the beginning of the list void popFront(); }; #endif // _LIST_H_ REPRESENTING A LINKED LIST ■ 465 ᮀ Representing List Elements You can use a recursive data structure to represent a linked list
...
Of course, the data structure cannot contain itself—that would be impossible—but it does contain a pointer to itself
...
A transaction is characterized by a date, a sum of money, and the reason for the transaction
...
The class shown on the opposite page, ListEl, was designed to represent list elements
...
The public declaration includes a constructor and access methods for the live data
...
It is common practice to let the pointer for the last element in the list point to NULL
...
ᮀ Representing a List To identify a linked list, you just point a pointer at the first element in the list
...
A pointer to the last element in the list is useful for appending new elements
...
The private section comprises two pointers, which reference the first and last list elements respectively
...
The destructor has a more complex task: it has to release the memory occupied by the remaining list elements
...
To do so, memory is allocated dynamically and the new element becomes the successor of what was previously the last element and the last pointer is updated
...
The popFront() method deletes the first element in the list
...
The special case with an empty list also applies
...
Arguments: Return value: The two int arrays, their length, and the position at which they are to be spliced
...
Arguments: Return value: The two int arrays and their length
...
Exercise 3 Complete and test the implementation of a linked list found in this chapter
...
Then overload the << operator for the class ListEl to allow formatted output of the data in the list elements
...
Then implement the destructor for the List class
...
Make sure that you read the pointer to the successor of each element before destroying it! Implement the methods pushBack() and popFront() used for appending and deleting list elements
...
Test the List class by inserting and deleting several list elements and repeatedly outputting the list
...
cpp // Implements the splice algorithm
...
using namespace std; // Prototype: int *splice( int v1[], int len1, int v2[], int len2, int pos); int main() { cout << "\n * * * Testing the splice function * * *\n" << endl; int i, len1 = 10, len2 = 5; int *a1 = new int[len1], *a2 = new int[len2]; // Initialize the random number generator // with the current time: srand( (unsigned)time(NULL)); for( i=0; i < len1; ++i) a1[i] = rand(); for( i=0; i < len2; ++i) a2[i] = -rand(); // Initialize the arrays: // with positive and // negative numbers
...
array: " << endl; for( i = 0; i < len1; ++i) cout << setw(12) << a1[i]; cout << endl; cout << "2
...
, " << len1 << " : "; int pos; cin >> pos; SOLUTIONS ■ int *a3, len3 = len1 + len2; a3 = splice( a1, len1, a2, len2, pos); if( a3 == NULL) cerr << "\n Invalid position!\n" << endl; else { cout << " The new spliced array: " << endl; for( i = 0; i < len3; ++i) cout << setw(12) << a3[i]; cout << endl; } delete[] a1; delete[] a2; delete[] a3; return 0; } // ------------------------------------------------------// Function splice() inserts the array v2 into v1 // starting at position pos
...
cpp // Implements the merge algorithm
...
selectionSort( a2, len2); // To sort array a2
...
inline void swap( int& a, int& b) // To swap
...
minp = p; swap( *arr, *minp); // To swap
...
int *merge( int v1[], int len1, int v2[], int len2) { int i = 0, i1 = 0, i2 = 0; int *v = new int[len1+len2]; // New int array
...
while( i1 < len1) v[i++] = v1[i1++]; else while( i2 < len2) v[i++] = v2[i2++]; return v; } 471 472 ■ CHAPTER 21 DYNAMIC MEMORY ALLOCATION Exercise 3 // // // // // // // // // // // ---------------------------------------------------date
...
---------------------------------------------------date
...
---------------------------------------------------These files are left unchanged from Chapter 14 (solutions)
...
h // Defines the classes ListEl and List // to represent a linked list
...
h" #include #include using namespace std; class ListEl { private: Date date; double amount; ListEl* next; // Date // Amount of money // Pointer to successor public: ListEl( Date d = Date(1,1,1), double b = 0
...
setDate(); } bool setDate( int day, int month, int year) { return setDate( day, month, year); } SOLUTIONS double getAmount() const { return amount; } void setAmount(double a) { amount = a; } ListEl* getNext() const { return next; } friend class List; }; // Output an element ostream& operator<<( ostream& os, const ListEl& le); // -----------------------------------------------------// Defines the List class class List { private: ListEl* first, *last; public: List(){ first = last = NULL; } // Constructor ~List(); // Destructor // Access first and last elements: ListEl* front() const { return first; } ListEl* back() const { return last; } // Appends a new element at the end of the list: void pushBack(const Date& d, double b); // Deletes an element at the beginning of the list
...
cpp // Implements the methods of class List, // which are not previously defined inline
...
h" // Destructor of the list: List::~List() { ListEl *pEl = first, *next = NULL; for( ; pEl != NULL; pEl = next) { next = pEl->next; delete pEl; } } ■ 473 474 ■ CHAPTER 21 DYNAMIC MEMORY ALLOCATION // Appends a new element at the end of the list: void List::pushBack(const Date& d, double b) { ListEl *pEl = new ListEl( d, b); if( last == NULL) // List empty? first = last = pEl; else last->next = pEl, last = pEl; } // Deletes an element from the beginning of the list
...
first = first->next; // Move to the next element
...
getDate()
...
getAmount(); return os; } // Outputs the list: ostream& operator<<( ostream& os, const List& List) { ListEl *pEl = List
...
cpp // Tests the List class
...
h" int main() { cout << "\n * * * << endl; List Testing the class list aList; cout << aList << endl; * * *\n" // A list // List is still empty
...
g
...
setDate( month, day, year) ) break; // Invalid date
...
pushBack( date, amount); } cout << "\nContent of the list:\n"; cout << aList << endl; cout << "\nRemoving the first element of the list:\n"; ListEl *ptrEl = ptrEl = aList
...
popFront(); } cout << "\nContent of the list:\n"; cout << aList << endl; return 0; } 475 This page intentionally left blank chapter 22 Dynamic Members This chapter describes how to implement classes containing pointers to dynamically allocated memory
...
A class designed to represent arrays of any given length is used as a sample application
...
1 6
...
2 2
...
4 Data members of class FloatArr // A class representing dynamic arrays of floats
...
int cnt; // Number of present elements public: // Public methods here }; MEMBERS OF VARYING LENGTH ■ 479 ᮀ Dynamic Members You can exploit the potential of dynamic memory allocation to leverage existing classes and create data members of variable length
...
In order to do this the class needs a pointer to the dynamically allocated memory that contains the actual data
...
When compiling a program that contains arrays, you will probably not know how many elements the array will need to store
...
ᮀ Requirements In the following section you will be developing a new version of the FloatArr class to meet these requirements and additionally allow you to manipulate arrays as easy as fundamental types
...
Example: v2 = v1; The object v2 itself—and not the programmer—will ensure that enough memory is available to accommodate the array v1
...
Example: FloatArr v3(v2); Here the object v3 ensures that enough memory is available to accommodate the array elements of v2
...
The statement Example: FloatArr fArr(100); allocates memory for a maximum of 100 array elements
...
In addition to this, two int variables are required to store the maximum and current number of array elements
...
h : Dynamic array of floats
...
int cnt; // Number of array elements public: FloatArr( int n = 256 ); // Constructor FloatArr( int n, float val); ~FloatArr(); // Destructor int length() const { return cnt; } float& operator[](int i); // Subscript operator
...
bool remove(int pos); // Delete position pos
...
h" #include using namespace std; int main() { FloatArr v(10); FloatArr w(20, 1
...
0
...
append( 0
...
length() << endl; // cout << " Current number of elements in w: " << w
...
You can enhance FloatArr class step by step by optimizing existing methods or adding new methods
...
ᮀ Constructors It should be possible to create an object of the FloatArr class with a given length and store a float value in the object, if needed
...
FloatArr(int n = 256); The number 256 is the default argument for the length of the array
...
An additional constructor FloatArr( int n, int val ); allows you to define an array where the given value is stored in each array element
...
Example: FloatArr arr( 100, 0
...
0
...
arr
...
You can overload the subscript operator [] to access individual array elements
...
0F; The index i must lie within the range 0 to cnt-1
...
The number of elements is then incremented by one
...
This reduces the current count by one, provided a valid position was stated
...
0F ); First, memory is allocated for the data members: Object fArr arrPtr max: 10 cnt: 10 Then storage is allocated for 10 array elements and the variables max and cnt are set to 10: Object fArr ? arrPtr max: ? ? ? ? ? ? ? ? 10 cnt: ? 10 Finally, a value of 1
...
0 1
...
0 1
...
0 1
...
0 1
...
0 1
...
The object itself only occupies the memory required for the data members arrPtr, max, and cnt
...
The additional dynamic memory allocation may need to be adjusted to meet new requirements, for example, if an assignment is made
...
ᮀ Constructing an Object The first constructor in the FloatArr class is defined as follows: FloatArr::FloatArr( int n ) { max = n; cnt = 0; arrPtr = new float[max]; } This allocates memory for n array elements
...
The second constructor fills the array with the supplied value and is therefore defined as follows: FloatArr::FloatArr(int n, float val) { max = cnt = n; arrPtr = new float[max]; for( int i=0; i < cnt; ++i) arrPtr[i] = val; } The opposite page shows how memory is allocated for the object fArr and how this object is initialized
...
Classes with dynamic members will always need a destructor to perform this task
...
FloatArr::~FloatArr() { delete[] arrPtr; } 484 ■ CHAPTER 22 ■ DYNAMIC MEMBERS IMPLEMENTING METHODS New version of class FloatArr // FloatArr
...
// ----------------------------------------------------#include "floatArr
...
// Subscript operator for objects that are not const: float& FloatArr::operator[]( int i ) { if( i < 0 || i >= cnt ) // Range checking { cerr << "\n class FloatArr: Out of range! "; exit(1); } return arrPtr[i]; } float FloatArr::operator[]( int i ) const { // Else as before
...
Example: FloatArr v(5, 0
...
2F; for( int i=0; i < v
...
However, you will need to support read-only access to constant objects
...
The first version returns a reference to the i-th array element and thus supports write access
...
The implementation of these versions is identical
...
If the index lies within the valid boundaries, an array element— or simply a value in the case of the read-only version—is returned
...
In the first version, the append() only works if there is at least one empty slot in the array
...
This also applies for a new method, insert(), which you will write as an exercise in this chapter
...
The current count is decremented by one
...
Another technique would be to copy the last element to the position of the element that needs to be deleted, simply overwriting that element
...
486 ■ CHAPTER 22 ■ DYNAMIC MEMBERS COPY CONSTRUCTOR Effect of the standard copy constructor FloatArr b(a); // Creates a copy of a
...
1 6
...
2 2
...
cpp: Implementing the methods
...
max; cnt = src
...
arrPtr[i]; } COPY CONSTRUCTOR ■ 487 ᮀ Initializing with an Object The next step is to ensure that an existing object can be used to initialize a new object
...
The FloatArr class needs a copy constructor to perform this task
...
Prototype: FloatArr( const FloatArr& ); ᮀ Standard Copy Constructor If a class does not contain a copy constructor, the compiler will automatically create a minimal version, known as the standard copy constructor
...
A standard copy constructor is normally sufficient for a class
...
This would merely copy the pointers, meaning that the pointers of several different objects would reference the same place in memory
...
This scenario would obviously mean trouble
...
The pointer for the second object would reference a memory area that no longer existed! ᮀ Proprietary Version of the Copy Constructor Clearly you will need to write a new copy constructor for classes with dynamic members, ensuring that the live data and not just the pointers are copied from the dynamically allocated memory
...
Calling new[] creates a new array and the array elements of the object passed to the method are then copied to that array
...
h : Dynamic arrays of floats
...
Data members as before public: //
...
cpp // The operator function implementing "="
...
max; cnt = src
...
arrPtr[i] = src
...
h" int main() { FloatArr v; FloatArr w(20, 1
...
// Array w - 20 float values // with initial value 1
...
// Use copy constructor // to create an object
...
ASSIGNMENT ■ 489 Each class comprises four implicitly defined default methods, which you can replace with your own definitions: ■ ■ the default constructor and the destructor the copy constructor and the standard assignment In contrast to initialization by means of the copy constructor, which takes place when an object is defined, an assignment always requires an existing object
...
ᮀ Default Assignment Given that v1 and v2 are two FloatArr class objects, the following assignment is valid: Example: v1 = v2; // Possible, but ok? Default assignment is performed member by member
...
However, this technique is not suitable for classes with dynamic members
...
In addition, memory previously addressed by a pointer of the target object will be unreferenced after the assignment
...
Generally speaking, if you need to define a copy constructor, you will also need to define an assignment
...
The operator function is implemented as a class method and returns a reference to the target object allowing multiple assignments
...
■ CHAPTER 22 ■ exercise s 490 DYNAMIC MEMBERS EXERCISES New methods of class List // Copy constructor: List::List(const List&); // Assignment: List& List::operator=( const List&); New methods of class FloatArr // Methods to append a float or an // array of floats: void append( float val); void append( const FloatArr& v); FloatArr& operator+=( float val); FloatArr& operator+=( const FloatArr& v); // Methods to insert a float or an // array of floats: bool insert( float val, int pos); bool insert( const FloatArr& v, int pos ); // In any case, more memory space must be allocated // to the array if the current capacity is // insufficient
...
First, modify your test program to create a copy of a list
...
Note how your program reacts
...
Since the class contains dynamic members, the following tasks must be performed: ■ ■ Define a copy constructor for the List class
...
Exercise 2 Add the methods shown opposite to the FloatArr class
...
As this could also be necessary for other methods, write a private auxiliary function for this purpose void expand( int newMax ); The method must copy existing data to the newly allocated memory
...
The insert() method inserts a float value or a FloatArr object at position pos
...
Also overload the shift operator << to output an array using the field width originally defined to output the array elements
...
Now add calls to the new methods to your test program and output the results after each call
...
h // Definition of classes ListEl and List // representing a linked list
...
h" #include #include using namespace std; class ListEl { // Unchanged as in Chapter 21 }; // -----------------------------------------------------// Definition of class List class List { private: ListEl* first, *last; public: // New methods: List(const List&); // Copy constructor List& operator=( const List&); // Assignment // Otherwise unchanged from Chapter 21 }; #endif // _LIST_H_ // ---------------------------------------------------// List
...
// ---------------------------------------------------#include "List
...
first = last = NULL; ListEl *pEl = src
...
pEl = src
...
// ------------------------------------------------------// List_t
...
// ------------------------------------------------------#include "List
...
Date date( 11,8,1999); // Insert 3 elements
...
56); list1
...
setDate( 1, 1, 2002); amount = -1000
...
pushBack( date, amount); date
...
11; list1
...
get(); List list2( list1); cout << "A copy of the 1st list has been created!\n" "Contents of the copy:\n" << endl; cout << list2 << endl; cout << "\nRemove the first element from the list:\n"; ListEl *ptrEl = ptrEl = list1
...
popFront(); } cout << "\nContent of the list:\n"; cout << list1 << endl; list1 = list2; // Reassign the copy
...
h : Dynamic arrays of floating-point numbers
...
Number of present array elements void expand( int newMax); // Helps enlarge the array public: // Constructors , destructor, // assignment, subscript operator, and method length() // as before in this chapter
...
// To output the array friend ostream& operator<<( ostream& os, const FloatArr& v); }; #endif // _FLOATARR_ 495 496 ■ CHAPTER 22 // // // // DYNAMIC MEMBERS ----------------------------------------------------FloatArr
...
----------------------------------------------------- #include "floatArr
...
// --- The new functions --- // Private auxiliary function to enlarge the array
...
void FloatArr::append( float val) { if( cnt+1 > max) expand( cnt+1); arrPtr[cnt++] = val; } void FloatArr::append( const FloatArr& v) { if( cnt + v
...
cnt); int count = v
...
arrPtr[i]; } // Necessary if v == *this SOLUTIONS // Insert a float or an array of floats bool FloatArr::insert( float val, int pos) { return insert( FloatArr(1,val), pos); } bool FloatArr::insert( const FloatArr& v, int pos ) { if( pos < 0 || pos >= cnt) return false; // Invalid position if( max < cnt + v
...
cnt); int i; for( i = cnt-1; i >= pos; --i) arrPtr[i+v
...
cnt; ++i) arrPtr[i+pos] = v
...
cnt; return true; // Shift up // starting at pos // Fill gap
...
width(); // Save field width
...
arrPtr; p < v
...
cnt; ++p) { os
...
cpp // Tests the class FloatArr
...
h" #include #include using namespace std; int main() { FloatArr v(10); FloatArr w(15, 1
...
0
...
length() << endl; " Current total of elements in w: " w
...
0F; for( ; x < 6 ; x += 1
...
append(x); // Append values
...
cout << "\nThe copy of v has been created
...
remove(3); w
...
0F); w
...
0F); // // // // Erase the element at position 3
...
And once more! v = w; cout << "\nAssignment done
...
insert( cv, 0); cout << "\nThe elements after inserting " " the copy at position 0: \n" << setw(5) << v << endl; return 0; } chapter 23 Inheritance This chapter describes how derived classes can be constructed from existing classes by inheritance
...
499 500 ■ CHAPTER 23 ■ INHERITANCE CONCEPT OF INHERITANCE Is relation Car Properties and capacities of class Car PassCar Truck Properties and capacities of class Properties and capacities of class Car Car Additional properties and capacities of class Additional properties and capacities of class Truck PassCar CONCEPT OF INHERITANCE ■ 501 ᮀ Base Classes and Derived Classes Inheritance allows new classes to be constructed on the basis of existing classes
...
But you can add more characteristics and functionality to the new class
...
All of these vehicles have an identification number that indicates the vehicle, the manufacturer, and the vehicle status, such as “hired,” “repair shop,” and so on
...
To differentiate between vehicle types, various classes are derived from the base class Car, such as PassCar, which is used to represent passenger-carrying vehicles
...
ᮀ Is Relationship An object of the PassCar type is a special object of the Car class
...
In cases like this we can say that the derived class establishes an is relationship to the base class
...
As already mentioned, a has relationship occurs between two classes when an member of one class has another class type
...
ᮀ Data Abstraction and Reusability Inheritance has a number of important benefits for software developers: ■ ■ data abstraction: General characteristics and abilities can be handled by generic (base) classes and specializations can be organized in hierarchical relationships by means of derived classes
...
re-usability: Classes that you have defined and tested can be reused and adapted to perform new tasks
...
502 ■ CHAPTER 23 ■ INHERITANCE DERIVED CLASSES Defining a derived class class C : public B { private: // Declaration of additional private // data members and member functions public: // Declaration of additional public // data members and member functions }; Direct and indirect derivation B C D Base class B B is a direct base class B is an indirect base class DERIVED CLASSES ■ 503 When you define a derived class, the base class, the additional data members and methods, and the access control to the base class are defined
...
The C class inherits the B class, which is defined in the public section following the colon
...
ᮀ Access to Public Members in the Base Class Access privileges to the base class B are designated by the public keyword that precedes the B
...
This kind of inheritance ports the public interface of the base class to the derived class where it is extended by additional declarations
...
A public base class, therefore, implements the is relationship; this is quite common
...
Only the methods of class C can still access the public members of B, but not the users of that class
...
ᮀ Access to Private Members of the Base Class The private members of the base class are protected in all cases
...
Imagine the consequences if this were not so: you would be able to hack access to the base class by simply defining a derived class, thus undermining any protection offered by data encapsulation
...
This allows for class hierarchies
...
In the graphic on the opposite page, the arrow ↑ means directly derived from
...
504 ■ CHAPTER 23 ■ INHERITANCE MEMBERS OF DERIVED CLASSES Base class Car and derived class PassCar // car
...
The Car class and a derived class PassCar are defined in the example
...
The derived class PassCar inherits these data members
...
The object includes a so-called base subobject of type Car
...
So a PassCar type object has a total of four data members
...
The base class Car contains a constructor, access methods, and the method display(), which is used for screen output
...
In the PassCar class a constructor, additional access methods, and a second output function also called display() are declared
...
The display() method is said to have been redefined
...
The member assumes a new meaning for the derived class
...
We will be looking at this point in more detail later
...
For example, you can call the getNr() method for an object named cabrio in the PassCar class
...
getNr(); The public interface of the derived class thus comprises ■ ■ the public members of the base class and the public members additionally defined in the derived class
...
long getNr(void);
...
} void PassCar::display( void) const { cout << "\nCar number: " << getNr(); cout << "\nProducer: " << getProd(); cout << "Type: "<< passCarType; cout << "Type: "<< passCarTyp if( sunRoof) cout << "yes"; else cout << " no"; cout << endl; } ok MEMBER ACCESS ■ 507 ᮀ Access to Additional Members The methods of derived classes can access any member additionally defined in the derived class
...
ᮀ Access to Private Members of the Base Class However, a private member of the base class is not directly accessible for the methods of the derived class
...
Methods belonging to derived classes only have indirect access to the private data members of the base class
...
The opposite page shows a version of the display() method that calls the get methods in its base class Car
...
The base class is identified by the this pointer, which is passed implicitly as an argument
...
The above example thus calls the getProd() in the base class Car, as the method is not defined in the PassCar class
...
cpp This version of method PassCar::display() calls the method display() of the base class
...
The name does not occur in the base class → no redefinition
...
The name already exists in the base class → redefinition
...
In other words, redefining members in a derived class has no effect on the base class
...
This situation is similar to the one seen for local and global variables
...
ᮀ Redefinition and Overloading Normally, methods are redefined in derived classes
...
When a method is redefined, the signature and the return type of the method can be changed
...
Redefining a method will always mask a method with the same name in the base class
...
ᮀ Access to the Members in the Base Class If you redefine a method in a derived class, this does not alter the fact that the base class method still exists
...
The range :: operator is used to access the base class method
...
The display() method defined in the base class is used to output the data members of the base class
...
Otherwise the display() method in the derived class will call itself and head off into an indefinite recursion
...
passCarType = tp; sunRoof = sr; // Initial values for data mem// bers of the derived class } Second version with base class initializer // Second version of the constructors of PassCar // ---------------------------------------------------PassCar::PassCar(const string& tp, bool sr, int n, const string& hs) : Car( n, hs) { passCarType = tp; // Initial values for data memsunRoof = sr; // bers of the derived class } Third version with base class and member initializer // Third version of the constructor of PassCar // ---------------------------------------------------PassCar::PassCar(const string& tp, bool sr, int n, const string& hs) : Car( n, hs), passCarType( tp ), sunRoof( sr ) { // There remains nothing to do } CONSTRUCTING AND DESTROYING DERIVED CLASSES ■ 511 ᮀ Constructor Calls The constructor of a derived class is required to create an object of the derived class type
...
The base class constructor is called to perform this task
...
The order in which the constructors are called is important
...
The object is thus constructed from its core outwards
...
An implicit call to the default constructor of the base class occurs prior to this, and the base sub-object is initialized with default values
...
A default constructor must be available in the base class and initialization with incorrect values before assigning live values impacts the response of the program
...
This immediately initializes the data members with correct values
...
The second version of the constructor for PassCar contains a base initializer
...
This means that you can state both the base and the member initializer in a list separated by commas
...
ᮀ Destroying Objects When an object is destroyed, the destructor of the derived class is first called, followed by the destructor of the base class
...
You need to define a destructor for a derived class if actions performed by the constructor need to be reversed
...
512 ■ CHAPTER 23 ■ INHERITANCE OBJECTS OF DERIVED CLASSES Sample program // car_t
...
// ----------------------------------------------------#include "car
...
display(); cout << "\nAnd the passenger car number again: " << beetle
...
setNr(1000); cabrio
...
display(); cout << "\nOnly data of the base class: "; cabrio
...
Two objects, beetle and cabrio, of the derived class PassCar type are declared
...
However, it is sufficient to state a PassCar type with or without a sunroof as default values exist for all other data members
...
However, the following call is invalid: Example: beetle
...
ᮀ Calling Redefined Methods When you call a redefined method, the object type determines what version of the method will be executed
...
The statement Example: cabrio
...
However, in the case of the van object in the Car class, calling Example: van
...
ᮀ Calling Methods in the Base Class You may be wondering if a base class method can be called for an object of a derived class, if the method has been redefined in the derived class
...
If you want to display the basic data of the cabrio object, you can use a direct call to the base class method display() to do so
...
Car::display(); The name of the method is preceded by the name of the base class and the scope resolution operator in this case
...
h : The classes Safe and Castle // --------------------------------------------------#include using namespace std; class Safe { private: int topSecret; protected: int secret; void setTopSecret( int n) { topSecret = n;} int getTopSecret() const { return topSecret;} void setSecret( int n){ secret = n;} int getSecret() const { return secret;} public: int noSecret; Safe() { topSecret = 100; secret = 10; noSecret = 0; } }; class Castle : public Safe { public: Castle() { // topSecret = 10; setTopSecret(10); secret = 1; noSecret = 0; } void test() { // top
...
When you create a class hierarchy you may want require the methods and friend functions of a derived class to communicate directly with the members of the base class
...
For example, a class used to represent a window on screen could contain the dimensions and other characteristics of a general windows
...
ᮀ Protected Members To allow methods and friend functions access to the sheltered members of a base class, let’s introduce an additional level of access control between private and public
...
A member declared protected is sheltered from external access just like a private member
...
However, in contrast to a private member, methods and friend functions of derived classes can access the member
...
In contrast to this, protected members are inaccessible to users of these classes
...
topSecret = 1; treasure
...
setTopSecret(5); treasure
...
If you change the declaration of a protected member, every class derived from this class must be examined to ascertain whether additional modifications are necessary
...
In addition, the class Truck is to be added to the class hierarchy
...
" ■ Define a destructor for the Car and PassCar classes
...
" ■ ■ ■ ■ Then define the class Truck, which is derived from Car, using the data members shown opposite, a constructor, a destructor, and the additional methods shown opposite
...
Use the base initializer to initialize the data members of Car
...
To test your class, create and display a Truck type object in your main function
...
Observe how the various objects and member objects are created and destroyed
...
” Additionally define an overdraft limit and an interest rate for the DepAcc class
...
■ ■ For both classes, define constructors to provide default values for all parameters, add access methods, and add a display() method for screen output
...
Then modify both a savings and a deposit account interactively and display the new values
...
scanner() printer() PrepackedFood Properties: Price per piece FreshFood Properties: Weight Price per pound Methods: getPrice() setPrice()
...
scanner() printer() EXERCISES ■ 519 Exercise 3 A supermarket chain has asked you to develop an automatic checkout system
...
Groceries are either sold in packages or by weight
...
The price of groceries sold by weight is calculated by multiplying the weight by the current price per kilo
...
The Product class, which contains generic information on all products (barcode, name, etc
...
■ ■ ■ The Product class contains two data members of type long used for storing barcodes and the product name
...
Add default values for the parameters to provide a default constructor for the class
...
For test purposes, these methods will simply output product data on screen or read the data of a product from the keyboard
...
Define two classes derived from Product, PrepackedFood and FreshFood
...
In both classes define a constructor with parameters providing default-values for all data members
...
Define the access methods needed for the new data members
...
Test the various classes in a main function that creates two objects each of the types Product, PrepackedFood and FreshFood
...
Use the default constructor to create the other object
...
■ CHAPTER 23 ■ solutions 520 INHERITANCE SOLUTIONS Exercise 1 // ---------------------------------------------------// Car
...
cpp // Implements the methods of Car, PassCar, and Truck // -----------------------------------------------------#include "car
...
" << endl; nr = n; producer = prod; } Car::~Car() { cout << "Destroying an object of type Car" << endl; } void Car::display() const { cout << "\n---------------------------- " << "\nCar number: " << nr << "\nProducer: " << producer << endl; } // ------------------------------------------------------// The methods of the derived class PassCar: PassCar::PassCar(const string& tp, bool sd, int n, const string& hs) : Car( n, hs), PassCarTyp( tp ), sunRoof( sd ) { cout << "I create an object of type PassCar
...
" << endl; } Truck::~Truck() { cout << "\nDestroying an object of type Truck\n"; } void Truck::display() const { Car::display(); cout << "Axles: " << axles << "\nCapacity: " << tons << " long tons\n"; } // ----------------------------------------------------// Car_t
...
// ----------------------------------------------------#include "car
...
5, 1111, "Volvo"); toy
...
display(); } cout << "\nDo you want to create an object " << " of type car? (y/n) "; cin >> c; if( c == 'y' || c == 'Y') { const Car oldy(3421, "Rolls Royce"); oldy
...
h: // Defines the classes Account, DepAcc, and SavAcc
...
0) : name(s), nr(n), balance(st) { } const string& getName() const { return name; } void setName(const string& n) { name = n;} unsigned long getNr() const { return nr; } void setNr(unsigned long n) { nr = n; } double getBalance() const { return balance; } void setBalance(double st){ balance = st; } void display() { cout << fixed << setprecision(2) << "----------------------------------------\n" << "Account holder: " << name << endl << "Account number: " << nr << endl << "Balance of the account:" << balance < } }; class DepAcc : public Account { private: double limit; // Overdraft limit double interest; // Interest public: DepAcc(const string& s = "X", unsigned long n = 1111111L, double st = 0
...
0, double ra = 0
...
0, double in = 0
...
double getInterest() const { return interest; } void setInterest(double in){ interest = in; } void display() { Account::display(); cout << fixed << setprecision(2) << "Interest rate: " << interest << endl << "----------------------------------\n" << endl << endl; } }; #endif SOLUTIONS ■ // ------------------------------------------------------// account_t
...
h" int main() { string s; double db; SavAcc mickey("Mickey Mouse", 1234567, 2
...
5); mickey
...
setName(s); mickey
...
display(); DepAcc dag("Donald Duck", 7654321, -1245
...
9); dag
...
setLimit(db); dag
...
h : Defines the classes // Product, PrepackedFood, and FreshFood // ---------------------------------------------------#ifndef _PRODUCT_H #define _PRODUCT_H #include #include #include using namespace std; class Product { private: long bar; string name; public: Product(long b = 0L, const string& s = "") : bar(b), name(s) { } void setCode(long b) { bar = b; } long getCode() const { return bar; } void setName(const string& s){ name = s; } const string& getName() const { return name; } void scanner() { cout << "\nBarcode: "; cin >> bar; cout << "Name: "; cin >> name; cin
...
clear(); } void printer() const { cout << "\n-------------------------------" << "\nBarcode: " << bar << "\nName: " << name << endl; } }; class PrepackedFood : public Product { private: double pce_price; SOLUTIONS ■ public: PrepackedFood(double p = 0
...
0, double p = 0
...
sync(); cin
...
cpp Tests classes Product, PrepackedFood, and FreshFood
...
h" int main() { Product p1(12345L, "Flour"), p2; p1
...
setName("Sugar"); p2
...
printer(); // Output the second product // Prepacked products: PrepackedFood pf1(0
...
printer(); // // cout << "\n Input data of a pf2
...
printer(); // Output the first prepacked product prepacked product: "; Input and output data of 2nd product FreshFood pu1(1
...
69, 98765, "Grapes"), pu2; pu1
...
scanner(); // pu2
...
"\n-------------------------------" "\n-------------------------------" "\nAgain in detail: \n" fixed << setprecision(2) "\nBarcode: " << pu2
...
getName() "\nPrice per Lbs: " << pu2
...
getWght() "\nEnd price: " << pu2
...
getWght() << endl; return 0; } chapter 24 Type Conversion in Class Hierarchies This chapter describes implicit type conversion within class hierarchies, which occurs in the context of assignments and function calls
...
529 530 ■ CHAPTER 24 ■ TYPE CONVERSION IN CLASS HIERARCHIES CONVERTING TO BASE CLASSES Example for implicit conversion #include "car
...
} // // // // // ok! Implicit conversion to base class
...
If this is inconvenient, an explicit type cast to type PassCar has to be performed
...
Objects of the derived class type then become special objects of the base class, just like an automobile is a special type of vehicle
...
It is possible to assign an object of a derived class to an object of the base class
...
The base class thus becomes a generic term for multiple special cases
...
ᮀ Assignments Implicit type conversion in class hierarchies occurs in assignments to ■ ■ base class objects pointers or references to the base class
...
Given the function compare() with the following prototype Example: bool compare( Car& , Car& ); and two objects of the derived PassCar class type, beetle and miata, the following statement is valid Example: compare( beetle, miata); The compiler performs implicit type conversion for the arguments beetle and miata, converting them to the parameter type, that is, to a reference to the base class Car
...
532 ■ CHAPTER 24 ■ TYPE CONVERSION IN CLASS HIERARCHIES TYPE CONVERSIONS IN ASSIGNMENTS ᮀ Effect of an assignment Car auto; PassCar bmw("520i", true, 4325, "Bayerische Motorenwerke"); auto = bmw; auto bmw nr: 4325 nr: 4325 producer: "Bayer
...
" passCarType: "520i" sunRoof: true TYPE CONVERSIONS IN ASSIGNMENTS ■ 533 ᮀ Assignment to a Base Class Object An object belonging to a derived class type can be assigned to an object of a base class
...
e
...
During an assignment the object bmw is copied to the data members of the object auto step by step
...
nr = bmw
...
producer = bmw
...
display(); The fact that you can assign an object belonging to a derived class to a base class object assumes that more will always fill less
...
ᮀ Assignments to Derived Class Objects This is not the case when you attempt to assign a base class object to an object of a derived class
...
An assignment in reverse order is only possible if you have defined an assignment of this type or a copy constructor with a parameter of the type “reference to base class
...
534 ■ CHAPTER 24 ■ TYPE CONVERSION IN CLASS HIERARCHIES CONVERTING REFERENCES AND POINTERS ᮀ Effect of a pointer assignment PassCar cabrio("Spitfire", true, 1001, "Triumph"); Car* carPtr = &cabrio; carPtr = &cabrio; carPtr cabrio nr 1001 producer "Triumph" carType "Spitfire" sunRoof true CONVERTING REFERENCES AND POINTERS ■ 535 ᮀ Converting to Base Class Pointers The is relationship between a derived class and a base class is also apparent when references and pointers are used
...
Example: Car* carPtr = &cabrio; In this case cabrio is an object of the class PassCar
...
The additional members defined in the derived class are therefore inaccessible
...
Although carPtr points to an object of the PassCar class in this case, it is impossible to call any methods additionally defined in the derived class
...
Thus, the following assignment is also invalid Example: PassCar auto; auto = *carPtr; // Error! although carPtr is pointing at an object of the PassCar type in this case! ᮀ Conversions in References to Base Classes A similar situation arises when you are working with references
...
The reference will address only the generic part of the object in this case
...
display(); carRef
...
536 ■ CHAPTER 24 ■ TYPE CONVERSION IN CLASS HIERARCHIES EXPLICIT TYPE CONVERSIONS Downcast Pointer to carPtr Base class Car Downcast static_cast(carPtr) Derived class PassCar Upcast Pointer to static_cast(PassCarPtr) Base class Car Upcast PassCarPtr Derived class PassCar EXPLICIT TYPE CONVERSIONS ■ 537 ᮀ Upcasts and Downcasts Type conversions that walk up a class hierarchy, or upcasts, are always possible and safe
...
Type conversions that involve walking down the tree, or downcasts, can only be performed explicitly by means of a cast construction
...
ᮀ Explicit Cast Constructions Given that cabrio is again an object of the derived class PassCar, the following statements Example: Car* carPtr = &cabrio; ( (PassCar*) carPtr )->display(); first point the base class pointer carPtr to the cabrio object
...
This allows you to access the display() method of the derived class PassCar via the pointer
...
The operator static_cast< > conforms to the following Syntax: static_cast(expression) and converts the expression to the target type type
...
They are read from left to right
...
ᮀ Downcast Safety Issues Type conversions from top to bottom need to be performed with great care
...
This also applies to references to base classes
...
This technique is available for polymorphic classes and will be introduced in the next chapter
...
Your job is to test various cast techniques for this class (see also Exercise 3 in Chapter 23)
...
Define an array with three pointers to the base class Product
...
The three objects are to be referenced by the array pointers
...
Initialize the pointer with the address of a dynamically allocated object of the same class
...
Which version of printer() is executed? Perform downcasting to execute the correct version of printer() in every case
...
Use the pointer of the derived class FreshFood to call the base class version of printer()
...
Test the function isLowerCode()by multiple calls to the function with various arguments
...
■ CHAPTER 24 TYPE CONVERSION IN CLASS HIERARCHIES ■ solution 540 SOLUTION // // // // // // ---------------------------------------------------product
...
cpp Tests up and down casts for the classes Product, PrepackedFood, and FreshFood
...
#include "product
...
49, 23456, "Salt"); pv[2] = new FreshFood(1
...
69, 98765, "Grapes"); pu = new FreshFood(2
...
69, 56789, "Peaches"); cout << "\nA fresh product: "; pu->printer(); cout << "\nThe generic data of the other products:"; int i; for(i=0; i < 3; ++i) pv[i]->printer(); cin
...
get(); cout << "\nAnd an upcast: " << endl; static_cast(pu)->printer(); SOLUTION cout << "\nNow compare the barcodes!" << endl; cout << "\nIs barcode for flour or salt smaller?"; isLowerCode(*pv[0], *pv[1])
...
printer(); return 0; } const Product& isLowerCode(const Product& p1, const Product& p2) { if(p1
...
getCode()) return p1; else return p2; } ■ 541 This page intentionally left blank chapter 25 Polymorphism This chapter describes how to develop and manage polymorphic classes
...
543 544 ■ CHAPTER 25 ■ POLYMORPHISM CONCEPT OF POLYMORPHISM Example Classes with virtual methods: Base: base class with virtual method display()
...
Base class pointer and objects: Base* basePtr; Derived1 angular; // Base class pointer // Objects Derived2 round; Calling virtual methods: When a virtual method is called, the corresponding version of the method is executed for the object currently referenced
...
This is the case when dynamic allocated objects are inserted into a data structure or deleted from that structure
...
However, you can only access the common base members of these objects
...
Given a base class pointer, carPtr, the statement Example: carPtr->display(); should output all the data members of the object currently being referenced
...
The type field stored the type of the current class
...
This solution has a disadvantage; adding derived classes at a later stage also meant adding a case label and recompiling
...
In C++, virtual methods are used to implement polymorphic classes
...
cpp : Tests the virtual method display() // of the classes Car and PassCar
...
h" // The Car class with virtual method display(): // class Car // { //
...
int i = 0; // Index pCar[0] = new Car( 5634L, "Mercedes"); pCar[1] = new PassCar("Miata",true,3421,"Mazda"); pCar[2] = new Truck( 5, 7
...
Example: virtual void display() const; The definition of a virtual method is no different from the definition of any other member function
...
The derived class then inherits the virtual method from the base class
...
Creating a proprietary version of a virtual method means redefining that method
...
the same signature and 2
...
The new version of a virtual method is automatically virtual itself
...
When you redefine a virtual function, be aware of the following: ■ ■ ■ if the return type is a pointer or reference to the base class, the new version of the virtual method can also return a pointer or reference to a derived class (Note: Not all compilers support this option
...
If you use a different signature or return type of a virtual base class method to define a method in a derived class, this simply creates a new method with the same name
...
In other words, only the non-virtual version of the method is available for a derived class object
...
cpp // Base class with a virtual destructor
...
When such an object reaches the end of its lifetime, the memory occupied by the object must be released by a delete statement
...
delete carPtr; ᮀ Destructor Calls When memory is released, the destructor for an object is automatically called
...
What does this mean for objects in derived classes? The destructor of the derived class is called first and then the destructor of the base class executed
...
However, non-virtual methods will always execute the base class version
...
As the PassCar destructor is not called, neither is the destructor called for the data member passCarType, which is additionally defined in the derived class
...
If multiple objects are created dynamically in the derived class, a dangerous situation occurs
...
ᮀ Virtual Destructors This issue can be solved simply by declaring virtual destructors
...
Just like any other virtual method, the appropriate version of the destructor will be executed
...
A class used as a base class for other classes should always have a virtual destructor defined
...
550 ■ CHAPTER 25 ■ POLYMORPHISM VIRTUAL METHOD TABLE VMT for the classes Car and PassCar Object of type Car VMT of Car VMT pointer nr 12345 producer Audi Address of Car::display() Address of Car::~Car() Object of type PassCar VMT pointer nr 54321 producer Geo passCarType 500 sunRoof true VMT of PassCar Address of PassCar::display() Address of PassCar::~PassCar() Object of type PassCar VMT pointer nr 98765 producer VW passCarType GTI sunRoof false VIRTUAL METHOD TABLE ■ 551 ᮀ Static Binding When a non-virtual method is called, the address of the function is known at time of compilation
...
This is also referred to as static or early binding
...
So this is also a case of early binding
...
The statement Example: carPtr->display(); could execute different versions of the display() method, depending on the object currently referenced by the pointer
...
This is referred to as late or dynamic binding
...
A VMT is created for each class with at least one virtual method—that is, an array with the addresses of the virtual methods in the current class
...
Dynamic binding causes the virtual function call to be executed in two steps: 1
...
2
...
In comparison with static binding, dynamic binding does have the disadvantage that VMTs occupy memory
...
However, this is a small price to pay for the benefits
...
This is particularly important when you consider commercial class libraries, from which a user can derive his or her own classes and virtual function versions
...
cpp // Dynamic casts in class hierarchies
...
h" bool inspect( PassCar* ), // Inspection of inspect(Truck* ); // car types
...
int main() { Car* carPtr = new PassCar("520i", true, 3265, "BMW"); Truck* truckPtr = new Truck(8, 7
...
to test some casts and
...
The GNU compiler activates these options automatically
...
If the referenced object does not correspond to the type of the derived class, fatal runtime errors can occur
...
But the following statement, truckPtr->setAxles(10); could cause the program to crash
...
At runtime the operator checks whether the required conversion is valid or not
...
The target type must be a pointer or reference to a polymorphic class or a void pointer
...
If the target type is a reference, expression must identify an object in memory
...
If this is not so, the dynamic_cast operator will return a NULL pointer
...
In any other case, that is, if the reference r_car does not identify a PassCar type object, an exception of the bad_cast type is thrown
...
The classes involved do not need to be polymorphic in this case
...
An erroneous upcast is recognized and reported by the compiler
...
First, define a class called CityCar that contains an array of pointers to the 100 objects in the Car class
...
The objects themselves will be created dynamically at runtime
...
The constructor will set the current number of array elements to 0
...
Make sure that you use a virtual destructor definition in the base class Car to allow correct releasing of memory for trucks and passenger vehicles
...
Each version will allocate memory to an object of the appropriate type—that is of the PassCar or Truck class—and use the arguments passed to it for initialization
...
The display() method outputs the data of all vehicles on screen
...
Create a new function called menu() and store this function in a new source file
...
Additionally, write two functions, getPassCar() and getTruck(), which read the data for a car or a truck from the keyboard and write the data into the appropriate arguments
...
Insert one car and one truck
...
If a user chooses “Add car” or “Add truck,” your program must read the data supplied and call the appropriate version of insert()
...
■ ■ ■ Declare the virtual methods scanner() and printer() in the base class Product
...
Write the record() function, which registers and lists products purchased in the store in a program loop
...
The checkout assistant is prompted to state whether a prepacked or fresh food item is to be scanned next
...
After scanning all the available items, a sequential list is displayed
...
Now create an application program to simulate a supermarket checkout
...
If so, the record() function is called; if not, the program terminates
...
h : Defines the base class Car and // the derived classes PassCar and Truck // -------------------------------------------------------#ifndef _CAR_H_ #define _CAR_H_ #include #include using namespace std; class Car { private: long nr; string producer; public: Car( long n = 0L, const string& prod = ""); virtual ~Car() {} // Virtual destructor
...
#endif // // // // -----------------------------------------------------car
...
// SOLUTIONS // -----------------------------------------------------// city
...
h" class CityCar { private: Car* vp[100]; int cnt; public: CityCar(){ cnt = 0;} ~CityCar(); bool insert(const string& long n, const bool insert(int a, double long n, const tp, bool sr, string& prod); t, string& prod); void display() const; }; #endif // _CITY_H // -----------------------------------------------------// city
...
h" CityCar::~CityCar() { for(int i=0; i < cnt; ++i) delete vp[i]; } // Insert a passenger car: bool CityCar::insert(const string& tp, bool sr, long n, const string& prod) { if( cnt < 100) { vp[cnt++] = new PassCar( tp, sr, n, prod); return true; } else return false; } ■ 559 560 ■ CHAPTER 25 POLYMORPHISM // Insert a truck: bool CityCar::insert( int a, double t, long n, const string& prod) { if( cnt < 100) { vp[cnt++] = new Truck( a, t, n, prod); return true; } else return false; } void CityCar::display() const { cin
...
clear(); // No previous input for(int i=0; i < cnt; ++i) { vp[i]->display(); if((i+1)%4 == 0) cin
...
cpp : Test the CityCar class // -------------------------------------------------#include "city
...
insert(6, 9
...
insert("A-class", true, 54320, "Mercedes"); char choice; do { choice = menu(); switch( choice ) { case 'Q': case 'q': cout << "Bye Bye!" << endl; break; SOLUTIONS case 'P': case 'p': getPassCar(tp, sr, n, prod); carExpress
...
insert(a, t, n, prod); break; case 'D': case 'd': carExpress
...
get(); break; default: cout << "\a"; // Beep break; } }while( choice != 'Q' return 0; ■ ); && choice != 'q'); } char menu() // Input a command
...
sync(); cin
...
sync(); cout << "Producer: "; getline(cin, prod); cin
...
clear(); } 561 562 ■ CHAPTER 25 POLYMORPHISM void getTruck(int& a, double& t, long& n, string& prod) { cout << "\nInput data for truck:" << endl; cout << "Number of axles: "; cin >> a; cout << "Weight in tons: "; cin >> t; cout << "Car number: "; cin >> n; cin
...
sync(); } Exercise 2 // // // // // ---------------------------------------------------product
...
class Product { private: long bar; string name; public: Product(long b = 0L, const string& s = "") : bar(b), name(s) { } // Access methods as previously used
...
SOLUTIONS // -----------------------------------------------------// counter
...
h" void record(); int main() { cout << "\nHere is a checkout desk!" << endl; char c; while(true) { cin
...
void record() { Product* v[100]; int x, i, cnt = 0; double sum = 0
...
sync(); cout << "\nWhat is the next article?" << endl; cout << " 0 = No more article\n" << " 1 = Fresh Food\n" << " 2 = Prepacked article\n" << "? " ; cin >> x; if( x <= 0 || x >= 3) break; ■ 563 564 ■ CHAPTER 25 POLYMORPHISM switch(x) { case 2: v[i] = new PrepackedFood; v[i]->scanner(); sum += ((PrepackedFood*)v[i])->getPrice(); break; case 1: v[i] = new FreshFood; v[i]->scanner(); sum += ((FreshFood*)v[i])->getPrice() * ((FreshFood*)v[i])->getWght(); break; } } cnt = i; for( i=0; i < cnt; i++) v[i]->printer(); // Output cout << "\n-----------------------------" << fixed << setprecision(2) << "\nTotal price: " << sum << endl; } chapter 26 Abstract Classes This chapter describes how abstract classes can be created by defining pure virtual methods and how you can use abstract classes as a polymorphic interface for derived classes
...
565 566 ■ CHAPTER 26 ■ ABSTRACT CLASSES PURE VIRTUAL METHODS The base class Coworker // Coworker
...
// ---------------------------------------------------#ifndef _COWORKER_H #define _COWORKER_H #include #include using namespace std; class Coworker { private: string name; // more information public: Coworker( const string& s = ""){ name = s; } virtual ~Coworker() {} // Destructor const string& getName() const{ return name; } void setName( const string& n){ name = n; } virtual void display() const; virtual double income() const = 0; virtual Coworker& operator=(const Coworker&); }; #endif ✓ NOTE The virtual operator function for the assignment will be described in the section on ”Virtual Assignments
...
It may happen that they rarely perform any useful tasks in the base class
...
In this case, of course, you can define a virtual dummy method whose address is entered in the VMT of the base class
...
It makes more sense not to define a function like this
...
ᮀ Declaration When a pure virtual method is declared, the method is identified by adding the expression = 0
...
A NULL pointer is then entered in the virtual method table for the pure virtual method
...
The class is used as a base class for various employees, blue-collar, white-collar, and freelancers
...
However, it could also contain the address of an employee, or the division where the employee works
...
It makes more sense to store data like this in derived classes where the hourly wage and number of hours worked by a blue-collar worker and the monthly salary for a white-collar worker are also defined
...
568 ■ CHAPTER 26 ■ ABSTRACT CLASSES ABSTRACT AND CONCRETE CLASSES The derived class Laborer // coworker
...
// -------------------------------------------------class Laborer : public Coworker { private: double wages; // Pay per hour int hr; public: Laborer(const string& s="", double w=0
...
” ABSTRACT AND CONCRETE CLASSES ■ 569 ᮀ Concrete or Abstract? If a class comprises pure virtual methods, you cannot create objects of this class type
...
This avoids calling a method for worker that still needs to be defined
...
✓ NOTE A class with pure virtual methods is an abstract class
...
ᮀ Deriving Abstract Classes When a class is derived from an abstract class, it inherits all the methods the base class contains, particularly the pure virtual methods
...
✓ NOTE A class derived from a class containing pure virtual methods is a concrete class, if it contains a definition for each pure virtual function
...
Since the hourly wage and the number of hours worked are both defined for blue-collar workers, it is possible to implement that method
...
In other words, an abstract class can be derived from a concrete class
...
If the class contains a protected constructor, objects of the class type cannot be created
...
A constructor of this type normally acts as base initializer, when an object of a derived class type is created
...
h: Extending the headerfile
...
0) : Coworker(s), salary(g){ } double getSalary() const { return salary; } void setSalary( double sa){ salary = sa; } void display() const; double income()const { return salary; } Employee& operator=( const Coworker& ); Employee& operator=( const Employee& ); }; Sample program // coworker_t
...
// ------------------------------------------------#include "coworker
...
, 40); felPtr[1] = new Employee("Smith, Eve", 3850
...
Example: Coworker *felPtr, &coRef; The pointer felPtr is a base class pointer that can address objects belonging to derived concrete classes
...
ᮀ References to Abstract Base Classes References to base classes are often used as parameters in functions
...
Example: Coworker( const Coworker& ); The copy constructor expects an object belonging to a derived class, since the base class is abstract
...
ᮀ Pointers to Abstract Base Classes Pointers to base classes are generally used to address dynamically allocated objects
...
Example: Coworker* felPtr; felPtr = new Laborer("Young, Neil",45
...
ᮀ Polymorphic Interface Defining pure virtual methods also determines interfaces for general operations, although the interfaces still need to implemented in the derived classes
...
Abstract classes are therefore also referred to as polymorphic interfaces to derived classes
...
The operator functions for the assignments are discussed and implemented in the following section
...
name; return *this; } Assignments for class Employee // Redefinition: virtual Employee& operator=(const Coworker& m) { if( this != &m ) // No self assignment { Coworker::operator=( m ); salary = 0
...
salary; } return *this; } ✓ NOTE Redefining the virtual operator function operator=(), which returns a reference to the derived class, is not yet supported by all compilers
...
VIRTUAL ASSIGNMENT ■ 573 ᮀ Virtual Operator Functions Operator functions implemented as methods can also be virtual
...
One example of this is the operator function for an assignment
...
Any additional data members of the derived class remain unchanged
...
The derived classes Laborer and Employee both contain their own versions
...
If the object is a Laborer type, the assignment of the Laborer class is performed
...
ᮀ Redefining the Standard Assignment When you define a new version for a virtual method in a derived class, this implies using the signature of the original method
...
The standard assignment for the Laborer class has the following prototype: Example: Laborer& operator=(const Laborer&); The type const Laborer& is different from the const Coworker& type of the parameter in the virtual operator function of the base class
...
This gives rise to two issues: ■ ■ the virtual operator function for the assignment must be defined for every derived class to ensure that the standard assignment is also available, the standard assignment must also be redefined in every derived class
...
h: Defining the classes Cell, BaseEl, and DerivedEl // -------------------------------------------------------#ifndef _CELL_ #define _CELL_ #include #include using namespace std; class Cell { private: Cell* next; protected: Cell(Cell* suc = NULL ){ next = suc; } public: virtual ~Cell(){ } // Access methods to be declared here
...
void display() const; }; class DerivedEl : public BaseEl { private: string rem; public: DerivedEl(Cell* suc = NULL,const string& s="", const string& b="") : BaseEl(suc, s), rem(b) { } // Access methods would be declared here
...
An inhomogeneous list is a linear list whose elements can be of different types
...
Due to implicit type conversions in class hierarchies, you can use the base class pointers to manage the list elements, that is, you can manage the elements in a linked list
...
The class contains a pointer of type Cell* as the data member used to link list elements
...
The Cell class does not contain any data that might need to be output
...
For this reason, Cell contains a declaration of the pure virtual method display(), which can be modified for multiple derivations
...
To keep things simple, the BaseEl class contains only a name, and the DerivedEl class additionally contains a comment
...
In addition, a suitable version of the display() method is defined
...
576 ■ CHAPTER 26 ■ ABSTRACT CLASSES IMPLEMENTING AN INHOMOGENEOUS LIST Defining class InhomList // List
...
h" class InhomList { private: Cell* first; protected: Cell* getPrev(const string& s); void insertAfter(const string& s,Cell* prev); void insertAfter(const string& s, const string& b,Cell* prev); public: // Constructor, Destructor etc
...
{ Cell* p = new BaseEl(prev->getNext(), s); prev->setNext(p); } } IMPLEMENTING AN INHOMOGENEOUS LIST ■ 577 ᮀ The InhomList Class The inhomogeneous list must allow sorted insertion of list elements
...
A pointer to the first element in the list is all you need for this task
...
The definition of the InhomList class is shown opposite
...
The constructor has very little to do
...
The list will be sorted by name
...
In our example, we first locate the immediate lexicographical predecessor
...
In this case, the new list element is inserted as the first element in the list
...
There are two cases that need to be looked at: 1
...
The new element becomes the first element
...
2
...
Instead, you have to modify two pointers
...
This situation also applies in cases where the successor was a NULL pointer, in other words, when the new element is appended to the list
...
They differ only in different calls to the new operator
...
Both versions first call the getPrev() method and the corresponding version of the insertAfter() method
...
■ ■ ■ ■ ■ ■ ■ ■ ■ Write the destructor for the InhomList class
...
Implement the getPrev() method and both versions of the insert() and insertAfter() methods
...
” Implement the displayAll() method, which walks through the list sequentially, outputting each element
...
Check whether the comments on the objects are output, if present
...
If the element is in the list, its address is returned
...
Write the erasePos() method, which deletes a list element at a given position
...
Since the destructor for Cell was declared virtual, only one version of the deletePos() method is necessary
...
Test deletion of list elements
...
Now implement the copy constructor and assignment
...
You can call the typeid() operator to ascertain the type of the list element currently to be inserted
...
Example: if( typeid(*ptr) == typeid(DerivedEl))
...
Then test the copy constructor and the assignment ■ CHAPTER 26 ■ solution 580 ABSTRACT CLASSES SOLUTION // -----------------------------------------------------// cell
...
// -----------------------------------------------------#ifndef _CELL_ #define _CELL_ #include #include using namespace std; class Cell { private: Cell* next; protected: Cell(Cell* suc = NULL ){ next = suc; } public: virtual ~Cell(){ } Cell* getNext() const { return next; } void setNext(Cell* suc) { next = suc; } virtual void display() const = 0; }; class BaseEl : public Cell { private: string name; public: BaseEl( Cell* suc = NULL, const string& s = "") : Cell(suc), name(s){} // Access methods: void setName(const string& s){ name = s; } const string& getName() const { return name; } void display() const { cout << "\n--------------------------------" << "\nName: " << name << endl; } }; SOLUTION class DerivedEl : public BaseEl { private: string rem; public: DerivedEl(Cell* suc = NULL, const string& s="", const string& b="") : BaseEl(suc, s), rem(b){ } // Access methods: void setRem(const string& b){ rem = b; } const string& getRem() const { return rem; } void display() const { BaseEl::display(); cout << "Remark: " << rem << endl; } }; #endif // -----------------------------------------------------// List
...
h" class InhomList { private: Cell* first; protected: Cell* getPrev(const string& s); Cell* getPos( const string& s); void insertAfter(const string& s, Cell* prev); void insertAfter(const string& s,const string& b, Cell* prev); void erasePos(Cell* pos); public: InhomList(){ first = NULL; } InhomList(const InhomList& src); ~InhomList(); InhomList& operator=( const InhomList& src); void insert(const string& n); void insert(const string& n, const string& b); void erase(const string& s); void displayAll() const; }; #endif ■ 581 582 ■ CHAPTER 26 ABSTRACT CLASSES // -----------------------------------------------------// List
...
h" #include // Copy constructor: InhomList::InhomList(const InhomList& src) { // Append the elements from src to the empty list
...
first; for( ; pEl != NULL; pEl = pEl->getNext() ) if(typeid(*pEl) == typeid(DerivedEl)) insert(dynamic_cast(pEl)->getName(), dynamic_cast(pEl)->getRem()); else insert(dynamic_cast(pEl)->getName()); } // Assignment: InhomList& InhomList::operator=(const InhomList& src) { // To free storage for all elements: Cell *pEl = first, *next = NULL; while( pEl != NULL ) { next = pEl->getNext(); delete pEl; pEl = next; } first = NULL; // Empty list // Copy the elements from src to the empty list
...
first; for( ; pEl != NULL; pEl = pEl->getNext() ) if(typeid(*pEl) == typeid(DerivedEl)) insert(dynamic_cast(pEl)->getName(), dynamic_cast(pEl)->getRem()); else insert(dynamic_cast(pEl)->getName()); return *this; } SOLUTION // Destructor: InhomList::~InhomList() { Cell *pEl = first, *next = NULL; while( pEl != NULL ) { next = pEl->getNext(); delete pEl; pEl = next; } } Cell* InhomList::getPrev(const string& n) { Cell *pEl = first, *prev = NULL; while( pEl != NULL ) { if( n > dynamic_cast(pEl)->getName() ) { prev = pEl; pEl = pEl->getNext(); } else return prev; } return prev; } Cell* InhomList::getPos( const string& n) { Cell *pEl = first; while( pEl != NULL && (n != dynamic_cast(pEl)->getName())) pEl = pEl->getNext(); if( pEl != NULL && n == dynamic_cast(pEl)->getName()) return pEl; else return NULL; } void InhomList::insertAfter(const string& s, Cell* prev) { if( prev == NULL ) // Insert at the beginning: first = new BaseEl( first, s); else // In the middle or at the end: { Cell* p = new BaseEl(prev->getNext(), s); prev->setNext(p); } } ■ 583 584 ■ CHAPTER 26 ABSTRACT CLASSES void InhomList::insertAfter( const string& s, const string& b, Cell* prev) { if( prev == NULL ) // Insert at the beginning: first = new DerivedEl( first, s, b); else // In the middle or at the end: { Cell* p = new DerivedEl(prev->getNext(), s, b); prev->setNext(p); } } void InhomList::insert(const string& n) { Cell* pEl = getPrev(n); insertAfter(n, pEl); } void InhomList::insert(const string& n, const string& b) { Cell* pEl = getPrev(n); insertAfter(n, b, pEl); } void InhomList::erasePos(Cell* pos) { Cell* temp; if( pos != NULL) if( pos == first ) // Delete the first element { temp = first; first = first->getNext(); delete temp; } else // Delete from the middle or at the end { // Get the predecessor temp = getPrev( dynamic_cast(pos) ->getName()); if(temp != NULL) // and bend pointer
...
cpp : Tests the sorted inhomogeneous list // -----------------------------------------------------#include "List
...
" << endl; liste1
...
insert("Cheers, Rita", "always merry"); liste1
...
insert("Banderas, Antonio"); liste1
...
get(); cout << "\nTo test deleting
...
erase("Banderas, Antonio"); liste1
...
erase("Cheers, Rita"); liste1
...
get(); cout << "\n----------------------------------" << "\nGenerate a copy and insert an element
...
liste2
...
displayAll(); return 0; } ■ 585 This page intentionally left blank chapter 27 Multiple Inheritance This chapter describes how new classes are created by multiple inheritance and explains their uses
...
587 588 ■ CHAPTER 27 ■ MULTIPLE INHERITANCE MULTIPLY-DERIVED CLASSES The multiply-derived class MotorHome Car Home MotorHome Definition scheme for class MotorHome class MotorHome : public Car, public Home { private: // Additional private members here protected: // Additional protected members here public: // Additional public members here }; MULTIPLY-DERIVED CLASSES ■ 589 A class can contain not just one but several different base classes
...
ᮀ The Multiply-Derived Class MotorHome This class Car is used to represent vehicles and the class Home contains characteristic values for an apartment, such as floor space, number and type of rooms, and typical operations, such as building, selling, or renting
...
The opposite page shows the inheritance and definition schemes for the new class
...
More specifically, the object contains two base sub-objects of type Car and Home
...
A MotorHome type object not only allows access to the additional public members but to all the public members of the base classes Car and Home
...
The MotorHome class could have the public base class Car and the protected base class Home
...
}; If the keyword is omitted, the base class will default to private
...
}; This statement defines the public base class Car and the private base class Home
...
In multiple inheritance each public base class establishes an is relationship
...
If the MotorHome class inherits two public base classes, a motor-home is a special kind of motor vehicle and a special kind of home
...
The following statement Example: class B : public A, public A // Error {
...
A class can be derived from several classes that have a common base class, however
...
The inheritance graph on the opposite page shows the multiply-derived class SUV, which was derived from the classes PassCar and Van
...
This makes Car a multiple indirect base class of the SUV class
...
Access to members of the Car class results in ambiguity
...
); cout << mySUV
...
In this case the compiler cannot decide which method is meant
...
If both the Home class and the Car class contain a method called getNr(), the getNr() method cannot be correctly identified in the following statement
...
); motorHome
...
Example: cout << motorHome
...
PassCar::getProd(); The getNr() method in the Home class is called first, followed by the getProd() method inherited by PassCar from the Car class
...
Why should a station wagon contain two versions of the manufacturer’s name or the chassis number for example? So you might be asking yourself whether you can define multiply-derived classes that will contain only one instance of an indirect base class
...
An object in a multiply-derived class contains only one instance of the members in a virtual base class
...
ᮀ Declaration A direct base class is declared virtual when a derived class is defined
...
In the definition scheme shown opposite, the Car class becomes the virtual base class of PassCar and Van
...
A virtual base class takes effect in cases of multiple inheritance
...
}; ensures that the SUV class only contains one instance of the virtual base class Car
...
More specifically, the statement Example: cout<<"Producer: " << mySUV
...
The following items are important with respect to virtual base classes: ■ ■ a virtual base class stays virtual even if further derivations are built
...
you cannot change the declaration of an indirect base class to virtual
...
Later modifications will require modifications to the source code of any derived classes
...
The sub-object whose class is nearer to the top of the inheritance graph is created first
...
The activation order used for the constructors in simple inheritance has been generalized for multiple inheritance
...
When a derived class is defined, the following rules apply: ■ In cases of multiple inheritance, base classes are entered into the inheritance graph from left to right in the order in which they were stated when the class was defined
...
If the class hierarchy does not contain any virtual base classes, the following applies to the activation order of the constructors
...
Finally, the constructor belonging to the current class, which is at the bottom of the inheritance graph, is executed
...
Then the constructor of MultiDerived is executed
...
If the base initializer definition is missing in a constructor definition, the default constructor of the base class is automatically executed
...
” 596 ■ CHAPTER 27 ■ MULTIPLE INHERITANCE INITIALIZING VIRTUAL BASE CLASSES Class SUV class SUV : public PassCar, public Van { private: //
...
) : Car(
...
However, if there is one virtual base class in the class hierarchy, the virtual base class constructor is executed before a constructor of a non-virtual base class is called
...
The constructor of the virtual base class nearest the top of the inheritance graph is executed first
...
In our example with the multiply-derived class SUV (Sport Utility Vehicle) the constructor for the virtual base class Car is called first, followed by the direct base classes PassCar and Van, and last but not least, the constructor of the SUV class
...
A base initializer of the directly-derived class or any other derivation could be responsible
...
e
...
The example opposite shows SUV containing a constructor with one base initializer
...
For the purpose of initialization, it does not matter whether a class derived directly from Car contains a base initializer or not
...
If the base classes PassCar and Van also contained base initializers for the virtual base class Car, these would be ignored too
...
Whatever happens, a default constructor must then exist in every virtual base class! Thus, base initializers that happen to exist in base classes are also ignored
...
■ ■ ■ ■ Define an enumeration type CATEGORY that represents the categories “Luxury,” “First Class,” “Middle Class,” and “Economy”
...
Supply default values for your constructor definition to create a default constructor
...
Define a class derived from the Car and Home classes called MotorHome, which is used to represent motorhomes
...
The MotorHome class contains a new data member used to store one value of the CATEGORY type
...
Place your definitions of the Home and MotorHome classes in a separate header file, which includes the existing header file car
...
Write a main function that first fully initializes a MotorHome type object and then outputs the object
...
Call all the set methods in the MotorHome class and its base classes to set your own values for the objects
...
600 ■ CHAPTER 27 MULTIPLE INHERITANCE Class hierarchy for the multiply-derived class SUV Car Data members: car number producer PassCar Van Data members: car type sun roof (y/n) Data members: capacity (lbs) SUV Data members: number of seats EXERCISES ■ 601 Exercise 2 Now fully define the SUV class for testing virtual base classes
...
h header file to make Car a virtual base class of the PassCar class
...
The new class should contain an additional data member used to represent the payload of the van in kilograms
...
The constructor should use default values to initialize the data members with defaults, thus providing a default constructor for the class
...
In addition to the access method display(), you still need to define methods for screen output
...
Store the number of seats available in the station wagon as a data member
...
Additionally, define access methods and a display() to define output
...
■ CHAPTER 27 ■ solutions 602 MULTIPLE INHERITANCE SOLUTIONS Exercise 1 // // // // // // // // // // // -----------------------------------------------------car
...
cpp Implementing methods of Car, PassCar, and Truck -----------------------------------------------------These files have been left unchanged from Chapters 23 and 25
...
h : Definition of the class Home and the // multiply-derived class MotorHome // -----------------------------------------------------#ifndef _MOTORHOME_H_ #define _MOTORHOME_H_ #include "car
...
0) { room = r; ft2 = m2;} void setRoom(int n){ room = n;} int getRoom() const { return room; } void setSquareFeet(double m2){ ft2 = m2;} double getSquareFeet() const { return ft2; } void display() const { cout << "Number of rooms: " << room << "\nSquare feet: " << fixed << setprecision(2) << ft2 << endl; } }; SOLUTIONS ■ class MotorHome : public Car, public Home { private: CATEGORY cat; public: MotorHome(long n=0L, const string& prod="", int ro=0, double m2=0
...
cpp // Testing the multiply-derived class MotorHome // -----------------------------------------------------#include "motorHome
...
5, LUXURY); rv
...
display(); cin
...
setNr(54321); holiday
...
setRoom(1); holiday
...
5); holiday
...
display(); return 0; } Exercise 2 // -----------------------------------------------------// car
...
cpp // Implementing the methods of Car, PassCar, and Truck // -----------------------------------------------------// // These files are carried over from Chapter 23 and 25, // with the following changes: // // Class Car is a virtual base class now class PassCar : public virtual Car { //
...
}; // -----------------------------------------------------// suv
...
h" class Van : public virtual Car { private: double capacity; SOLUTIONS public: Van(long n=0L, const string& prod="", double l=0
...
0, int z = 1) : PassCar(tp,sb), Car(n,prod), Van(n,prod,l), cnt(z) { } void display() const { PassCar::display(); Van::display(); cout << "Number of seats: } }; #endif " << cnt << endl; ■ 605 606 ■ CHAPTER 27 MULTIPLE INHERITANCE // -----------------------------------------------------// suv_t
...
h" int main() { SUV mobil("Bravada", true, 120345, "Oldsmobile",350,6); mobil
...
display(); trucky
...
setProd("Renault"); trucky
...
); trucky
...
In addition to throwing and catching exceptions, we also examine how exception specifications are declared and exception classes are defined, additionally looking into the use of standard exception classes
...
{
...
...
if(func()<=0) // All errors // are // handled
...
{
...
error else if (x == -1) //2
...
} } } int func( void) {
...
if(catastrophe) return -1;
...
Some common causes of errors are ■ ■ ■ ■ ■ division by 0, or values that are too large or small for a type no memory available for dynamic allocation errors on file access, for example, file not found attempt to access an invalid address in main memory invalid user input Anomalies like these lead to incorrect results and may cause a computer to crash
...
One of the programmer’s most important tasks is to predict and handle errors
...
ᮀ Traditional Error Handling Traditional structured programming languages use normal syntax to handle errors: ■ ■ errors in function calls are indicated by special return values global error variables or flags are set when errors occur, and then checked again later
...
Example: if( func()> 0 ) // Return value positive => o
...
else // Treat errors Error variables and flags must also be checked after every corresponding action
...
If you do happen to forget to check for errors, the consequences may be fatal
...
cpp: Defining the function calc(), // which throws exceptions
...
Exception handling is based on keeping the normal functionality of the program separate from error handling
...
The calling environment performs central error handling
...
When reporting an error, specific information on the error cause can be added
...
ᮀ The throw Statement An exception that occurs is recorded to the calling environment by means of a throw statement; this is why we also say that an exception has been thrown
...
It can belong to any type except void
...
ᮀ Exception Classes Normally, you define your own exception classes to categorize exceptions
...
An exception class need not contain data members or methods
...
Generally, the exception class will contain members that provide more specific information on the cause of the error
...
In the first case, the exception thrown is a string
...
Instead of creating a local exception object errorObj, a temporary object can be created: Example: throw Error(); // It is shorter 612 ■ CHAPTER 28 ■ EXCEPTION HANDLING EXCEPTION HANDLERS Syntax of try and catch blocks try { // Exceptions thrown by this block will be // caught by the exception handlers, // which are defined next
...
} [ catch( Type2 exc2) { // Type2 exceptions are handled here
...
//etc
...
) { // All other exceptions are handled here
...
] in a syntax description indicate that the enclosed section is optional
...
An exception handler catches the exception object thrown to it and performs error handling
...
This means that you need to specify two things when implementing exception handling: ■ ■ the part of the program that can throw exceptions the exception handlers that will process the various exception types
...
Each keyword precedes a code block and thus they are often referred to as try and catch blocks
...
ᮀ try and catch Blocks A try block contains the program code in which errors can occur and exceptions can be thrown
...
Each catch block defines an exception handler, where the exception declaration, which is enclosed in parentheses, defines the type of exceptions the handler can catch
...
A minimum of one catch block is required
...
If there is no handler defined for a particular exception type, the program will not simply enter an undefined state but will be orderly terminated by a call to the standard function terminate()
...
This functionality is provided by a special syntax in the catch statement with an exception declaration consisting of just three dots
...
) { // General handler for // all other exceptions } Since the application program decides what reaction is applicable for certain error conditions, the try and catch blocks are formulated in the application
...
cpp: Tests the function calc(), // which throws exceptions
...
} catch( string& s) // 1st catch block { cerr << s << endl; } catch( Error& ) // 2nd catch block { cerr << "Division by 0! " << endl; } catch(
...
return 0; } ✓ NOTE As the Error class contains no data members, the corresponding catch block declares only the type of exception, and no parameters
...
THROWING AND CATCHING EXCEPTIONS ■ 615 ᮀ Backing Out of an Error Condition When the throw statement is executed, an exception object is thrown
...
Example: throw "Cyclone!"; This creates a string as an exception object and copies the string "Cyclone!" to it
...
The exception object is then thrown and the program control leaves the try block
...
This specifically involves destroying any local, non-static objects
...
ᮀ Searching for Handlers After leaving the try block, the program control is transferred to an matching handler in the catch blocks that follow
...
A handler is called when the type in the exception declaration is ■ ■ ■ identical to the exception type thrown or a base class of the exception type or a base class pointer and the exception is a pointer to a derived class
...
) always has to be defined last
...
ᮀ Continuing the Program After executing a handler, the program continues with the first statement following the catch blocks, unless the handler throws another exception or terminates the program
...
The first two catch blocks handle both exceptions that the calc() function can throw
...
If an unexpected exception occurs, a message is again displayed, but in this case the program then terminates
...
try { // Type1 and Type2 exceptions are thrown here
...
} // Other Type1 exceptions // can be thrown
...
} catch(
...
} ✓ NOTE This scenario assumes that the error classes Type1 and Type2 are not derived one from another
...
NESTING EXCEPTION HANDLING ■ 617 ᮀ Nested try and catch Blocks A program will normally contain multiple try blocks with appropriate exception handlers
...
However, a try block can contain additional try blocks
...
Handlers in a nested try block can also pre-handle specific errors and then pass control to the try block wrapper for final handling
...
This is achieved using a throw statement that does not expect an exception object
...
The statement is only valid within a nested catch block for this reason
...
The application programmer must have knowledge of both the function prototype and the exceptions the function can throw to ensure that he or she will be capable of programming correct function calls and taking appropriate action in case of errors
...
Example: int func(int) throw(BadIndex, OutOfRange); The list BadIndex, OutOfRange states the exceptions that the function func()can throw
...
If the throw statement is also missing, there is no specific information about possible exceptions and any exception can be thrown
...
cpp: New version of function calc(), // which throws exceptions of type // MathError
...
} catch( MathError& err) // catch block { cerr << err
...
return 0; } double calc( int a, int b ) throw (MathError) { if ( b < 0 ) throw MathError("Denominator is negative!"); if( b == 0 ) throw MathError("Division by 0!"); return ((double)a/b); } DEFINING YOUR OWN ERROR CLASSES ■ 619 ᮀ Exception Class Members When an exception is thrown, the exception object type determines which exception handler will be executed
...
However, an exception class can contain data members and methods—just like any other class
...
Thus, the exception handler can no longer access the previously existing objects
...
For example, you can store data important for exception handling in an exception object
...
The calc() function throws an exception when a number input by a user is negative or 0
...
The exception handler can then use the getMessage() method to evaluate the message
...
A base class will normally represent a general error type and specific errors will be represented by derived classes
...
” You could call these classes DivisionByZero and OverflowError, for example
...
620 ■ CHAPTER 28 ■ EXCEPTION HANDLING STANDARD EXCEPTION CLASSES Exception classes derived from logic_error invalid_argument Invalid argument out_of_range Argument value not in its expected range length_error Length exceeds maximum capacity domain_error Domain error reported by the implementation Exception classes derived from runtime_error range_error Range error in internal computation underflow_error Arithmetic underflow error overflow_error Arithmetic overflow error Using standard exception classes // Subscript operator of class FloatArr throws // exceptions with type of standard class out_of_range: // --------------------------------------------------#include #include using namespace std; double& FloatArr::operator[](int i) throw(out_of_range) { if( i < 0 || i >= anz ) throw out_of_range("Invalid index!"); else return arrPtr[i]; } // -------------- Test Program -----------------int main() { try { // Uses arrays of type FloatArr } catch(out_of_range& err) { cerr << err
...
} STANDARD EXCEPTION CLASSES ■ 621 ᮀ Hierarchy of Standard Exception Classes The C++ standard library contains various exception classes, which are used in the string and container libraries, for example
...
Their definitions are to be found in the header file stdexcept
...
In addition to a default constructor, a copy constructor, and an assignment, this class contains a virtual public method, what(), which returns a message on the error cause as a C string
...
These errors can be avoided
...
These errors are unpredictable
...
For example, the method at() in the string class throws an out_of_range type exception when an invalid string position is passed to it
...
An exception of the overflow_error or underflow_error type is thrown if the value to be displayed is too large or too small for the type in use
...
A constructor with a string as a parameter is defined in every class derived from exception
...
The what() method returns this error message as a C string
...
The second exception handler’s message: Error in writing: Invalid index:
...
■ ■ ■ Define the exception class BadIndex for this purpose and store the class in the header file “floatArr
...
The exception class must contain a data member to store the invalid index
...
The const access method getBadIndex() returns the data member
...
Add an exception specification to the declaration of the subscript operators
...
Add appropriate exception specifications to the definitions and change the return types from bool to void
...
Write a main function where a constant vector is created and initialized with fixed values
...
The array elements are displayed and an index is read until an invalid index value is input
...
Then create a non-constant array
...
Elements are appended or inserted within a try block
...
Then finally output the array elements outside the try and catch blocks
...
EXERCISES ■ 625 Exercise 2 Implement exception handling for the Fraction class, which is used to represent fractions (see Exercise 2, Chapter 19)
...
■ Define the exception class DivError, which has no data members, within the Fraction class
...
Change the definition of the constructor in the Fraction class
...
Similarly modify the operator functions
...
You will need to arrange three different try and catch blocks sequentially
...
Create several fractions, including some with a numerator value of 0 and one with 0 as its denominator
...
The second try/catch block tests divisions
...
The corresponding exception handler should send the second error message shown opposite to your standard output
...
If the value of the denominator is 0, the denominator is read again
...
■ CHAPTER 28 ■ solutions 626 EXCEPTION HANDLING SOLUTIONS Exercise 1 // -----------------------------------------------------// floatArr
...
// Methods throw exceptions for an invalid index
...
Current number of elements
...
public: FloatArr( int n = 256 ); // Constructors FloatArr( int n, float val); FloatArr(const FloatArr& src); ~FloatArr(); // Destructor FloatArr& operator=( const FloatArr&); // Assignment int length() const { return cnt; } // Subscript operators: float& operator[](int i) throw(BadIndex); float operator[](int i) const throw(BadIndex); // Methods to append a float value // or an array of floats: void append( float val); void append( const FloatArr& v); SOLUTIONS ■ FloatArr& operator+=( float val) { append( val); return *this; } FloatArr& operator+=( const FloatArr& v) { append(v); return *this; } // Methods to insert a float value // or an array of float values: void insert( float val, int pos) throw(BadIndex); void insert(const FloatArr& v,int pos) throw(BadIndex); void remove(int pos) throw(BadIndex); // Remove // at pos
...
width(); // Save field width
...
arrPtr; p < v
...
cnt; ++p) { os
...
cpp // Implementing the methods of FloatArr
...
h" // --- Constructors --FloatArr::FloatArr( int n ) { max = n; cnt = 0; arrPtr = new float[max]; } // Sets max and cnt
...
max; cnt = src
...
arrPtr[i]; } // --- Destructor --FloatArr::~FloatArr() { delete[] arrPtr; } // Private functions to help enlarge the array
...
max; cnt = src
...
arrPtr[i] = src
...
void FloatArr::append( float val) { if( cnt+1 > max) expand( cnt+1); arrPtr[cnt++] = val; } void FloatArr::append( const FloatArr& v) { if( cnt + v
...
cnt); int count = v
...
for( int i=0; i < count; ++i) arrPtr[cnt++] = v
...
void FloatArr::insert(float val, int pos) throw(BadIndex) { insert( FloatArr(1, val), pos); } void FloatArr::insert( const FloatArr& v, int pos ) throw( BadIndex ) { if( pos < 0 || pos > cnt) // Append is also possible
...
cnt) expand(cnt + v
...
cnt] = arrPtr[i]; // position pos
...
cnt; ++i) // Fill the gap
...
arrPtr[i]; cnt = cnt + v
...
cpp // Tests exception handling for float arrays
...
h" int main() { const FloatArr v(10, 9
...
\n" << "\nInvalid index: " << err
...
insert(1
...
w
...
2F, 1); w
...
3F, 3); // Error! w[10] = 5
...
remove(7); // Error! } catch(BadIndex& err) { cerr << "\nError in writing! " << "\nInvalid index: " << err
...
h // A numeric class to represent fractions, // exception handling is included
...
numerator * denominator + numerator * a
...
denominator; return *this; } Fraction& operator-=(const Fraction& a) { *this += (-a); return *this; } Fraction& operator++() { numerator += denominator; return *this; } Fraction& operator--() { numerator -= denominator; return *this; } friend Fraction operator+(const Fraction&, const Fraction&); friend Fraction operator-(const Fraction&, const Fraction&); friend Fraction operator*(const Fraction&, const Fraction&); friend Fraction operator/(const Fraction&, const Fraction&) throw(Fraction::DivisionByZero); friend ostream& operator<<(ostream&, const Fraction&); friend istream& operator>>(istream& is, Fraction& a) throw(Fraction::DivisionByZero); }; #endif SOLUTIONS ■ // ------------------------------------------------------// fraction
...
// ------------------------------------------------------#include #include using namespace std; #include "fraction
...
denominator = a
...
denominator; temp
...
numerator*b
...
numerator * a
...
numerator = a
...
numerator; temp
...
denominator * b
...
numerator == 0) throw Fraction::DivisionByZero(); // Multiply a with the inverse of b: Fraction temp; temp
...
numerator * b
...
denominator = a
...
numerator; if( temp
...
numerator = -temp
...
denominator = -temp
...
numerator << "/" << a
...
numerator; cout << " Denominator != 0: "; is >> a
...
denominator == 0) { cout << "\nError: The denominator is 0\n" << "New Denominator != 0: "; is >> a
...
denominator == 0) throw Fraction::DivisionByZero(); if( a
...
numerator = -a
...
denominator = -a
...
cpp : Testing the class Fraction
...
cpp fraction
...
h> #include "fraction
...
We will also illustrate how to make objects in polymorphic classes persistent, that is, how to save them in files
...
637 638 ■ CHAPTER 29 ■ MORE ABOUT FILES OPENING A FILE FOR RANDOM ACCESS Combined open modes for read and write access Open mode Effects Must the file exist? ios::in | ios::out yes ios::in | ios::out | ios::trunc Opens the file for input and output
...
no ios::in | ios::out | ios::app ✓ To open the file for input and output
...
If the file does not exist, it will be created
...
no NOTE 1
...
2
...
OPENING A FILE FOR RANDOM ACCESS ■ 639 ᮀ Random File Access So far we have only looked at sequential file access
...
Random file access gives you the option of reading and writing information directly at a pre-defined position
...
After pointing the pointer, you can revert to using the read and write operations that you are already familiar with
...
This implies opening the file in binary mode to avoid having to transfer additional escape characters to the file
...
fle", mode); This statement opens the file "Account
...
The file will be created if it did not previously exist
...
To enable random read and write access to a file, the file can be opened as follows: Example: ios::openmode mode = ios::in | ios::out | ios::binary; fstream fstr("account
...
If the file does not exist, you can use the ios::trunc flag to create it
...
fle" cannot be found
...
The tellp() and tellg() methods return the current position of the put or get pointers as a long value
...
tellg(); This statement queries the current position of the read pointer in the myfile stream
...
The current file position can be modified using the seekp() or seekg() method
...
ᮀ Positioning Flags Three ios::seekdir type positioning flags are defined in the ios class for this purpose; these are ios::beg, ios::cur, and ios::end
...
fle" at offset pos
...
fle", ios::out | ios::binary); fstr
...
write( fstr ); This calls the write() method in the Account class, which allows an object to write its own data members to a file (see Chapter 18)
...
The statement used to position the write pointer in the last example can therefore be formulated as follows: Example: fstr
...
However, you cannot position the read/write pointer before the beginning of the file
...
This only makes sense if all the empty slots in the file are of an equal length, as they can be overwritten later
...
642 ■ CHAPTER 29 ■ MORE ABOUT FILES POSITIONING FOR RANDOM ACCESS (CONTINUED) Representing an index entry // index
...
and: int recordSize() const { return sizeof(key) + sizeof(recNr); } fstream& write( fstream& ind) const; fstream& read( fstream& ind); fstream& write_at(fstream& ind, long pos) const; fstream& read_at( fstream& ind, long pos); }; #endif The read_at() and write_at() methods // index
...
h" //
...
seekp(pos); ind
...
write((char*)&recNr, sizeof(recNr) ); return ind; } fstream& IndexEntry::read_at(fstream& ind, long pos) { ind
...
read((char*)&key, sizeof(key) ); ind
...
You should be aware that the first argument is 0L to indicate that long type is required
...
seekg(0L, ios::end); unsigned long count = fstr
...
These positioning methods are useful for files opened in binary mode
...
In text mode, conversions of LF <=> CR/LF prevent the methods from working correctly
...
Given that size is the length of a record 0L, size, 2*size,
...
If you are working with variable length records, you cannot exactly compute their positions
...
The index stores pairs of keys and record positions, so-called index entries in a file
...
If the index is sorted, the position that correlates to the required key can be quickly found using the binary search algorithm
...
The class comprises methods for reading and writing an index entry at the current file position or at any given position
...
644 ■ CHAPTER 29 MORE ABOUT FILES ■ FILE STATE Representing an index // index
...
close(); } void insert( long key, long pos); long search( long key); void retrieve(IndexEntry& entry, long pos ); }; Constructor of class IndexFile // index
...
open(file
...
clear(); mode |= ios::trunc; index
...
c_str(), mode); if(!index) return; } else name = file; } //
...
A file operation can also fail if a file cannot be opened, or if a block is not transferred correctly
...
Each state flag corresponds to a single bit in a status-word, which is represented by the iostate type in the ios class
...
g
...
The “flag” ios::goodbit is an exception to the rule since it is not represented by a single bit, but by the value 0 if no other flag has been set
...
A method exists for each state flag; these are eof(), fail(), bad(), and good()
...
This means you can discover the end of a file with the following statement: Example: if( fstr
...
The status-word of a stream can be read using the rdstate() method
...
rdstate() == ios::badbit )
...
If you call clear() without any arguments, all the state flags are cleared
...
ᮀ The IndexFile Class The IndexFile class, which uses a file to represent an index, is defined opposite
...
A new file can then be created
...
646 ■ CHAPTER 29 ■ MORE ABOUT FILES EXCEPTION HANDLING FOR FILES Defining your own exception classes // exceptio
...
For example, a method that reads records from a file can throw an exception when the state flag ios::eof is raised, that is, when the end of the file is reached
...
In each case the file name is saved for evaluation by the exception handler
...
You can use the exceptions() method to specify the flags in the status-word of a stream that will cause exceptions to be thrown
...
The method expects one or multiple state flags separated by the | sign
...
Example: ifstream ifstrm("account
...
exceptions(ios::failbit | ios::badbit); On accessing the fstrm stream an exception is thrown if either one of the flags ios::failbit or ios::badbit is raised
...
The exception thrown here is of a standard exception class, failure
...
The exception handler will normally send the string to standard error output
...
If a bit is set in the return value of the exceptions() method, an appropriate exception will be thrown whenever this error occurs
...
exceptions(); if( except & ios::eofbit)
...
648 ■ CHAPTER 29 ■ MORE ABOUT FILES PERSISTENCE OF POLYMORPHIC OBJECTS // account
...
// -------------------------------------------------//
...
public: // Constructor, access methods
...
TypeId getTypeId() const { return DEP_ACC; } ostream& write(ostream& fs) const; istream& read(istream& fs); }; class SavAcc: public Account { // Data members, constructor,
...
cpp: Implements the methods
...
h" ostream& DepAcc::write(ostream& os) const { if(!Account::write(os)) return os; os
...
write((char*)&deb, sizeof(deb) ); return os; } istream& DepAcc::read(istream& is) { if(!Account::read(is)) return is; is
...
read((char*)&deb, sizeof(deb)); return is; } //
...
You need to ensure that an object can be reconstructed precisely when it is read
...
So it is not simply a case of making the data members of an object into records and writing them to a file
...
You must write both the type and the data members of the object to a file
...
If the objects contain dynamic members, you must save the referenced objects themselves along with information on the object type
...
The methods can have a virtual definition within the class hierarchy
...
ᮀ Storing Objects of the Account Hierarchy The opposite page shows the Account class, with which you should already be familiar
...
The implementation of the read() and write() methods was discussed earlier in Chapter 18, “Fundamentals of File Input and Output,” and is unchanged
...
The implementation first calls the appropriate base class method
...
At present, no type information will be written to file or read from file
...
The following section contains more details on this topic
...
h : (continued) // Add definition of AccFile class // -------------------------------------------------//
...
h" class AccFile { private: fstream f; // Stream string name; // Filename public: AccFile(const string& s) throw(OpenError); ~AccFile(){ f
...
cpp: (continued) // Implements methods of class AccFile
...
seekp(0L, ios::end); // Seek to end, long pos = f
...
if( !f ) throw WriteError(name); TypeId id = acc
...
write( (char*)&id, sizeof(id)); if(!f) throw WriteError(name); else acc
...
// To write an object // to the file
...
Since the objects you need to save belong to different types, you must save both the data members and the type of object
...
The methods in the class you are going to define should throw exceptions if errors occur
...
ᮀ The AccFile Class The AccFile class, which is used for random access to a file with account data, is defined opposited
...
The constructor saves the file name and opens a given file for reading and appending at end-of-file
...
The append() method writes an account passed to it as an argument at end-of-file and returns the position where the account was written into the file
...
Depending on this type the append() method will write the appropriate type field to the file and then call the virtual method write(), allowing the current object to write itself to the file
...
The retrieve() method first reads the type identifier and then determines the type of object it needs to allocate memory for
...
Here too, an exception will be thrown if a stream access error occurs
...
cpp: (continued) // Implements the methods of class IndexFile
...
void IndexFile::insert(long k, long n) throw(ReadError, WriteError) { IndexEntry entry; int size = entry
...
index
...
seekg(0, ios::end); long nr = index
...
if(!index) throw ReadError(name); nr -= size; // Last entry
...
read_at(index, nr)) throw ReadError(name); if( k < entry
...
{ entry
...
setKey(k); entry
...
write_at(index, nr + size); if(!index) throw WriteError(name); } // insert APPLICATION: INDEX FILES ■ 653 It makes sense to organize data sequentially in files if you need to walk through all the records regularly
...
However, most applications need to provide quick access to specific data
...
Index files can mean a real performance boost in cases like this
...
The index consists of pairs of keys and record positions for the primary file
...
This situation can be more easily explained by the following graphic: Index Primary file 12345 | The index is sorted by reference to the keys for speed of access, allowing you to perform a binary search to locate the position of a record
...
The insert() method, which correctly inserts a new record into the sorted index, is defined opposite
...
If the current position is 0, that is, the file is empty, the entry is inserted at offset 0
...
654 ■ CHAPTER 29 ■ MORE ABOUT FILES IMPLEMENTING AN INDEX FILE Representing the index file // index
...
prim"), IndexFile(s + "
...
cpp: Implementing the methods
...
getNr()) == -1) { long pos = append(acc); // Insert in primary file if(pos != -1) // Insert in IndexFile::insert(acc
...
} } Account* IndexFileSystem::retrieve(long key ) { long pos = search(key); // Get the record address: if( pos == -1 ) // Account number found? return NULL; else { IndexEntry entry; // Read the index entry: IndexFile::retrieve(entry, pos); // Read from primary file: return( AccFile::retrieve( entry
...
Let’s now look at a sample index file, used for managing bank accounts
...
The only data member is a string for the file name
...
Base initializers are then used to open the corresponding files
...
ᮀ Inserting and Retrieving Records The insert() method was defined to insert new records
...
If not, a new record is appended to the end of the primary file using the append() method
...
The IndexFileSystem class also contains the retrieve() method, which is used to retrieve records from the primary file
...
Then the record is retrieved from the primary file by the AccFile class retrieve() method
...
It’s your job to implement these three methods as your next exercise! Using a sorted file to implement an index has the disadvantage that records need to be shifted to make room to insert new records
...
■ CHAPTER 29 ■ exercise s 656 MORE ABOUT FILES EXERCISES Exercise 1 Class IndexFile class IndexFile { private: fstream index; string name; // Filename of the index public: IndexFile(const string s) throw(OpenError); ~IndexFile( ){ index
...
close(); } long append( Account& acc) throw(WriteError); Account* retrieve( long pos ) throw(ReadError); }; EXERCISES ■ 657 Exercise 1 Complete and test the implementation of the IndexFileSystem class
...
a
...
b
...
The method retrieves a record at a given position in the index
...
Define the search() method, which looks up an index entry for an account number passed to it as an argument
...
Return value: The position of the record found, or -1 if the account number is not found in the index
...
Then define the retrieve() method for the AccFile class, which first evaluates the type field at a given position in the account file, then dynamically allocates memory for an object of the appropriate type, and finally reads the data for an account from the file
...
Write a main() function that uses a try block to create an IndexFileSystem type object and to insert several accounts of various types into the index file
...
Write an exception handler to handle the various errors that could occur
...
658 ■ CHAPTER 29 MORE ABOUT FILES Hash Files Hash Files profit from random file access in localizing file records directly
...
Hash file 4713123434 Hash function Key The idea behind hashing is to provide a function, the hash function, which is applied to the hash key of a record and yields the address of the file record
...
However, hash keys, such as account or insurance numbers, consist of a fixed number of digits that do not start with 0
...
The number 0 is the first record number and b-1 is the last record number in the address space of the hash file
...
However, the hash function maps the hash key values key, key + b, key + 2*b, etc
...
In this case collisions may occur, for example, when a new record being inserted hashes to an address that already contains a different record
...
The process of finding another position at which to insert the new record is called collision resolution
...
The new file record is then inserted at this position
...
The concept of hash files is explained on the opposite page
...
The customer id is the key used by the hash function opposite to compute the address of the record
...
Note: Linear solution provides for adequate access times if the address space is sufficiently large and not too full
...
The hash function opposite will guarantee a good distribution if b is a sufficiently large prime number
...
You need to store the customer id as an unsigned long value and the name of the customer as a char array with a length of 30
...
Both methods expect the position and the stream as arguments
...
The private members of the class comprise an fstream type file stream, a string used to store the file name, an int variable used to store the number b, and the hash function shown opposite as a method
...
It opens the corresponding file for read and write access
...
Additionally declare the methods insert() and retrieve() to insert or retrieve single records
...
If a collision occurs, the methods perform a sequential search to locate the next free slot in the address space (mod of address space size), or the desired customer data
...
g
...
Add various customer information records to the hash file and then retrieve this data
...
■ CHAPTER 29 ■ solutions 660 MORE ABOUT FILES SOLUTIONS Exercise 1 // -----------------------------------------------------// exceptio
...
earlier in this chapter)
...
h : // Defines the classes // Account, DepAcc, and SavAcc // with virtual read and write methods as well as // the class AccFile to represent account files
...
h" enum TypeId { ACCOUNT, DEP_ACC, SAV_ACC }; class Account { private: string name; unsigned long nr; double balance; // Data elements: public: // Constructor: Account( const string c_name = "X", unsigned long c_nr = 1111111L, double c_balance = 0
...
// The other methods: virtual TypeId getTypeId() const { return ACCOUNT; } virtual ostream& write(ostream& fs) const; virtual istream& read(istream& fs); virtual void display() const { cout << fixed << setprecision(2) << "----------------------------------\n" << "Account holder: " << name << endl << "Account number: " << nr << endl << "Balance of account: " << balance << endl << "----------------------------------\n" << endl; } }; class DepAcc : public Account { private: double limit; // Overdrawn limit double interest; // Interest rate public: DepAcc( const string s = "X", unsigned long n = 1111111L, double bal = 0
...
0, double ir = 0
...
// The other methods are implicit virtual: TypeId getTypeId() const { return DEP_ACC; } ostream& write(ostream& fs) const; istream& read(istream& fs); 661 662 ■ CHAPTER 29 MORE ABOUT FILES void display() const { Account::display(); cout << "Overdrawn limit: " << limit << endl << "Competitive interest: " << interest << "\n----------------------------------\n" << endl; } }; class SavAcc: public Account { private: double interest; // Compound interest public: // Methods as in class DepAcc
...
close(); } long append( Account& acc) throw(WriteError); Account* retrieve( long pos) throw(ReadError); void display() throw( ReadError); }; #endif SOLUTIONS // ---------------------------------------------------// account
...
// ---------------------------------------------------#include "account
...
write((char*)&nr, sizeof(nr) ); os
...
read((char*)&nr, sizeof(nr) ); is
...
write((char*)&limit, sizeof(limit) ); os
...
read((char*)&limit, sizeof(limit) ); is
...
■ 663 664 ■ CHAPTER 29 MORE ABOUT FILES // ---- Methods of class AccFile ---AccFile::AccFile(const string& s) throw( OpenError) { ios::openmode mode = ios::in | ios::out | ios::app | ios::binary; f
...
c_str(), mode); if(!f) throw OpenError(s); else name = s; } void AccFile::display() throw(ReadError) { Account acc, *pAcc = NULL; DepAcc depAcc; SavAcc savAcc; TypeId id; if( !f
...
read((char*)&id, sizeof(TypeId)) ) { switch(id) { case ACCOUNT: pAcc = &acc; break; case DEP_ACC: pAcc = &depAcc; break; case SAV_ACC: pAcc = &savAcc; break; default: cerr << "Invalid flag in account file" << endl; exit(1); } if(!pAcc->read(f)) break; pAcc->display(); cin
...
eof()) throw ReadError(name); f
...
seekp(0L, ios::end); // Seek to end, long pos = f
...
if( !f ) throw WriteError(name); TypeId id = acc
...
write( (char*)&id, sizeof(id)); // Write the TypeId if(!f) throw WriteError(name); else acc
...
if(!f) throw WriteError(name); else return pos; } Account* AccFile::retrieve( long pos) throw(ReadError) { f
...
seekg(pos); // Set the get pointer if( !f ) throw ReadError(name); TypeId id; f
...
h: Contains definitions of classes // IndexEntry representing an index entry, // Index representing the index and // IndexFile representing an index file
...
h" using namespace std; class IndexEntry { private: long key; long recPos; // Key // Offset public: IndexEntry(long k=0L, long n=0L){ key=k; recPos=n; } void long void long setKey(long k) getKey() const setPos(long p) getPos() const { { { { key = k; } return key; } recPos = p; } return recPos; } int recordSize() const { return sizeof(key) + sizeof(recPos); } fstream& write( fstream& ind) const; fstream& read( fstream& ind); fstream& write_at(fstream& ind, long pos) const; fstream& read_at( fstream& ind, long pos); SOLUTIONS ■ void display() const { cout << "Account Nr: " << key << " Position: " << recPos << endl; } }; class IndexFile { private: fstream index; string name; // Filename of index public: IndexFile( const string& s) throw (OpenError); ~IndexFile() { index
...
prim"), IndexFile(s + "
...
cpp : Methods of the classes // IndexEntry, Index, and IndexFile // -----------------------------------------------------#include "index
...
seekp(pos); ind
...
write((char*)&recPos, sizeof(recPos) ); return ind; } fstream& IndexEntry::read_at(fstream& ind, long pos) { ind
...
read((char*)&key, sizeof(key) ); ind
...
write((char*)&key, sizeof(key) ); ind
...
read((char*)&key, sizeof(key) ); ind
...
open( file
...
clear(); mode |= ios::trunc; index
...
c_str(), mode); if(!index) throw OpenError(name); } name = file; } SOLUTIONS ■ void IndexFile::display() throw(ReadError) { IndexEntry entry; index
...
read(index)) break; entry
...
eof()) throw ReadError(name); index
...
int size = entry
...
index
...
seekg(0L, ios::end); end = index
...
read_at(index, mid*size); if(!index) throw ReadError(name); key = entry
...
read_at(index, begin * size); if(!index) throw ReadError(name); if( k == entry
...
recordSize(); // Length of an index // entry
...
clear(); index
...
tellg(); // Get file length // 0, if file is empty
...
read_at(index, nr)) throw ReadError(name); // Last entry
...
getKey()) // To shift
...
write_at(index, nr + size); nr -= size; } else { found = true; } } entry
...
setPos(n); entry
...
clear(); if(!entry
...
bool IndexFileSystem::insert( Account& acc) throw(ReadError, WriteError) { if(search(acc
...
{ long pos = append(acc); // Add to primary file
...
getNr(), pos); // Add to Index return true; } else return false; } Account* IndexFileSystem::retrieve(long key ) { // Get the record address from the index: long pos = search(key); // Byte offset of // index entry
...
return NULL; else // Account number does exist: { IndexEntry entry; // To read the index eintry IndexFile::retrieve( entry, pos); // Get from primary file: return( AccFile::retrieve( entry
...
cpp : Testing the index file // -----------------------------------------------------#include #include using namespace std; #include "index
...
h" 671 672 ■ CHAPTER 29 MORE ABOUT FILES int main() { try { IndexFileSystem database("AccountTest"); Account acc1( "Vivi", 490UL, 12340
...
insert( acc1 ); SavAcc acc2( "Ulla", 590UL, 4321
...
5); database
...
20, 10000
...
9); database
...
IndexFile::display(); cin
...
AccFile::display(); unsigned long key; cout << "Key? "; cin >> key; if(database
...
retrieve(key); if( pAcc != NULL ) { pAcc->display(); delete pAcc; pAcc = NULL; } else cout << "Retrieving failed" << endl; } catch(OpenError& err) { cerr << "Error on opening the file:" << err
...
getName() << endl; exit(1); } SOLUTIONS ■ catch(ReadError& err) { cerr << "Error on reading from the file: " << err
...
) { cerr << " Unhandled Exception" << endl; exit(1); } return 0; } Exercise 2 // ------------------------------------------------------// exceptio
...
// ------------------------------------------------------// hashFile
...
// ------------------------------------------------------#ifndef _HASH_H_ #define _HASH_H_ #include #include #include #include #include
...
h" class HashEntry { private: unsigned long nr; char name[30]; 673 674 ■ CHAPTER 29 MORE ABOUT FILES public: HashEntry(unsigned long n = 0L, const string& s = "") { nr = n; strncpy(name, s
...
c_str(), 29); name[30]='\0'; } int getSize() const { return(sizeof(long) + sizeof(name)); } fstream& write(fstream& fs); fstream& read(fstream& fs); fstream& write_at(fstream& fs, unsigned long pos); fstream& read_at(fstream& fs, unsigned long pos); virtual void display() { cout << fixed << setprecision(2) << "----------------------------------\n" << "Client number: " << nr << endl << "Client: " << name << endl << "----------------------------------\n" << endl; cin
...
cpp : Methods of classes HashEntry and HashFile // ------------------------------------------------------#include "hashFile
...
write((char*)&nr, sizeof(nr) ); f
...
read((char*)&nr, sizeof(nr) ); f
...
seekp(pos); f
...
write( name, sizeof(name) ); return f; } fstream& HashEntry::read_at(fstream& f, unsigned long pos) { f
...
read((char*)&nr, sizeof(nr) ); f
...
open(file
...
if(!f) // If file doesn't exist: { f
...
open(file
...
seekp(0L); for( unsigned long i=0; i < b; i++) // Initialize { // the address space rec
...
getSize(); // Hash-Wert: unsigned long pos = hash_func(rec
...
read_at(f, pos*size); if(!f) throw ReadError(name); else { if(temp
...
write_at(f, pos*size); else { bool found = false; // Read a slot
...
No => Search for a free slot
...
read_at(f, p); if(!f) throw ReadError(name); else if(temp
...
else // Proceed to the next slot: p = (p + size)%(b*size); } if( p == pos*size ) // Address space full
...
write_at(f,p); // Add to file
...
getSize(); unsigned long pos = hash_func(key); // Hash value
...
read_at(f, pos*size); // Read a slot
...
getNr() == key) // Found? return temp; // Yes => finish else // No => search { unsigned long p = (pos*size + size)%(b*size); while( p!= pos *size ) { temp
...
getNr() == key) return temp; else p = (p + size)%(b*size); } temp
...
setName(""); // Record found
...
// Key doesn't // exist
...
seekg(0L); for(unsigned int i = 0; i < b; i++) { temp
...
display(); } f
...
cpp : Tests hash files // ------------------------------------------------------#include #include #include "hashFile
...
fle", 7); cout << "\nInsert: " << endl; HashEntry kde( 3L, "Vivi"); hash
...
setNr(10L); kde
...
insert( kde ); kde
...
setName("Alexa"); hash
...
setNr(21L); kde
...
insert( kde ); kde
...
setName("Jeany"); hash
...
display(); unsigned long key; cout << "Key? "; cin >> key; HashEntry temp = hash
...
getNr() != 0L) temp
...
getName() << endl; exit(1); } catch(WriteError& err) { cerr << "Error writing to file: " << err
...
getName() << endl; exit(1); } return 0; } ■ 679 This page intentionally left blank chapter 30 More about Pointers This chapter describes advanced uses of pointers
...
An application that defines a class used to represent dynamic matrices is introduced
...
cpp: Sorts an array of pointers to accounts // according to the account numbers // --------------------------------------------------#include "account
...
for( ; kptr < lastp; ++kptr ) { minp = kptr; for( temp = kptr + 1; temp <= lastp; ++temp ) { if( (*temp)->getNr() < (*minp)->getNr() ) minp = temp; } ptrSwap( kptr, minp ); } } void ptrSwap( Account **p1, Account **p2 ) { Account *help; help = *p1; *p1 = *p2; *p2 = help; } POINTER TO POINTERS ■ 683 ᮀ Motivation Pointer variables are objects that have an address in memory, and this means you can use pointers to address them
...
This is necessary if ■ ■ an array of pointers is to be dynamically allocated, or a function expects an array of pointers as an argument
...
Since each element in the array is a pointer, this pointer variable must be a pointer to a pointer
...
Example: Account** ptr = new Account*[400]; The pointer ptr is now pointing at the first pointer in the array with a total of 400 Account* type pointers
...
Example: void accSort( Account **kptr, int len); You can use the kptr parameter to manipulate a pointer array whose length is stored in the second parameter, len
...
Instead of Account **kptr you can also use the equivalent form Account *kptr[]
...
The func- tion uses the selection sort algorithm (which you have already worked with) for sorting
...
This saves time-consuming copying
...
h> int func( char *buffer, int max,
...
long arg3;
...
arg3 = va_arg( argptr, long ); // Read arguments
...
...
VARIABLE NUMBER OF ARGUMENTS ■ 685 C++ allows you to define functions that allow a variable number of arguments
...
The printf() function uses the conversion specifiers in the format string to compute the number and type of arguments that follow
...
At least one obligatory argument is required
...
The optional arguments are represented by three dots
...
The function shown opposite, func(), expects two or more arguments
...
); To allow functions with a variable number of arguments to be defined, C++ pushes the last argument onto the stack first
...
The optional arguments are accessed via a pointer, the so-called argument pointer, which is designated by argptr here
...
h contain macros, which conform to ANSI standard, to manage the pointer and assure that the source code will be portable
...
The va_list type argument pointer argptr must be declared in addition to other local variables
...
h as a typeless or char pointer
...
The macro va_start() is then called to point the argument pointer argptr to the first optional argument
...
Example: va_start( argptr, max ); 686 ■ CHAPTER 30 ■ MORE ABOUT POINTERS VARIABLE NUMBER OF ARGUMENTS (CONTINUED) The function input() // input
...
// The input can be corrected with backspace
...
Pointer to the input buffer
...
Maximum number of characters to be read / 3
...
// This list has to end with CR = '\r'! // Returns: Character that breaks the input
...
h> #include
...
) { int c, breakc; // Current character, character to // break with
...
va_list argp; // Pointer to the following arguments
...
// For special keys: // Extended code + 256
...
do // Compare with break characters: if( c == (breakc = va_arg(argp,int)) ) return(breakc); while( breakc != '\r'); va_end( argp); if( c == '\b' && nc > 0) // Backspace? { --nc, --buffer; putch(c); putch(' '); putch(c); } else if( c >= 32 && c <= 255 && nc < max ) { // Place character into the buffer ++nc, *buffer++ = c; putch(c); // and output
...
} } VARIABLE NUMBER OF ARGUMENTS (CONTINUED) ■ 687 3
...
The arguments of va_arg() are the name of the argument pointer and the type of the optional argument: Example: arg3 = va_arg( argptr, long); Each call to the macro va_arg() sets the argument pointer to the next optional argument
...
It must be identical to the type of the corresponding optional argument
...
A specific value (such as NULL, Ϫ1 or CR) can be used, or the current number of arguments can be defined by an obligatory argument
...
After evaluating the arguments the argument pointer is set to NULL by the va_end() macro: Example: va_end( argptr); Optional arguments can also be read more than once
...
ᮀ Notes on the Example Opposite The sample function input() on the opposite page uses the getch() function to read character input from the keyboard and store it in the buffer addressed by the first argument
...
All other arguments are characters that can terminate keyboard input
...
Input can be terminated by pressing the space, ESC, F1, or return keys
...
Non-printable characters are ignored unless stated as optional arguments
...
For function keys this code is within the range 59–68
...
A table of extended codes is available in the Appendix
...
cpp: Demonstrates the use of an array // of pointers to functions
...
put(c); } void message_low( char *s) { int c; for( ; *s != '\0';++s) c = tolower(*s), cout
...
It addresses the machine code for the function
...
There are many uses for pointers to functions
...
Individual functions are then accessible via an index
...
This makes sense if the function you are calling needs to work with different functions depending on the current situation
...
qsort() uses the quick sort algorithm to sort an array
...
ᮀ Declaring Pointers to Functions A pointer to a function is declared as follows: Syntax: type (* funcptr)( parameter_list ); This defines the variable funcptr, which can store the address of a function
...
The first pair of parentheses is also important for the declaration
...
Now let’s point funcptr to the function compare() and call compare() via the pointer
...
1, 7
...
The declaration of compare() is necessary to let the compiler know that compare is the name of a function
...
functab is initialized by the functions stated in its definition and thus functab[0] points to error_message(), functab[1] to message(), etc
...
690 ■ CHAPTER 30 ■ MORE ABOUT POINTERS COMPLEX DECLARATIONS 1st Example: char (* strptr) [50] 3
...
0
...
0
...
pointer to 2
...
char
...
4
...
0
...
3
...
func is a 1
...
pointer to 3
...
pointer to 5
...
3rd Example: char * 6
...
(* (* funcptr ) () ) [] 3
...
0
...
funcptr is a 1
...
a function with return value of type 3
...
an array with elements of type 5
...
char
...
4
...
These operators are: Operator Significance [] Array with elements of type () Function with return value of type * Pointer to & Reference to A complex declaration always uses more than one of these operators
...
In a declaration, a combination of the three operators is permissible, however, the following exceptions apply: ■ ■ the elements of an array cannot be functions a function cannot return a function or an array (but it can return a pointer to a function or an array)
...
You can use parentheses to redefine the order of precedence
...
Always start with the identifier being declared
...
If the parentheses/brackets () or [] are on the right, they are interpreted
...
If there is nothing or just a right bracket on the right ), the asterisk on the left is interpreted, if it exists
...
This proceeding is demonstrated by the example opposite
...
692 ■ CHAPTER 30 ■ MORE ABOUT POINTERS DEFINING TYPENAMES 1st Example: typedef DayTime FREETIME; FREETIME timeArr[100]; 2nd Example: typedef struct { double re, im; } COMPLEX; COMPLEX z1, z2, *zp; 3rd Example: typedef enum { Mo, Tu, We, Th, Fr } WORKDAY; WORKDAY day; 4th Example: typedef enum { Diamonds, Hearts, Spades, Clubs } COLOR; typedef enum { seven, eight, nine, ten , jack, queen, king, ace } VALUE; typedef struct { COLOR f; VALUE w; } CARD; typedef CARD[10] HAND; HAND player1, player2, player3; DEFINING TYPENAMES ■ 693 ᮀ The typedef Keyword C++ allows you to give types a new name using the keyword typedef
...
The statement Example: BYTE array[100]; will then define an array array with 100 elements of the unsigned char type
...
Examples: typedef int* INTPTR; typedef enum{ RED, AMBER, GREEN } Lights; Here, INTPTR identifies the type “pointer to int” and Lights is an enumerated type
...
Omitting the typedef prefix will define a variable name but not a new type name
...
They simply introduce a new name for an existing type
...
” The declaration Example: PTR_TO_FUNC search; is then equivalent to char* (*search)(); ᮀ Advantages The major advantage of using typedef is that it improves the readability of your programs, especially when complex types are named
...
When a program is ported to another platform, you only need to change the platform dependent type once in the typedef definition
...
h: Representing dynamic matrices
...
Matrices are used for computing vectors needed to move, rotate, or zoom images in graphics programming, for example
...
Additionally, it should be possible to use the index operator to access the elements of the matrix
...
As you already know, a matrix is a single-dimensional array whose elements are single-dimensional arrays themselves
...
The index operator is overloaded for the Row class to allow an exception of the out_of_range type to be thrown for invalid indices
...
mat is thus a pointer to a pointer
...
A loop is then used to allocate memory to the rows dynamically
...
The subscript operator in the Matrix class returns the line array i for a given index i
...
Then the subscript operator of the Row class is called for the line array
...
You will be enhancing the Matrix class in the exercises to this chapter by overloading the copy constructor and the assignment, for example
...
The size of each element is specified by size
...
qsort() calls the function with two arguments that point to the elements being compared
...
The function must return an integer less than, equal to, or greater than zero if the first argument is less than, equal to, or greater than the second
...
); Refer to the Appendix “Binding C Functions
...
The function expects a variable number of unsigned int values as arguments
...
✓ Exercise 3 Write a C++ program that compares the speed of the quick sort and selection sort algorithms Sort two identical sequences of random numbers of the type int to test the sort algorithms
...
Display the time in seconds required for the sort operation on screen after each sort operation
...
Use the standard function qsort(), whose prototype is defined opposite, as quick sort algorithm for this exercise
...
■ ■ ■ Add a const version of the subscript operator to the Row and Matrix classes
...
Define a constructor for the Matrix class
...
Also write a copy constructor
...
Addition is defined for two n ϫ n matrices, A and B, which have equal numbers of rows and columns
...
, n-1 Test the Matrix with a suitable main function that calls all the methods
...
■ CHAPTER 30 ■ solutions 698 MORE ABOUT POINTERS SOLUTIONS Exercise 1 Screen output: P WHITE E LUE K Exercise 2 // ------------------------------------------------------// minivar
...
// The function expects a variable number of arguments // with unsigned int types
...
h> unsigned int min( unsigned int first,
...
cpp // Compares the performances of sorting algorithms // quick sort and selection sort // For this purpose, two identical arrays are dynamically // generated and initialized with random numbers
...
// ------------------------------------------------------#include #include #include #include using namespace std; void isort(int *v, int lenv); // For qsort(): extern "C" int intcmp(const void*, const void*); main() { unsigned int i, size; int *numbers1, *numbers2; long time1, time2; cout << << << << "\n The performance of the sorting algorithms" "\n quick sort and selection sort" "\n is being compared
...
\n"; srand((unsigned)time(NULL)); // Initialize the // random number generator
...
\n"; time(&time1); // Length of time // for quick sort
...
\n"; 699 700 ■ CHAPTER 30 MORE ABOUT POINTERS cout << "\nI am sorting again
...
\n" << "\nOutput sorted numbers? (y/n)\n\n"; char c; if( c == for( i cout cin >> c; 'Y' || c == 'y') = 0 ; i < size ; ++i) << setw(12) << numbers1[i]; cout << endl; return 0; } extern "C" int intcmp( const void *a, const void *b) { return (*(int*)a - *(int*)b); } // ------------------------------------------------------// isort() sorts an array of int values // using the selection sort algorithm
...
minp points to the "current" smallest array element
...
help = *a, *a = *minp, *minp = help; } } // Swap
...
h : Represents dynamic matrices // ------------------------------------------------------#ifndef _MATRIX_H_ #define _MATRIX_H_ #include #include