CONSTRUCTORS WITH NO ARGUMENTS It is important to remember not to use any parentheses when you declare a class variable and want the constructor invoked with no arguments... SYNTAX FOR A
Trang 1member function set (which we included in the old version of the class shown in Display 6.4).
CONSTRUCTORS WITH NO ARGUMENTS
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.>
Trang 2The 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
CALLING A CONSTRUCTOR
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
SYNTAX FOR AN OBJECT DECLARATION WHEN YOU HAVE CONSTRUCTORS
Class_Name Variable_Name(Arguments_for_Constructor);
EXAMPLE
DayOfYear holiday(7, 4);
SYNTAX FOR AN EXPLICIT CONSTRUCTOR CALL
Variable = Constructor_Name(Arguments_For_Constructor);
EXAMPLE
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
Trang 3■ 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
ALWAYS INCLUDE A DEFAULT CONSTRUCTOR
A constructor that takes no arguments is called a dddeeeeffffaaaauuuulllltttt ccccoooonnnssssttttrrrruun uccccttttooou orrrr 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
1Note 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
Trang 4To 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
Trang 5Self-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;
CONSTRUCTORS WITH NO ARGUMENTS
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( );
Trang 6YourClass yetAnotherObject();
anObject = YourClass(99, ’B’);
anObject = YourClass();
anObject = YourClass;
2 What is a default constructor Does every class have a default constructor?
BankAccount CLASS
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
Trang 7Display 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
Trang 8Display 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
Trang 9Display 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
Trang 10Display 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