I don’t care to belong to any club that will accept me as a member.
GROUCHO MARX, The Groucho Letters
A class is a data type whose variables are objects. In Chapter 6 we described an object as a variable that has member functions as well as the ability to hold data values.1 Thus, within a C++ program, the definition of a class should be a data type definition that describes what kinds of values the variables can hold and also what the member functions are. A structure definition describes some of these things. A structure is a defined type that allows you to define values of the structure type by defining member variables. To obtain a class from a structure, all you need to do is add some member functions.
A sample class definition is given in the program shown in Display 10.3. The type DayOfYear
defined there is a class definition for objects whose values are dates, such as January 1 or July 4. These values can be used to record holidays, birthdays, and other special dates. In this definition of DayOfYear
, the month is recorded as an int
value, with 1
standing for January, 2
standing for February, and so forth. The day of the month is recorded in a second int
member variable. The class DayOfYear
has one member function called output
, which has no arguments and outputs the month and day values to the screen. Let’s look at the definition for the class DayOfYear
in detail.
Sample Dialogue
Enter today's date: Enter month as a number: 10 Enter the day of the month: 15 Enter your birthday: Enter month as a number: 2 Enter the day of the month: 21 Today's date is month = 10, day = 15 Your birthday is month = 2, day = 21 Happy Unbirthday!
The definition of the class DayOfYear
is shown near the top of Display 10.3. For the moment, ignore the line that contains the keyword public
. This line simply says that the member variables and functions have no restriction on them. We will explain this line later in this chapter. The rest of the definition of the class DayOfYear
is very much like a structure definition, except that it uses the keyword class
instead of struct
and it lists the member function output
(as well as the member variables month
and day
). Notice that the member function output
is listed by giving only its function declaration. The definitions for the member functions are given elsewhere. (In a C++ class definition, you can intermix the ordering of the member variables and member functions in any way you wish, but the style we will follow has a tendency to list the member functions before the member variables.) Objects (that is, variables) of a class type are declared in the same way as variables of the predefined types and in the same way as structure variables.
Member functions for classes that you define are called in the same way as we described in Chapter 6 for predefined classes. For example, the program in Display 10.3 declares two objects of type DayOfYear
in the following way:
DayOfYear today, birthday;
The member function output
is called with the object today
as follows:
today.output();
and the member function output
is called with the object birthday
as follows:
birthday.output();
When a member function is defined, the definition must include the class name because there may be two or more classes that have member functions with the same name. In Display 10.3 there is only one class definition, but in other situations you may have many class definitions, and each class may have a member function called output
. The definition for the member function output
of the class DayOfYear
is shown in Display 10.3. The definition is similar to an ordinary function definition, but there are some differences.
The heading of the function definition for the member function output
is as follows:
void DayOfYear::output()
The operator ::
is called the scope resolution operator, and it serves a purpose similar to that of the dot operator. Both the dot operator and the scope resolution operator are used to tell what a member function is a member of. However, the scope resolution operator ::
is used with a class name, whereas the dot operator is used with objects (that is, with class variables). The scope resolution operator consists of two colons with no space between them. The class name that precedes the scope resolution operator is often called a type qualifier, because it specializes (“qualifies”) the function name to one particular type.
Look at the definition of the member function DayOfYear::output
given in Display 10.3. Notice that in the function definition of DayOfYear::output
, we used the member names month
and day
by themselves without first giving the object and dot operator. That is not as strange as it may at first appear. At this point we are simply defining the member function output
. This definition of output
will apply to all objects of type DayOfYear
, but at this point we do not know the names of the objects of type DayOfYear
that we will use, so we cannot give their names. When the member function is called, as in
today.output();
all the member names in the function definition are specialized to the name of the calling object. So the function call above is equivalent to the following:
{
cout << "month = " << today.month
<< ", day = " << today.day << endl;
}
In the function definition for a member function, you can use the names of all members of that class (both the data members and the function members) without using the dot operator.
Below we have redefined the class DayOfYear
from Display 10.3 so that it now has one additional member function called input
. Write an appropriate definition for the member function input
.
class DayOfYear
{
public:
void input();
void output();
int month;
int day;
};
Given the following class definition, write an appropriate definition for the member function set
:
class Temperature
{
public:
void set(double newDegrees, char newScale);
//Sets the member variables to the values given as
//arguments.
double degrees;
char scale; //'F' for Fahrenheit or 'C' for Celsius.
};
Carefully distinguish between the meaning and use of the dot operator and the scope resolution operator ::
.
The predefined types such as double
are not implemented as C++ classes, but the people who wrote your C++ compiler did design some way to represent values of type double
in your computer. It is possible to implement the type double
in many different ways. In fact, different versions of C++ do implement the type double
in slightly different ways, but if you move your C++ program from one computer to another with a different implementation of the type double
, your program should still work correctly.2 Classes are types that you define, and the types that you define should behave as well as the predefined types. You can build a library of your own class type definitions and use your types as if they were predefined types. For example, you could place each class definition in a separate file and copy it into any program that uses the type.
Your class definitions should separate the rules for using the class and the details of the class implementation in as strong a way as was done for the predefined types. If you change the implementation of a class (for example, by changing some details in the definition of a member function in order to make function calls run faster), then you should not need to change any of the other parts of your programs. In order to realize this ideal, we need to describe one more feature of class definitions.
Look back at the definition of the type DayOfYear
given in Display 10.3. The type DayOfYear
is designed to hold values that represent dates such as birthdays and holidays. We chose to represent these dates as two integers, one for the month and one for the day of the month. We might later decide to change the representation of the month from one variable of type int
to three variables of type char
. In this changed version, the three characters would be an abbreviation of the month’s name. For example, the three char
values 'J'
, 'a'
, and 'n'
would represent the month January. However, whether you use a single member variable of type int
to record the month or three member variables of type char
is an implementation detail that need not concern a programmer who uses the type DayOfYear
. Of course, if you change the way the class DayOfYear
represents the month, then you must change the implementation of the member function output
—but that is all you should need to change. You should not need to change any other part of a program that uses your class definition for DayOfYear
. Unfortunately, the program in Display 10.3 does not meet this ideal. For example, if you replace the one member variable named month
with three member variables of type char
, then there will be no member variable named month
, so you must change those parts of the program that perform input and also change the if-else
statement.
With an ideal class definition, you should be able to change the details of how the class is implemented and the only things you should need to change in any program that uses the class are the definitions of the member functions. In order to realize this ideal, you must have enough member functions so that you never need to access the member variables directly, but access them only through the member functions. Then, if you change the member variables, you need change only the definitions of the member functions to match your changes to the member variables, and nothing else in your programs need change. In Display 10.4 we have redefined the class DayOfYear
so that it has enough member functions to do everything we want our programs to do, and so the program does not need to directly reference any member variables. If you look carefully at the program in Display 10.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
, nor bachBirthday.day
anywhere outside of the definitions of member functions.
Sample Dialogue
Enter today's date: Enter the month as a number: 3 Enter the day of the month: 21 Today's date is month = 3, day = 21 J. S. Bach's birthday is month = 3, day = 21 Happy Birthday Johann Sebastian!
The program in Display 10.4 has one new feature that is designed to ensure that no programmer who uses the class DayOfYear
will ever directly reference any of its member variables. Notice the line in the definition of the class DayOfYear
that contains the keyword private
. All the member variable names that are listed after this line are private members, which means that they cannot be directly accessed in the program except within the definition of a member function. If you try to access one of these member variables in the main
part of your program or in the definition of some function that is not a member function of this particular class, the compiler will give you an error message. If you insert the keyword private
and a colon in the list of member variables and member functions, all the members that follow the label private:
will be private members. The variables that follow the label private:
will be private member variables, and the functions that follow it will be private member functions.
All the member variables for the class DayOfYear
defined in Display 10.4 are private members. A private member variable may be used in the definition of any of the member functions, but nowhere else. For example, with this changed definition of the class DayOfYear
, the following two assignments are no longer permitted in the main
part of the program:
DayOfYear today; //This line is OK.
today.month = 12; //ILLEGAL
today.day = 25; //ILLEGAL
Any reference to these private variables is illegal (except in the definition of member functions). Since this new definition makes month
and day
private member variables, the following are also illegal in the main
part of any program that declares today
to be of type DayOfYear
:
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 then no way to change its value (or to reference the member variable in any other way) except by using one of the member functions. This is a severe restriction, but it is usually a wise restriction to impose. Programmers find that it usually makes their code easier to understand and easier to update if they make all member variables private.
It may seem that the program in Display 10.4 does not really disallow direct access to the private member variables, since they can be changed using the member function DayOfYear::set
, and their values can be discovered using the member functions DayOfYear::getMonth
and DayOfYear::getDay
. While that is almost true for the program in Display 10.4, it might not be so true if we changed the implementation of how we represented the month and/or day in our dates. For example, suppose we change the type definition of DayOfYear
to the following:
class DayOfYear
{
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.
int getMonth();
//Returns the month, 1 for January, 2 for February, etc.
int getDay();
//Returns the day of the month.
private:
void DayOfYear::checkDate( );
char firstLetter; //of month
char secondLetter; //of month
char thirdLetter; //of month
int day;
};
It would then be slightly more difficult to define the member functions, but they could be redefined so that they would behave exactly as they did before. For example, the definition of the function getMonth
might start as follows:
int DayOfYear::getMonth()
{
if (firstLetter == 'J' && secondLetter == 'a'
&& thirdLetter == 'n')
return 1;
if (firstLetter == 'F' && secondLetter == 'e'
&& thirdLetter == 'b')
return 2;
. . .
This approach would be rather tedious, but not difficult.
Also notice that the member functions DayOfYear::set
and DayOfYear::input
check to make sure the member variables month
and day
are set to legal values. This is done with a call to the member function DayOfYear::checkDate
. If the member variables month
and day
were public instead of private, then these member variables could be set to any values, including illegal values. By making the member variables private and manipulating them only via member functions, we can ensure that the member variables are never set to illegal or meaningless values. (In Self-Test Exercise 14 you are asked to redefine the member function DayOfYear::checkDate
so that it does a complete check for illegal dates.)
It is also possible to make a member function private. Like a private member variable, a private member function can be used in the definition of any other member function, but nowhere else, such as in the main
part of a program that uses the class type. For example, the member function DayOfYear::checkDate
in Display 10.4 is a private member function. The normal practice is to make a member function private if you only expect to use that member function as a helping function in the definitions of the member functions.
The keyword public
is used to indicate public members the same way that the keyword private
is used to indicate private members. For example, for the class DayOfYear
defined in Display 10.4, all the member functions except DayOfYear::checkDate
are public members (and all the member variables are private members). A public member can be used in the main
body of your program or in the definition of any function, even a nonmember function.
You can have any number of occurrences of public
and private
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. For example, the member function doSomethingElse
and the member variable moreStuff
in the following structure definition are private members, while the other four members are all public:
class SampleClass
{
public:
void doSomething();
int stuff;
private:
void doSomethingElse();
char moreStuff;
public:
double doYetAnotherThing();
double evenMoreStuff;
};
If you list members at the start of your class definition and do not insert either public:
or private:
before these first members, then they will be private members. However, it is a good idea to always explicitly label each group of members as either public
or private
.
When defining a class, the normal practice is to make all member variables private. This means that the member variables can only be accessed or changed using the member functions. Much of this chapter is dedicated to explaining how and why you should define classes in this way.
The operator ==
can be used to test two values of a simple type to see if they are equal. Unfortunately, the predefined operator ==
does not automatically apply to objects. In Chapter 11 we will show you how you can make the operator ==
apply to the objects of the classes you define. Until then, you will not be able to use the equality operator ==
with objects (nor can you use it with structures). This can produce some complications. When defining a class, the preferred style is to make all member variables private. Thus, in order to test two objects to see if they represent the same value, you need some way to access the values of the member variables (or something equivalent to the values of the member variables). This allows you to test for equality by testing the values of each pair of corresponding member variables. To do this in Display 10.4, we used the member functions getMonth
and getDay
in the if-else
statement.
Member functions, such as getMonth
and getDay
, that allow you to find out the values of the private member variables are called accessor functions. Given the techniques you have learned to date, it is important to always include a complete set of accessor functions with each class definition so that you can test objects for equality. The accessor functions need not literally return the values of each member variable, but they must return something equivalent to those values. In Chapter 11 we will develop a more elegant method to test two objects for equality, but even after you learn that technique, it will still be handy to have accessor functions.
Member functions, such as set
in Display 10.4, that allow you to change the values of the private member variables are called mutator functions. It is important to always include mutator functions with each class definition so that you can change the data stored in an object.
The private member function DayOfYear::checkDate
in Display 10.4 allows some illegal dates to get through, such as February 30. Redefine the member function DayOfYear::checkDate
so that it ends the program whenever it finds any illegal date. Allow February to contain 29 days, so you account for leap years. (Hint: This is a bit tedious and the function definition is a bit long, but it is not very difficult.)
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
part 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
part of your program?
hyundai.price = 4999.99;
jaguar.setPrice(30000.97);
double aPrice, aProfit;
aPrice = jaguar.getPrice();
aProfit = jaguar.getProfit();
aProfit = hyundai.getProfit();
if (hyundai == jaguar)
cout << "Want to swap cars?";
hyundai = jaguar;
Suppose you change Self-Test Exercise 15 so that the definition of the class Automobile
omits the line that contains the keyword private
. How would this change your answer to the question in Self-Test Exercise 15?
Explain what public:
and private:
do in a class definition. In particular, explain why we do not just make everything public:
and save difficulty in access.
How many public:
sections are required in a class for the class to be useful?
How many private:
sections are required in a class?
What kind of section do you have between the opening {
and the first public:
or private:
section label of a class?
What kind of section do you have between the opening {
and the first public:
or private:
section label of a structure?
It is perfectly legal to use the assignment operator =
with objects or with structures. For example, suppose the class DayOfYear
is defined as shown in Display 10.4 so that it has two private member variables named month
and day
, and suppose that the objects dueDate
and tomorrow
are declared as follows:
DayOfYear dueDate, tomorrow;
The following is then perfectly legal (provided the member variables of the object tomorrow
have already been given values):
dueDate = tomorrow;
The previous assignment is equivalent to the following:
dueDate.month = tomorrow.month;
dueDate.day = tomorrow.day;
Moreover, this is true even though the member variables named month
and day
are private members of the class DayOfYear
.3 When an object is copied in this manner, it is called a shallow copy. It is called shallow because only the direct member variables are copied. If a member variable is a reference, then the copy will point to the same reference.
Display 10.5 contains a class definition for a bank account that illustrates all of the points about class definitions you have seen thus far. This type of bank account allows you to withdraw your money at any time, so it has no term as did the type CDAccount
that you saw earlier. A more important difference is that the class BankAccount
has member functions for all the operations you would expect to use in a program. Objects of the class BankAccount
have two private member variables: one to record the account balance and one to record the interest rate. Let’s discuss some of features of the class BankAccount
.
BankAccount
ClassSample Dialogue
Start of Test: account1 initial statement: Account balance $123.99 Interest rate 3.00% account1 with new setup: Account balance $100.00 Interest rate 5.00% account1 after update: Account balance $105.00 Interest rate 5.00% account2: Account balance $105.00 Interest rate 5.00%
First, notice that the class BankAccount
has a private member function called fraction
. Since fraction
is a private member function, it cannot be called in the body of main
or in the body of any function that is not a member function of the class BankAccount
. The function fraction
can only be called in the definitions of other member functions of the class BankAccount
. The only reason we have this (or any) private member function is to aid us in defining other member functions for the same class. In our definition of the class BankAccount
, we included the member function fraction
so that we could use it in the definition of the function update
. The function fraction
takes one argument that is a percentage figure, like 10.0
for 10.0%, and converts it to a fraction, like 0.10
. That allows us to compute the amount of interest on the account at the given percentage. If the account contains $100.00 and the interest rate is 10%, then the interest is equal to $100 times 0.10, which is $10.00.
When you call a public member function, such as update
, in the main
body of your program, you must include an object name and a dot, as in the following line from Display 10.5:
account1.update();
However, when you call a private member function (or any other member function) within the definition of another member function, you use only the member function name without any calling object or dot operator. For example, the following definition of the member function BankAccount::update
includes a call to BankAccount::fraction
(as shown in Display 10.5):
void BankAccount::update()
{
balance = balance + fraction(interestRate) * balance;
}
The calling object for the member function fraction
and for the member variables balance
and interestRate
are determined when the function update
is called. For example, the meaning of
account1.update();
is the following:
{
account1.balance = account1.balance +
account1.fraction(account1.interestRate) * account1.balance;
}
Notice that the call to the member function fraction
is handled in the same way in this regard as the references to the member variables.
Like the classes we discussed earlier, the class BankAccount
has a member function that outputs the data information stored in the object. In this program we are sending output to the screen. However, we want to write this class definition so that it can be copied into other programs and used unchanged in those other programs. Since some other program may want to send output to a file, we have given the member function output
a formal parameter of type ostream
so that the function output
can be called with an argument that is either the stream cout
or a file output stream. In the sample program we want the output to go to the screen, so the first function call to the member function output
has the form
account1.output(cout);
Other calls to output
also use cout
as the argument, so all output is sent to the screen. If you want the output to go to a file instead, then you must first connect the file to an output stream, as we discussed in Chapter 6. If the file output stream is called fout
and is connected to a file, then the following would write the data information for the object account1
to this file rather than to the screen:
account1.output(fout);
The value of an object of type BankAccount
represents a bank account that has some balance and pays some interest rate. The balance and interest rate can be set with the member function set
. Notice that we have overloaded the member function named set
so that there are two versions of set
. One version has three formal parameters, and the other has only two formal parameters. Both versions have a formal parameter of type double
for the interest rate, but the two versions of set
use different formal parameters to set the account balance. One version has two formal parameters to set the balance, one for the dollars and one for the cents in the account balance. The other version has only a single formal parameter, which gives the number of dollars in the account and assumes that the number of cents is zero. This second version of set
is handy, since most people open an account with some “even” amount of money, such as $1,000 and no cents. Notice that this overloading is nothing new. A member function is overloaded in the same way as an ordinary function is overloaded.
Classes have all of the properties that we described for structures plus all the properties associated with member functions. The following is a list of some points to keep in mind when using classes.
Classes have both member variables and member functions.
A member (either a member variable or a member function) may be either public or private.
Normally, all the member variables of a class are labeled as private members.
A private member of a class cannot be used except within the definition of another member function of the same class.
The name of a member function for a class may be overloaded just like the name of an ordinary function.
A class may use another class as the type for a member variable.
A function may have formal parameters whose types are classes. (See Self-Test Exercises 19 and 20.)
A function may return an object; that is, a class may be the type for the value returned by a function. (See Self-Test Exercise 21.)
Give a definition for the function with the following function declaration. The class BankAccount
is defined in Display 10.5.
double difference(BankAccount account1, BankAccount account2);
//Precondition: account1 and account2 have been given values
//(that is, their member variables have been given values).
//Returns the balance in account1 minus the balance in
account2.
Give a definition for the function with the following function declaration. The class BankAccount
is defined in Display 10.5. (Hint: It’s easy if you use a member function.)
void doubleUpdate(BankAccount& theAccount);
//Precondition: theAccount has previously been given a value
//(that is, its member variables have been given values).
//Postcondition: The account balance has been changed so that
//two years' interest has been posted to the account.
Give a definition for the function with the following function declaration. The class BankAccount
is defined in Display 10.5.
BankAccount newAccount(BankAccount oldAccount);
//Precondition: oldAccount has previously been given a value
//(that is, its member variables have been given values).
//Returns the value for a new account that has a balance of zero
//and the same interest rate as the oldAccount.
For example, after this function is defined, a program could contain the following:
BankAccount account3, account4;
account3.set(999, 99, 5.5);
account4 = newAccount(account3);
account4.output(cout);
This would produce the following output:
Account balance $0.00
Interest rate 5.50%
You often want to initialize some or all the member variables for an object when you declare the object. As we will see later in this book, there are other initializing actions you might also want to take, but initializing member variables is the most common sort of initialization. C++ includes special provisions for such initializations. When you define a class, you can define a special kind of member function known as a constructor. A constructor is a member function that is automatically called when an object of that class is declared. A constructor is used to initialize the values of member variables and to do any other sort of initialization that may be needed. You can define a constructor the same way that you define any other member function, except for two points:
A constructor must have the same name as the class. For example, if the class is named BankAccount
, then any constructor for this class must be named BankAccount
.
A constructor definition cannot return a value. Moreover, no return type, not even void
, can be given at the start of the function declaration or in the function header.
For example, suppose we wanted to add a constructor for initializing the balance and interest rate for objects of type BankAccount
shown in Display 10.5. The class definition could be as follows. (We have omitted some of the comments to save space, but they should be included.)
class BankAccount
{
public:
BankAccount(int dollars, int cents, double rate);
//Initializes the account balance to $dollars.cents and
//initializes the interest rate to rate percent.
void set(int dollars, int cents, double rate);
void set(int dollars, double rate);
void update();
double getBalance();
double getRate();
void output(ostream& outs);
private:
double balance;
double interestRate;
double fraction(double percent);
};
Notice that the constructor is named BankAccount
, which is the name of the class. Also notice that the function declaration for the constructor BankAccount
does not start with void
or with any other type name. Finally, notice that the constructor is placed in the public section of the class definition. Normally, you should make your constructors public member functions. If you were to make all your constructors private members, then you would not be able to declare any objects of that class type, which would make the class completely useless.
With the redefined class BankAccount
, two objects of type BankAccount
can be declared and initialized as follows:
BankAccount account1(10, 50, 2.0), account2(500, 0, 4.5);
Assuming that the definition of the constructor performs the initializing action that we promised, the previous declaration will declare the object account1
, set the value of account1.balance
to 10.50
, and set the value of account1.interestRate
to 2.0
. Thus, the object account1
is initialized so that it represents a bank account with a balance of $10.50 and an interest rate of 2.0%. Similarly, account2
is initialized so that it represents a bank account with a balance of $500.00 and an interest rate of 4.5%. What happens is that the object account1
is declared and then the constructor BankAccount
is called with the three arguments 10
, 50
, and 2.0
. Similarly, account2
is declared and then the constructor BankAccount
is called with the arguments 500
, 0
, and 4.5
. The result is conceptually equivalent to the following (although you cannot write it this way in C++):
BankAccount account1, account2; //PROBLEMS--BUT FIXABLE
account1.BankAccount(10, 50, 2.0); //VERY ILLEGAL
account2.BankAccount(500, 0, 4.5); //VERY ILLEGAL
As the comments indicate, you cannot place those three lines in your program. The first line can be made to be acceptable, but the two calls to the constructor BankAccount
are illegal. A constructor cannot be called in the same way as an ordinary member function is called. Still, it is clear what we want to happen when we write those three lines, and that happens automatically when you declare the objects account1
and account2
as follows:
BankAccount account1(10, 50, 2.0), account2(500, 0, 4.5);
The definition of a constructor is given in the same way as any other member function. For example, if you revise the definition of the class BankAccount
by adding the constructor just described, you need to also add the following definition of the constructor:
BankAccount::BankAccount(int dollars, int cents, double rate)
{
if ((dollars < 0) || (cents < 0) || (rate < 0))
{
cout << "Illegal values for money or interest rate.\n";
exit(1);
}
balance = dollars + 0.01*cents;
interestRate = rate;
}
Since the class and the constructor function have the same name, the name BankAccount
occurs twice in the function heading: The BankAccount
before the scope resolution operator ::
is the name of the class, and the BankAccount
after the scope resolution operator is the name of the constructor function. Also notice that no return type is specified in the heading of the constructor definition, not even the type void
. Aside from these points, a constructor can be defined in the same way as an ordinary member function.
You can overload a constructor name like BankAccount::BankAccount
, just as you can overload any other member function name, such as we did with BankAccount::set
in Display 10.5. In fact, constructors usually are overloaded so that objects can be initialized in more than one way. For example, in Display 10.6 we have redefined the class BankAccount
so that it has three versions of its constructor. This redefinition overloads the constructor name BankAccount
so that it may have three arguments (as we just discussed), two arguments, or no arguments.
Screen Output
account1 initialized as follows: Account balance $100.00 Interest rate 2.30% account2 initialized as follows: Account balance $0.00 Interest rate 0.00% account1 reset to the following: Account balance $999.99 Interest rate 5.50%
For example, suppose you give only two arguments when you declare an object of type BankAccount
, as in the following example:
BankAccount account1(100, 2.3);
Then the object account1
is initialized so that it represents an account with a balance of $100.00 and an interest rate of 2.3%.
On the other hand, if no arguments are given, as in the following example,
BankAccount account2;
then the object is initialized to represent an account with a balance of $0.00 and an interest rate of 0.0%. Notice that when the constructor has no arguments, you do not include any parentheses in the object declaration. The following is incorrect:
BankAccount account2(); //WRONG! DO NOT DO THIS!
In some cases, you can omit mutator member functions such as set
once you have a good set of constructor definitions. You can use the overloaded constructor BankAccount
in Display 10.6 to create a new BankAccount
object with the values of your choice. However, invoking the constructor will create a new object, so if you want to change the existing member variables in the object, then you should use a mutator function.
The constructor with no parameters in Display 10.6 deserves some extra discussion since it contains something we have not seen before. For reference, we reproduce the defining of the constructor with no parameters:
BankAccount::BankAccount() : balance(0), interestRate(0.0)
{
//Body intentionally empty
}
The new element, which is shown on the first line, is the part that starts with a single colon. This part of the constructor definition is called the initialization section. As this example shows, the initialization section goes after the parentheses that ends the parameter list and before the opening brace of the function body. The initialization section consists of a colon followed by a list of some or all the member variables separated by commas. Each member variable is followed by its initializing value in parentheses. This constructor definition is completely equivalent to the following way of writing the definition:
BankAccount::BankAccount( )
{
balance = 0;
interestRate = 0.0;
}
The function body in a constructor definition with an initialization section need not be empty. For example, the following definition of the two-parameter constructor is equivalent to the one given in Display 10.6:
BankAccount::BankAccount(int dollars, double rate)
: balance(dollars), interestRate(rate)
{
if ((dollars < 0) || (rate < 0))
{
cout << "Illegal values for money or interest rate.\n";
exit(1);
}
}
Notice that the initializing values can be given in terms of the constructor parameters.
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 conveniently 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 (that is, to the class variable). For example, the following line of code is a call to the constructor BankAccount
that creates an anonymous object with a balance of $999.99
and interest rate of 5.5%
. This anonymous object is assigned to object account1
so that it too represents an account with a balance of $999.99 and an interest rate of 5.5%:
account1 = BankAccount(999, 99, 5.5);
As you might guess from the notation, a constructor behaves like a function that returns an object of its class type. However, since a call to a constructor always creates a new object and a call to a set member function merely changes the values of existing member variables, a call to set may be a more efficient way to change the values of member variables than a call to a constructor. Thus, for efficiency reasons or if you need to change the values of member variables without creating a new object, you may wish to have both the set member functions and the constructors in your class definition.
C++ does not always generate a default constructor for the classes you define. If you give no constructor, the compiler will generate a default constructor that does nothing. This constructor will be called if class objects are declared. On the other hand, if you give at least one constructor definition for a class, then the C++ compiler will generate no other constructors. Every time you declare an object of that type, C++ will look for an appropriate constructor definition to use. If you declare an object without using arguments for the constructor, C++ will look for a default constructor, and if you have not defined a default constructor, none will be there for it to find.
For example, suppose you define a class as follows:
You should recognize the following as a legal way to declare an object of type SampleClass
and call the constructor for that class:
SampleClass myObject(7, 7.77);
However, you may be surprised to learn that the following is illegal:
SampleClass yourObject;
The compiler interprets this declaration as including a call to a constructor with no arguments, but there is no definition for a constructor with zero arguments. You must either add two arguments to the declaration of yourObject
or add a constructor definition for a constructor with no arguments.
A constructor that can be called with no arguments is called a default constructor, since it applies in the default case where you declare an object without specifying any arguments. Since it is likely that you will sometimes want to declare an object without giving any constructor arguments, you should always include a default constructor. The following redefined version of SampleClass
includes a default constructor:
class SampleClass
{
public:
SampleClass(int parameter1, double parameter2);
SampleClass(); ← Default constructor
void doStuff();
private:
int data1;
double data2;
};
If you redefine the class SampleClass
in this manner, then the previous declaration of yourObject
would be legal.
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 perfectly legal. It does nothing when called except make the compiler happy:
SampleClass::SampleClass()
{
//Do nothing.
}
Note that if a class is created as a dynamic variable using the new
operator then the default constructor is invoked.
Default Initialization of Member Variables
C++11 supports a feature called member initialization that is present in most object-oriented programming languages. This feature allows you to set default values for member variables. When an object is created the member variables are automatically initialized to the specified values. Consider the following definition and implementation of the Coordinate
class:
class Coordinate
{
public:
Coordinate();
Coordinate(int x);
Coordinate(int x, int y);
int getX();
int getY();
private:
int x=1;
int y=2;
};
Coordinate::Coordinate()
{ }
Coordinate::Coordinate(int xVal) : x(xVal)
{ }
Coordinate::Coordinate(int xVal, int yVal) : x(xVal), y(yVal)
{ }
int Coordinate::getX()
{
return x;
}
int Coordinate::getY()
{
return y;
}
If we create a Coordinate
object, then member variable x will be set to 1 and member variable y will be set to 2 by default. These values can be overridden if we invoke a constructor that explicitly sets the variable. In the snippet below, the default values for x and y are set for c1
, but for c2
the default value is only set for y because x is explicitly set to the input argument:
Coordinate c1, c2(10);
cout << c1.getX() << " " << c1.getY() << endl; // Outputs 1 2
cout << c2.getX() << " " << c2.getY() << endl; // Outputs 10 2
A related feature supported by C++11 is constructor delegation. Simply put, this allows one constructor to call another constructor. For example, we could modify the implementation of the default constructor so it invokes the constructor with two parameters:
Coordinate::Coordinate() : Coordinate(99,99)
{ }
The object defined by Coordinate c1;
will invoke the default constructor which will in turn invoke the constructor to set x to 99
and y to 99
.
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;
YourClass yetAnotherObject();
anObject = YourClass(99, 'B');
anObject = YourClass();
anObject = YourClass;
How would you change the definition of the class DayOfYear
in Display 10.4 so that it has two versions of an (overloaded) constructor? One version should have two int
formal parameters (one for the month and one for the day) and should set the private member variables to represent that month and day. The other should have no formal parameters and should set the date represented to January 1. Do this without using a constructor initialization section in either constructor.
Redo the previous exercise, but this time use a constructor initialization section to initialize all member functions in each constructor.