Tải bản đầy đủ (.pdf) (10 trang)

Absolute C++ (4th Edition) part 25 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (216.72 KB, 10 trang )

Classes 243
A programmer who uses a class also should not need to know how the data of the
class is implemented. The implementation of the data should be as hidden as the imple-
mentation of the member functions. In fact, it is close to impossible to distinguish
between hiding the implementation of the member functions and the implementation
of the data. To a programmer, the class
DayOfYear (Display 6.3) has dates as data, not
numbers. The programmer should not know or care whether the month March is
implemented as the
int value 3, the quoted string "March", or in some other way.
Defining a class so that the implementation of the member functions and the imple-
mentation of the data in objects are not known, or is at least irrelevant, to the program-
mer who uses the class is known by a number of different terms. The most common
terms used are information hiding, data abstraction, and encapsulation, each of
which means that the details of the implementation of a class are hidden from the pro-
grammer who uses the class. This principle is one of the main tenets of object-oriented
programming (OOP). When discussing OOP, the term that is used most frequently is
encapsulation. One of the ways to apply this principle of encapsulation to your class
definitions is to make all member variables private, which is what we discuss in the next
subsection.

PUBLIC AND PRIVATE MEMBERS
Look back at the definition of the type DayOfYear given in Display 6.3. In order to use
that class, you need to know that there are two member variables of type
int that are
named
month and day. This violates the principle of encapsulation (information hiding)
that we discussed in the previous subsection. Display 6.4 is a rewritten version of the
class
DayOfYear that better conforms to this encapsulation principle.
Notice the words


private: and public: in Display 6.4. All the items that follow
the word
private: (in this case the member variables month and day) are said to be
private, which means that they cannot be referenced by name anyplace except within
the definitions of the member functions of the class
DayOfYear. For example, with this
changed definition of the class
DayOfYear, the following two assignments and other
indicated code are no longer permitted in the
main function of the program and are
not permitted in any other function definition, except for member functions of the
class
DayOfYear:
DayOfYear today; //This line is OK.
today.month = 12;//ILLEGAL
today.day = 25;//ILLEGAL
cout << today.month;//ILLEGAL
cout << today.day;//ILLEGAL
if (today.month
== 1) //ILLEGAL
cout << "January";
Once you make a member variable a private member variable, there is no way to
change its value (or to reference the member variable in any other way) except by using
one of the member functions. That means that the compiler will enforce the hiding of
encapsu-
lation
private:
private
member
variable

06_CH06.fm Page 243 Wednesday, August 13, 2003 12:54 PM
244 Structures and Classes
Display 6.4 Class with Private Members
(part 1 of 3)
1 #include <iostream>
2 #include <cstdlib>
3 using namespace std;
4 class DayOfYear
5 {
6 public:
7 void input(
);
8 void output(
);
9 void set(int newMonth, int newDay);
10 //Precondition: newMonth and newDay form a possible date.
11 void set(int newMonth);
12 //Precondition: 1 <= newMonth <= 12
13 //Postcondition: The date is set to the first day of the given month.

14 int getMonthNumber(
); //Returns 1 for January, 2 for February, etc.
15 int getDay(
);
16 private:
17 int month;
18 int day;
19 };
20 int main(
)

21 {
22 DayOfYear today, bachBirthday;
23 cout << "Enter today’s date:\n";
24 today.input(
);
25 cout << "Today’s date is ";
26 today.output(
);
27 cout << endl;
28 bachBirthday.set(3, 21);
29 cout << "J. S. Bach’s birthday is ";
30 bachBirthday.output(
);
31 cout << endl;
32 if ( today.getMonthNumber(
) == bachBirthday.getMonthNumber( ) &&
33 today.getDay(
) == bachBirthday.getDay( ) )
34 cout << "Happy Birthday Johann Sebastian!\n";
35 else
36 cout << "Happy Unbirthday Johann Sebastian!\n";
37
38 return 0;
39 }
40 //Uses iostream and cstdlib:
41 void DayOfYear::set(int newMonth, int newDay)
Private members
This is an improved version
of the class
DayOfYear that we gave

in Display 6.3.
Note that the function name set is
overloaded. You can overload a
member function just like you can
overload any other function.
06_CH06.fm Page 244 Wednesday, August 13, 2003 12:54 PM
Classes 245
Display 6.4 Class with Private Members
(part 2 of 3)
42 {
43 if ((newMonth >= 1) && (newMonth <= 12))
44 month = newMonth;
45 else
46 {
47 cout << "Illegal month value! Program aborted.\n";
48 exit(1);
49 }
50 if ((newDay >= 1) && (newDay <= 31))
51 day = newDay;
52 else
53 {
54 cout << "Illegal day value! Program aborted.\n";
55 exit(1);
56 }
57 }
58 //Uses iostream and cstdlib:
59 void DayOfYear::set(int newMonth)
60 {
61 if ((newMonth >= 1) && (newMonth <= 12))
62 month = newMonth;

63 else
64 {
65 cout << "Illegal month value! Program aborted.\n";
66 exit(1);
67 }
68 day = 1;
69 }
70
71 int DayOfYear::getMonthNumber(
)
72 {
73 return month;
74 }
75 int DayOfYear::getDay(
)
76 {
77 return day;
78 }
79 //Uses iostream and cstdlib:
80 void DayOfYear::input(
)
81 {
82 cout << "Enter the month as a number: ";
83 cin >> month;
84 cout << "Enter the day of the month: ";
85 cin >> day;
Mutator functions
Accessor functions
Private members may
be used in member

function definitions
(but not elsewhere).
06_CH06.fm Page 245 Wednesday, August 13, 2003 12:54 PM
246 Structures and Classes
the implementation of the data for the class DayOfYear. If you look carefully at the pro-
gram in Display 6.4, you will see that the only place the member variable names
month
and day are used is in the definitions of the member functions. There is no reference to
today.month, today.day, bachBirthday.month, or bachBirthday.day anyplace outside
the definitions of member functions.
All the items that follow the word
public: (in this case the member functions) are
said to be public, which means that they can be referenced by name anyplace. There
are no restrictions on the use of public members.
Any member variables can be either public or private. Any member functions can be
public or private. However, normal good programming practices require that all mem-
ber variables be private and that typically most member functions be public.
You can have any number of occurrences of
public and private access specifiers in
a class definition. Every time you insert the label
public:
the list of members changes from private to public. Every time you insert the label
private:
the list of members changes back to being private members. You need not have just one
public and one private group of members. However, it is common to have just one
public section and one private section.
Display 6.4 Class with Private Members
(part 3 of 3)
86 if ((month < 1) || (month > 12) || (day < 1) || (day > 31))
87 {

88 cout << "Illegal date! Program aborted.\n";
89 exit(1);
90 }
91 }
92 void DayOfYear::output(
)
93

<
The rest of the definition of DayOfYear::output is given in Display 6.3.
>
S
AMPLE
D
IALOGUE
Enter today’s date:
Enter the month as a number: 3
Enter the day of the month: 21
Today’s date is March 21
J. S. Bach’s birthday is March 21
Happy Birthday Johann Sebastian!
public:
public
member
variable
06_CH06.fm Page 246 Wednesday, August 13, 2003 12:54 PM
Classes 247
There is no universal agreement about whether the public members should be listed
first or the private members should be listed first. The majority seem to prefer listing
the public members first. This allows for easy viewing of the portions programmers

using the class actually get to use. You can make your own decision on what you wish
to place first, but the examples in the book will go along with the majority and list the
public members before the private members.
In one sense C++ seems to favor placing the private members first. If the first group
of members has neither the
public: nor the private: specifier, then members of that
group will automatically be private. You will see this default behavior used in code and
should be familiar with it. However, we will not use it in this book.

ACCESSOR AND MUTATOR FUNCTIONS
You should always make all member variables in a class private. You may sometimes
need to do something with the data in a class object, however. The member functions
will allow you to do many things with the data in an object, but sooner or later you will
want or need to do something with the data for which there is no member function.
How can you do anything new with the data in an object? The answer is that you can do
anything you might reasonably want, provided you equip your classes with suitable
accessor and mutator functions. These are member functions that allow you to access
and change the data in an object in a very general way. Accessor functions allow you to
read the data. In Display 6.4, the member functions
getMonthNumber and getDay are
accessor functions. The accessor functions need not literally return the values of each
member variable, but they must return something equivalent to those values. For exam-
ple, for a class like
DayOfYear, you might have an accessor function return the name of
the month as some sort of string value, rather than return the month as a number.
Mutator functions allow you to change the data. In Display 6.4, the two functions
named
set are mutator functions. It is traditional to use names that include the word
get for accessor functions and names that include the word set for mutator functions.
(The functions

input and output in Display 6.4 are really mutator and accessor func-
tions, respectively, but I/O is such a special case that they are usually just called I/O
functions rather than accessor or mutator functions.)
Your class definitions should always provide an adequate collection of accessor and
mutator functions.
It may seem that accessor and mutator functions defeat the purpose of making mem-
ber variables private, but that is not so. Notice the mutator function
set in Display 6.4.
It will not allow you to set the
month member variable to 13 or to any number that does
not represent a month. Similarly, it will not allow you to set the
day member variable to
any number that is not in the range
1 to 31 (inclusive). If the variables were public you
could set the data to values that do not make sense for a date. (As it is, you can still set
the data to values that do not represent a real date, such as February 31, but it would be
easy to exclude these dates as well. We did not exclude these dates to keep the example
simple.) With mutator functions, you can control and filter changes to the data.
accessor
function
mutator
function
06_CH06.fm Page 247 Wednesday, August 13, 2003 12:54 PM
248 Structures and Classes
Tip
Self-Test Exercises
12. Suppose your program contains the following class definition,
class Automobile
{
public:

void setPrice(double newPrice);
void setProfit(double newProfit);
double getPrice(
);
private:
double price;
double profit;
double getProfit(
);
};
and suppose the main function of your program contains the following declaration and that
the program somehow sets the values of all the member variables to some values:
Automobile hyundai, jaguar;
Which of the following statements are then allowed in the main function of your program?
hyundai.price = 4999.99;
jaguar.setPrice(30000.97);
double aPrice, aProfit;
aPrice = jaguar.getPrice(
);
aProfit = jaguar.getProfit(
);
aProfit = hyundai.getProfit(
);
hyundai = jaguar;
13. Suppose you change Self-Test Exercise 12 so that in the definition of the class Automobile
all member variables are public instead of private. How would this change your answer to
the question in Self-Test Exercise 12?
14. Explain what
public: and private: mean in a class definition.
15. a. How many

public: sections are required in a class for the class to be useful?
b. How many
private: sections are required in a class?
S
EPARATE
I
NTERFACE

AND
I
MPLEMENTATION
The principle of encapsulation says that you should define classes so that a programmer who uses
the class need not be concerned with the details of how the class is implemented. The programmer
who uses the class need only know the rules for how to use the class. The rules for how to use the
class are known as the ii
ii
nn
nn
tt
tt
ee
ee
rr
rr
ff
ff
aa
aa
cc
cc

ee
ee
or AA
AA
PP
PP
II
II
. There is some disagreement on exactly what the initials API
stand for, but it is generally agreed that they stand for something like
application programmer
interface
API
06_CH06.fm Page 248 Wednesday, August 13, 2003 12:54 PM
Classes 249
Tip
interface
or
abstract programming interface
or something similar. In this book we will call these
rules the
interface
for the class. It is important to keep in mind a clear distinction between the
interface and the implementation of a class. If your class is well designed, then any programmer
who uses the class need only know the interface for the class and need not know any details of the
implementation of the class. A class whose interface and implementation are separated in this
way is sometimes called an
abstract data type (ADT)
or a nicely encapsulated class. In Chapter 11
we will show you how to separate the interface and implementation by placing them in different

files, but the important thing is to keep them conceptually separated.
For a C++ class, the ii
ii
nn
nn
tt
tt
ee
ee
rr
rr
ff
ff
aa
aa
cc
cc
ee
ee
consists of two sorts of things: the comments, usually at the begin-
ning of the class definition, that tell what the data of the object is supposed to represent, such as
a date or bank account or state of a simulated car wash; and the public member functions of the
class along with the comments that tell how to use these public member functions. In a well-
designed class, the interface of the class should be all you need to know in order to use the class
in your program.
The ii
ii
mm
mm
pp

pp
ll
ll
ee
ee
mm
mm
ee
ee
nn
nn
tt
tt
aa
aa
tt
tt
ii
ii
oo
oo
nn
nn
of a class tells how the class interface is realized as C++ code. The implemen-
tation consists of the private members of the class and the definitions of both the public and pri-
vate member functions. Although you need the implementation in order to run a program that
uses the class, you should not need to know anything about the implementation in order to write
the rest of a program that uses the class; that is, you should not need to know anything about the
implementation in order to write the
main function of the program and to write any nonmember

functions or other classes used by the
main function.
The most obvious benefit you derive from cleanly separating the interface and implementation of
your classes is that you can change the implementation without having to change the other parts
of your program. On large programming projects this division between interface and implemen-
tation will facilitate dividing the work among different programmers. If you have a well-designed
interface, then one programmer can write the implementation for the class while other program-
mers write the code that uses the class. Even if you are the only programmer working on a project,
you have divided one larger task into two smaller tasks, which makes your program easier to
design and to debug.
A T
EST

FOR
E
NCAPSULATION
If your class definition produces an ADT (that is, if it properly separates the interface and the
implementation), then you can change the implementation of the class (that is, change the data
representation and/or change the implementation of some member functions) without needing
to change any (other) code for any program that uses the class definition. This is a sure test for
whether you have defined an ADT or just some class that is not properly encapsulated.
For example, you can change the implementation of the class
DayOfYear in Display 6.4 to the
following and no program that uses this class definition would need any changes:
class DayOfYear
{
implemen-
tation
06_CH06.fm Page 249 Wednesday, August 13, 2003 12:54 PM
250 Structures and Classes

public:
void input(
);
void output(
);
void set(int newMonth, int newDay);
//Precondition: newMonth and newDay form a possible date.
//Postcondition: The date is reset according to the arguments.
void set(int newMonth);
//Precondition: 1 <= newMonth <= 12
//Postcondition: The date is set to first day of the month.
int getMonthNumber(
);
//Returns 1 for January, 2 for February, etc.
int getDay(
);
private:
char firstLetter;//of month
char secondLetter;//of month
char thirdLetter;//of month
int day;
};
In this version, a month is represented by the first three letters in its name, such as ’J’, ’a’, and
’n’ for January. The member functions should also be rewritten, of course, but they can be
rewritten to behave
exactly
as they did before. For example, the definition of the function get-
MonthNumber
might start as follows:
int DayOfYear::getMonthNumber( )

{
if (firstLetter == ’J’ && secondLetter == ’a’
&& thirdLetter == ’n’)
return 1;
if (firstLetter == ’F’ && secondLetter == ’e’
&& thirdLetter == ’b’)
return 2;
. . .
This would be rather tedious, but not difficult.

STRUCTURES VERSUS CLASSES
Structures are normally used with all member variables public and with no member
functions. However, in C++ a structure can have private member variables and both
public and private member functions. Aside from some notational differences, a C++
structure can do anything a class can do. Having said this and satisfied the “truth in adver-
tising” requirement, we advocate that you forget this technical detail about structures. If
06_CH06.fm Page 250 Wednesday, August 13, 2003 12:54 PM
Classes 251
C
LASSES

AND
O
BJECTS
A class is a type whose variables can have both member variables and member functions. The
syntax for a class definition is given below.
S
YNTAX
class
Class_Name

{
.
public:
Member_SpecificationN+1
Member_SpecificationN+2
.
.
.
private:
Member_Specification_1
Member_Specification_2
.
.
.

Member_SpecificationN
};
Each
Member_Specification_i
is either a member variable declaration or a member function
declaration (prototype).
Additional public: and private: sections are permitted. If the first group of members does
not have either a
public: or a private: label, then it is the same as if there were a private:
before the first group.
E
XAMPLE
class Bicycle
{
public:

char getColor( );
int numberOfSpeeds( );
void set(int theSpeeds, char theColor);
private:
int speeds;
char color;
};
Once a class is defined, an object variable (variable of the class type) can be declared in the same
way as variables of any other type. For example, the following declares two object variables of
type
Bicycle:
Bicycle myBike, yourBike;
Public members
Private members
Do not forget this semicolon.
06_CH06.fm Page 251 Wednesday, August 13, 2003 12:54 PM
252 Structures and Classes
Self-Test Exercises
Tip
you take this technical detail seriously and use structures in the same way that you use
classes, then you will have two names (with different syntax rules) for the same con-
cept. On the other hand, if you use structures as we described them, then you will have
a meaningful difference between structures (as you use them) and classes; and your
usage will be the same as that of most other programmers.
One difference between a structure and a class is that they differ in how they treat an
initial group of members that has neither a public nor a private access specifier. If the
first group of members in a definition is not labeled with either
public: or private:,
then a structure assumes the group is public, whereas a class would assume the group is
private.

T
HINKING
O
BJECTS
If you have not programmed with classes before, it can take a little while to get the feel of pro-
gramming with them. When you program with classes, data rather than algorithms takes center
stage. It is not that there are no algorithms. However, the algorithms are made to fit the data, as
opposed to designing the data to fit the algorithms. It’s a difference in point of view. In the
extreme case, which is considered by many to be the best style, you have no global functions at
all, only classes with member functions. In this case, you define objects and how the objects inter-
act, rather than algorithms that operate on data. We will discuss the details of how you accom-
plish this throughout this book. Of course, you can ignore classes completely or relegate them to a
minor role, but then you are really programming in C, not C++.
16. When you define a C++ class, should you make the member variables public or private?
Should you make the member functions public or private?
17. When you define a C++ class, what items are considered part of the interface? What items
are considered part of the implementation?
■ A structure can be used to combine data of different types into a single (compound)
data value.
■ A class can be used to combine data and functions into a single (compound) object.
■ A member variable or a member function for a class can be either public or private.
If it is public, it can be used outside the class. If it is private, it can be used only in
the definition of a member function.
Chapter Summary
06_CH06.fm Page 252 Wednesday, August 13, 2003 12:54 PM

×