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

Absolute C++ (4th Edition) part 27 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 (169.17 KB, 10 trang )

Constructors 263
Pitfall
member function set (which we included in the old version of the class shown in
Display 6.4).
C
ONSTRUCTORS

WITH
N
O
A
RGUMENTS
It is important to remember not to use any parentheses when you declare a class variable and
want the constructor invoked with no arguments. For example, consider the following line from
Display 7.1:
DayOfYear date1(2, 21), date2(5), date3;
Display 7.1 Class with Constructors
(part 2 of 2)
41 DayOfYear::DayOfYear(int monthValue) : month(monthValue), day(1)
42 {
43 testDate( );
44 }
45 DayOfYear::DayOfYear( ) : month(1), day(1)
46 {/*Body intentionally empty.*/}
47 //uses iostream and cstdlib:
48 void DayOfYear::testDate( )
49 {
50 if ((month < 1) || (month > 12))
51 {
52 cout << "Illegal month value!\n";
53 exit(1);


54 }
55 if ((day < 1) || (day > 31))
56 {
57 cout << "Illegal day value!\n";
58 exit(1);
59 }
60 }
S
AMPLE
D
IALOGUE
Initialized dates:
February 21
May 1
January 1
date1 reset to the following:
October 31
<
Definitions of the other member
functions are the same as in Display 6.4.
>
07_CH07.fm Page 263 Wednesday, August 13, 2003 12:58 PM
264 Constructors and Other Tools
The object date1 is initialized by the constructor that takes two arguments, the object date2 is
initialized by the constructor that takes one argument, and the object
date3 is initialized by the
constructor that takes no arguments.
It is tempting to think that empty parentheses should be used when declaring a variable for which
you want the constructor with no arguments invoked, but there is a reason why this is not done.
Consider the following, which seems like it should declare the variable

date3 and invoke the con-
structor with no arguments:
DayOfYear date3( );//PROBLEM! Not what you might think it is.
The problem with this is that although you may mean it as a declaration and constructor invoca-
tion, the compiler sees it as a declaration (prototype) of a function named
date3 that has no
parameters and that returns a value of type
DayOfYear. Since a function named date3 that has
no parameters and that returns a value of type
DayOfYear is perfectly legal, this notation always
has that meaning. A different notation (without parentheses) is used when you want to invoke a
constructor with no arguments.
C
ALLING

A
C
ONSTRUCTOR
A constructor is called automatically when an object is declared, but you must give the argu-
ments for the constructor when you declare the object. A constructor can also be called explicitly,
but the syntax is different from what is used for ordinary member functions.
S
YNTAX

FOR

AN
O
BJECT
D

ECLARATION
W
HEN
Y
OU
H
AVE
C
ONSTRUCTORS
Class_Name

Variable_Name(Arguments_for_Constructor);
E
XAMPLE
DayOfYear holiday(7, 4);
S
YNTAX

FOR

AN
E
XPLICIT
C
ONSTRUCTOR
C
ALL
Variable
=
Constructor_Name

(
Arguments_For_Constructor
);
E
XAMPLE
holiday = DayOfyear(10, 31);
A constructor must have the same name as the class of which it is a member. Thus, in the above
syntax descriptions,
Class_Name
and
Constructor_Name
are the same identifier.
07_CH07.fm Page 264 Wednesday, August 13, 2003 12:58 PM
Constructors 265
Tip

EXPLICIT CONSTRUCTOR CALLS
A constructor is called automatically whenever you declare an object of the class type,
but it can also be called again after the object has been declared. This allows you to con-
veniently set all the members of an object. The technical details are as follows. Calling
the constructor creates an anonymous object with new values. An anonymous object is
an object that is not named (as yet) by any variable. The anonymous object can be
assigned to the named object. For example, the following is a call to the constructor
DayOfYear that creates an anonymous object for the date May 5. This anonymous
object is assigned to the variable
holiday (which has been declared to be of type DayOf-
Year
) so that holiday also represents the date May 5.
1
holiday = DayOfYear(5, 5);

(As you might guess from the notation, a constructor sometimes behaves like a func-
tion that returns an object of its class type.)
Note that when you explicitly invoke a constructor with no arguments, you do
include parentheses as follows:
holiday = DayOfYear( );
The parentheses are only omitted when you declare a variable of the class type and
want to invoke a constructor with no arguments as part of the declaration.
A
LWAYS
I
NCLUDE

A
D
EFAULT
C
ONSTRUCTOR
A constructor that takes no arguments is called a dd
dd
ee
ee
ff
ff
aa
aa
uu
uu
ll
ll
tt

tt


cc
cc
oo
oo
nn
nn
ss
ss
tt
tt
rr
rr
uu
uu
cc
cc
tt
tt
oo
oo
rr
rr
. This name can be mislead-
ing because sometimes it is generated by default (that is, automatically) and sometimes it is not.
Here is the full story. If you define a class and include absolutely no constructors of any kind, then
a default constructor will be automatically created. This default constructor does not do any-
thing, but it does give you an uninitialized object of the class type, which can be assigned to a

variable of the class type. If your class definition includes one or more constructors of any kind,
no constructor is generated automatically. So, for example, suppose you define a class called
SampleClass. If you include one or more constructors that each takes one or more arguments,
but you do not include a default constructor in your class definition, then there is no default con-
structor and any declaration like the following will be illegal:
SampleClass aVariable;
The problem with the above declaration is that it asks the compiler to invoke the default construc-
tor, but there is no default constructor in this case.
1
Note that this process is more complicated than simply changing the values of member vari-
ables. For efficiency reasons, therefore, you may wish to retain the member functions named
set
to use in place of an explicit call to a constructor.
default
constructor
07_CH07.fm Page 265 Wednesday, August 13, 2003 12:58 PM
266 Constructors and Other Tools
To make this concrete, suppose you define a class as follows:
class SampleClass
{
public:
SampleClass(int parameter1, double parameter2);
void doStuff(
);
private:
int data1;
double data2;
};
You should recognize the following as a legal way to declare an object of type SampleClass and
call the constructor for that class:

SampleClass myVariable(7, 7.77);
However, the following is illegal:
SampleClass yourVariable;
The compiler interprets the above declaration as including a call to a constructor with no argu-
ments, but there is no definition for a constructor with zero arguments. You must either add two
arguments to the declaration of
yourVariable or else add a constructor definition for a con-
structor with no arguments.
If you redefine the class SampleClass as follows, then the above declaration of yourVariable
would be legal:
class SampleClass
{
public:
SampleClass(int parameter1, double parameter2);
SampleClass(
);
void doStuff(
);
private:
int data1;
double data2;
};
To avoid this sort of confusion, you should always include a default constructor in any class you
define. If you do not want the default constructor to initialize any member variables, you can
simply give it an empty body when you implement it. The following constructor definition is per-
fectly legal. It does nothing but create an uninitialized object:
SampleClass::SampleClass( )
{/*Do nothing.*/}
Default constructor
07_CH07.fm Page 266 Wednesday, August 13, 2003 12:58 PM

Constructors 267
Self-Test Exercises
1. Suppose your program contains the following class definition (along with definitions of the
member functions):
class YourClass
{
public:
YourClass(int newInfo, char moreNewInfo);
YourClass(
);
void doStuff(
);
private:
int information;
char moreInformation;
};
Which of the following are legal?
YourClass anObject(42, ’A’);
YourClass anotherObject;
C
ONSTRUCTORS

WITH
N
O
A
RGUMENTS
A constructor that takes no arguments is called a default constructor. When you declare an
object and want the constructor with zero arguments to be called, you do not include any paren-
theses. For example, to declare an object and pass two arguments to the constructor, you might

do the following:
DayOfYear date1(12, 31);
However, if you want the constructor with zero arguments to be used, you declare the object as
follows:
DayOfYear date2;
You do
not
declare the object as follows:
DayOfYear date2( );//PROBLEM!
(The problem is that this syntax declares a function that returns a DayOfYear object and has no
parameters.)
You do, however, include the parentheses when you explicitly invoke a constructor with no argu-
ments, as shown below:
date1 = DayOfYear( );
07_CH07.fm Page 267 Wednesday, August 13, 2003 12:58 PM
268 Constructors and Other Tools
Example
YourClass yetAnotherObject( );
anObject = YourClass(99, ’B’);
anObject = YourClass(
);
anObject = YourClass;
2. What is a default constructor. Does every class have a default constructor?
BankAccount
C
LASS
Display 7.2 contains the definition of a class representing a simple bank account embedded in a
small demonstration program. A bank account of this form has two pieces of data: the account
balance and the interest rate. Note that we have represented the account balance as two values of
type

int, one for the dollars and one for the cents. This illustrates the fact that the internal repre-
sentation of the data need not be simply a member variable for each conceptual piece of data. It
may seem that the balance should be represented as a value of type
double, rather than two int
values. However, an account contains an exact number of dollars and cents, and a value of type
double is, practically speaking, an approximate quantity. Moreover, a balance such as $323.52 is
not a dollar sign in front of a floating-point value. The $323.52 cannot have any more or fewer
than two digits after the decimal point. You cannot have a balance of $323.523, and a member
variable of type
double would allow such a balance. It is not impossible to have an account with
fractional cents. It is just not what we want for a bank account.
Note that the programmer who is using the class BankAccount can think of the balance as a
value of type
double or as two values of type int (for dollars and cents). The accessor and
mutator functions allow the programmer to read and set the balance as either a
double or two
ints. The programmer who is using the class need not and should not think of any underlying
member variables. That is part of the implementation that is “hidden” from the programmer
using the class.
Note that the mutator function
setBalance, as well as the constructor names, are overloaded.
Also note that all constructors and mutator functions check values to make sure they are appro-
priate. For example, an interest rate cannot be negative. A balance can be negative, but you can-
not have a positive number of dollars and a negative number of cents.
This class has four private member functions:
dollarsPart, centsPart, round, and frac-
tion
. These member functions are made private because they are only intended to be used in the
definitions of other member functions.
07_CH07.fm Page 268 Wednesday, August 13, 2003 12:58 PM

Constructors 269
Display 7.2 BankAccount Class
(part 1 of 5)
1 #include <iostream>
2 #include <cmath>
3 #include <cstdlib>
4 using namespace std;
5 //Data consists of two items: an amount of money for the account balance
6 //and a percentage for the interest rate.
7 class BankAccount
8 {
9 public:
10 BankAccount(double balance, double rate);
11 //Initializes balance and rate according to arguments.
12 BankAccount(int dollars, int cents, double rate);
13 //Initializes the account balance to $dollars.cents. For a negative balance both
14 //dollars and cents must be negative. Initializes the interest rate to rate percent.
15 BankAccount(int dollars, double rate);
16 //Initializes the account balance to $dollars.00 and
17 //initializes the interest rate to rate percent.
18 BankAccount( );
19 //Initializes the account balance to $0.00 and the interest rate to 0.0%.
20 void update( );
21 //Postcondition: One year of simple interest has been added to the account.
22 void input( );
23 void output( );
24 double getBalance( );
25 int getDollars( );
26 int getCents( );
27 double getRate( );//Returns interest rate as a percentage.

28 void setBalance(double balance);
29 void setBalance(int dollars, int cents);
30 //Checks that arguments are both nonnegative or both nonpositive.
31 void setRate(double newRate);
32 //If newRate is nonnegative, it becomes the new rate. Otherwise, abort program.
33
34 private:
35 //A negative amount is represented as negative dollars and negative cents.
36 //For example, negative $4.50 sets accountDollars to -4 and accountCents to -50.
37 int accountDollars; //of balance
38 int accountCents; //of balance
39 double rate;//as a percent
Private members
07_CH07.fm Page 269 Wednesday, August 13, 2003 12:58 PM
270 Constructors and Other Tools
Display 7.2 BankAccount Class
(part 2 of 5)
40 int dollarsPart(double amount);
41 int centsPart(double amount);
42 int round(double number);
43 double fraction(double percent);
44 //Converts a percentage to a fraction. For example, fraction(50.3) returns 0.503.
45 };
46 int main( )
47 {
48 BankAccount account1(1345.52, 2.3), account2;
49 cout << "account1 initialized as follows:\n";
50 account1.output( );
51 cout << "account2 initialized as follows:\n";
52 account2.output( );

53 account1 = BankAccount(999, 99, 5.5);
54 cout << "account1 reset to the following:\n";
55 account1.output( );
56 cout << "Enter new data for account 2:\n";
57 account2.input( );
58 cout << "account2 reset to the following:\n";
59 account2.output( );
60 account2.update( );
61 cout << "In one year account2 will grow to:\n";
62 account2.output( );
63 return 0;
64 }
65 BankAccount::BankAccount(double balance, double rate)
66 : accountDollars(dollarsPart(balance)), accountCents(centsPart(balance))
67 {
68 setRate(rate);
69 }
70 BankAccount::BankAccount(int dollars, int cents, double rate)
71 {
72 setBalance(dollars, cents);
73 setRate(rate);
74 }
75 BankAccount::BankAccount(int dollars, double rate)
76 : accountDollars(dollars), accountCents(0)
77 {
78 setRate(rate);
This declaration causes a call
to the default constructor. Notice
that there are no parentheses.
an explicit call to the constructor

BankAccount::BankAccount
These functions check that the
data is appropriate.
07_CH07.fm Page 270 Wednesday, August 13, 2003 12:58 PM
Constructors 271
Display 7.2 BankAccount Class
(part 3 of 5)
79 }
80 BankAccount::BankAccount( ): accountDollars(0), accountCents(0), rate(0.0)
81 {/*Body intentionally empty.*/}
82 void BankAccount::update( )
83 {
84 double balance = accountDollars + accountCents*0.01;
85 balance = balance + fraction(rate)*balance;
86 accountDollars = dollarsPart(balance);
87 accountCents = centsPart(balance);
88 }
89 //Uses iostream:
90 void BankAccount::input( )
91 {
92 double balanceAsDouble;
93 cout << "Enter account balance $";
94 cin >> balanceAsDouble;
95 accountDollars = dollarsPart(balanceAsDouble);
96 accountCents = centsPart(balanceAsDouble);
97 cout << "Enter interest rate (NO percent sign): ";
98 cin >> rate;
99 setRate(rate);
100 }
101 //Uses iostream and cstdlib:

102 void BankAccount::output( )
103 {
104 int absDollars = abs(accountDollars);
105 int absCents = abs(accountCents);
106 cout << "Account balance: $";
107 if (accountDollars < 0)
108 cout << "-";
109 cout << absDollars;
110 if (absCents >= 10)
111 cout << "." << absCents << endl;
112 else
113 cout << "." << '0' << absCents << endl;
114 cout << "Rate: " << rate << "%\n";
115 }
116 double BankAccount::getBalance( )
117 {
118 return (accountDollars + accountCents*0.01);
119 }
For a better definition of
BankAccount::input
see Self-Test Exercise 3.
07_CH07.fm Page 271 Wednesday, August 13, 2003 12:58 PM
272 Constructors and Other Tools
Display 7.2 Bank Account Class
(part 4 of 5)
120 int BankAccount::getDollars( )
121 {
122 return accountDollars;
123 }
124 int BankAccount::getCents( )

125 {
126 return accountCents;
127 }
128 double BankAccount::getRate( )
129 {
130 return rate;
131 }
132 void BankAccount::setBalance(double balance)
133 {
134 accountDollars = dollarsPart(balance);
135 accountCents = centsPart(balance);
136 }
137 //Uses cstdlib:
138 void BankAccount::setBalance(int dollars, int cents)
139 {
140 if ((dollars < 0 && cents > 0) || (dollars > 0 && cents < 0))
141 {
142 cout << "Inconsistent account data.\n";
143 exit(1);
144 }
145 accountDollars = dollars;
146 accountCents = cents;
147 }
148 //Uses cstdlib:
149 void BankAccount::setRate(double newRate)
150 {
151 if (newRate >= 0.0)
152 rate = newRate;
153 else
154 {

155 cout << "Cannot have a negative interest rate.\n";
156 exit(1);
157 }
158 }
159 int BankAccount::dollarsPart(double amount)
160 {
The programmer using the class
does not care if the balance is stored
as one
real or two ints.
This could be a regular function
rather than a member function,
but as a member functions we were
able to make it private.
07_CH07.fm Page 272 Wednesday, August 13, 2003 12:58 PM

×