C++ Interactive Course
C++ Interactive Course
INTRODUCTION
The Key
Postfix Increment Operators
The Stack Class
Pushing and Popping
An Array Disguised as a Stack
Not a Constructor
Quiz 2
Exercise 1
Exercise 2
Session 3: Arrays of Objects
Defining an Array of Objects
New Syntax for Access
Array of airtime Objects
Array of employee Objects
Quiz 3
Exercise 1
Exercise 2
Session 4: Strings
String Variables
String Constants
Improved String I/O
Using const Variables
Eating Extra Characters
Real Strings for the employee Class
String I/O
External Variables
Quiz 4
Exercise 1
Exercise 2
Midchapter Discussion
Session 5: String Library Functions
Library Functions for Strings
Finding String Lengths
Copying Strings
Appending Strings
Comparing Strings
A Homemade String Class
Library Functions
Clean Format
Data Conversions
Appending
Assignment
Overflow Protection
Wrapping
Quiz 5
Exercise 1
Exercise 2
Session 6: Arrays of Strings
Syntax of Arrays of Strings
Arrays of Empty Strings
Arrays of Initialized Strings
The weekday Class
The stricmp() Function
Arithmetic Assignment Operators
Quiz 6
Exercise 1
Exercuse 2
Session 7: Structures
Specifying a Structure
Defining Structure Variables
Accessing Structure Members
Initializing Structure Variables
Structure Usage
Structures versus Classes
Quiz 7
Exercise 1
Exercise 2
Session 8: enum and bool
Enumerated Data Types
Specifying an Enumerated Type
Creating and Using Enumerated Variables
They’re Really Integers
A Card Game Program
The bool Keyword
Quiz 8
Exercise 1
Exercise 2
Summary: Chapter 3
End-of-Chapter Discussion
CHAPTER 4—FUNCTIONS
Session 1: Function Review and Function Declaration
Review
Function Calls
Function Definitions
Arguments
Return Values
Function Declarations
Arguments
Functions Called from main()
Functions Called from Member Functions
Library Functions
The Rule
Quiz 1
Session 2: Standalone Member Functions
Inline Functions
Specifying an Inline Function
When Should You Inline a Function?
Member Functions Defined Within a Class
Member Functions Defined Outside a Class
The Scope Resolution Operator
The Compiler Has Its Own Ideas
Revised weekdays Program
Move ’em Out?
Macros
Quiz 2
Exercise 1
Exercise 2
Session 3: Overloaded Functions
Need for Function Overloading
How Does It Know?
A Member Function Example
Quiz 3
Exercise 1
Exercise 2
Session 4: Default Arguments
A Power Example
A Member Function Example
The cin.getline() Function
Limitations
When to Do What
Quiz 4
Exercise 1
Exercise 2
Midchapter Discussion
Session 5: Storage Classes
Declarations and Definitions
Two Kinds of Declarations
Lifetime and Visibility
Automatic Variables
Register Variables
Blocks
External Variables
External Variables and Multiple Files
External Variables in the Doghouse
Local Static Variables
The Storage Class Table
Objects
Visiblity of Instance Data
Lifetime of Instance Data
Quiz 5
Session 6: Static Members
Static Member Data
Creating Static Data
Accessing Static Data
Static Functions
Count-the-Objects Example
Quiz 6
Exercise 1
Exercise 2
Session 7: Reference Arguments
Swapping Integers
Passing by Value
Passing by Reference
A Reference Is a Different Name
Swapping Objects
Standalone References
Behind the Scenes
Advantages of Passing by Reference
Quiz 7
Exercise 1
Exercise 2
Session 8: Returning by Reference
Setting an Object to a Value
Cautions
What’s It For?
Quiz 8
Summary: Chapter 4
End-of-Chapter Discussion
CHAPTER 5—CONSTRUCTORS
Session 1: Introducing Constructors
Initialization
Creation
Destructors
Constructors and Destructors in Action
Same Name as the Class
No Return Value
What They Do
Initializing Variables
Initialization List
Default Constructor
Quiz 1
Exercise 1
Exercise 2
Session 2: Constructor Arguments
A Two-Argument Constructor
The Wrong Way
The Right Way
“Calling” the Constructor
A No-Argument Constructor
Quiz 2
Exercise 1
Exercise 2
Session 3: The One-Argument Constructor
Conversions
Converting Strings to xStrings
Converting Meters to English Distances
Meters to English Objects
Escape Characters
Not Always Appropriate
More Conversions to Come
Quiz 3
Exercise 1
Exercise 2
Session 4: Arrays as Instance Data
Array Sized with Constant
Array Sized with External const
Initializing Member Variables
Instance Data Initialization
Initialization Is Not Assignment
Arrays Sized at Compile Time
The enum Hack
Static Constant Variables
The Improved STACK Program
Initializing a Member Array
Quiz 4
Exercise 1
Exercise 2
Midchapter Discussion
Session 5: Copy Constructors
Copying Variables
Equivalent Syntax
Copying Objects
Not the Normal Constructor
The Default Copy Constructor
const for Function Arguments
Passing by Reference
Passing by Value
A Simple Example of a Copy Constructor
Just Another Constructor
Argument Must Be Passed by Reference
Argument Should Be const
Quiz 5
Exercise 1
Exercise 2
Session 6: Copy Constructors at Work
Numbering the Objects
An Intelligent Copy Constructor
Initialization List
A Variation on strcpy()
Other Reasons for Using Copy Constructors
Copy Constructor Invoked in Pass by Value
Passing by Value Creates a Copy
Why the Copy Constructor Must Use a
Reference Argument
Copy Constructor Invoked in Return by Value
Quiz 6
Exercise 1
Exercise 2
Session 7: const Objects
The Wandering Noon
const Functions
The Fixed Noon
Quiz 7
Exercise 1
Exercise 2
Session 8: Visualizing Construction and Destruction
Two Kinds of Total
Program Features
External Variables
Variables in main()
Passing by Value
Local Variables
Quiz 8
Exercise 1
Exercise 2
Summary: Chapter 5
End-of-Chapter Discussion
CHAPTER 7—INHERITANCE
Session 1: Introduction to Inheritance
Reusability
Rewriting Code
Function Libraries
Class Libraries
Inheritance and Program Design
Composition: A “Has a” Relationship
Inheritance: A “Kind of” Relationship
Not Exactly a Family Tree
Inheritance Syntax
Inheriting Attributes
Accessing Base Class Data
Calling a Base Class Function
Function Overloading in Base and Derived Classes
Quiz 1
Program Design: The employee Class
Class Hierarchy
The EMPINH Program
Abstract Classes
Interaction with EMPINH
Quiz 2
Exercise 1
Exercise 2
Session 3: Reusability: An Improved Stack Class
Reusability
The STACKINH Program
A Smarter Object
The Base Class Constructor
The protected Access Specifier
Functions That Aren’t Inherited
Quiz 3
Exercise 1
Exercise 2
Session 4: Constructors and Inheritance
The Great Chain of Constructors
When Are Derived Class Constructors Necessary?
No Arguments
Arguments
The Initializer List
Adding Functionality to the Derived Class
Constructor
Quiz 4
Exercise 1
Exercise
Midchapter Discussion
Session 5: Access Control
Access Review
Keeping Data Private
A Stack Example
A Graphics Example
A Hierarchy of Shapes
The Classes
Public and Private Inheritance
The Compiler and Public Inheritance
Private Inheritance
The Compiler and Private Inheritance
Protected Inheritance
Access Summary
Quiz 5
Exercise 1
Exercise 2
Session 6: Grandparents
Deriving foreman from laborer
Constructors
Summary
Quiz 6
Exercise 1
Exercise 2
Session 7: Composition
A safearay Object in a Stack Class
Should a scientist “Have an” employee?
Summary
Quiz 7
Exercise 1
Exercise 2
Session 8: Multiple Inheritance
Two Base Classes: employee and student
Repeated Base Classes
It’s Controversial
Ambiguous Subobjects
Virtual Base Classes
Composition to the Rescue
Chapter
Exercise 1
Exercise 2
Summary: Chapter 7
End-of-Chapter Discussion
CHAPTER 8—POINTERS
Session 1: Addresses and Pointers
Addresses (Pointer Constants)
The Address of Operator &
Pointer Variables
Pointers to Basic Types
Syntax Quibbles
Pointers Must Have a Value
Pointers to Objects
Accessing the Variable Pointed To
Pointer to void
Quiz 1
Session 2: Pointers, Arrays, and Functions
Pointers and Arrays
Array Elements and Pointer Notation
Pointer Constants and Pointer Variables
Pointers and Functions
Passing Simple Variables
Passing Arrays as Arguments
Quiz 2
Exercise 1
Exercise 2
Session 3: Pointers and Strings
Pointers to String Constants
Strings as Function Arguments
Copying a String Using Pointers
Library String Functions
Arrays of Pointers to Strings
Membership Access Operator (->)
Quiz 3
Exercise 1
Exercise 2
Session 4: Memory Management with new and delete
The new Operator
The delete Operator
A String Class That Uses new
Constructor in NEWSTR
Destructor in NEWSTR
Glitch in xString Class
Creating Objects with new
An Array of Pointers to Objects
Program Operation
Accessing Member Functions
Quiz 4
Exercise 1
Exercise 2
Midchapter Discussion
Session 5: this and const
The this Pointer
Accessing Member Data with this
Using this for Returning Values
Pointers and the const Modifier
const Variables
Two Places for const
Function Arguments and const
Returning const Values
Quiz 5
Session 6: A Memory-Efficient String Class
To Copy or Not to Copy?
A String Counter Class
The strCount Class
The xString Class
Returning *this by Reference
Quiz 6
Exercise 1
Exercise 2
Session 7: A Linked List Class
A Chain of Pointers
Adding an Item to the List
Displaying the List Contents
Self-Containing Classes
Augmenting the linklist Program
Containers
Quiz 7
Exercise 1
Exercise 2
Session 8: A Sorted Array Class
Inserting Objects in Sorted Order
The employee Class
The SortedArray Class
In main()
Searching for a Specific Element
The Binary Search
Constructors Used to Initialize Array
Quiz 8
Exercise 1
Exercise 2
Summary: Chapter 8
End-Of-Chapter Discussion
APPENDIX A
APPENDIX B
APPENDIX C
APPENDIX D
APPENDIX E
APPEBDIX F
INDEX
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
Table of Contents
-----------
Introduction
If you want to know whether you should buy this book, read this
introduction!
First question: Why learn C++ at all? That's easy: It’s today’s dominant
computer language. When the going gets tough, the professionals turn to
C++. There are many other languages, of course, but they lack the
universality and power of C++. For example, Visual Basic is useful for
quickly putting together applications that aren’t too large or demanding,
assembly language is good if you’re writing a device driver, and Java is
great for the World Wide Web. (Java is derived from C++ anyway.) But for
a major standalone application, C++ is the most popular language that has
the power and flexibility to produce the fastest, best-performing code. For
these reasons, C++ is a major part of the Computer Science curriculum at
almost every school and university. If you’re a programmer, chances are
that sooner or later you’ll need to know it.
Easy to Understand
This book starts off with very easy examples; we don’t assume you’re a
rocket scientist. We try to explain everything with no assumptions about
what you may already know. We use plenty of figures and analogies to
clarify the text. The program examples are heavily commented to make
everything as clear as possible.
As you progress further into the book, the examples become more
challenging, but we try to keep the increase in difficulty gradual, so you’re
always ready for what comes next. Ultimately we cover all the main
features of C++.
Unique Organization
Unlike most other C++ books, this one uses, from the very first example,
real object-oriented programs. Most books, in an attempt to stick with the
familiar, start with old-fashioned procedural examples and work up to full
object-based examples half-way through. This may seem easier, but in the
long run it’s counter-productive, because it encourages bad thinking habits.
The whole point of C++ is to write object-oriented programs. If they don’t
do that from the beginning, they’re missing the point. And, as it turns out,
object-oriented programs aren’t that hard anyway.
Go For It!
Learning C++ has never been easier. With its short lessons,
easy-to-understand writing style, and the support of Waite Group Press via
their Web site, you should be on your way to C++ mastery in less time than
you thought possible.
Installation
The companion CD-ROM contains all of the source code and the
executables from the lessons in the book, as well as the NetManage
Chameleon installer. Since all of the code for the lessons in this book are on
the companion CD-ROM, there is no need to type the code if you want to
use it for your own projects. We will illustrate how to copy the files from
the companion CD-ROM to your hard drive.
This section will walk you through the steps necessary to install the source
code from the CD-ROM to your hard drive for DOS/Windows 3.x and
Windows 95. To install the NetManage Chameleon, please see the section
The Chameleon Sampler.
Note: For the following examples, we are going to assume that the
CD-ROM drive you want to copy files from is the D: drive and the hard
drive you want to copy files to is the C: drive. If your system is set up
differently, please substitute the appropriate drive letters for your system.
DOS
These instructions for DOS assume you have no familiarity with DOS
commands. If you feel uncomfortable with DOS and are using these
instructions from the Windows DOS Prompt, please exit and follow the
instructions for your version of Windows.
1. Move to the drive that you want to copy the files to. If you want to
copy the files onto the C: drive, type
C:
and press ENTER. Ensure you are at the root directory by typing
CD \
MD CPPIC
CD CPPIC
XCOPY D:\*.* /V /S
MD CHAP_02
MD CHAP_05
MD CHAP_07
and press ENTER after each line. Then you would type
CD \CPPIC\CHAP_02
XCOPY D:\CHAP_02 /V /S
CD \CPPIC\CHAP_05
XCOPY D:\CHAP_05 /V /S
CD \CPPIC\CHAP_07
XCOPY D:\CHAP_07 /V /S
and press ENTER after each line. The /V is a DOS switch to verify
files while copying and /S is a DOS switch to copy the
subdirectories. Depending on the configuration and performance of
your system, these steps may take from a few moments to a few
minutes.
Windows 3.x
The following steps are for the use of Windows 3.x with short file names.
1. Open the File Manager.
2. In File Manager, locate the drive you want to copy to and click on
it.
3. If you have a directory to copy the files to, skip to Step 4.
Otherwise, create a new directory by selecting File, Create Directory.
Type
CPPIC
APP_C
CHAP_02
CHAP_03
CHAP_04
CHAP_05
CHAP_06
CHAP_07
CHAP_08
CHAP_09
CHAP_10
CHAP_11
CHAP_12
NTMANAGE
Control-click on the directories that you want to copy and drag the
selection to the destination drive. Depending on how fast your
computer is and also depending on the options set for your computer,
the copying process may take a few moments to a few minutes.
Windows 95
The easiest way to copy files using Windows 95 is by using the Desktop.
1. Double-click on the My Computer icon. Your drives will appear
in a window on the desktop.
2. Double-click on your hard drive and create a new folder, such as
C++ Interactive Course, by selecting File, New, Folder from the
window menu. A folder called New Folder will be created on your
hard drive with the name highlighted. Type in the name you want and
press the ENTER key.
3. Go back to your drive window and double-click on the icon that
represents your CD-ROM. You will see a window that has 11 chapter
folders, one appendix folder, and one Program folder.
4. Select the directories you want to copy (control-click on the
folders if you’re not copying all of them) and drag your selection to
the directory you created on your hard drive. You might need to
reposition your windows to make the window for your hard drive
visible. Depending on your system’s performance, this may take a
few moments to a few minutes
To copy the sampler software onto your hard disk, run the Setup program.
While under Windows, select File, Run in the Program Manager. In the
Run dialog box, type
d:\windows\browsers\ntmanage\disk_1\setup.exe
and then press the OK button.
The Setup program will ask you where to install the NetManage program.
The default suggested is fine for most people. If you want it installed
elsewhere, type in the drive and directory of your choosing and select
Continue.
After a few moments, the Setup program will ask you to type in the path of
the second batch of files. Select the 1 in DISK_1 and change it to 2, and
select Continue.
After another few moments, the Setup program will ask you to type in the
path of the third batch of files. Select the 2 in DISK_2 and change it to 3,
and select Continue.
Click OK when Setup tells you that installation is complete. You are now
ready to setup your Internet account!
Note: You will not actually be charged any provider fees until you
officially register with the service. You can cancel the registration
transaction at any time during the sign-on process. If you do decide to
register, your credit card number will be sent over a secure phone line.
As you work through the sign-up process, there may be other tabs asking
for additional information. If so, click these tabs and fill in the forms.
Select the Phone List button at the bottom of the screen. The Phone List
dialog appears, listing possible phone numbers you can use to register. If
one of the numbers is in your area code, select it. Otherwise, select the
toll-free 800 number.
Note: If necessary, you can edit the registration phone number. Some
systems, for example, require you to dial a 9 to reach an outside line. Just
type in this 9.
When you’ve typed in all your vital stats, return to the first registration tab.
Click Send to dial the toll-free number and begin the registration process.
The icons to the right will light up as each stage of the dialing process is
completed. The set of traffic lights tell you if each stage—initializing the
modem, dialing, connecting, and communicating—has worked.
Note: You may need to click the Advanced button to specify special
modem ports or commands.
Follow the instructions that appear as the registration proceeds. You will be
given the option to select from various service and pricing plans. Your
account information (username, e-mail address, password, dial-up number,
and IP address) will automatically be configured into the Chameleon
package. An interface will be created for the Custom program, which
quickly and flawlessly connects you to the Internet.
That’s it! You can now reboot your system to kick-start everything.
If you used the Chameleon package to sign up with your Internet provider,
an automatic configuration file should have already been written for you.
Otherwise, Chameleon comes with the configurations for most popular
Internet providers. Select File, Open and look for the configuration file for
your provider. If your provider is not listed, you’ll need to contact them and
ask what the proper settings are. They may even be able to send you a
prewritten Chameleon configuration file.
If you do need to enter the connection settings yourself, use the appropriate
values you have obtained from your Internet provider. You can verify or
edit the following information under the Setup menu:
• IP Address
• Subnet Mask
• Host Name
• Domain Name
• Port
• Modem
• Dial
• Login
• Interface Name
• BOOTP
You may also need to fill in the following under the Services menu:
• Default Gateway
• Domain Servers
Read Chapter 1 for more information about these terms.
Logging In
Once your configuration settings are in place, simply click the Connect
menu to dial up your Internet provider and get connected. If all goes well,
you should hear a small beep, and a program known as Newt will run. This
program lets Windows communicate with the Internet. You can then
minimize the Custom program and run the Internet application of your
choice.
Logging Out
When you’re done using the Internet, call up the Custom program and click
the Disconnect menu.
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
Table of Contents
-----------
Dedication
This book is dedicated to the memory of my father, who got me started with
superheterodyne circuits and the 6SJ7. And everything else, for that matter.
Acknowledgments
This book has taken so long to write that it's gone through a whole series of
Waite Group managing editors. My thanks to Andrea Rosenberg, John
Crudo, and–in the distant past—Scott Calamar.
Special thanks also to Mitch Waite, who—once again—is dragging me
kicking and screaming into today's technology.
Lyn Cordell, in her dual role as a content editor plus copy editor, is the best.
If she says change it, I change it; I know she's right. Mike Radtke, technical
editor, pulled the fat out of the fire on more occasions than I care to think
about. (Any remaining problems are, of course, entirely my fault.)
Table of Contents
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
CHAPTER 1
A FIRST LOOL AT OOP AND C++
Welcome to the exciting world of object-oriented programming! In this
first chapter, I’ll start by discussing why object-oriented programming
(OOP) was invented and why it offers advantages to the programmer. I’ll
also provide a quick overview of the main features of object-oriented
languages. You’ll learn about the two most fundamental aspects of OOP,
objects and classes. Then I’ll focus on a particular kind of object—hot dog
stand—and show how real hot dog stands on the street relate to hot dog
stand objects in a program. You’ll see how to use C++ to describe a class of
hot dog stand objects and how to make these objects carry out tasks.
This approach goes to the very heart of OOP. Most books begin by
skittering around the edges of C++, talking about old-fashioned procedural
details. This one attacks objects and classes head-on. If you think of OOP
as a fierce fire-breathing dragon, then you’re going to walk right up to it,
look it squarely in the eye, and tell it you want answers, now!
Procedural Languages
As programs grow ever larger and more complex, even the structured
programming approach begins to show signs of strain. You may have heard
about, or been involved in, horror stories of program development. The
project is too complex, the schedule slips, more programmers are added,
complexity increases, costs skyrocket, the schedule slips further, and
disaster ensues (see The Mythical Man-Month, by Frederick P. Brooks, Jr.,
Addison-Wesley, 1982, for a vivid description of this scenario).
Analyzing the reasons for these failures reveals weaknesses in the
procedural paradigm itself. No matter how well the structured programming
approach is implemented, large programs become excessively complex.
What are the reasons for this failure of procedural languages? One of the
most crucial is the role played by data.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Data Is Undervalued
What is needed is a way to restrict access to the data, to hide it from all but
a few critical functions. This will protect the data, simplify maintenance,
and offer other benefits, as you’ll see.
Procedural programs are often difficult to design. The problem is that their
chief components—functions and data structures—don’t model the real
world very well. For example, suppose you are writing code to create the
elements of a graphics user interface: menus, windows, and so on. Quick
now, what functions will you need? What data structures? The answers are
not obvious, to say the least. It would be better if windows and menus
corresponded more closely to actual program elements.
There are other problems with traditional languages. One is the difficulty of
creating new data types. Computer languages typically have several built-in
data types: integers, floating-point numbers, characters, and so on. What if
you want to invent your own data type? Perhaps you want to work with
complex numbers, or two-dimensional coordinates, or dates—quantities the
built-in data types don’t handle easily. Being able to create your own types
is called extensibility because you can extend the capabilities of the
language. Traditional languages are not usually extensible. Without
unnatural convolutions, you can’t bundle both x and y coordinates into a
single variable called Point and then add and subtract values of this type.
Traditional programs are more complex to write and maintain.
I should mention that what are called member functions in C++ are called
methods in some other object-oriented (OO) languages such as Smalltalk,
one of the first OO languages. Also, data items may be called instance
variables. Calling an object’s member function is often referred to as
sending a message to the object. These terms are often used by C++ writers.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The Corporate Analogy
Quiz 1
(Note: In this—and all quizzes in this book—a question may have more
than one correct answer.)
1. Pascal, BASIC, and C are __________ languages, whereas C++ is
___________.
a. object-oriented, traditional
b. interpreted, compiled
c. traditional, procedural
d. procedural, compiled
e. procedural, object-oriented
2. Which of the following are weaknesses of traditional languages
such as C?
a. Important data is vulnerable to accidental modification.
b. Such languages are hard to use for small programs.
c. Functions don’t correspond neatly to real-world entities.
d. It is difficult to extend such languages by adding new data
types.
e. The syntax is excessively complex.
3. In C++, you will typically access an object’s data using
a. member functions of other objects in that class.
b. member functions of any class.
c. any function with that object’s password.
d. member functions associated with that particular object.
e. any function outside of the object’s class.
4. The two major components of an object are __________ and
____________.
a. a class, its data
b. data, functions that may act on that data
c. messages, member functions
d. encapsulation, polymorphism
e. hidden data, ordinary data
5. Asking the sales manager of a company to get you data from the
sales department is like
a. calling the member function of an object to access the
object’s data.
b. creating a class of objects with data hidden inside.
c. programming a member function that can insert data in an
object.
d. creating an object with a member function that can access
its own data.
e. sending a message to a class.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Real-World Objects
Classes
In OOP, objects are instances of classes. What does this mean? Let’s look
at an analogy. Almost all computer languages have built-in data types. For
instance, a data type int, meaning integer, is predefined in C++. You can
declare as many variables of type int as you need in your program:
int day;
int count;
int divisor;
int answer;
In a similar way, you can define many objects of the same class, as shown
in Figure 1-5. A class serves as a plan, or a template. It specifies what data
and what functions will be included in objects of that class. Defining the
class doesn’t create any objects, just as the mere existence of a type int
doesn’t create any variables of type int.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Inheritance
The idea of classes leads to the idea of inheritance. In our daily lives, we
use the concept of classes being divided into subclasses. You know that the
class of animals is divided into mammals, amphibians, insects, birds, and so
on. The class of vehicles is divided into cars, trucks, buses, and
motorcycles.
The principle in this sort of division is that each subclass shares common
characteristics with the class from which it’s derived. Cars, trucks, buses,
and motorcycles all have wheels and a motor and are used to transport
people or goods; these are the defining characteristics of vehicles. In
addition to the characteristics shared with other members of the class, each
subclass also has its own particular characteristics: Buses, for instance, have
seats for many people, whereas trucks have space for hauling heavy loads.
This idea is shown in Figure 1-6. Notice in the figure that features A and B,
which are part of the base class, are common to all the derived classes, but
that each derived class also has features of its own.
In a similar way, an OOP class can be used as the basis for one or more
different subclasses. In C++, the original class is called the base class; other
classes can be defined that share its characteristics, but add their own as
well. These are called derived classes.
Inheritance is somewhat analogous to using functions to simplify a
traditional procedural program. If you find that three different sections of a
procedural program do almost exactly the same thing, you can recognize an
opportunity to extract the common elements of these three sections and put
them into a single function. The three sections of the program can call the
function to execute the common actions and they can perform their own
individual processing as well. Similarly, a base class contains elements
common to a group of derived classes. As functions do in a procedural
program, inheritance shortens an object-oriented program and clarifies the
relationship among program elements.
Reusability
Once a class has been written, created, and debugged, it can be distributed
to other programmers for use in their own programs. This is called
reusability. It is similar to the way a library of functions in a procedural
language can be incorporated into different programs.
However, in OOP, the concept of inheritance provides an important
extension to the idea of reusability. A programmer can take an existing
class and, without modifying it, add features and capabilities to it. This is
done by deriving a new class from the existing one. The new class will
inherit all the capabilities of the old one, but may also include new features
of its own.
For example, you might have written (or purchased from someone else) a
class that creates a menu system, such as that used in the Turbo C++
Integrated Development System (IDE). This class works fine and you don’t
want to change it, but you want to add the capability to make some menu
entries flash on and off. To do this, simply create a new class that inherits
all the capabilities of the existing one but adds flashing menu entries.
The ease with which existing software can be reused is a major
benefit—possibly the major benefit—of OOP. Many companies find that
reusing classes on a second project provides a major return on their original
investment. I’ll have more to say about this as I go along.
One of the benefits of objects is that they give the programmer a convenient
way to construct new data types. Suppose you work with two-dimensional
positions (such as x and y coordinates or latitude and longitude) in your
program. You would like to express operations on these positional values
with normal arithmetic operations, such as
Note that the = (equal) and + (plus) operators, used in the position
arithmetic shown above, don’t act the same way they do in operations on
built-in types such as int. The objects position1 and so on are not
predefined in C++, but are programmer-defined objects of class
Position. How do the = and + operators know how to operate on
objects? We must define new operations for these operators. These
operators will be member functions of the Position class.
Using operators or functions in different ways, depending on what they are
operating on, is called polymorphism (one thing with several distinct
forms). When an existing operator, such as + or =, is given the capability to
operate on additional data types, it is said to be overloaded. Functions are
overloaded when multiple functions have the same name but different
arguments. Overloading can make programs easier to write and to
understand. It’s a kind of polymorphism; it is also an important feature of
OOP.
C++ and C
In fact, the practical differences between C and C++ are larger than you
might think. You can write a program in C++ that looks like a program in
C, but doing so misses the whole point of object-oriented programming.
If you already know C, you will have a head start in learning C++ (although
you may also have some habits to unlearn), but much of the material will be
new.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
OOP in a Nutshell
In summary, OOP is a way of organizing programs. Object orientation has to do with how
programs are designed, not the details of individual program statements. In particular, OOP
programs are organized around objects, which contain both data and functions. These
functions, which act on the data, are called member functions. A class is the specification for
a number of similar objects.
C++ is a superset of C. It adds to the C language the capability to implement OOP. It also
adds a variety of other features. In addition, the emphasis is changed in C++ so that some
features common to C, although still available in C++, are seldom used, whereas others are
used far more frequently. The result is a surprisingly different language.
The general concepts discussed in this chapter will become more concrete as you learn more
about the details of C++. You may want to refer back to this chapter as you progress further
into this book.
Quiz
1. When you design a program for an object-oriented language, you look first for
_________ in real life that will correspond to __________ in the program.
a. organizations, data types
b. information, data
c. things, objects
d. actions, functions
e. categories, classes
2. A widget is to the blueprint for a widget as an object is to
a. a member function.
b. a class.
c. an operator.
d. a data item.
e. a program.
3. Inheritance allows you to start with ___________ and derive ___________ from it,
which will share common characteristics with it.
a. a class, objects
b. an object, classes
c. a member function, other member functions
d. an object, other objects
e. a class, other classes
4. Which of the following would probably make reasonable objects in an OOP
program?
a. The 4:30 flight to Dallas.
b. The alphabet.
c. A document.
d. Happiness.
e. Bill Jones.
5. Overloading an operator in C++ involves
a. giving an operator different meanings depending on the context.
b. writing new member functions.
c. allowing an operator to work on a different data type.
d. causing the operator to carry out several operations at once.
e. making the operator carry out an operation on user-defined objects.
Actually, an object in this program is not going to model an entire hot dog stand. It will model
only the data necessary to run the stand. These objects don’t cook the hot dogs or make
change. Their job is to keep track of an important aspect of the stand’s operation: how many
hot dogs and buns are on hand at the stand. It would be more accurate to say that an object
will model the hot dog stand’s inventory.
Let’s say an entrepreneur named Sally owns six hot dog stands, located in different parts of
town. She has hired someone to operate each stand while she remains at the central office
with her computer. Each stand has a telephone, so she can stay in touch with the operators.
The stands are numbered from 1 to 6. Sally has hired you to write a program that she can use
to keep track of the supplies on hand at each stand.
At the beginning of the day, each stand’s operator calls Sally and tells her the number of buns
and hot dogs on hand. Also, each time a hot dog is sold, the operator calls to inform her of
this fact. (This may not sound too realistic but remember, these are not high-volume
locations.) With this input and the output of the hot dog stand program, Sally can keep track
of how many buns and hot dogs remain in each stand. This information enables her to call the
supplier to order more hot dogs and buns at appropriate times.
What do you want the hot dog stand program to do? Let’s say three kinds of interaction are
desirable. You want Sally to be able to
1. enter the number of hot dogs and buns on hand at the beginning of the day
2. record each hot dog sale (which decreases the number of buns and hot dogs by 1)
3. ask the program how many hot dogs and buns remain at any given time
Figure 1-9 shows how Sally interacts with some of her hot dog stands.
Figure 1-9 Data flow between Sally and her hot dog stands
For the moment, don’t worry about the part of the program that asks you which of these three
options you want to choose. Instead, concentrate on designing the class of hot dog stands. I’ll
present the overall program in the next chapter.
Here’s how the first step, inputting the data, might look as Sally interacts with the program:
Recording a Sale
When an operator calls Sally and tells her a hot dog has been sold, she simply enters the stand
number:
Sally can ask the program what the situation is at any given hot dog stand.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Designing the Hot Dog Stand Program
You’ve seen the kind of desired interaction with the hot dog stand program. How do you go
about designing an object-oriented program that will allow this interaction?
The first question an OOP programmer asks is, what are the program’s objects going to be?
Sometimes the answer isn’t easy. However, whenever you find a number of similar items in
the real world, they are candidates to be made into objects in the program.
But there are several categories of similar objects in the hot dog stand situation. There are hot
dogs, there are buns, there are hot dog stands, there are operators. Which should be the
objects?
The hot dogs and the buns are probably too simple. They don’t do much except get sold. The
hot dog stands, on the other hand, are more interesting. They contain a variable number of hot
dogs, a variable number of buns, and you can imagine asking them how many buns and hot
dogs they have on hand and telling them you’ve made a sale. The operators aren’t really
connected with the inventory of buns and hot dogs; if Sally fires Joe and hires Alphonse, that
doesn’t affect the inventory. (But if you were writing a program that dealt with employees, the
operators would be an obvious candidate for objects.)
In more complex situations, you sometimes need to experiment a bit in choosing what entities
to make into objects. You may guess wrong the first time and find your initial program too
awkward. However, with experience you will become increasingly able to select appropriate
objects on the first try.
What kinds of things should go in each hot dog stand object? You can deduce that its data
should include the number of buns and the number of hot dogs on hand:
• Hot dogs on hand
• Buns on hand
These two kinds of data will be the same for each object. That is, every stand must store the
number of buns and the number of hot dogs. Of course the actual quantities of these items
will vary from stand to stand. Stand 1 has 101 hot dogs and stand 2 has 30, but they both must
have a variable that stores this quantity, whatever it is.
To interact with the stand, you need some member functions as well. Member functions will
interact with the data in specific ways. Looking at the kinds of interaction you want with the
program, you see that you need functions to
• Initialize the number of buns and hot dogs in each stand at the beginning of the day
• Tell the stand that a sale has been made
• Ask the stand for the number of buns and hot dogs remaining
The two data items and the three member functions will be the same for each object.
When many objects in a program are the same, it doesn’t make sense to describe each one
separately. It’s more efficient to develop a single specification for all such objects. You could
call this specification a plan or a blueprint. Once you’ve designed the specification, you can
use it to create however many objects you actually need.
In OOP, this specification for creating objects is called a class. Let’s see how such a class
specification would look in C++. Create a HotDogStand class that can be used to make
HotDogStand objects. Many aspects of this specification won’t be clear yet, but you should
see that it embodies two data items and three member functions. I’ll get to the details soon.
The class specification consists of the keyword class, the name of the class (here it’s
HotDogStand), an opening brace, a closing brace, and a semicolon:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Variable Declarations
Two variables, HotDogsOnHand and BunsOnHand, are declared and specified to be integers (type
int). This is one of several basic data types available in C++. I’ll discuss data types in detail in the next
session.
int HotDogsOnHand;
int BunsOnHand;
Notice that these declarations don’t give values (such as 47) to these variables, they merely give them
names and specify that they will require a certain space in memory (although memory space is not
actually set aside until an object is created).
Functions
There are three functions in the class specification: initData(), SoldOneDog(), and
displayData(). Tell the C++ compiler that these names apply to functions (and not variables or
something else) by appending parentheses to the name:
}
The keyword preceding the function name indicates the type of data returned by the function. None of
the hot dog stand functions returns a value, so the void keyword is used. Later you’ll see that functions
may return a value of any data type. If a function returns an int, for example, you can write
int someFunc()
{
}
You could also add parameters to the function by placing values or variable names within the
parentheses. Arguments convey values to the function, like this:
The idea behind public and private is to allow some parts of an object to be accessed by program
statements outside the object while other parts can be accessed only from within the object itself, as
shown in Figure 1-10.
The public and private keywords, followed by colons, are used to designate these parts. Here all
the data is private and all the member functions are public. This is the usual situation: You want to hide
the data from the outside world so it is protected from accidental alteration. However, you want the
member functions to be public so other parts of the program can call them to tell the object to do
something. To work with the data, don’t access it directly; instead, ask a member function to access it
for you.
Quiz 3
1. An object in our hot dog stand program will correspond to what entity in the real world?
a. An entire hot dog stand, including the operator.
b. All relevant data for all the hot dog stands.
c. The inventory data for a particular hot dog stand.
d. The physical hot dogs and buns at a particular stand.
e. A hot dog.
2. What items will be part of a HotDogStand object?
a. A function to initialize the number of hot dogs and buns to zero.
b. The number of buns.
c. The number of hot dogs.
d. A function to increment the number of buns and hot dogs.
e. A function to display the number of hot dogs and buns on hand.
3. Data is often private and member functions are often public because
a. data is not accessed as often as functions.
b. an object’s data should be hidden for safety but its member functions should be
accessible so the rest of the program can interact with the object.
c. data must be accessed only by objects of the same class, whereas member functions can
be accessed by any object.
d. data must not be changed, whereas member functions may be.
e. data takes up less memory space than member functions do.
4. An object’s member functions usually operate on
a. data within the functions themselves.
b. global data.
c. data in another specified object.
d. data in that object.
e. data in any object of the same class.
5. Which item is not always part of a class specification?
a. Braces.
b. A semicolon.
c. The keyword class.
d. A class name.
e. The definition of an object.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Characters
Let’s look at each of these data types in turn. Characters are stored in
variables of type char. The statement
char ch3;
creates space in memory for a character and names it ch3. To store a
particular character in this variable, use a statement like
ch3 = ‘a’;
Character constants, such as ‘a’, ‘B’, ‘&’ or ‘4’, are surrounded by
single quotes.
Assignment Operator
The equal sign (=) causes the value on its right to be assigned to (placed in)
the variable on its left; that is, following this statement, the variable ch3
will have the value ‘a’. The equal sign is called the assignment operator
because it assigns the value on the right to the variable on the left.
All characters are actually stored as numbers (which, as you know, is all a
computer understands). The ASCII code is used to translate characters into
numbers. Thus ‘A’ is 65, ‘B’ is 66, and so on. The ASCII code is shown
in Appendix E.
Escape Sequences
ch3 = 44;
However, the range of numerical values that can be stored in type char is
from -128 to 127, so this works only for very small numbers. Whole
numbers are usually stored in variables of type int, which is faster for the
computer to process than type char.
A variable of type char occupies 1 byte, or 8 bits, of memory.
Integers
Integers represent whole numbers, that is, values that can be counted, such
as the number of people living in Thomasville (12,348) or lines of text on a
page (33). Integers cannot represent numbers with decimal points or
fractional parts, such as 2.3 or 4/7. Integers can be negative: -7, -413.
There are three integer types in C++: short, int, and long. They are
similar but occupy different amounts of memory and can handle numbers in
different numerical ranges, as Table 1-3 shows. I also include type char in
this table, even though it is mostly used for characters, because it can be
used to store small whole numbers as well.
Table 1-3 Integer types in C++
Type
Size Range
Name
Type short always occupies 2 bytes. It can store numbers in the range of
-32,768 to 32,767. Type long always occupies 4 bytes and can store
numbers in the range of -2,147,483,648 to 2,147,483,647.
In 16-bit systems, type int occupies 2 bytes, the same as short. In 32-bit
systems, it occupies 4 bytes, the same as long, and can therefore handle a
larger range of values. Older operating systems, such as DOS and Windows
3.1, are 16-bit systems. Newer systems, such as Windows 95, OS/2, and
Windows NT, are 32-bit systems. Unix has always been a 32-bit system.
The int type is the most commonly used integer type and operates the
most efficiently whatever system you use. However, if you want to
guarantee a 2-byte variable even in a 32-bit system (to save space), you
must use short; if you want to guarantee a 4-byte variable on a 16-bit
system (to hold large values), you must use long.
Here’s an example of defining some integer variables and giving them
values:
Unsigned Integers
All the integer types have unsigned versions. Unsigned variables can’t hold
negative numbers, but their range of positive values is twice as large as that
of their signed brothers. Table 1-4 shows how this looks.
Table 1-4 Unsigned integers
Type Name Size Range
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Floating Point
Floating-point numbers are used to represent values that can be measured, such as the length of a room
(which might be 5.32 meters) or the weight of a rock (124.65 pounds). Floating-point values are normally
expressed with a whole number to the left of a decimal point and a fraction to the right.
Instead of a decimal point, you can use exponential notation for floating-point numbers. Thus, 124.65 in
normal notation is 1.2465e2 in exponential notation, where the number following the e indicates the number
of digits the decimal point must be moved to the right to restore the number to normal notation. Exponential
notation is commonly used to display numbers that are inconveniently long in decimal notation. Thus,
9,876,000,000,000,000,000 in normal notation is 9.876e18 in exponential notation.
There are three kinds of floating-point numbers in popular operating systems, as shown in Table 1-5. (Some
systems don’t have long double.)
Table 1-5 Floating-point numbers
Type Name Size Range Precision
The most common floating-point type is probably type double, which is used for most C++ mathematical
library functions. Type float requires less memory than double and may speed up your calculations.
Type long double is used in the floating-point processor in Intel microprocessors and is useful for very
large numbers.
Here’s an example of defining and using variables of the various floating-point types:
float pi_float;
double pi_double;
long double pi_long_double;
pi_float = 3.1415;
pi_double = 3.14159265358979;
pi_long_double = 3.141592653589793238;
Here I’ve assigned constants representing the mathematical constant pi to variables of the three types, using
as many digits of precision as each type will allow.
Figure 1-11 shows the amounts of memory required for all the data types except long double. You can
initialize variables to specific values when they are first declared. Thus the six statements above could be
condensed into
Whitespace
C++ doesn’t mind if you put extra spaces into a program line. You can use them to align things so they’re
easier to read. You could say
Comments
You can add comments to your program listing to help yourself—and anyone else who might need to look at
your listing—understand what it does. Comments are preceded with a double slash: //.
Here’s a code fragment, taken from the previous section, that uses a full-line comment and comments
following each of three statements:
/*
if you have a really long multiline
comment it is easier to
use this arrangement than to write
the double-slash symbol before
every line
*/
The /* symbol starts the comment and the */ ends it. The end of a line does not automatically end a
comment that starts with /* (as it does with those starting with //), so you can write as many comment lines
as you want before terminating with the */ symbol. This comment style is harder to type and requires two
symbols per comment instead of one, so it is not usually used for single-line comments.
As you know, adding numerous comments to your code makes it far more comprehensible to anyone else
who must understand your program. And, difficult as it may be to believe, you yourself may find comments
helpful if you try to read a listing you haven’t seen for some time.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 4
Exercise 1
Exercise 2
Write statements to set the three variables in Exercise 1 to ‘V’, 67.95, and
1,000,000, respectively.
Midchapter Discussion
Imagine that this book is being used in a programming class and that the
students get together from time to time to discuss how the course is going.
The following is an example of one such discussion. I’ll include two
discussions per chapter: one halfway through the chapter and one at the
end.
George: Oh, boy, I’m in trouble. The only thing I understood at all was
about data types, because they’re the same as C.
Estelle: You know C? Then this course should be a pushover for you.
George: Are you kidding? Everything but the data types was over my
head. Objects? Classes? I don’t have a clue.
Estelle: I know what you mean; it’s not totally clear to me either. I
think I have to take it on faith that all this will lead to
something useful.
Don: But you get the main idea, don’t you? About needing a better
way to design programs because the old procedural approach
just got too out of control?
Estelle: Well, I guess I understand it in theory. But I’ve never written
such a huge program. Just what I took in my Pascal class.
Don: I was involved in a big project in C at work, and it certainly got
complicated. If there’s some way to make working with huge
programs easier, I’m all for it.
George: Yeah, but how does this object stuff lead to anything practical?
I’m not learning how to write program statements that actually
do things, like adding two numbers or whatever.
Estelle: But C++ isn’t a procedural language. It’s based on objects, so
I’ve got to learn to make objects before I can do anything
useful.
Don: Right. In a procedural language, the programmer gives
instructions to the computer to do things but in an OOP
language, the programmer tells objects to do things. And you
can’t tell an object something if you don’t have any objects.
Estelle: And to create objects you need a class to tell you what the
objects will look like.
Don: But to specify the class you need to know how to declare data
variables, which I just learned about, and write functions,
which I bet I get to this afternoon.
George: Well, I want to display some numbers! Do some arithmetic! I
want a complete program!
Estelle: Patience, George, patience.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
String Constants
The text “This text will appear on the screen.” is called a string constant. A
string constant is surrounded by double quotes (unlike a character constant, which is surrounded
by single quotes). The entire statement, as with all C++ statements, is terminated with a
semicolon.
You can output numerical constants the same way. The statement
Formatting Output
It’s nice to have some control over the way output is formatted. In C++ stream I/O, a variety of
techniques are available for this purpose. I’ll show two examples here.
Some languages, such as BASIC, automatically move to a new line at the end of every output
statement, but C++ does not. Nor are different variables in the same statement placed on separate
lines. New lines are not created automatically by the cout object or the put to operator. For
example, the statements
6.2530.92.5
where all the output is run together. This is probably not what you want.
Escape Sequences
One of the easiest ways to format data is to insert a character called an escape sequence into a
string constant. For example, you can use the ‘\n’ escape sequence to start a new line:
cout << “\nNow I'll ask you some questions about yourself.”;
cout << “\nFirst, enter your age: ”;
produces the display
There’s another approach to starting new lines in C++. An object called a manipulator can be
inserted into an output stream, just like a data item. Manipulators can be used to format output,
among other purposes. Probably the most common manipulator is endl (a contraction of “end
line”). Inserted into a cout statement, endl causes the cursor to move to the start of the next
line, just as '\n’ does. (Also, endl flushes any output that may be waiting in the output buffer
to the display.)
Earlier, I showed two lines that used the ‘\n’ escape sequence to start text on a new line. Here’s
how to achieve the same result using endl:
Here’s how to input a number, entered by the user from the keyboard, and store it in a variable
intvar:
int intvar;
cin >> intvar;
The cin object (for “C in”) represents the keyboard, and the get from operator (>>) takes
whatever is on the left and puts it in the variable on the right. When this statement is executed, the
program waits for the user to type a number and press . Figure 1-13 shows how this
looks.
Usually, of course, you want to prompt the user before waiting for input:
int age;
cout << “Enter your age: ”;
cin >> age;
This produces the following interaction:
int age;
float height;
cout << “Enter your age and height:”;
cin >> age >> height;
Here the user presses , , or after each value before entering the next
one. However, it’s usually better to prompt the user for only one value at a time, to avoid any
possibility of confusion.
Stream I/O
The techniques for input and output I’ve shown here are called stream I/O. A stream is a general
term for a flow of data. As you’ll see when you write a complete program, to use stream I/O, you
need to include a file of declarations in your program. This file, IOSTREAM.H, is called a header or
include file.
There is much more to be said about stream I/O in C++. I’ll return to this topic in Chapter 10.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Old-Style C I/O
If you are a C programmer, you are familiar with a completely different style of input and output
using printf(), scanf(), and similar library functions (declared in STDIO.H). You can use
these functions in C++ as well, but the preferred approach is to use stream I/O. Why? First, it’s
easier to avoid mistakes. Have you ever used the wrong format specifier in printf() (e.g., %d
instead of %f), so that data is displayed incorrectly? It’s easy to do, and in scanf(), the wrong
specifier can crash your program as well. Second, the stream I/O approach lets you use cout and
cin with classes you write yourself. This allows you to perform I/O on any programming object
the same way it’s performed on basic data types. This is a very powerful technique, which is
unavailable with standard C I/O. Some old-time C programmers can’t get the hang of stream I/O
and persist in using standard I/O, but they are missing out on some powerful and important
features of C++.
Quiz 5
1 2
3 4
a. cout << “1\t2\n3\t4\n”;
b. cout <<‘1’<<‘\t’<<‘2’<<‘\n’<<‘3’<<‘\t’<<‘4’<<‘\n’;
c. cout << “1\n2\t3\n4\t”;
d. cout <<1<<‘\t’<<2<<‘\n’<<3<<‘\t’<<4<<‘\n’;
e. cout << ‘1’ << ‘\n’ << ‘2’ ‘\t’; cout << ‘3’ ‘\n’ ‘4’
‘\t’;
5. In a comparison of C++ I/O (stream I/O) and old-fashioned C I/O (standard I/O), which
of the following is true?
a. Stream I/O is faster.
b. You can modify almost any object to use stream I/O but not standard I/O.
c. All objects know how to display themselves automatically in stream I/O.
d. Standard I/O is more intuitive.
e. Standard I/O allows you to avoid format specifiers.
Exercise 1
Thinking about the hot dog program, write a program fragment that displays the number of hot
dogs and buns on hand. The output might look like this:
Exercise 2
Write a program fragment that asks the user for the initial values of hot dogs and buns on hand,
gets responses from the user, and then sets appropriately named variables to these values.
Exercise 3
At the start of each day, you want to initialize the number of hot dogs and buns at a stand. Use the
initData() function for this. This requires both cout (for the prompts) and cin statements.
Here’s how it looks:
void initData()
{
cout << “Enter dogs on hand:”;
cin >> HotDogsOnHand;
cout << “Enter buns on hand:”;
cin >> BunsOnHand;
}
An example of interaction with this function would be
Recording a Sale
When a hot dog is sold, the stand operator calls Sally to tell her this fact. When she receives such a
call, she wants the program to decrease both the number of hot dogs and the number of buns by
one. Here’s a member function that does the job:
void SoldOneDog()
{
HotDogsOnHand = HotDogsOnHand - 1; // subtract 1 from variable
BunsOnHand = BunsOnHand - 1; // subtract 1 from variable
}
Here there’s no interaction with the user, only a little arithmetic.
Figure 1-14 shows how Sally interacts with an object of class HotDogStand, using its member
functions.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Displaying Data
Remember that a HotDogStand object records the numbers of hot dogs and
buns on hand, in the variables HotDogsOnHand and BunsOnHand. You can
use cout statements to display these values. The resulting displayData()
function looks like this:
void displayData()
{
cout << “Hot dogs on hand = ”
<< HotDogsOnHand << endl;
cout << “Buns on hand = ”
<< BunsOnHand << endl;
}
The output from this function will be something like this:
Arithmetic Operators
+ Addition
- Subtraction
* Multiplication
/ Division
% Remainder
The first four operators perform familiar operations. The remainder operator, %
(also called the modulus operator), is used to calculate the remainder when one
integer is divided by another. Thus the expression
20 % 3
evaluates to 2, because 20 divided by 3 is 6, with a remainder of 2.
You can use any of the arithmetic operators just as you would in normal
arithmetic or algebra or most other programming languages. They are called
binary operators because they operate on two quantities. That is, expressions
involving them have the form alpha+beta, where the + operates on alpha
and beta.
Of course, you can make arithmetic expressions as complicated as you want. For
example,
c = (f-32) * 5 / 9;
converts a temperature in Celsius to one in Fahrenheit. Note that I use
parentheses so the subtraction will be carried out first, despite its lower
precedence. (The term precedence refers to the order in which operations are
carried out. The * and / operators have higher precedence than the + and -
operators.)
In C++, it’s perfectly all right to mix different arithmetic types in the same
expression. For example, in the above statements, f might be type int and c
might be type float. The compiler would not complain. Instead, it would
automatically convert the types appropriately before carrying out the arithmetic.
void SoldOneDog()
{
--HotDogsOnHand; // subtract 1 from HotDogsOnHand
--BunsOnHand; // subtract 1 from BunsOnHand
}
The decrement operator consists of two minus signs together: —. If
HotDogsOnHand were 30 and BunsOnHand were 52, then after executing
this function, these two variables would be 29 and 51.
Similarly, the increment operator, ++, increases the variable it’s applied to by 1.
The increment and decrement operators are called unary operators because they
operate on only one variable. Their priority is higher than that of arithmetic
operators, so in the expression ++x + 3, the value of x will be incremented
before the addition is carried out.
Now that I’ve defined some member functions, you may be wondering how I get
them to do something; that is, how I call them or cause them to be executed. Be
patient. You’ll learn all about that in the next chapter.
Quiz 6
Exercise 1
Start with the code fragment of Exercise 3 in Session 5, which sets values for the
numerator and denominator of a fraction and displays them. Create two
functions: one, setFrac(), to get values for num and denom from the user;
the other, showFrac(), to display the fraction in the form 7/12.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Exercise 2
Imagine a class called Employee with two data items, employee_number and
employee_salary, which record an employee’s ID number and salary. Write statements to
create these variables, the first type int and the second type double. Write a function,
get_emp(), to get values for these variables from the user and another function, put_emp(),
to display the values.
class HotDogStand
{
private:
int HotDogsOnHand; // dogs remaining
int BunsOnHand; // buns remaining
public:
void displayData() // display data
{
cout << “Hot dogs on hand = ”
<< HotDogsOnHand << endl;
cout << “Buns on hand = ”
<< BunsOnHand << endl;
}
void initData() // get initial data from user
{
cout << “Enter dogs on hand: ”;
cin >> HotDogsOnHand;
cout << “Enter buns on hand: ”;
cin >> BunsOnHand;
}
void SoldOneDog() // adjust data to reflect sale
{
--HotDogsOnHand;
--BunsOnHand;
}
}; // end of class HotDogStand
All I’ve done is to fill in the function bodies to complete the specification. However, it’s easier
now to see the relation between the class data and its member functions. The member functions
all operate on the data, but in different ways. The displayData() function sends the values
of the data (HotDogsOnHand and BunsOnHand) to cout so they will be displayed. The
initData() function asks the user for values and inserts these values in to the data variables.
The SoldOneDog() function decrements both data items by 1 to reflect the sale of a hot dog
(with bun).
I’ll devote the remainder of this lesson to the exercises, which will do more than anything to
solidify your understanding of class specifications.
Quiz 7
Exercise 1
Rewrite the class specification for the HotDogStand class to include a data item for the cash
on hand (you can call it CashOnHand), stored in dollars and cents format (e.g., 198.95).
Assume the retail price of a hot dog (including the bun) is $1.95. Modify the member functions
as follows:
• Add statements to initData() so the user can specify the initial amount of cash on
hand at the beginning of the day.
• Add statements to SoldOneDog() that subtract the price of a hot dog from
CashOnHand when a sale is made.
• Add statements to displayData() that display the cash on hand along with the
number of dogs and buns.
Exercise 2
Write a complete class specification, including member functions, for an elevator class. Each
object of this class should include a data item that records the floor the elevator is currently on
(e.g., from 1 at the bottom to 20 at the top). Write member functions that take the following
actions:
• Display the floor the elevator is on.
• Move the elevator up one floor.
• Move the elevator down one floor.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Creating Objects from a Class Specification
As it turns out, you use the same syntax to create an object that you use to
create a variable of a basic type such as int or float. This is no accident.
In C++, objects are treated very much like variables and classes are treated
very much like data types.
Here’s how to create an object called stand1 of the class
HotDogStand:
HotDogStand stand1;
What happens when this statement is executed? First, your program finds
the specification for the HotDogStand class (which must have appeared
earlier in your listing). It figures out how large such an object needs to be
and sets aside enough memory to hold it. It then gives this memory space a
name: stand1. This is exactly what it would do with a variable of a basic
type. However, stand1 is more complicated because it has several pieces
of data and several member functions as well.
You can create as many objects as you need.
HotDogStand stand1;
HotDogStand stand2;
HotDogStand stand3;
Or (as with basic data types) you can also use a single statement to create
multiple objects of the same type.
Once an object has been created, you need to interact with it. To do this, use
its member functions, that is, the functions described in the class
specification. You call an object’s member function. This is the same as
saying you send a message to the object.
A special syntax is used for this. When you send a message to an object,
there are two things to consider. First, which object are you communicating
with? In the case of the hot dog stands, is it stand1, stand2, or another
stand? Second, what message are you sending to the object? This is the
same as asking which of the object’s member functions you are calling.
Thus, you need a syntax with two parts: the name of the object and the
name of the member function.
Here’s how you would send a message to stand1 asking it to display its
data with displayData():
stand1.displayData();
The object name and the function name are connected by the period symbol
(.), which is called the dot operator or, more formally, the class member
access operator. This rather ponderous name means that the operator
allows you to access an object’s member functions or data, but let’s stick
with the handier dot operator. Figure 1-15 shows how this looks.
stand2.displayData();
It will then display its data (which is probably not the same as that in
stand1).
stand3.initData();
When this statement is executed, stand3 will call its initData()
function and the following interaction with the user might take place:
stand3.SoldOneDog();
This would cause the HotDogsOnHand and the BunsOnHand variables
in stand3 to be decremented, although nothing would appear on the
screen. If you then executed
stand3.displayData();
you would see
Quiz 8
Exercise 1
Exercise 2
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Summary: Chapter 1
Let’s review the main points in this chapter. You’ve seen that the
fundamental building blocks of C++ programs are objects, which are
created using a class as a blueprint. Designing a program means figuring
out what the objects will represent.
The two important constituents of objects are instance data and member
functions. An object’s data is usually made private, or inaccessible to all
parts of the program except the object’s own member functions. This makes
it less likely the data will be corrupted by a programming error.
Once a class is specified and objects of that class are created, a program
“sends messages” to the objects—that is, it calls their member
functions—to cause them to carry out appropriate tasks, usually involving
the object’s own data.
In Chapter 2, you’ll put together everything you’ve learned in this chapter,
plus a few new details, to create a complete, working C++ program.
End-Of-Chapter Discussion
Don: I think I’m starting to get it. Everything in the program is an
object. A class specification describes how a bunch of similar
objects will look. So the trick in designing a program is to
figure out what the objects are going to be.
Estelle: That seems hard now, but I bet it’ll get easier.
George: I think it’s nuts. If I want to print a line of text, I’ve got to
figure out what objects to use, write a specification for a class,
define objects, and send one of them a message to print the
text. You’ve got to be kidding! I can print a text with one
statement in regular C code!
Don: So if all your program does is print one line, write it in C. The
point is, most programs do more than that.
George: So be sarcastic. I’m just afraid all this OOP stuff will be so
complicated it won’t be worth the trouble, even for major
programs.
Don: Lots of people seem to think it works fine for major programs.
Estelle: I’ve got a different question. I can’t understand how anyone
ever figured out that it would be a good idea to put data and
functions together to make objects.
Don: I know, it’s not exactly an obvious concept. It must have been a
major inspiration.
George: More likely a lucky guess. If it works at all, which I doubt.
Estelle: It’ll work. In the next chapter, we’ll see a complete program.
George: It’s about time!
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
CHAPTER 2
WRITING COMPLETE OOP PROGRAMS
So far, you’ve seen bits and pieces of a C++ program. In the beginning of this chapter, I’m
going to put these pieces together and demonstrate a complete, working program. Then, in the
next part of the chapter, I’ll improve this program by using loops and decisions, the
fundamental C++ control statements that allow your program to do something more than once
and to do different things in different circumstances.
In the second half of this chapter, I’ll introduce another complete C++ program—one that
models time values. This is a completely new use for classes: modeling new data types rather
than physical objects such as hot dog stands.
I’ll show other improvements you can make to this program by introducing function arguments
and function return values. Along the way, I’ll touch on such topics as software reusability,
output formatting, and creating temporary objects.
// hotdog1.cpp
// hot-dog stand inventory database program
/////////////////////////////////////////////////////////////
void main()
{
HotDogStand stand1; // create hot-dog stand objects
HotDogStand stand2;
// set initial data
cout << “\nInitialize data for stand 1”;
stand1.initData();
cout << “\nInitialize data for stand 2”;
stand2.initData();
// record some sales
cout << “\nSelling 2 dogs from stand1”;
stand1.SoldOneDog();
stand1.SoldOneDog();
cout << “\nSelling 3 dogs from stand2”;
stand2.SoldOneDog();
stand2.SoldOneDog();
stand2.SoldOneDog();
cout << endl;
// display current data
cout << “\nSupplies on hand, stand1”;
stand1.displayData();
cout << “\nSupplies on hand, stand2”;
stand2.displayData();
}
You’ve seen the specification for the HotDogStand class before. You’ve also seen the
statements that create hot dog stand objects and that access their member functions. Let’s look
at two unfamiliar parts of the program.
To use I/O streams, you need to place in the program various class specifications from which
the cin and cout objects, the << and >> operators, the endl manipulator, and so on are
derived. These specifications, along with various other definitions, are stored in a file called
IOSTREAM.H, which comes with your compiler. It’s a text file, just like the .CPP source files
you write yourself.
To insert the IOSTREAM.H file into your source file, place a line of text called a preprocessor
directive into your code. It looks like this:
#include <iostream.h>
This directive inserts all the text from the file IOSTREAM.H into your source file (or at least the
compiler treats it as if it had been inserted; actually your source file isn’t altered).
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Preprocessor Directives
Preprocessor directives are instructions to the compiler. By contrast, ordinary C++ statements, such
as alpha=17; are instructions to the microprocessor, which the compiler translates into machine
language that the microprocessor can understand. Preprocessor directives always start with a pound
sign (#).
When the compiler encounters the #include preprocessor directive shown above, it starts to search
for the file IOSTREAM.H. There is a particular subdirectory where such files are stored, usually called
…\INCLUDE\ (where the dots represent the first part of the path name, such as C:\BC5). This
subdirectory is usually found in the general directory for the compiler you’re using. The compiler
should know where such a directory is located; if not, it may need to be told (see the appendix in this
book that applies to your particular compiler). Once it finds the file, the compiler (actually a part of
the compiler called the preprocessor) simply inserts its text into the source file in place of the
#include directive.
If you want to see what’s in IOSTREAM.H, you can go to the …\INCLUDE\ directory and examine it
with the compiler’s editor or any other text editor. (Be careful not to change the file.) The contents
won’t make much sense at this point, but you will at least prove to yourself that IOSTREAM.H is a text
file, written in normal ASCII characters.
There are other preprocessor directives besides #include; you’ll encounter a few more as you go
along.
I should note that there are two formats for specifying the file in an #include directive. Using the
appropriate format speeds up the compiler’s search for the file. In the example above, angle brackets
are used to delimit the file name.
#include <filename.ext>
This causes the compiler’s search for the file to start in the standard …\INCLUDE\ directory. If quotes
are used instead
#include “filename.ext”
then the search for the file will begin in the directory where the program’s source files are stored.
This is the format you would use if you had written the header file yourself, a situation you’ll
encounter in Chapter 11.
There are many header files. Some are used with the C function library. For example, if you need to
use mathematical functions such as sin() and cos(), you need to include a header file called
MATH.H. If you’re going to operate on strings, you’ll need STRING.H; if you’re going to perform
certain kinds of data conversion, you may need to include STDLIB.H. Various specialized I/O
functions may require CONIO.H or STDIO.H.
Other header files are needed for other kinds of class specifications. For example, your compiler may
be bundled with a container class library. Container is a general name for a data structure, such as an
array, a stack, a queue, or a linked list. The container class library contains classes that model these
data structures, but if you want to use one, you’ll need to include a header file specific to the
particular container, such as ARRAYS.H, STACKS.H, or QUEUES.H.
Don’t worry if there seems to be an endless number of header files. All you really need to know
about header files at this point is that they are an important part of C++ programs and you’ll need to
include IOSTREAM.H to run any program that does stream I/O.
Everything must have a beginning, and your program begins executing in a function called main().
This is not a member function of a class; it’s a special standalone function to which control is
transferred from the operating system. The first statement in main(), whatever it may be, is the first
statement in your program to be executed.
Here’s how main() looks when it has no statements installed in it:
void main()
{
}
Every program must contain a main() function. If it doesn’t, you’ll get error messages from the
linker when you try to compile and link your program.
Above I show main() with a return type of void. You can also use a return type of int. When
you do, the value returned is a code, which is usually used to indicate whether the program ran
successfully. (This is useful for batch files.) The example programs in this book don’t return a code,
so the return type of main() will always be void. You don’t need to worry about return types now
anyway.
The main() function can also take arguments, which are used when a program is called from the
command line and has extra text, such as a file name, typed after the program name. However, I’ll
ignore this possibility as well.
Here’s some typical interaction with this version of the hot dog stand program:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Program Organization
Quiz 1
1. Preprocessor directives
a. tell the compiler to do something before translating the
source file into machine code.
b. give the compiler instructions that will be translated into
machine code.
c. are executed just before the program executes.
d. are executed just before the program is compiled.
e. are executed just before the program is linked.
2. Which of the following will most efficiently include the file
CONIO.H, located in the compiler system’s …\INCLUDE\ directory,
into your source file?
a. #include “conio.h”
b. #include “..\include\conio.h”
c. #include <conio.h>
d. #include <..\include\conio.h>;
e. #include “conio”;
3. The main() function
a. must contain any class specifications.
b. is a member function of all classes.
c. may return void or int.
d. is the first function to be executed when a program is
executed.
e. may contain statements that create objects.
4. In HOTDOG1, the user can’t interactively signal the program every
time a hot dog is sold because
a. such interaction is impossible in an object-oriented
language.
b. there is no member function to handle this activity.
c. you haven’t learned yet how to write loops and decisions in
C++.
d. the main() function does not include code to handle this
activity.
e. there are no I/O statements in the program.
5. The general scheme of program organization you’ve seen so far is
that
a. a class is specified outside of main() and objects of that
class are created in main().
b. objects are defined outside of main() and told what to do
within main().
c. all parts of the program are within main().
d. objects are told what to do inside of main().
e. classes tell objects what to do and when to do it.
Exercise 1
Exercise 2
Start with Exercise 2 in Chapter 1, Session 8. This augmented hot dog stand
class (hotdogstand2) included a cash-on-hand data item in addition to
the number of hot dogs and buns. Create a complete program, with
functionality similar to HOTDOG1, that uses the capabilities of this class.
Create two hot dog stand objects from the hotdogstand2 class.
Interaction with the program should look something like this:
Session 2: Loops
You can’t write interesting programs without loops. A loopless program
does something once, then exits. A program with a loop, on the other hand,
can do something as many times as necessary. Now I’m going to introduce
the three kinds of loops available in C++. If you already know C, you can
probably skip this lesson, because loops are the same in C and in C++.
To determine how many times to cycle around a loop, all C++ loops check
whether an expression is true or false. This tells them whether to cycle one
more time or to exit the loop immediately. Thus, to understand loops you
must first examine what makes an expression true or false,and how to
construct such true/false expressions. Then you can examine specific kinds
of loops: the while loop, the do loop, and the for loop.
Loops (and decisions, which I’ll discuss next) make decisions based on
values that can be either true or false. In C++, a value of 0 (zero) is false
and any other value is true. Thus, the constant 0 is false by definition, but
the constant 1 is true, as are -1, 275, and any other nonzero numbers. Some
languages have a special Boolean data type to hold true/false values, but in
C++ these values are simply stored in any of the integer data types (char,
int, short, long, and their unsigned counterparts).
Sometimes the value of a single variable is used by a loop to decide
whether to cycle again or exit. For example, a loop might check whether the
variable avar is true or false (nonzero or zero), and quit when it becomes
false. More often, however, loops check whether a relationship between
two variables, or between a variable and a constant, is true or false. That is,
a loop might want to continue cycling only if j is greater than 0; another
loop might want to continue if ch is not equal to ‘x’. Being equal to,
greater than, and so on are calculated with relational operators in C++, so
let’s examine these operators before examining examples of loops.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Relational Operators
Relational operators compare two values and evaluate to a true/false value, depending on
whether the comparison is true or not. There are six such operators, as shown in Table 2-1.
Table 2-1 Relational operators
Symbol Meaning Example
== equal to a == b
!= not equal to a != b
< less than a<b
> greater than a>b
<= less than or equal to a <= b
>= greater than or equal to a >= b
The expressions in the Example column will be either true or false depending on the values
of a and b. For example, suppose a is 9 and b is 10. Then a==b is not true, because 9
does not equal 10, but a!=b is true, as are a<b and a<=b. The expressions a>b and
a>=b are false.
You can compare characters as well as numbers, because characters have underlying
(ASCII) numerical values. Thus, it’s true that ‘a’<‘b’ and that ‘A’==65, but not true
that ‘z’<=‘a’ (because ‘z’ in fact has a higher ASCII value than ‘a’).
Now that you understand relational operators, let’s look at the three kinds of C++ loops
and see how they decide what to do based on expressions that use these operators.
while Loops
A while loop lets you do something over and over until a condition changes. The
condition is something that can be expressed by a true/false value. For example, a while
loop might repeatedly ask the user to enter a character. It would then continue to cycle
until the user enters the character ‘q’ (for “quit”).
Here’s an example of a while loop that behaves just this way:
while(ch != 'q')
{
cout << “Enter a character: ”;
cin >> ch;
}
If the user does not press ‘q’, the loop continues. Some sample interaction might look
like this:
Enter a character: c
Enter a character: a
Enter a character: t
Enter a character: s
Enter a character: q
A while loop consists of the keyword while followed by a test expression (also called a
conditional expression or condition) enclosed in parentheses. The body of the loop is
delimited by braces (but no semicolon), just like a function. Figure 2-2 shows how this
looks.
Note: Note that the test expression is checked before the body of the loop is executed. If
the condition is false when the loop is entered, then the body of the loop will never be
executed. This is appropriate in some situations, but it means you must be careful that a
variable in the test expression has an appropriate value before you enter the loop. The ch
in the first example must not have a value of ‘q’ when you enter the loop, or the loop
body will never be executed. The n in the second loop must be initialized to a value less
than 100.
do Loops
The do loop (often called the do while loop) operates like the while loop except that
the test expression is checked after the body of the loop is executed. This is nice when you
always want something (whatever is in the body of the loop) done at least once, no matter
what the initial true/false state of the condition is. Figure 2-4 shows how this looks.
do
{
cout << “\nEnter two numbers (to quit, set first to 0): ”
cin >> x >> y;
cout << “The sum is ” << x + y;
} while(x != 0);
A do loop begins with the keyword do, followed by the body of the loop in braces, then
the keyword while, a test expression in parentheses, and finally a semicolon. This
arrangement is shown in Figure 2-5. Note that the do loop is the only loop that is
terminated with a semicolon. The semicolon is necessary because the test expression
follows the loop body, so the closing brace of the loop body can’t act as a delimiter for the
entire loop.
The do loop has a slightly dubious reputation among C++ programmers because its syntax
is not quite so clean and easy to read as that of the while loop. The consensus is to use a
while loop unless there’s a really good reason to use a do loop.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
for Loops
In both the while and do loops, you usually don’t know, at the time you enter the loop, how
many times the loop will be executed. The condition that terminates the loop arises
spontaneously inside the loop: The user answers ‘n’ instead of ‘y’, for example. This is not
the case with for loops.
In a for loop, the number of times the loop will be executed is (usually) stated at the beginning
of the loop. Here’s a loop that prints 20 asterisks in a line across the page:
0 1 3 6 10 15 21 28 36 45
Notice that there is no rule that says that the loop variable must be increased by 1 each time
through the loop. You can also decrease it by 1:
10 9 8 7 6 5 4 3 2 1
or you can increase it or decrease it by any other amount. This code
0 10 20 30 40 50 60 70 80 90
There is a surprising amount of flexibility in what you can put in the three expressions in a for
loop. For example, you can use multiple statements in the initialization expression and the test
expression. Here’s an example of a for loop with such multiple statements:
0 1 3 6 10 15 21 28 36 45
as in the earlier example. However, here the variable total is set to 0 in the initialization
expression instead of before the loop, and increased by j in the increment expression instead of
in the loop body. The individual statements in these expressions are separated by commas.
Another option is to leave out any or all of the three for loop expressions entirely, retaining
only the semicolons.
Generally, taking advantage of the flexibility of the for loop in these ways causes more
confusion than it’s worth, but big-time C gurus enjoy it.
Nested Loops
You can nest one loop inside another. For example, the following program fragment prints a 10
by 10 square of Xs, like this:
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
in the upper-left corner of the screen.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Logical Operators
An AND expression is true only if the expressions on both sides of the &&
operator are true. Thus, the example in the table is true only if x is greater
than 0 and also less than 10. The AND operator is often used to determine
whether something is in a specified range. In the example in the table, the
expression is true if x is in the range of 1 to 9. Similarly, the expression
j==0 || ch == 'q'
is true if either j is 0 or ch equals ‘q’, or if both are true.
The OR operator is often used to discover if a value is outside a specified
range. The expression
temp<65 || temp>75
is true if temp is outside the 65 to 75 range (the “comfort zone” for
Fahrenheit temperatures).
The ! (NOT) operator negates the variable following it. Thus, !alpha is
true if alpha is false, and false if alpha is true. (As you can see, this
operator has an Alice in Wonderland quality.) It’s often used when you
want a while loop to continue until something is true, instead of
continuing until it’s false. For example,
while( !alpha )
{
}
will cycle until alpha becomes true (nonzero).
Logical expressions can often be written in several ways. The expression
temp<65 || temp>75
could also be written
Precedence
2*2+3*3
the 2s are multiplied (which gives 4), then the 3s are multiplied (giving 9),
and only then are these results added, yielding 13. The multiplications are
carried out before the addition because the * operator has a higher
precedence than the + operator.
If there was no precedence and the compiler just evaluated expressions
blindly from left to right, the compiler would obtain a different answer.
Multiplying the 2s gives 4, adding the 3 makes 7, and multiplying the result
by 3 gives 21, which is not what you expect. Thus precedence is important
in normal arithmetic expressions. It’s also important when different C++
operators are used.
You may have wondered why, when I say
temp<65 || temp>75
how I can be sure that the true/false value of temp<65 and temp>75 are
evaluated first, before being ORed together. If the processor proceeded
from left to right, for example, temp<65 would be evaluated, ORed with
temp (whatever that would mean, because temp is a number, not a
true/false value), and the result compared with 75. This isn’t what I want, of
course.
The expression is evaluated correctly because relational operators have a
higher precedence than logical operators.
How is the expression
n + 2 < x + 3
evaluated? You want to compare two arithmetic expressions, n+2 and x+3.
Is that what’s happening? Yes, because arithmetic operators have a higher
precedence than relational operators. Table 2-3 shows the precedence
relations, with the higher precedence operators higher in the table.
Table 2-3 Precedence relations
Operators Operator Types Precedence
* / % Multiplicative Higher
+ - Additive
< > <= >= == != Relational
&& || Logical
= Assignment Lower
Notice that the assignment operator, =, has the lowest precedence of all;
that is, it’s applied after all the other operators. You’ll see other examples
of precedence relations as you go along.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 2
1. True values and false values are represented in C++ by the numerical values _____ and
_____, respectively.
a. any number except 0, 0
b. any positive number, 0
c. 0, any negative number
d. 0, 1
e. 1, 0
2. Relational operators compare two
a. true/false values and evaluate to a number (such as 77).
b. numerical values and evaluate to a true/false value.
c. logical values and evaluate to a true/false value.
d. logical values and evaluate to a number.
e. true/false values and evaluate to a logical value.
3. What expression(s) will cause the while loop to cycle as long as j is greater than 0 and
ch is not ‘q’?
a. while( !(j<=0 || ch==’q’) )
b. while(j > (0 && ch) != ‘q’)
c. while(j > 0 && ch != ‘q’)
d. while(j <= 0 && ch == ‘q’)
e. while( !(j <= 0) || !(ch == ‘q’) )
4. If you want a loop variable to run from 12 to 36 (inclusive) in steps of 3, you might write
a. for(alpha==12, alpha<37, alpha=alpha+3)
b. for(x==12; x<=37; x=x+3)
c. for(b=12, b<37, b+b+3)
d. for( gamma=12; gamma<37; gamma=gamma+3 )
e. for(rad=12; rad<=36; ++rad, ++rad, ++rad)
5. while loops and do loops differ in that
a. a do loop is terminated by a condition arising within the loop.
b. the number of times a while loop will cycle is known before the loop is entered.
c. a while is terminated by a condition arising within the loop.
d. the body of a do loop is always executed at least once.
e. the loop variable may be incremented within a do loop.
Exercise 1
Write a loop that repeatedly asks the user to type a character and then displays the ASCII value of
the character. Hint: You can cause a variable of type char to print as a number by converting it to
type int. Use an expression such as int(ch), which converts the variable ch to an int. Have
the loop exit when the user enters the character ‘q’.
Exercise 2
Write a program fragment, probably consisting of some nested loops, that asks the user what
character to use to create a 10 by 10 square on the upper-left corner of the screen. Then display
this square and ask for another character. Exit from the outermost loop when the user enters ‘q’ .
The if Statement
The simplest way to make a decision in C++ is with the if statement. Here’s an example of an if
statement at work:
if(denominator == 0)
cout << “Division by zero is illegal”;
If a variable called denominator is 0, this fragment causes a message to be displayed. If
denominator is not 0, then nothing happens.
As with loops, if you use more than one statement in the body of an if statement, you need to
surround them with braces.
In a simple if statement, something happens if the condition is true, but if the condition is not
true, nothing happens at all. Suppose you want something to happen either way: one action if the
condition is true and a different action if the condition is false. To do this, you use an if…else
statement. The following fragment takes the hour of the day, expressed in 24-hour time, and
displays it in 12-hour time, with “am” or “pm” as appropriate:
Test Expression
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Nested if…else Statements
You can nest if…else statements within one another. Typically, the nested statements end up
in the else body rather than in the if body. For example
if(age<2)
cout << “\nInfant”;
else
if(age<18)
cout << “\nChild”;
else // age >= 18
cout << “\nAdult”;
This if…else “ladder” prints an appropriate description of a person’s age.
An insidious problem may arise when you nest if…else statements. Here’s a program
fragment that is intended to have a similar functionality to the one above, except it can’t print the
adult designation:
if(age>2)
if(age<18)
cout << “\nChild”;
else // the else goes with the preceding if
cout << “\nAdult”; // appropriate response, age>=18
If you really want an else to be associated with an if that doesn’t immediately precede it, you
must surround the intervening if statement with braces
if(age>2)
{
if(age<18)
cout << “\nChild”;
}
else // this else goes with the top if
cout << “\nInfant”; // appropriate response
The braces make the entire if statement invisible to the else. (It would be nice if we could
conceal ourselves from telemarketers this easily.)
The moral is to be careful with complicated if…else statements. Forgetting which if gets the
else is a common source of annoying program bugs.
Now that you know how to cause a program to do things more than once and make decisions,
you can improve the hot dog stand program, HOTDOG1. Rewrite the main() part of the
program so it runs continuously, waiting for the user to tell it a hot dog has been sold. For
simplicity, work with only one hot dog stand, represented by the stand1 object.
When first started, the program asks the user to enter the initial amounts of buns and hot dogs on
hand. Then the program enters a while loop, waiting for user input. If the user enters ‘s’, the
program records a sale. If the user enters ‘q’, the program displays the hot dogs and buns on
hand and exits. Presumably, the user enters ‘q’ when the last sale has been made and the hot
dog stand has closed down for the day.
Notice that I use the if statement to see if the user has typed an ‘s’, and the while loop to
check for ‘q’. Listing 2-2 shows HOTDOG2.
Listing 2-2 HOTDOG2
// hotdog2.cpp
// hot dog stand inventory database
// uses while loop and if statement
/////////////////////////////////////////////////////////////
void main()
{
char choice = 'x'; // user's letter choice
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
A Glimpse of Reusability
One of the most important things to notice about Listing 2-2 is that the
specification for the HotDogStand class is exactly the same as it was in
the HOTDOG1 program. I have made significant changes to the functionality
of the program, but I have not altered the class at all. In an embryonic way,
this demonstrates one of the major strengths of OOP. I have reused the
HotDogStand class in a new program. I haven’t worried about
integrating or modifying the parts of the class; I simply inserted the entire
class, just as I found it, into the new program. In a small program, the gain
may not appear significant, but in larger programs, the savings in time and
effort that result from being able to reuse already existing classes can be
substantial. The savings may be especially large when an entire library of
classes can be reused.
Of course, it is also possible to reuse code, especially functions, in
old-fashioned procedural languages such as C. However, the OOP
approach, which reuses classes instead of functions, provides a more
coherent and easily understood package for reuse, with both data and
functions combined into a single entity. The interface between the class,
which specifies how objects behave, and main(), which creates objects
and sends messages to them, is cleaner and more easily understood than the
relationship between some functions and some data that’s unrelated to the
functions and statements in main() that call functions and access the data.
Quiz 3
Exercise 1
Exercise 2
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Session 4: Advanced Decisions
In this session, I’ll cover more advanced ways for a program to choose between different actions: the
else if construction, the powerful switch statement, and the weird but compact conditional
operator. I’ll also apply the switch statement to the hot dog stand program to enhance its
functionality.
In the previous session, I showed an example of an if…else statement that displayed “am” or
“pm” for hour values less than and greater than 12. I also acknowledged that this statement didn’t
handle the occasions when hour was 0 or 12. How can I fix this? I can check for these specific
values of hour and respond accordingly. Here’s how that looks with an if…else ladder:
if(hours == 0)
cout << “Midnight”;
else if(hours == 12)
cout << “Noon”;
else if(hours < 12)
cout << hours << “ am”;
else
cout << hours-12 << “ pm”;
The if that follows each else is simply moved up onto the same line, thus creating a sort of
artificial else if construction and removing the multiple levels of indentation. This arrangement
not only saves space, it presents a clearer picture of the program’s logic (at least, after you’ve gotten
used to it).
Notice that the else if construction is not really a part of the syntax of the C++ language; it’s
merely a way to rewrite an if…else ladder by rearranging the whitespace on the page. You can do
this because—as you know—the compiler doesn’t care about whitespace.
Fine-Tuning Loops
This is a good place to introduce the break and continue statements, even though they pertain to
loops, because they are used most effectively in conjunction with decisions. Also, break is an
important feature in the switch statement, which I’ll demonstrate next.
Usually loops work well with the straightforward syntax I showed in the last session. However,
sometimes you need to fudge things a bit to make a loop behave as you want. The break and
continue statements provide this added flexibility.
The break statement causes you to exit immediately from a loop, as shown in Figure 2-10.
The break statement is often used to handle unexpected or nonstandard situations that arise within a
loop. For example, here’s a code fragment that sets the variable isPrime to 1 if an integer n is a
prime number or to 0 if n is not a prime number. (A prime number is divisible only by itself and 1.)
To tell if n is prime, I use the straightforward approach of trying to divide it by all the numbers up to
n-1. If any of them divide evenly (with no remainder), then it’s not prime.
The continue statement is similar to the break statement in that it is usually activated by an
unexpected condition in a loop. However, it returns control to the top of the loop—causing the loop
to continue—rather than causing an exit from the loop. Figure 2-11 shows how this looks.
Whereas the break statement causes an exit from a loop, the continue statement causes part of
the loop to be “short-circuited” or bypassed while the loop keeps running. That is, following a
continue, control goes back to the top of the loop. Here’s an example:
do
{
cout << “Enter dividend: ”;
cin >> dividend;
cout << “Enter divisor: ”;
cin >> divisor;
if(divisor == 0) // if user error,
{
cout << “Divisor can't be zero\n”;
continue; // go back to top of loop
}
cout << “Quotient is ” << dividend / divisor;
cout “\nDo another (y/n)? ”;
cin >> ch;
} while(ch != 'n');
Division by zero is illegal, so if the user enters 0 for the divisor, control goes back to the top of the
loop and the program prompts for a new dividend and divisor so the user can try again. To exit from
the loop, the user must answer ‘n’ to the “Do another” question.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The switch Statement
Now you’re ready for perhaps the most powerful decision-making construction in C++. The switch
statement checks a variable and routes program control to any of a number of different sections of code,
depending on the value of the variable. Here’s an example:
switch(diskSpeed)
{
case 33: // if diskSpeed is 33
cout << “Long-playing album”;
break;
case 45: // if diskSpeed is 45
cout << “Single-selection”;
break;
case 78: // if diskSpeed is 78
cout << “Old single-selection”;
break;
default: // if nothing matches
cout << “Unknown format”;
}
The switch statement consists of the keyword switch followed by a variable name in parentheses. The
body of the switch statement, enclosed in braces, follows. Within the body are a number of labels, which
are names followed by a colon. In a switch statement, these labels consist of the keyword case followed
by a constant and then the colon. When the value of the switch variable is equal to the constant following
a particular case, control will go to the statements following this case label.
The above section of code prints different messages depending on the value of the diskSpeed variable. If
diskSpeed is 33, control jumps to the label case 33. If diskSpeed is 45, control jumps to the label
case 45, and so on. If diskSpeed doesn’t match any of the cases, control jumps to the default
label (or, if there is no default, falls through the bottom of the switch). Figure 2-12 shows the syntax
of the switch statement and Figure 2-13 shows its operation.
The variable or expression used to determine which label is jumped to (diskSpeed, in this example)
must be an integer or a character or must evaluate to an integer or a character. The values following the
cases must be—or must evaluate to—integer or character constants. That is, you can use variable names
or expressions, such as alpha, j+20, and ch+’0’, as long as alpha, j, and ch already have
appropriate values.
Once control gets to a label, the statements following the label are executed one after the other from the
label onward. In this example, the cout statement will be executed. Then what? If the break weren’t
there, control would continue down to the next cout statement, which is not what you want. Labels don’t
delimit a section of code, they merely name an entry point. The break causes control to break out of the
switch entirely.
Here’s another example that might be used in the hot dog stand program. It gives the user the choice of
three stands for which to record the sale of a hot dog. The user types a digit from ‘1’ to ‘3’, which is
then used as the switch variable.
Let’s put this switch construction into a complete program. A while loop will allow the user to
repeatedly record the sale of hot dogs from the three stands. Listing 2-3 shows HOTDOG3.
Listing 2-3 HOTDOG3
// hotdog3.cpp
// hot dog stand inventory database
// uses while loop and switch statement
/////////////////////////////////////////////////////////////
void main()
{
char choice = 'x'; // user's letter choice
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The user can enter initial amounts of hot dogs and buns for three different stands, then record an
arbitrary number of sales at each stand, and then, at the end of the day, display the remaining
inventory before exiting from the program. Here’s some sample interaction:
The conditional operator was invented because a particular construction occurs often in C++
programs and it is nice to shorten it. Here’s an example of the lengthy version of the code:
if(alpha<beta)
min = alpha;
else
min = beta;
I have a variable min and I want to set it to alpha or beta, whichever is smaller. This if…else
statement requires four lines of code. (I could put it all on one line, of course, but it would still be
long and complicated.) However, using the conditional operator, I can shorten it to
absvalue = (n<0) ? -n : n;
imitates an absolute value function. (The absolute value of a number is simply the number with any
negative sign removed.) The result is -n if n is less than 0 and +n otherwise.
Quiz 4
1. Suppose you want to display “Weekday” if a variable day is between 1 (Monday) and 5
(Friday), but you want to display “Saturday” if day is 6 and “Sunday” if day is 7.
Which of the following fragments might reasonably be part of the code for this task? Assume
day is always in the range of 1 to 7.
a. if else(day==7) cout << “Sunday”;
b. else if(day==6) cout << “Saturday”; else cout << “Sunday”;
c. else if(day<6) cout << “Weekday”; else if(day==6);
d. else cout << “Weekday”;
e. if(day<6) cout << “Weekday” else cout << “Saturday”;
2. Which of the following are true?
a. break brings you back to the top of a loop, whereas continue continues on from
the same point within the loop.
b. continue brings you back to the top of the loop, whereas break takes you out of the
bottom of the loop.
c. break takes you out of all the loops in which you may be nested
d. continue takes you immediately to the top of the innermost loop in which you’re
nested
e. break takes you immediately out of an if statement body
3. What will be displayed if the following code is executed when var has the value ‘b’?
switch(var)
{
case 'a': cout << “Alpha ”;
case 'b': cout << “Beta ”;
case 'c':
cout << “Gamma ”;
break;
default:
cout << “Not on list”;
}
a. Alpha
b. Beta
c. Gamma
d. Alpha Beta
e. Beta Gamma
4. If you want to display “Too slow” if speed is less than 40, “Too fast” if speed is
greater than 65, and nothing otherwise, it would be appropriate to use
a. a switch statement.
b. a series of if statements.
c. nested if…else statements.
d. an else if ladder.
e. a conditional operator.
5. You want to send a message to each object in a group of objects. You already know the
names of all the objects. The message would retrieve a certain item of instance data from each
object. Your program would then display “Found one!” if this data item in any object was
divisible by 7. You would be likely to use
a. a switch in a do loop.
b. nested if…else statements in a while loop.
c. an else if ladder in a do loop.
d. an if in a for loop.
e. an else if ladder in a for loop.
Exercise 1
Rewrite the program of Exercise 1 in Session 3 in this chapter so that it uses a switch statement to
distinguish among the user’s choices.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Exercise 2
Rewrite the program of Exercise 2 in Session 3 in this chapter (which uses the
hotdogstand2 class with a CashOnHand variable) so that it has the same
functionality as the HOTDOG3 program in this session and uses a switch statement to
distinguish among the user’s choices.
Midchapter Discussion
Estelle: So George, what terrible negative thoughts do you have today?
George: No negative thoughts. I’m on top of the whole thing. I see how a C++
program fits together. It’s like there are two halves: the class
specification and main(). In the specification, you define what the
objects are and how they’ll act. In main(), you make objects based on
the class and interact with them.
Don: Well said. I think I’m beginning to see something else, too. There’s a
philosophical difference between a class specification and the code in
main(). The class is more general. You can imagine one programmer
writing a class specification, and then a whole bunch of different
programmers using the class for their own special purposes, each one
writing a different main(). It’s like someone designing a tool and other
people using it in different ways.
Estelle: Anyone with a chain of hot dog stands is a potential customer for the
HotDogStand class, but they might want to write their own main().
Don: Exactly.
George: I’m also relaxed because all the material on if and while and for
isn’t exactly news to an old C programmer such as myself.
Estelle: Lucky you. Actually, I didn’t think it was too hard either, except for the
conditional operator, which has too much going on in too small a space.
It’ll take me all day to figure out a line of code if it’s got one of those
things in it.
George: You don’t need to use a conditional operator if you don’t want to; just
use if…else instead.
Don: What I thought was silly is how in a switch statement you need to put
break statements at the end of every case. Why not have control
jump to the end of the switch automatically after a case is
completed?
Estelle: It’s a trade-off. The advantage of requiring a case is that, if you want,
two cases can go to the same code.
Don: When would you ever do that?
Estelle: Hmm…oh, I know. Suppose you wanted your program to do the same
thing if the user typed either an upper- or lowercase version of the same
character. Then you could put two cases right after each other. Here, I’ll
write it on the board:
case 'a':
case 'A':
// do something
break;
I guess that’s reasonable. But you know what I think is weird? The
Don:
continue statement.
George: Don’t worry about it. It doesn’t come up that much in C programming,
so I bet it doesn’t in C++ either.
Why would you want to create a new data type? Don’t int and float and the other
basic types pretty much allow you to do anything you want? Well, not really. You
may be able to do everything, but you can’t do it very elegantly or quickly. It would
be nice if you could treat airtime values just as though they were basic types and
make statements such as
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
A Simple Version of the airtime Class
Let’s first examine a simple version of the airtime class. This class does
little more than handle input and output for airtime variables, but it will
introduce the idea of user-defined data types. Note that airtime variables
use 24-hour time, where 13:00 is 1:00 pm, and 23:59 is 11:59 pm. Listing
2-4 is the complete TIME1 program.
Listing 2-4 TIME1
// time1.cpp
// a class that models a time data type
#include <iostream.h>
class airtime
{
private:
int hours; // 0 to 23
int minutes; // 0 to 59
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1, t2; // create two airtime variables
t1 = 10:15
t2 = 23:30
The user enters two times and the program displays them.
Considering that it creates a data type instead of a group of hot dog stands,
this program is surprisingly similar to the HOTDOG1 program. But when
you think about it, time quantities and hot dog stands share common
characteristics. They both store data. Also, they should have similar
capabilities: they should specify their initial data, modify it, and display it.
One of the easiest ways to demonstrate the power of using classes for data
types is to assign the value of one object to another. You know how that
works with basic types such as int. The statement
avar = 3;
gives the value 3 to the variable avar. Can you do the same thing with
variables (objects) of data types (classes) that you’ve defined yourself?
Absolutely. Here’s a modification of the main() part of the TIME1
program. It gets a value for one airtime variable t1, assigns this value to
t2 with the statement
t2 = t1;
and then displays the value of t2.
void main()
{
airtime t1, t2; // create two airtime variables
I’ve said that when you assign one object to another, its instance data is
copied into the other object. Are its member functions copied as well?
Conceptually, it’s probably easiest to think of them being copied along with
the data: Everything associated with one object is copied to another. You
probably won’t get into too much trouble if you assume this is true.
However, that’s not exactly what happens.
The truth is, no matter how many objects of a given class exist, there is only
one image of each member function stored in memory. This makes sense,
because the member functions are the same for each object, unlike instance
data, which in general contains different values for each object. It would
waste memory to duplicate the member functions, so all objects share the
class member functions, as shown in Figure 2-16.
t2.display(); // display t2
you always call the same display() function, but it operates on different
data, depending on what object called it. In this case, display() operates
on the data stored in t2.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
More Manipulators: Formatting Leading Zeros
As with most languages, there is a rich vocabulary in C++ for formatting output to the screen. So
far I’ve ignored such possibilities, except for endl and escape characters such as \n. However,
the airtime class suffers from an annoying drawback: It doesn’t print the hours with leading
zeros when appropriate. For example, in the time1 program, you might have the following
interchange:
t1 = 12:0
t2 = 3:5
We humans don’t recognize 12:0 and 3:5 as time values. We want the times to be displayed with
leading zeros when appropriate, as the user entered them. Here’s how to modify the display()
member function to supply leading zeros:
void display()
{
cout << hours << ':'; // hours and colon
<< setfill('0'); // fill character is '0'
<< setw(2) << minutes; // minutes is 2 chars wide
}
This arrangement makes use of two new manipulators. Recall that a manipulator is an object that
can be inserted into an I/O stream. You’ve already made the acquaintance of endl, a manipulator
that takes no arguments.
The setw() manipulator takes one argument, which is the width in characters, of the field to be
occupied by the next output value. Ordinarily, the field width is automatically adjusted to the
number of digits to be displayed (which does not include leading zeros). By setting a fixed width
with setw(2), I specify that I want minutes to be displayed as two characters, even if it has a
value less than 10. The unused space will be filled with a fill character, which by default is a blank
(‘ ’). I want it to be a ‘0’ character, so I set this with the setfill(‘0’) manipulator. This
arrangement always prints two digits, using 0 for the left one when appropriate.
With this revised version of the display function, the input shown above will display
t1 = 12:00
t2 = 3:05
as it should.
There is a subtle difference between the setfill() and setw() manipulators. The effect of
setfill() lasts for the entire cout statement, no matter how many values are output. The
effect of setw(), on the other hand, lasts only until the next value has been displayed. Thus, you
must use setw() before every value if you want to change its field width.
There are other manipulators in C++; you’ll see some in Chapter 10.
Quiz 5
alpha = beta;
Exercise 1
Rewrite the specification for the airtime class in the TIME1 program so it includes a seconds
data member. The new class should work with the same main() that the old class did.
Exercise 2
Add a member function to the original airtime class that will advance the time by 1 minute.
Handle the case where the minutes value is 59 and adding a minute will cause the hours value to be
incremented. If the hours value becomes 24, set it back to 00.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Adding Hours to an airtime Value
Suppose that I need to add a certain number of hours to variables of type airtime,
described in the last session. I might do this to convert an airline departure or arrival to a
different time zone. Something that happens at 12:30 in San Francisco happens at 15:30
in Philadelphia, for example. I would carry out this conversion by adding the integer 3 to
the hours value of the airtime 12:30. I don’t need to touch the minutes value at all.
However, I don’t always want to increase the number of hours by 3. The time difference
between Salt Lake City and Chicago is 1, but between Chicago and London it’s 6. How
do I convey the number of hours, which will be 1 or 3 or 6 or whatever, to the airtime
object? As you’ve guessed, I use a function argument.
t1.addhours(3);
The argument is placed between the parentheses following the function name. Or I could
use a variable name, provided it has already been given the appropriate value.
int diffhours = 3;
…
t1.addhours(diffhours);
When I write a member function that takes an argument, I must place two things within
the parentheses in the function definition: the data type and a variable name, as shown in
Figure 2-17.
Here’s the definition of the addhours() member function of the airtime class:
void addhours(int h)
{
hours = hours + h; // add hours
if(hours > 23) // if carry to next day,
hours = hours - 24; // subtract a day
}
The int in
void addhours(int h)
specifies the type of data expected as an argument and the h is the name of the argument.
This argument h is a variable that can be accessed anywhere in the function. However,
note that h is visible (has meaning) only within the function; it is unknown to other parts
of the program.
The addhours() function adds h to the hours value of its object. It then checks to see
if the resulting hours value exceeds 23; if so, it subtracts 24. (Don’t worry about the
date.)
Let’s put everything together to see how this new member function might be used in a
program. Listing 2-5 is a revision of the TIME1 program that incorporates addhours()
and lets the user exercise this function. This program is called HOURADD.
Listing 2-5 HOURADD
// houradd.cpp
// a class that models a time data type
// includes member function to add hours to a time
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int hours; // 0 to 23
int minutes; // 0 to 59
public:
void set()
{
char dummy;
void main()
{
airtime t1; // airtime
int diffhours; // hours
char choice; // user's choice: 'n' or 'y'
do
{
cout << “For t1, ”;
t1.set(); // set t1
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The set() Member Function with Arguments
There’s more than one way to design a class. In the airtime class (and the hotdogstand
class as well), I initialized the values of an object’s data members by having a member function
request the values directly from the user. However, I could instead pass these values as
arguments to the member function. This would shift the responsibility for getting user input from
the class to the main() function. Depending on circumstances, this might be a superior choice.
Let’s revise the set() member function from the airtime class, as seen in the TIME1
program, so that it obtains values as arguments rather than from the user. Let’s also revise
main() to handle user input, as shown in Listing 2-6.
Listing 2-6 TIME2
// time2.cpp
// a class that models a time data type
// uses arguments to the set() function
#include <iostream.h>
class airtime
{
private:
int hours; // 0 to 23
int minutes; // 0 to 59
public:
void set(int h, int m) // set airtime value
{ // (values supplied by arguments)
hours = h; minutes = m;
}
void display() // display airtime value
{
cout << hours << ':' << minutes;
}
};
void main()
{
int hhs, mms; // variables for user-supplied values
char dummy; // for colon
airtime t1, t2; // two airtime variables
I’ve shown only arguments of type int, but arguments may be of any type, and there can be any
number of arguments to a function. Here’s the skeleton for a member function that takes three
arguments of different types:
class Foo
{
…
void func(int ivar, float fvar, char cvar) // define function
{
}
…
};
main()
{
Foo foo; // make an object
int iarg = 17; // make some variables
float farg = 6.025e23;
char carg = 'x';
…
foo.func(iarg, farg, carg); // call the function
…
}
I specify a class Foo with a member function func() that takes three arguments of three
different types. Then in main(), I create an object of class Foo and call func() for this
object, with appropriate values for the arguments.
Because I’m focusing on the object-oriented approach to programming, using examples with
simple classes and programs, I’ve shown only one situation involving function calls: from
main() to a member function. However, functions may be called in all sorts of other situations
as well. First, the main() part of the program may be divided up into many functions, all of
which are called from main() or perhaps called from other functions that are called from
main(). This is a way of organizing the program. Second, member functions within classes
may call other functions. You’ll see examples as we go along. In the meantime, keep in mind the
comforting thought that things can always be more complicated than I show here.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Passing by Value
The method of passing arguments that I have shown here is called passing
by value. This means that the function creates an entirely new variable (a
place in memory with a name and a suitable size) to hold the value of each
argument passed to it. In the skeleton example above, there are three
variables in main(), iarg, farg, and carg. When the function is
called, it creates three new variables, ivar, fvar, and cvar, and copies
the values into them, as shown in Figure 2-18.
The function may modify the variables it has created, but doing so has no
effect on the variables in main(). This provides a built-in protection
mechanism: main() can use variables as arguments without worrying that
they might be modified by the function it supplies them to.
Sometimes you want a function to be able to modify the variables passed to
it. When this is the case, you can use a different approach: passing by
reference. With this mechanism, the function operates on the original
variables in main(). This approach is used less often, but sometimes it’s
essential, as you’ll see later.
Quiz 6
Exercise 1
Exercise 2
Write a member function for the airtime class that will change the
seconds data by an integer amount supplied as an argument to the
function. You’ll need to handle the cases where seconds overflow past 60
and hours overflow past 23. Write a main() program to test this new
member function.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Adding airtime Values
The add() member function in the airtime class takes two time values as arguments, adds
these values, and places the result in the object that called it. That is, if add() is called like this
t3.add(t1, t2);
then t1 will be added to t2 and the result placed in t3. For example, if t1 is 10:10 and t2 is
2:30, then the result placed in t3 will be 12:40. (Technically, this amounts to adding a time
interval, not another time, to a time. Adding 2 o’clock to 3 o’clock doesn’t really make much
sense, but if you leave Denver at 10 minutes after 10 and it takes you 2 hours and 30 minutes to
fly to Dallas, then it does makes sense to say you’ll arrive there at 12:40.) Listing 2-7 shows
TIMEADD.
// timeadd.cpp
// a class that models a time data type
// includes member function to add two times
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 t0 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1, t2, t3; // create three airtime variables
char choice;
do
{
cout << “For t1, ”;
t1.set(); // set t1
cout << “For t2, ”;
t2.set(); // set t2
It’s important to notice that you can use function arguments of types (classes) you’ve defined
yourself (airtime in this case) just as you can with basic types such as int. Here the add()
member function takes two values of type airtime as arguments:
t3 = t1 + t2;
instead of
t3.add(t1, t2);
It makes sense to add objects of type airtime because such objects represent a quantity that is
basically numerical: time. (Assuming, as I noted, that you imagine adding a time interval to a
time.) However, addition doesn’t make sense for objects of all classes. If I add two objects of the
hotdogstand class, for example, it’s not clear what meaning the “sum” would have.
Notice that the add() member function can access the private data members of the objects
passed to it as arguments. It receives copies of the data in t1 and t2 when it’s called in the
statement
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Converting Minutes to airtime
The add() member function operated on two variables. Let’s look at a different function
that operates on a single variable. This function will take as its only argument an int
value representing minutes (which can be larger than 59). It converts this minutes value
into an airtime value. This airtime value is then stored in the object that called the
function. The result is a minutes-to-airtime conversion function. Listing 2-8 shows
TIMECNV1.
// timecnv1.cpp
// a class that models a time data type
// includes a member function to convert minutes to airtime
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 to 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1;
int iminutes; // minutes (can be > 59)
char choice;
do
{
cout << “Enter minutes (for example, 241): ”;
cin >> iminutes;
t1.MinsToAirtime(iminutes);
Nonautomatic Conversions
Conversions, such as the one from minutes to airtime, play an important role in C++
programs. Remember that C++ automatically converts basic types from one to another.
For example, you can say
fvar = ivar;
and the C++ compiler will arrange for the integer value 27 to be converted to the
equivalent floating-point value, 27.0, for storage in fvar. Such conversions are
completely automatic. The conversion routines are built into the compiler, and it knows
how to use them.
However, when you convert between basic types and types you’ve specified yourself,
such as airtime, there are no routines built into the compiler to handle the
conversion. (After all, it doesn’t know what type you might invent.) You must write the
routines yourself, as I’ve done here with MinsToAirtime(). I’ll be returning to the
subject of conversions.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Multiplying an airtime Value
Let’s look at another example of arithmetic on the user-defined data type airtime. Multiply
an airtime value by a floating-point number. Such an operation might be handy if, for
example, you want to know how long it would take an airplane mechanic to service 23 jet
engines if it takes her 1 hour and 15 minutes to service one engine. Listing 2-9 shows
TIMEMULT.
// timemult.cpp
// a class that models a time data type
// includes a member function to multiply a time by a float
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 t0 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1, t2; // create airtime variables
int m; // multiplier
char choice;
do
{
cout << “For t1, ”; // get t1 from user
t1.set(); // set t1
cout << “Enter multiplier: ”;
cin >> m; // get multiplier
A member function can call another member function. In the mult() function in TIMEMULT,
I performed the same conversion from minutes to airtime that I did in the
MinsToAirtime() function in the TIMECNV1 program. I could have saved myself the
trouble of rewriting this conversion code by using the MinsToAirtime() function instead.
Here’s how I would rewrite mult() to do this:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 7
Exercise 1
Write a sub() member function for the airtime class that subtracts one
airtime value from another. Write a main() that allows the user to test
this function. Assume that the smaller (earlier) time value will always be
subtracted from the larger so that negative values will not arise.
Exercise 2
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Converting airtime to Minutes
As an example of a function that returns a value, I’ll develop a member function for the
airtime class that’s the opposite of MinsToAirtime(). That is, the new function,
AirtimeToMins(), will convert an airtime value to a minutes value and will
return this minutes value. (This minutes value can be greater than 59 because it
represents both hours and minutes.) Listing 2-10 shows TIMECNV2.
Listing 2-10 TIMECNV2
// timecnv2.cpp
// a class that models a time data type
// includes member function to convert airtime to minutes
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 to 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1;
int iminutes; // minutes (can be > 59)
char choice;
do
{
cout << “For t1, ”; // get airtime value from user
t1.set();
// convert airtime to minutes
iminutes = t1.AirtimeToMins();
Any function will return to the code that called it when control “falls through” the
bottom of the function (passes from the last statement to the closing brace). However,
this simple approach doesn’t allow you to return a value. For a function to return a
value, it must use a return statement. The keyword return can be followed by an
expression that evaluates to the value to be returned. In AirtimeToMins(), I say
return imins;
which causes the minutes value, stored in imins, to be returned, as shown in Figure
2-21.
A return statement causes control to jump immediately out of the function and to
return to the code that called it. The expression following return must be the same
type as the type of the function. This expression is optional. If it’s not included, then the
function must be of type void, meaning it does not return a value. In void functions,
a return statement is not necessary (as you’ve already seen in numerous examples),
provided you want the function to end at its last statement. Using return gives you
the option of exiting from the function anywhere.
You’ve seen examples of variables that are defined in a function body. Mostly these
definitions have occurred at the beginning of the function and defined a variable
without giving it a value.
int somevar;
…
somevar = 3;
However, it’s also possible to initialize a variable at the same time it’s defined. Not only
that, but the value used to initialize it can be calculated in the same statement. This is
what I do in the AirtimeToMins() function.
The Stack
Automatic variables are stored in a part of computer memory called the stack. The stack
grows and shrinks as functions are called and returned. It has a maximum size, usually
several thousand bytes, so you can’t use huge amounts of automatic data (unless you
make special arrangements to enlarge the stack). When an automatic variable is first
created, it has a random “garbage” value, probably not zero. This is because the stack
has just expanded into an (often) previously occupied part of memory. The moral is:
Don’t trust that an automatic variable will have an initial value of 0; be sure to initialize
it before you use it.
You can simplify the AirtimeToMins() function. You don’t actually need to give a
name to the variable that holds the result of the calculation. Instead, rewrite the function
like this:
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Returning a Value from add()
C++ is nothing if not versatile, and there are often many ways to carry out a given task. As an
example, I’ll rewrite the add() member function of airtime so that it returns a value instead of
storing the result of its addition in the object that called it. As you may remember, add() in the
TIMEADD program was called this way
t3.add(t1, t2);
where the values of t1 and t2, passed as arguments, were added and the result was stored in t3,
the object that called add() in the first place.
However, if I use a version of add() that returns a value, I can rewrite this statement in a
different way.
t3 = t1.add(t2);
This is a mixed blessing in terms of clarity. It’s more natural to assign t3 the result of the
addition, because that’s closer to the way it would be written in normal algebra: t3=t1+t2.
However, t1 and t2 are now treated differently: t1 is the object for which the function is called
and t2 is an argument to the function. This looks odd, because t1 and t2 play the same kind of
role.
Whether it’s any clearer or not, this new version of add() works just as well as the old one.
Listing 2-11 shows TIMERET.
Listing 2-11 TIMERET
// timeret.cpp
// a class that models a time data type
// member function to adds times, returns time value
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 t0 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime t1, t2, t3; // create three airtime variables
char choice;
do
{
cout << “For t1, ”;
t1.set(); // set t1
cout << “For t2, ”;
t2.set(); // set t2
Returning by Value
I should note that the return statements I examine in this session return by value. The actual
value is passed back to the code that called the function. In other words, there will be—at least
briefly—two variables holding the same value: one in the function and one in the code that called
the function.
Another mechanism can be used to handle return values: returning by reference. In this case, the
code that calls the function receives only a reference to the original variable in the function.
However, you’ll need to learn about constructors before I talk about returning by reference.
Library Functions
Now that you know about function arguments and return values, I can mention the existence of
C-style library functions. These functions were developed for the C language, but can also be used
in C++. Many of these functions are not as useful in C++ as they are in C (such as I/O functions,
assuming you use the C++ stream classes). However, C library functions are still an essential
aspect of C++ programming. There are library functions for input/output, data conversions, string
handling, directory and file control, memory allocation, math, process control, and so on. You’ll
encounter many of these functions.
As an example, let’s look at the sqrt() function, which returns the square root of a
floating-point number. This skeleton code shows the essentials.
The documentation will also tell you the data types of the function’s arguments and return value.
The sqrt() function takes a single argument of type double and returns the same type, so in
this example, both somenum and answer are type double. Other examples of library functions
will pop up in future lessons.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 8
Exercise 1
Exercise 2
Write statements to set the three variables in Exercise 1 to ‘V’, 67.95 and
1,000,000, respectively.
Summary: Chapter 2
In this chapter, you’ve seen two quite different kinds of complete, working
C++ programs: one in which a class represents hot dog stand inventory data
and another in which a class models a data type that represents time.
Because time is a numerical entity, you can treat a class that models it as a
new, user-defined C++ data type.
I also focused on some of the nuts and bolts of C++ programming: loops
(while, do, and for), decisions (if, if…else, the else if
construction, switch, and the conditional operator), and function
arguments and return values. I used various kinds of loops and decisions in
programs that further extend the capability of the hot dog stand and time
programs.
A major focus was expanding the kinds of member functions you can write.
You saw how to write member functions that perform arithmetic on objects
and that convert from one data type to another.
Finally, I discussed several more subtle features of C++. You discovered
that you can assign the value of one object to another the same way you can
with basic C++ types. You also learned that, although it may be convenient
to think of each object as containing both data and member functions, the
member functions are actually shared by all objects of a class. The function
examples you’ve seen so far pass arguments by value; that is, values are
copied from the calling program to variables in the function. The functions
also return by value, which means that a copy of a variable in the function
is returned to the calling program.
You learned that member functions can access the object for which they are
called. They can also access objects sent to them as arguments, provided
these objects are of the same class as the member function.
Automatic variables or objects are created automatically when a function is
called and are destroyed when the function returns. Automatic variables are
stored on the stack. Some automatic variables have no name and are created
temporarily to store the results of evaluating expressions.
C-style library functions are available to carry out many tasks. They are not
object oriented but they are very helpful in some situations, such as
mathematics operations.
End-Of-Chapter Discussion
George: I was just getting used to the idea that objects represent things
in the real world, and now it turns out they can represent data
types. Anyway, doing arithmetic on objects is too weird. No
one would do addition using a function!
Estelle: Poor George. I know you hate this answer, but again I think the
payoff is going to come later.
Don: Right. It doesn’t buy you much to add two airtime values
with a function such as add(), but it’ll be a lot more
interesting to say t3=t1+t2, just like in ordinary arithmetic.
George: Yeah, that may be cute, but what good is it? I can calculate
time values in C just by using separate variables for hours and
minutes. I don’t see that this airtime class is making my life
any easier. These sample programs he’s showing us are more
complicated than a C program would need to be.
Estelle: That’s because you’re looking at both parts of the program: the
class specification and the main() function. But suppose you
bought a whole airtime class library. Then the class
specification and all the member functions would already be
written and you wouldn’t need to put them in your listing, you
could just use an #include. You probably wouldn’t even need
to look at the source files for the class. You’d just need to
create objects and send them messages.
George: You mean define weird things and call their member functions.
Estelle: Whichever way you like to say it.
Don: You’d need a description of how the member functions
worked, like what arguments and return values to use for
add() and display() or whatever.
Estelle: Right, so you’d know how to use them. But looking up a short
description of a function is a lot easier than trying to figure it
out from the source code.
Don: Usually.
Estelle: And if you look at the listings of the programs we’ve seen so
far, about half the lines of code are the class specification. If all
you had to worry about was main(), things would be pretty
easy.
Easy as chopping wood with a broom, as my granny used to
George:
say.
Estelle: Come on, George. Get with the program.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
CHAPTER 3
ARRAYS AND STRINGS
Arrays are the most common way to combine a number of data items into a single unit. Most
computer languages include array handling in their fundamental syntax, and C++ is no exception.
Because arrays are such a fundamental data storage mechanism, I’ll devote several sessions to them.
Also, as it turns out, a text string in C++ is treated as an array of characters. So after you’ve seen
what arrays can do in general, I’ll introduce strings.
Arrays do have an important limitation: The variables stored in an array must all be of the same type.
However, you can group variables of different types together in another storage mechanism called a
structure, so I’ll talk about structures as well. Finally, I’ll introduce the enumerated data type, which
is a sort of simple way to create your own data types. Along the way, you’ll make the acquaintance of
some new and surprising classes.
Defining an Array
To define an array, you tell the compiler to set aside storage for a given number of data items of a
specified type. You also tell the compiler the name of the array. Here’s an example of an array
definition that creates storage for four integers. I’ll give this array the name age; perhaps it will be
used to store the ages of four people.
int age[4];
The int specifies the type of data to be stored, age is the name of the array, and 4 is the size of the
array; that is, the maximum number of variables of type int that it will hold. Brackets [] (not
braces or parentheses) surround the size. It’s the brackets that tell the compiler I’m defining an array
and not something else, such as a function. Figure 3-1 shows the format of this array definition.
You can define arrays of any data type, of course. Here’s an array of 100 variables of type float,
called foo:
Array Elements
Each variable stored in an array is called an element. The elements are numbered. These numbers are
called index numbers or indexes. Some people also refer to them as subscripts. The index of the first
array element is 0, the index of the second is 1, and so on. If the size of the array is n, the last element
has the index n-1. For example, in the age array, which has a size of 4, the elements are numbered 0,
1, 2, and 3. This numbering can be the source of some confusion. Keep in mind that the last element
in an array has an index one less than the size of the array.
Figure 3-2 shows the array elements for the age array stored in memory. (Here each element is
assumed to occupy 2 bytes.) The elements have been given the values 44, 16, 23, and 68. Don’t
confuse the values of the elements with their index numbers (0 to 3).
You refer to individual array elements using their index numbers and the array name. Somewhat
confusingly, the brackets [] are used again, but in a different context than in defining an array. As
you can see in Figure 3-2, the first element is called age [0], the second is age [1], and so on.
You can make statements such as
age[2] = 23;
which sets element age [2] to a value, and
int age[4];
defines an array of four elements, but the expression
age[2]
does not define an array of two elements; it refers to the third element of the array.
The real power of arrays comes from the fact that you can use a variable, rather than a constant such
as 2, as an array index. For example, you can print out all the values in the age array with the code
44
16
23
68
depending on what values had been inserted into the array. To set the elements of the array age to
values obtained from the user, you might say
int age[4];
...
for(int j=0; j<4; ++j)
{
cout << “Enter the value of element ” << j << “: ”;
cin >> age[j];
}
Interaction with this fragment might be
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Initializing Array Elements
You can set array elements to a value when you first define the array. Here’s an example:
1 5 10 25 50 100
Here’s another example:
Multidimensional Arrays
So far, you’ve looked only at one-dimensional arrays. You can create arrays of as many
dimensions as you like, and each dimension can be a different size. Here’s the definition of a 4
by 3 array:
Individual array elements are accessed using two indexes. Thus in Figure 3-4, the element in the
upper-right corner of sales is sales[0][[2] and the element in the lower-left corner is
sales[3][0]. To display all the elements of such an array, you would probably use two
nested for loops.
You should be aware that C++ contains no built-in mechanism to prevent a program from using
an incorrect index value. If your program generates an index value that is smaller than 0 or so
large it points beyond the last element of the array, you may be in for big trouble. If you read
data from nonexistent array elements (really memory outside your array), you will obtain
meaningless data. However, if you write data into memory outside your array, you may write
over other data, your program, or who knows what.
There are two morals here. The first is to be careful when programming arrays. The second is
that it would be nice to have a “safe array,” one that automatically checks all index values to be
sure they are in bounds. As it turns out, you can use a class to create safe arrays. I’ll return to this
topic later.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 1
The employee class contains three data items: an array of type char to hold the letters of a
name, an integer n that specifies the length of the name (i.e., how many characters are currently
stored in the array), and another integer for the employee serial number. The member functions
that obtain and display this data for the user are called input() and output(), respectively.
Listing 3-1 shows EMPLOY1.
Listing 3-1 EMPLOY1
// employ1.cpp
// models an employee
// uses array of type char for name
#include <iostream.h>
#include <conio.h> // for getche()
class employee
{
private:
char name[20]; // name (20 chars max)
int n; // length of name
int serial_number;
public:
void input() // get data from user
{
char ch;
n = 0;
cout << “ Enter name: ”;
do
{
ch = getche(); // get one char at a time
name[n] = ch; // store in “name” array
++n;
} while(ch != '\r'); // quit if “Enter” key
cout << “\n Enter serial number: ”;
cin >> serial_number;
}
void output() // display employee data
{
cout << “ Name = ”;
for(int j=0; j<n; ++j) // display one character
cout << name[j]; // at a time
cout << “\n Serial number = ” << serial_number;
}
};
void main()
{
employee e1, e2;
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Library Function getche()
I used a new library function in the EMPLOY1 program. A weakness of the stream I/O
approach to C++ is that there’s no way to input a character without pressing the key
afterward. However, if you’re typing a name, you don’t want to press after every
character. Fortunately, the C function library contains a function that does just what we want:
getche(). This function waits until a single key is pressed on the keyboard and then returns
with the ASCII value of the key. There’s no need to press to get the character. The
getche() function requires that the CONIO.H header file be included. I used this function in
a do loop to obtain all the letters of the employee name.
do
{
ch = getche(); // get one char at a time
name[n] = ch; // store in “name” array
++n; // increment the array index
} while(ch != '\r'); // quit if “Enter” key
Each time through the loop, the getche() function returns with a new character, which is
then assigned to an array element. The index n starts at 0 and is incremented each time
through the loop by the ++n; statement. (I’ll show a more compact way to increment n in a
moment.)
The Key
What is the ASCII code for the key? It turns out it’s 13 (decimal), but you don’t
really need to know this, because the escape code ‘\r’ (for carriage Return) represents this
code. When the program encounters this code, it exits from the do loop. At this point, it
obtains the serial number from the user, a simpler process.
Displaying a name is the reverse of storing it. The program goes through the array one
character at a time, using a for loop, displaying each one.
When I store characters in the array name, I want to start with the array index of 0, as usual,
and then increment this index for each additional character. I do this with the code
name[++n] = ch;
Can I actually do this? Well, it’s perfectly legal syntax as far as the compiler is concerned.
However, there’s a glitch. The index n starts off at 0, and I want to put the first character in
array element 0, so I don’t want to increment n until after the contents of ch have been
placed in the array. Unfortunately, ++n causes n to be incremented before it is used. The
result will be that the first character of the name will go in name[1] instead of in name[0].
Is there a way to increment n after it’s used? The designers of C and C++ anticipated just this
situation and built the necessary capability into the increment and decrement operators. Here’s
the statement rewritten so it works properly:
name[n++] = ch;
When the ++ operator follows its operand, it’s called a postfix operator. When it precedes its
operand, as you’ve seen several times before, it’s called a prefix operator. The prefix operator
is applied before the value of the variable is used, whereas the postfix operator is used after
the variable is used. This is summarized as follows.
do
{
ch = getche(); // get one char at a time
name[n++] = ch; // store in “name” array
} while(ch != '\r'); // quit if “Enter” key
This saves a line of code, which is widely believed to make the listing more readable. You
will not be surprised to learn that there is a postfix version of the decrement operator (n--) as
well as of the increment operator.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The Stack Class
An array is a built-in way to store data, but arrays are inappropriate in many situations. A
major task of programming is creating other data-storage structures such as linked lists,
stacks, queues, hash tables, dictionaries, and vectors. (Here the word structure is used in a
general sense to mean any mechanism that stores data, not in reference to the C++ struct,
which you’ll encounter later.) Each such storage structure has advantages and disadvantages.
It’s faster to access a random data item or easier to add an item using some containers,
whereas using others, it’s easier to search or sort the data.
As it turns out, data storage structures belong in another category that lends itself to being
modeled by classes. Along with real-world objects such as hot dog stands and data types
such as time, you can also model arrays, linked lists, and so on.
Let’s look at an example in which a class models a stack. A stack is a data storage structure
that is convenient when you want to access the most recently stored data item first. This is
often referred to as LIFO, for Last In First Out. It’s like a stack of trays in a cafeteria. The
dishwasher puts trays on the stack and customers take them off. The last tray placed on the
stack goes on top, so it’s the first one to be removed. (That’s why the tray you get is often
still warm and damp; it’s been recently washed. Trays at the bottom of the stack may have
been there, unused, for weeks.)
Stacks are useful in many programming situations, such as parsing algebraic expressions
like 2*x+4*(3+y), where they are convenient for storing intermediate results. Hewlett
Packard calculators use a stack-based approach to calculations.
Stacks can hold any kind of data, from basic types to complicated class objects. However,
like an array, each stack usually holds only one kind of data, int or float or whatever,
but not a mixture. (You can avoid this limitation if you use templates, which you’ll
encounter in Chapter 11.) When you place a value on a stack, you are said to push it; when
you take a value off, it’s called popping it. Figure 3-6 shows what this looks like.
In this example, I use an array, as instance data of the Stack class, to store a number of
integers. Listing 3-2 shows STACK1, which specifies the Stack class and then tests it by
creating a stack, pushing three integers on it, and then popping them back off and displaying
them.
Listing 3-2 STACK1
// stack1.cpp
// class models a stack
#include <iostream.h>
void main()
{
Stack s1; // create a stack object
s1.init(); // initialize it
s1.push(11); // push 3 items onto stack
s1.push(12);
s1.push(13);
cout << s1.pop() << endl; // pop 3 items and display them
cout << s1.pop() << endl;
cout << s1.pop() << endl;
}
When you pop items off a stack, they appear in reversed order. Thus the output from this
program is
13
12
Notice that I use both prefix and postfix operators to manipulate the array index. This
instance variable, top, represents the top of the stack. (Think of a pointer to the top
cafeteria tray.) It’s initialized to -1 with the init() member function when main() first
starts. When items are pushed, top is incremented first (++top), and only then used as the
index, so top always points one place beyond the last item pushed. When items are popped,
they are accessed first and then the index is decremented (top--), so top again points just
above the top of the stack.
The actual instance data, st, which is used as the storage mechanism in an object of the
Stack class, is an ordinary array of type int. However, to the user, a Stack object seems
to operate like a stack: Data is added and removed with the push() and pop() member
functions, rather than with index numbers as in an array. The Stack class wraps an array
with class member functions so it looks, to the user, like a completely different storage
mechanism.
This is a common theme in OOP classes: They create a new interface between the
programmer and the data. An example of this is the use of classes to wrap the basic
Windows Application Program Interface (API) functions in a new and presumably
easier-to-use set of classes and member functions.
Not a Constructor
Initializing top to -1 using a function like init(), as I do in the STACK1 program, is not
the favored approach in OOP. One reason is that the programmer who writes main() must
remember to call this init() function every time an object (in this example, the Stack
object s1) is created. According to Murphy’s law, anything that can be forgotten, will
eventually be forgotten, so init() is not a good idea. The solution to this is the
constructor, which I’ll discuss in Chapter 5.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 2
1. To insert into an array the characters that constitute a name (such as John Smith)
entered by the user, you might say
a. j=0; while(ch != ‘ ‘) { ch=getche();
charray[++j]=ch; }
b. j=1; while( ch != ‘\r’ ) { ch=getche();
charray[++j]=ch; }
c. j=0; while(ch != ‘\r’) { ch=getche();
charray[j++]=ch; }
d. j=1; while( ch != ‘ ‘ ) { ch=charray[j++];
getche(ch); }
e. j=0; while(ch != 13) { ch=charray[++j]; getche(ch);
}
2. The employee class in the EMPLOY1 program
a. contains an instance data item that holds the names of all the employees.
b. stores data for a number of employees in each object.
c. includes a member function that displays an employee’s name and serial
number.
d. requires the length of the name data item to be initialized to -1.
e. requires that main() create an array of employee objects.
3. Data storage structures such as stacks and linked lists
a. can be implemented with classes.
b. must be implemented with classes.
c. should not be implemented with classes.
d. exist because an array isn’t always the most useful approach to data storage.
e. can store data using a different approach than an array.
4. When you push a data item onto a stack in the STACK1 program, you are actually
a. creating an object.
b. reading data.
c. calling an object’s member function.
d. inserting the data item in array position 0.
e. storing the data item in an array.
5. Which of the following arrangements of Stack class member functions would
work together properly, without wasting space?
a.
Exercise 1
Seven years after an employee leaves the Amalgamated Widgets company, the employee
records are purged of all information about that employee. Add a member function
called purge() to the employee class of the EMPLOY1 program. It should write over
the existing data for an employee so that when displayed, the name will have no
characters and the serial number will be 0. Modify main() so it tests this function.
Exercise 2
Sometimes it’s useful to examine the data item at the top of a stack without actually
popping the item off the stack. (That is, after you’ve read its value, the item remains at
the top of the stack.) A member function that does this is traditionally called peek().
Write such a function that works with the Stack class of the STACK1 program. Modify
main() to check it out.
The syntax for defining an array of objects is the same as that for defining an array of a
basic type. Of course, you must place the class specification in your file before you try to
define any arrays of objects (or single objects, for that matter). If you have already
created the specification for the Xerxes class, for example, then you can define an
array of objects of this class, called Xarray, like this:
class Xerxes
{
private:
int ivar;
float fvar;
public:
// member functions go here
};
then the elements of the array Xarray, each of which is an object of the class Xerxes,
will look as shown in Figure 3-7.
As it happens, you need to learn a new syntax, or at least a variation on some old syntax,
to reference member functions of objects stored in an array. Suppose you have a
specification for a class X like this:
class Xerxes
{
...
public:
void afunc()
{
// does something here
}
};
and further suppose that in main(), you’ve defined an array to 10 objects of class X
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Array of airtime Objects
I’ll use the same airtime class I introduced in the TIMERET program in Chapter 2.
In main() the program defines an array of airtime objects, obtains data for as
many such objects as the user wants to type, and finally, when the user has finished
with input, the program displays all the data. Listing 3-3 shows ARRAYAIR.
Listing 3-3 ARRAYAIR
// arrayair.cpp
// creates array of airtime objects
#include <iostream.h>
#include <iomanip.h> // for setw(), etc.
class airtime
{
private:
int minutes; // 0 to 59
int hours; // 0 to 23
public:
void set()
{
char dummy; // for colon
void main()
{
airtime at[20]; // array of 20 airtime objects
int n=0; // number of airtimes in array
char choice;
do
{ // get time from user
cout << “Airtime ” << n << “. ”;
at[n++].set(); // and insert in array
cout << “Do another (y/n)? ”;
cin >> choice;
} while(choice != 'n');
Airtime 0 = 1:01
Airtime 1 = 23:30
Airtime 2 = 10:00
The important statements in ARRAYAIR are
In a similar way, I can define an array of objects of type employee, last seen in the
EMPLOY1 program earlier in this chapter. Listing 3-4 shows ARRAYEMP.
// arrayemp.cpp
// models an employee
// uses array of employees
#include <iostream.h>
#include <conio.h> // for getche()
class employee
{
private:
char name[20]; // name (20 chars max)
int n; // length of name
int serial_number;
public:
void input() // get data from user
{
char ch;
n = 0;
cout << “ Enter name: ”;
do
{
ch = getche(); // get one char at a time
name[n++] = ch; // store in “name” array
} while(ch != '\r'); // quit if “Enter” key
cout << “\n Enter serial number: ”;
cin >> serial_number;
}
void output() // display employee data
{
cout << “ Name = ”;
for(int j=0; j<n; ++j) // display one character
cout << name[j]; // at a time
cout << “\n Serial number = ” << serial_number;
}
};
void main()
{
employee emps[100]; // array of employee objects
int n = 0; // number of objects in array
char choice;
do
{
cout << “Enter employee ” << n << “ data” << endl;
emps[n++].input(); // get data
cout << “Do another (y/n)? ”;
cin >> choice;
} while(choice != 'n');
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 3
1. The member function f() takes no arguments. To call this function for the fourth
element in an array arr of objects of type X, you would say
a. f().arr[3];
b. f(X).arr[3];
c. arr.f(X);
d. arr[3].f();
e. X arr.f()[3];
2. Suppose you have a two-dimensional array of airtime values, defined as
airtime aat[50][100];
How would you call the display() member function for the airtime variable
located at the 25th element in the 20th subarray?
a. aat[19, 24].display();
b. aat[19][24].display();
c. aat[24][19].display();
d. aat.display([24][19]);
e. aat[25][20].display();
3. An array of objects is useful when
a. there are many objects of the same class.
b. a number of objects of different classes must be stored and accessed.
c. a number of objects will all have the same values in their instance data.
d. there are many variables of the same basic type to store as class instance data.
e. a number of objects all have the same member functions.
4. Assume that n is 5 just before the statement emps[n++].input();, taken from
the ARRAYEMP program, is executed. You can conclude that
a. the sixth element of the array will be accessed.
b. the statement contains a subtle bug.
c. no additional statement is necessary to move the index to the next array
element.
d. no elements of emps will be modified.
e. data will be obtained from the user and placed in an object of type employee.
5. In the ARRAYEMP program, assume the user has entered three employees called
Henry James, Nathaniel Hawthorne, and Edgar Allan Poe (as in the example). How
many bytes of memory are allocated to storing names?
a. 45
b. 2,000
c. 48
d. 60
e. 51
Exercise 1
Start with the program HOTDOG3 from Session 4 in Chapter 2. Write a program that uses the
HotDogStand class, allows the user to enter data for up to 10 hot dog stands, and then
displays the data for all the stands, as in the arrayemp program. Use an array to hold the
HotDogStand objects.
Exercise 2
Starting with the program in Exercise 1, modify main() so the user can choose what action to
take by entering one of four letters: ‘i’ to initialize the data at a particular stand, ‘s’ to
record a sale at a particular stand, ‘r’ to report the current data for all the stands, or ‘q’ to
quit the program. You may want to use a switch statement to select among these choices and
a while or do loop to cycle repeatedly through the switch.
Session 4: Strings
You learned in Session 2 in this chapter that text—such as a name—can be treated strictly as an
array of characters. Loops can be used to input and output the text from such arrays, one
character at a time. This is all a little awkward. Text is so common in computer programs that
having to program a loop every time text must be handled is not acceptable. Fortunately, C++
provides a sophisticated repertoire of ways to simplify text handling.
The C++ method of text handling treats text as a string, which is a sequence of characters
terminated by a special character. This approach to text was developed in C, long before the
arrival of OOP, so such strings are not object oriented. I will sometimes call them C strings to
avoid confusion with more sophisticated string classes, which can be created only in C++.
However, in most cases the context is clear, and I’ll simply call them strings.
Although they are old fashioned, strings are a key feature of both C and C++ programming, and
often form the foundation of more sophisticated string classes. It’s therefore important to learn
about strings, which is what you’ll do in the next few lessons.
String Variables
A string is a sequence of characters in which the last character has a numerical value of 0
(zero). As a character, this value can be represented by the escape sequence ‘\0’. It is often
called the null character. Using a special value like this to indicate the end of a text string
means that there is no reason to store the length of the text as a separate integer value, as I did
in the EMPLOY1 program. Instead, string-handling routines look for the ‘\0’ to determine
when the string ends.
A string variable is an array of type char. Like other variables, it may or may not contain a
value at any given time. Here’s how you would define a string variable called str:
The user types the characters of the string and then presses . (As you know, this is
called entering the text.) Figure 3-8 shows how the array str looks in memory after the user
has entered the string “Amanuensis” (which means one employed to copy manuscripts).
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
String Constants
You can initialize a string variable to a value when you define it, just as you can
with other variables. Here’s one way to initialize a string variable called name to
the string “George”:
I’ve mentioned that you can use cout and cin for strings.
char str[80];
char str[80];
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Using const Variables
The code fragment just shown provides the motivation to introduce a new keyword. Notice
that the number 80 appears in two places: as the size of the array str and as the maximum
number of characters that can be placed in that array by cin.get(). These numbers should
be the same; if they’re not, the input from get() may overflow the array, or—less
seriously—space may be wasted in the array because get() can’t fill it up.
To ensure that these numbers are the same, it’s desirable to use a variable instead of a
numerical constant, like this:
#define SIZE 80
This construction can also be used in C++, but it has fallen out of favor because no data type
can be applied to the name, which means the compiler can’t verify that the correct type is
used. This makes programs potentially more error prone.
The const keyword can be used to improve program reliability in a variety of other
situations, notably with function arguments. I’ll explore these possibilities in Session 7 in
Chapter 5.
Nothing is perfect, and sometimes when you mix cin.get() with cin >>, a problem
arises (at least with some compilers). For example, suppose you say
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Real Strings for the employee Class
Earlier in this chapter, I showed several programs that used the employee class. In this class,
I stored the employee’s name as a simple array of characters, not as a real null-terminated
string. Let’s rewrite this class to use real strings. This will simplify input and output, because I
can use cin and cout for text, instead of for loops. I’ll also add a switch statement to
main() so the user can operate the program interactively, choosing repeatedly from a list of
options. Listing 3-5 shows STREMP.
Listing 3-5 STREMP
// stremp.cpp
// models database of employees
// uses switch statement for user choice
#include <iostream.h>
const int SMAX = 21; // maximum length of strings
const int EMAX = 100; // maximum number of employees
class employee
{
private:
char name[SMAX]; // name (20 chars max)
int serial_number;
public:
void input() // get data from user
{
cout << “ Enter name: ”;
cin.get(name, SMAX);
cout << “ Enter serial number: ”;
cin >> serial_number;
}
void main()
{
employee emps[EMAX]; // array of employee objects
int n = 0; // current number of objects in array
int j; // loop variable
char choice = 'x'; // (ensure it's not 'q')
switch(choice)
{
case 'a': // get employee data
cout << “Enter data for employee ” << (n+1) << endl;
emps[n++].input();
break;
case 'd': // display all employees
for(j=0; j<n; j++)
{
cout << “\nData for employee ” << (j+1) << endl;
emps[j].output();
}
break;
case 'q': // let the while loop
break; // terminate the program
default:
cout << “Unknown command”;
break;
} // end switch
} // end while
} // end main()
String I/O
Notice how using real strings simplifies the input() and output() member functions of
employee. The do and for loops have vanished.
I use cin.get() to read the employee’s name in the input() member function. This
requires that I use cin.ignore() in main() to eat the ‘\n’ after the user’s letter choice
so cin.get() won’t become confused.
The switch statement embedded in a while loop makes the program interactive. It displays
a list of possible letter choices; when the user enters one, the program carries out the
appropriate task before returning to the list of choices. Here’s some sample interaction with
STREMP:
External Variables
I should mention that SMAX and EMAX are defined in a new way. The variables you’ve seen
before have been defined either inside a class or inside the main() function. SMAX and EMAX
, on the other hand, are defined outside of everything: There are no braces surrounding them.
Variables defined this way are called external variables. External variables are accessible to all
parts of the program, whereas variables defined within a class are accessible only within the
class and variables defined inside a function are accessible only within the function. I’ll delve
into external variables Session 5 in Chapter 4.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 4
Exercise 1
Modify the employee class in the STREMP program of this lesson so that
it includes an additional item of instance data to hold the employee’s title,
such as “Editorial Assistant”, “Vice-president of Marketing”, “Laborer”,
and so forth. This modified class should work with the same main()
found in STREMP.
Exercise 2
Write a class called book that contains the data necessary to manage books
in a bookstore. This includes the title, publisher, price, and quantity on
hand. Write a main() that stores book objects in an array and allows the
user to add a book to the array interactively, display all the books, and sell a
book by decrementing the number on hand.
Midchapter Discussion
Don: Well, at least arrays are pretty straightforward.
George: Except for that little detail about the last element being
numbered one less than the size of the array. Or was it one
more? I can never remember. Anyway, whatever happened to
10 apples numbered from 1 to 10? That’s the natural way to
number things.
Estelle: But then you waste the place for number 0. In computers, the
first address, or the first anything, is always 0. It’s more
logical.
Don: You could do it either way, but in C++, arrays just happen to
start with 0. You better get used to it.
George: No need to get huffy.
Estelle: How come I learned about two different kinds of strings?
George: Huh?
Estelle: You know, I started off with “homemade” strings, as in the
EMPLOY1 program in Session 2. Then I learned about “real”
C++ strings, which are terminated with a zero.
George: I didn’t know there was a difference.
Estelle: Uh, oh. You’re in trouble.
Don: The homemade strings just showed how to treat text as as array
of characters. But doing that isn’t practical, because you need
loops to handle the characters individually. Real C strings are a
lot easier to use.
Estelle: You can use a single operator such as cout << to output a
real string all at once, instead of doing it one character at a
time.
Don: And real strings are null terminated, instead of having to store a
separate character count.
George: Is that good?
Don: Well, probably. It’s another deal where either way would work.
A C string is always one character longer than the number of
characters, but on the other hand, you don’t need an extra
integer variable to specify the length.
Estelle: Isn’t there a lot of stuff about strings I don’t know how to do?
Adding strings together, copying them, and comparing them?
Are there operators built into C++ to do all that?
Don: I think they use library functions instead of operators, and I bet
we’ll get to them soon.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Session 5: String Library Functions
The function library that accompanies most C and C++ compilers includes dozens of functions that operate on
strings. Functions copy strings, compare them, search them for other strings, and so on. You’ll look at a few of
these library functions in this lesson: the most common ones and those that will be useful later in this book.
Then you’ll put some of these library functions to work to create a simple string class, which can replace
ordinary C strings.
As with all library functions, you can find out more about the string functions by looking them up in your
compiler’s documentation or online help. What I cover here is just enough to get you started and give you an
idea what’s available. These string-oriented library functions all require the header file STRING.H.
The strlen() function returns the length of a string used as an argument. Here’s a fragment in which a string
variable s1 is initialized to “Songs” and the strlen() function is applied to s1:
Length of s1 = 5
Notice that the terminating ‘\0’ character is not included in the length reported by strlen(). Nevertheless,
that character is there in s1, taking up space in memory. The array s1 is actually 6 bytes long.
Copying Strings
You can copy a string variable or constant to another string variable using the strcpy() function.
Appending Strings
Appending strings might be called adding strings. If you append the string “hurry” onto the string “You
better “ the result is “You better hurry”. Here’s how that might look as a program fragment:
Comparing Strings
It’s often useful to compare strings—in checking passwords, for example. The strcmp() function compares
two strings and returns a number indicating that the strings are the same or, if they aren’t the same, which
comes first alphabetically. This function is case sensitive, so “Smith” is not the same as “smith”.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
A Homemade String Class
The motivation for creating a string class arises from the deficiencies of ordinary C strings
and string functions. What are these deficiencies? Here are a few:
• You must define an array, not a single variable, to store a string.
• You can’t copy one string to another with the assignment operator (=).
• The string functions don’t warn you if you overflow a string array.
• You can’t concatenate strings with the + operator.
• You can’t compare strings with the ==, !=, <, and > operators.
In this section, I’m going to take a first step toward creating a homemade string class. I’ll
address the first two items on the list here; the last three require that you know how to
overload C++ operators, so I’ll defer them until later.
I’ll name the string class xString. Its instance data consists of an ordinary string and it
includes member functions to initialize itself to an ordinary string, get a string from the user,
display its own contents, and append one string to another. A surprising benefit of making a
string into a class is that you can use the equal sign to set one xString object equal to
another. Listing 3-6 shows STRCLASS.
Listing 3-6 STRCLASS
// strclass.cpp
// uses a class to models strings
#include <iostream.h>
#include <string.h> // for strlen(), strcpy(), etc.
class xString
{
private:
char str[MAX]; // ordinary C string
public:
void init( char s[] ) // initialize with string
{
strcpy(str, s);
}
void input() // get string from user
{
cin.get(str, MAX);
}
void display() // display string
{
cout << str;
}
void append(xString xs) // append argument string
{
if(strlen(str) + strlen(xs.str) < MAX-1)
strcat(str, xs.str);
else
cout << “\nError: xString too long” << endl;
}
};
void main()
{
xString s1, s2, s3; // make xString objects
s1.append(s2); // append s2 to s1
s3 = s1; // set s3 to s1
s3.display(); // display s3
}
In main(), the program creates three xString objects. It initializes s1 to an ordinary
string using the function init(), gets text from the user for s2 with the input() function,
appends s2 to s1 with append(), sets s3 equal to s1 using the assignment operator, and
finally displays s3 with display(). This may not be exactly what you would want to do
with three string objects, but it does provide a chance to exercise all the member functions of
the xString class.
Now let’s look at some features of this program.
Library Functions
I use the strcpy(), strlen(), and strcat() library functions to copy, find the length
of, and append ordinary C strings. As you can see, these functions are far more convenient
than writing your own loops to do the same thing character by character.
Clean Format
You can see how easy it is to create xString objects. You don’t need to define arrays, only
simple variables (or what look like simple variables but are actually user-defined class
objects).
Data Conversions
As I’ve noted, the init() function is a rather clumsy way to initialize an object. You’ll
learn a more elegant approach when I talk about constructors. However, the init() function
does do something interesting and useful: It acts as a conversion function, converting an
ordinary C string into an xString object. The C string is given as an argument to init(),
which assigns its value to the xString object that called it. Converting from one data type
to another is an important topic in OOP; I’ll return to it later.
Appending
The member function append() appends its argument to the object that called it. In this
example, whatever name the user enters into s2 is appended to “Greetings, “.
Assignment
Perhaps surprisingly, the assignment operator (=) works with objects (at least these objects).
The program sets s3 equal to s1; when it displays s3, you can sees that s3 has indeed taken
on the value of s1. How can the equal sign work with objects that contain arrays, when it
doesn’t work with arrays? It seems odd at first, but makes sense when you think about it. For
one thing, because all objects of the same class are identical (except for their contents), it’s
easy to copy the data from one into another, whereas one array may be a different size than
another.
Overflow Protection
The input() member function provides built-in protection against the user overflowing the
xString storage buffer (the str array). The programmer doesn’t need to take any special
precautions, such as specifying a buffer size. Also, the append() function checks that the
two strings that it’s about to append won’t exceed the size of an xString. Thus the
xString class is safer to use than ordinary C strings.
Of course there’s a downside to this safety: All xStrings must be the same length. This
may waste a lot of memory space for short xStrings and precludes using long strings.
(Later I’ll show how objects of a string class can be made exactly as large as they need to be
using dynamic memory allocation.)
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Wrapping
Imagine that the xString class was created by one programmer, but another
programmer must write a main() that makes use of this class. The main()
programmer could use ordinary C strings and library functions such as strcpy(), or
she could choose to use the xString class instead. Thus, although the xString class
itself uses ordinary C strings and string library functions internally, these are invisible to
the main() programmer, who instead sees xString objects and member functions
such as input() and append(). This is another example of wrapping one set of
functions with another to create a new user interface.
Quiz 5
Exercise 1
For the xString class, create a member function called compare() that compares
an xString given to it as an argument with the xString that called it. This function
should return -1 if the argument comes before the object alphabetically, 0 if the two are
the same, and 1 if the argument comes after the object.
Exercise 2
For the xString class, create a member function that takes two xString objects as
arguments, concatenates them, and places the resulting value in the object that called
the function. You can name this function concat(). Add some statements to main()
to test it.
You can create an array of empty string variables using a simple two-dimensional array.
Because a string is an array, an array of strings is an array of arrays.
Suppose you don’t know yet what strings you want to store but you want to create space
for them. To store five strings, each of which can be up to 10 characters long, you
would define an array this way:
You might write some code so the user could enter names into this array:
Here the for loop won’t let the user enter more than five names. By pressing
and thus inserting a 0-length name into the array, the user can exit the loop after
entering fewer than five names.
Notice that a single string is referred to as names[j], with only one index. As I
mentioned earlier, this is how you refer to one subarray in a two-dimensional array.
You can initialize the strings in an array of strings when you create them. Here’s an
example in which I store the days of the week:
Note: Note in Figure 3-10 that there is some space wasted at the ends of the shorter
string constants because all the string variables are the same length. You can avoid this
if you use pointers, as you’ll see later.
I’ve used the variable names MAX and DPW for the array dimensions and made them
type const, as discussed earlier. I’ve also made the entire array type const because
presumably the days of the week will never need to be changed.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The weekday Class
I’ll turn the weekday names into a class. Because I don’t want to duplicate the day_name
array in every object, I’ll make it an external variable. (I could also have made it a static
variable within the class. I’ll discuss static variables, along with external variables, in Chapter
4. For the moment, all you need to know is that external variables can be accessed from
anywhere in the program.) Listing 3-7 shows WEEKDAYS.
Listing 3-7 WEEKDAYS
// weekdays.cpp
// creates a class of days of the week objects
#include <iostream.h>
#include <string.h> // for stricmp()
////////////////////////////////////////////////////////////////
void main()
{
weekday wd; // make a weekday object
Notice in the interaction with the program that the user typed “wednesday” with a lowercase
‘W’. The weekday class recognizes this as a legitimate weekday name because it uses the
stricmp() comparison function, which ignores case. This provides a more forgiving
approach to user input.
You may find the two lines in the add() member function somewhat puzzling. To add a fixed
number of days to the day_number variable, you might expect a statement such as
day_number += days;
What’s this all about? The designers of C and C++ considered brevity a good thing and so
developed this shorthand format. Both statements have exactly the same effect, but the
day_number variable is named only once in the second statement. This is made possible by
the arithmetic assignment operator +=. This operator takes the value on its right and adds it to
the variable on its left, leaving the result in the variable on its left. Figure 3-11 shows how this
looks when the value of item is added to the value of total and the result is stored in
total.
It turns out that there are arithmetic assignment operators for all the arithmetic operations.
a += b; // same as a = a + b
a -= b; // same as a = a - b
a *= b; // same as a = a * b
a /= b; // same as a = a / b
a %= b; // same as a = a % b
There are a few other assignment operators as well, but you can ignore them for now.
In the WEEKDAYS program, I use the last of these operators, %=, to make sure that
day_number is always in the range of 0 to 6, no matter how many days you add to it.
day_number %= DPW;
This sets day_number to the remainder produced when day_number is divided by 7.
Arithmetic assignment operators provide a way to make your listing look less cumbersome,
possibly at the expense of some clarity for newcomers to C++.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 6
Exercise 1
Exercuse 2
Create a class called ordinal that models the names of the ordinal
numbers, “first”, “second”, “third”, “fourth”, “fifth”, and so on, up to
“twelfth”. Provide input and output member functions that get and display
such values using either of two formats: ordinary integers (1, 2, and so on)
or ordinals. Also, provide an add() function that allows you to add a fixed
integer to objects of the class. Assume that no number larger than 12 or less
than 1 will be represented.
Session 7: Structures
A C++ structure is a way to group together several data items that can be of
different types. An array, by contrast, groups a number of data items of the
same type. Structures are typically used when several data items form a
distinct unit but are not important enough to become a class. Structures are
more important in C, where there are no classes. In C++, a class plays many
of the roles filled by structures in C. Nevertheless, there are still many
situations where structures are useful.
Specifying a Structure
struct part
{
int modelnumber;
int partnumber;
float cost;
};
A structure consists of the keyword struct, followed by the structure
name (also called the tag) and braces that surround the body of the
structure. It is terminated with a semicolon. The body of the structure
usually consists of various data items, which may be of different types.
These individual data items are called members. (As you recall, the
individual data items within an array are called elements.) Figure 3-12
shows the syntax.
To define variables of type struct part, you would say something like
struct part
{
int modelnumber;
int partnumber;
float cost;
} cp1, cp2;
This code specifies the structure and creates two variables of that structure
at the same time. A peculiarity of this format is that you can remove the
structure name or tag (part); it’s not needed because the variables already
know what structure they’re based on.
This shortcut format is not as easy to understand as using separate
statements for the specification and the definition, so I’ll avoid it in the
example programs.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Accessing Structure Members
The dot operator is used to access structure members, just as it’s used to access member functions
from objects. Here’s how that looks:
You can provide initial values for structure variables just as you can for arrays. Here’s how you
would initialize a structure variable of type part to the same values seen in Figure 3-14:
Structure Usage
Let’s modify an earlier example program so that it uses a structure. Notice in the STACK1
program, from Session 2 in this chapter, that the array that stores data items and the index to this
array (the top of the stack) are closely related. You might find it convenient to combine these two
data items into a structure, and then use the structure as a single data member of the Stack class.
Listing 3-8 shows how this might look.
Listing 3-8 STRUSTAK
// strustak.cpp
// class models a stack, uses struct for array and index
#include <iostream.h>
struct stackette // structure
{
int arr[20]; // storage array
int top; // index of top of stack
};
void main()
{
Stack s1; // create a stack object
s1.init(); // initialize it
s1.push(11); // push 3 items onto stack
s1.push(12);
s1.push(13);
cout << s1.pop() << endl; // pop 3 items and display them
cout << s1.pop() << endl;
cout << s1.pop() << endl;
}
Here the structure stackette holds the array of integers and the index that points to the top of
the stack (the last item placed in the array).
struct stackette
{
int arr[20]; // storage array
int top; // index of top of stack
};
The only item of instance data in the Stack class is now a variable of class stackette.
stackette st;
Member functions of Stack now refer to individual data members of st, using the dot operator
st.top = -1;
and
st.arr[++st.top] = var;
Notice that main() is identical to the main() in the STACK1 program. I’ve modified the class
but kept the class interface the same, so no rewriting is necessary for the functions that use the
class.
It’s doubtful whether in this particular programming example the use of a structure provides
significant simplification. However, once I discuss pointers, you’ll encounter examples where the
use of structures as class members can indeed clarify the workings of a program. (See the linked
list example in Session 7 in Chapter 8.)
I’ve emphasized the use of structures as aggregates of data items. This is the way structures are
usually used. With this emphasis, structures appear quite different from classes. We might say that
a structure is a passive grouping of data items, whereas a class is an active combination of data
and functions. Of course, classes are far more important in C++ than structures are. Classes form
the very basis of object-oriented programming, whereas structures are a minor part of C++ usage.
However, these differences obscure the fact that the syntax of structures and classes is almost
identical. You can install member functions within a structure just as you can in a class, and
conversely you can remove the member functions from a class so that it acts like a structure.
There is only one real syntactical difference between a structure and a class: The members of a
structure are public by default, whereas the members of a class are private by default. That is, if
you don’t use the keywords public or private, class members are private.
class Foo
{ // no need for keyword, private by default
int george;
int harry;
public: // public must be specified
void init()
{ }
void display()
{ }
};
For clarity, I’ll always use the keyword private in class specifications in the example programs,
but in fact it’s optional. In structures, the situation is reversed.
struct Bar
{ // no need for keyword, public by default
void init()
{ }
void display()
{ }
private: // private must be specified
int george;
int harry;
};
In structures, you usually want all the data members to be public, so you normally leave out this
keyword.
These syntax distinctions are all rather academic because in most situations, using a structure as a
class or vice versa would simply cause confusion. No doubt, the designers of C++ borrowed the
syntax of C structures when they invented classes and then—perhaps to make compiler design
easier—augmented structures to make them as similar as possible to classes.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Quiz 7
1. Specifying a structure
a. uses syntax similar to a class specification.
b. creates a structure variable.
c. requires the use of the keyword public.
d. usually involves a number of member data items.
e. usually involves a number of member functions.
2. Structures are normally used
a. as an alternative way to create classes and objects.
b. to group data items of the same type.
c. to group data items of different types.
d. to combine data items that are closely related into a single unit.
e. to increase memory efficiency.
3. Accessing a structure’s member data
a. is normally carried out using the structure’s member functions.
b. is normally carried out by a function located somewhere outside the
structure.
c. uses a similar format to accessing member functions in classes.
d. is easy because the data is public by default.
e. requires the dot operator (ignore pointers).
4. In the STRUSTAK program,
a. statements in main() access the stackette structure using the dot
operator.
b. a structure variable is included in the Stack class.
c. a structure specification is included in the Stack class.
d. there is only one member data item in the Stack class.
e. member functions in Stack need not be modified when a
stackette structure is inserted in the class in place of individual data
items of basic types.
5. Structures and classes differ in that
a. the public versus private distinction is applicable to classes but not to
structures.
b. structures are usually used only for data, not for data and functions.
c. data members may be accessed using the dot operator in classes, but
not in structures.
d. structures use brackets, whereas classes use braces.
e. both data and function members are public by default in structures, but
private by default in classes.
Exercise 1
Exercise 2
Modify the EMPLOY1 program from Session 2 in this chapter so that the array name
and the integer n, used as instance data in the employee class, are combined into a
structure. Substitute a variable of this structure type for these two member data items
and make whatever modifications are necessary for the employee class to work as it
did in EMPLOY1.
Creating a new type using enumerated data types is akin to creating a new type using a
class, as I did in programs that used the airtime class. However, enumerated data
types are much more limited than types created using classes. You can’t create
member functions to specify the operation of enum types; instead, they always behave
more or less like integers. Enumerated types existed in C long before OOP was
invented. You can think of them as a sort of poor person’s approach to creating one’s
own data types. However, given the right situation, they work well and they are
simpler to implement than classes. Enumerated types are usually used when a variable
has only a few allowable values, because every value must be named.
In the WEEKDAYS example (Listing 3-7), I used a class to define a new data type
consisting of days of the week. This type also lends itself to being specified by an
enumerated type. Here’s how that would look:
Here’s how you might create and give values to some enumerated variables:
// specify a type
day1 = Easter;
will not compile because day1 is a variable of type days_of_week and Easter is
not on the list of possible values for days_of_week.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
They’re Really Integers
The compiler actually stores enumerated values as integers, starting with 0 for the first name
specified. In the previous figure, north is 0, south is 1, east is 2, and west is 3. In the
days-of-the-week example Sun is 0, Mon is 1, Tue is 2, and so on. Alternatively, you can
specify that the series begins on a value other than 0. For example, you could say
Let’s look at a lengthy example that uses arrays, structures, and enumerated types. This
program features a class, card, that models the playing cards used in games such as bridge,
hearts, and poker. Each object of this class represents a single card. Instance data records the
card’s number (2 through 10, jack, queen, king, and ace) and suit (clubs, diamonds, hearts,
spades). Member functions are provided to give the card an initial value and to display its
value.
In main(), the program defines an array of 52 objects of type card and calls it deck. It
then gives appropriate values to all these cards in order and displays the result. Next, it
shuffles the deck by exchanging each card with another in a random location in the deck
array. Finally, it displays the resulting shuffled deck. Listing 3-9 shows CARDARAY.
Listing 3-9 CARDARAY
// cardaray.cpp
// cards as objects
#include <iostream.h>
#include <stdlib.h> // for randomize(), rand
#include <time.h> // for randomize()
#include <conio.h> // for getche()
class card
{
private:
int number; // 2 to 10, jack, queen, king, ace
Suit suit; // clubs, diamonds, hearts, spades
public:
void init(int n, Suit s) // initialize card
{ suit = s; number = n; }
void display() // display the card
{
if( number >= 2 && number <= 10 )
cout << number;
else
switch(number)
{
case jack: cout << “J”; break;
case queen: cout << “Q”; break;
case king: cout << “K”; break;
case ace: cout << “A”; break;
}
switch(suit)
{
case clubs: cout << 'c'; break;
case diamonds: cout << 'd'; break;
case hearts: cout << 'h'; break;
case spades: cout << 's'; break;
}
} // end display()
}; // end class card
void main()
{
card deck[52]; // deck of cards
int j = 0; // counts thru deck
int num; // card number
Ordered deck:
2c 3c 4c 5c 6c 7c 8c 9c 10c Jc Qc Kc Ac
2d 3d 4d 5d 6d 7d 8d 9d 10d Jd Qd Kd Ad
2h 3h 4h 5h 6h 7h 8h 9h 10h Jh Qh Kh Ah
2s 3s 4s 5s 6s 7s 8s 9s 10s Js Qs Ks As
Shuffled deck:
3c 5c Qc 9s Ah Kd 6h 7h 4s As 2h 5d Ks
7c Js 3s 10h 8s Jc Jh Ac 5s 7s Qs 10d 2c
Jd 8d 4d 2d 6s 4h 10s 6d 4c Ad Qh 7d 6c
10c 9c 3h 8c 5h 2s Kc 9h Qd 8h 3d 9d Kh
The program uses an enum type for the suit because there are only four possible values:
clubs, diamonds, hearts, and spades. This makes it impossible for the program to assign any
value other than these four. If you try to use an integer value, for example, the compiler will
issue a warning.
The enum approach would be unwieldy for the card number, so I use an integer that can have
a value from 2 to 14, with the special names jack, queen, king, and ace given to the
values 11, 12, 13, and 14. The display() member function uses switch statements to
figure out the appropriate display, given the suit and number of each card.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
The bool Keyword
Until recently, there was no Boolean type in C++. Such a type (named after George Boole, a
British mathematician), has only two values: true and false. Variables of this type are used to store
the results of logic tests, such as the answer to such questions as, “Is alpha less than 10?”.
Until recently, C and C++ programmers either used an integer type for a Boolean value or had to
create their own using an enum.
boolean flag;
and set to one of the two permissible values
if(alpha<10)
flag = false;
However, this homemade Boolean type is now unnecessary. A recent revision to the C++ draft
standard introduces a new keyword to specify Boolean variables, bool, and two new predefined
literal values for it, true and false. A variable of type bool must have one of these two
values.
Thus, to define a Boolean variable flag, simply say
bool flag;
There’s no need for an enum.
You can convert bool values to integers: true becomes 0 and false becomes 1. You can also
convert integers to bool values: 0 becomes false and all other values become true.
In the WEEKDAYS program in Session 6 in this chapter, I used an integer variable, gotit, to
indicate whether or not the user had typed a correct day name. I used the value 0 to indicate false
and 1 to indicate true. That’s not as clear or elegant as it might be. The new bool type allows you
to use a type designed for true/false values. Listing 3-10 shows WEEKDAYS rewritten to use a
bool type for the gotit flag.
Listing 3-10 BOOL
// bool.cpp
// creates a class of days of the week objects
// uses bool for logic value
#include <iostream.h>
#include <string.h> // for stricmp()
////////////////////////////////////////////////////////////////
void main()
{
weekday wd; // make a weekday object
Quiz 8
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Exercise 1
Revise the main() part of the CARDADAY program so that, after shuffling
the deck, it deals a hand of 13 cards to each of four players (as in bridge). A
player can be represented by an array holding that player’s cards. After
dealing the four hands, display them.
Exercise 2
Summary: Chapter 3
In this chapter, I’ve focused on arrays and strings, with brief looks at
structures and enumerated types. An array is a way to group a number of
variables of the same type and to refer to them using an index number or
subscript. The index number is represented by a number in brackets, such as
[5]. Arrays are the most commonly used data storage structure, but they
do not warn the programmer when an array index is out of bounds, so it’s
easy to make programming mistakes that cause serious malfunctions.
The increment and decrement operators, ++ and --, can be placed either
before or after their operands. If placed before (++x), the operand is
incremented (or decremented, if -- is used) before its value is used in an
expression. If placed after (x++), the operand is incremented (or
decremented) after its value is used.
A string is a group of characters terminated with a null character, ‘\0’,
which has the numerical value 0. A string variable is an array of type char
used for storing strings. A string constant is defined using double quotes:
“George Smith”. Special library functions exist for processing strings.
Strings (i.e., ordinary C strings) have nothing to do with classes and OOP.
However, string classes can be created that act like strings and improve
upon them in various ways.
A variable defined with the const modifier must be initialized to a value
when it’s defined; this value cannot be modified during the course of the
program.
A structure is, from a syntax standpoint, almost identical to a class; the only
difference is that a structure’s members are public by default, whereas a
class’s are private. However, a structure usually does not have member
functions; it’s used only as a way to group data items together. In many
cases in C++, a class replaces a structure.
Enumerated types allow the programmer to specify a new data type that can
be given only a small number of values whose names are specified. This
can aid in clarity and safety.
End-of-Chapter Discussion
George: Great. Now I have three kinds of strings to worry about.
Homemade, regular, and classes. When do you use what?
Estelle: Forget about homemade strings. No one would use them in a
real program; that was just a teaching device.
Don: In C++, assuming you have a string class, it’s easier to use
strings that are class objects than ordinary C strings.
Estelle: Why?
Don: Because you can define whatever string operation you want,
using member functions. But to write the class in the first
place, you need to use C strings. They’re the fundamental way
to handle text.
George: So you need to understand both?
Don: Ideally. But in your case, I’d stick with one or the other. No use
getting confused.
George: A wise guy, eh?
Estelle: I don’t understand why I need structures. It seems like classes
do everything structures do and then some.
Don: I don’t think structures are as important in C++ as they are in
C, for just that reason. But every now and then you probably
want to stick some data items together without going to the
trouble of making it a full-blown class.
George: What about enumerated types? What are they really for?
Don: I bet if they hadn’t already been part of C when C++ was
invented, no one would have bothered. For sure you can use a
class to do everything that enumerated types do. At least, I
think so. But because they’re in the language, you may as well
use them.
George: Use them when?
Estelle: Whenever some variable is going to have just a small number
of possible values.
George: What’s the advantage?
Don: It makes the listing clearer and makes it easier for the compiler
to find bugs in your program. If you assign an incorrect value
to an enumerated type, the compiler lets you know.
George: Who needs that? I never make programming mistakes anyway.
Estelle: Oh, right.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
CHAPTER 4
FUNCTIONS
Functions are important not only because they are one of the two major components of
objects but also because they are one of the basic ways to organize C++ programs.
I’ll first summarize what you’ve learned about functions so far. Then I’ll examine a new
way to write member functions outside of the class specification. I’ll look at the
advantages of overloading functions, which means giving the same name to different
functions, and default arguments, which allow a function to be called with fewer
arguments than it really has.
In the second half of this chapter, I’ll switch gears to a related topic: storage classes.
You’ll see how variables acquire different characteristics from two sources: first, from
the location of the variable with regard to functions and classes and second, from the use
of special keywords. Finally, I’ll examine references, a new way to pass arguments to
functions and to return values from them.
Review
I’ve been using functions all along. So far, you’ve seen them in three different situations.
First, I introduced member functions, which are one of the two fundamental components
of classes, along with instance data.
Second, functions appear independently in the listing, outside of any class specification.
An important function in this category is main(), which is the function to which control
is transferred when a C++ program starts to run. At the end of this lesson, you’ll see that
main() can, in effect, be divided into many functions.
Third, you’ve seen examples of C library functions such as getche() and strlen(),
which are called from main() or from member functions. The programmer does not
need the source code for library files; instead, object (.OBJ) or library (.LIB) files are
linked to user-written object files to create the final .EXE file. Library functions require
that the appropriate header file (e.g., CONIO.H for the getche() function) be included
in the source file.
Now let’s look in more detail at the various aspects of functions.
Function Calls
func1();
This call causes the function func1() to be executed. This particular call takes no
arguments and does not return a value. It consists of the name of the function followed by
parentheses. Note that, like any other program statement, it is terminated with a
semicolon.
Functions, along with classes, serve as one of the important organizing principles of C++
programs. They also increase memory efficiency: A function can be executed many times
without the need for inserting duplicates of its code into the listing. Figure 4-1 shows how
three different statements cause the same section of code to be executed.
Function Definitions
A function definition consists mostly of the function’s code, that is, the statements that
make up the function and cause it to do something. When I say function, I usually mean
the function definition. The function definition starts with a line called the declarator,
which specifies the function name, its return type, and the types and names of arguments.
Parentheses follow the function name and enclose the arguments, if any. The function’s
statements, which are the function body, are enclosed in braces. Figure 4-2 shows a
typical function definition.
Arguments
Arguments are the mechanism by which information may be passed from a function call
to the function itself. As an example, say that somewhere in your program is a function
called repchar(), whose purpose is to display a given character a certain number
times. The character to be displayed and the number of times to display it are passed as
arguments to the function. If you want to display 10 x’s in a line, for example, you might
call the function this way:
// repchar()
// displays n characters with value ch
repchar('=', 30);
it will display 30 equal signs.
When data is passed as arguments in this way, the data is copied and the duplicate values
are stored in separate variables in the function. These variables are named in the function
declarator. This is called passing by value. Figure 4-3 shows how this looks.
Technically, the term arguments means the values specified in the function call, whereas
the term parameters is given to the variables in the function definition into which the
values are copied. However, argument is often used loosely for both meanings.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.
-----------
Return Values
A function can return a value to the statement that called it. Here’s a function that
converts pounds (lbs) to kilograms (kg). It takes one argument, a value in pounds, and
returns the corresponding value in kilograms.
// lbstokg()
// converts pounds to kilograms
float lbstokg(float pounds) <--declarator
{
float kilograms = 0.453592 * pounds;
return kilograms;
}
The type of the value returned, in this case float, must precede the function name in
the function declarator. As I’ve mentioned, if a function does not return any value,
void is used as a return type to indicate this.
If a function does return a value, the return statement must be used. The value to be
returned is specified following the keyword return.
return kilograms;
This statement causes control to return immediately to the function call, even if it is not
the last statement in the function definition. It also causes the entire function call
expression to take on the value of the returned variable kilograms.
This function might be called this way:
kgs = lbstokg(lbs);
The function call expression, lbstokg(lbs), itself takes on the value returned and
can be assigned to a variable, in this case, kgs.
If the pounds variable happens to be 165.0 when this call is made, then the value
74.84 (i.e., 165.0 times 0.453592) is assigned to kgs when the function returns. Figure
4-4 shows how this value is copied from the kilograms variable in the function to the
kgs variable in main().
Function Declarations
In the example programs you’ve seen so far, all the functions were member functions of
a class, the main() function itself, or library functions. However, there are many other
possibilities. For one thing, main() can call other functions. Or, member functions
may call other functions located inside or outside the class.
When the compiler generates the machine language instructions for a function call, it
needs to know the name of the function, the number and types of arguments in the
function, and the function’s return value. If the function was defined someplace before
the function call, this isn’t a problem. The compiler will have taken note of these
characteristics and stored them away for reference. However, if the function definition
follows the function call, the compiler won’t know how to handle the call and you’ll get
an “undefined function” error message.
To keep this from happening (and assuming it’s not convenient to rearrange things so
the function definition precedes all calls to it), you must declare the function before any
function call. If you do this, the function can be defined anywhere. A function
declaration is a single line that tells the compiler the name of the function, the number
and types of arguments, and the return type. Here’s how you would declare the
lbstokg() function:
Arguments
Although they aren’t necessary, argument names can be used in the declaration.
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is
prohibited. Read EarthWeb's privacy statement.