|
Table of Contents
Learning C++:
An Index of Entry Points
2. The A reference document on the basic elements of C++.
3. The Patterns
|
A. Initializing Instances We know that it is possible in C++ to simultaneously declare and initialize variables as in: It turns out that C++ also offers a way to simultaneously declare and initialize class instances. When an object is declared (instantiated), one can think of that object as being ‘constructed’ in the sense that a frame work is being built to hold the property values of that object. In other words, when the instance, ‘contract1’ of the Contract class is instantiated, one can think of the system has constructing the instance - in the sense that a framework is built for the properties of 'contract1'. When we talk about an instance being constructed, we are referring to the act of setting aside enough memory for the instance and associating that memory both with the name of the instance being created and with the class of the instance. In the code we wrote, the instances contract1, contract2, contract3, contract4, and contract5 were all constructed and left with garbage for the property values. Specifically:
As you can see, quite a bit takes place when an instance is created. As a matter of fact, there is so much that happens that a special function called a constructor exists to handle all this. It turns out that we can declare our own constructors for classes, but C++ creates a ‘default’ constructor whenever the programmer does not declare one. This default constructor handles the three bulleted items above. In the case of the code we wrote, the system declared a default constructor for the Contract class. If we declare our own constructor, we can use it to also initialize any and all data members of the instance being created. B. Declaring Constructors
Now for the receives and returns. Special rules exist for constructors in C++. First, any constructor for a class must have the same name as the class. Therefore, the constructor for the class 'contract' must be called ‘Contract’. Second, constructors, by definition, do NOT return anything and should not have the word 'void' in front of them. This means we can ignore the second of the two questions usually asked of functions. The first is still relevant:
For all constructors the answer takes the same form - what is needed are the values for whatever data members need to be set at the moment the instance is created. In our case, we decided to set all three data members , squareFootage, numDesks, and numDays. Therefore, that is the data that must be passed to the constructor for the class 'Contract'. Later, we will see slightly more complex versions of this answer, especially involving the idea of default values - values that are assumed to be valid unless the class is told otherwise. For instance, if almost all office cleaning contracts were for five days (Monday ... Friday) we could have five as a default value for 'number of days' and pass in a different value only when needed. (You do not know how to do that yet but you will learn!). In any case, determining what a constructor needs as 'receives' always comes down to determining what data members must be initialized and how. Here is the design for the 'Contract' constructor: Contract Purpose: to instantiate (create) an instance of the class 'contract'. Goal: a new instance of class 'contract' now exists Receives: Square Footage, Number of Desks, Number of Days (all integers) Returns: NONE It is rare that the algorithm for a constructor is anything more complex than a series of assignment statements involving the properties being initialized so you will not be required to explicitly write an algorithm for the constructor, Be sure, however, that you understand what is supposed to happen and review carefully the definition of the constructor below. The declaration of the constructor (inside the declaration of the class)looks like this:
class contract
{public:
Contract(int sqFootage, int numDesks, int numDays);
/*
Purpose: to instantiate (create) an instance of the class 'contract'.
Goal: a new instance of class 'contract' now exists
Receives: Square Footage, Number of Desks, Number of Days (all
integers)
Returns: NONE
*/
.
.
private:
.
.
};
Again, note that no type is placed in front of the function name and that the function name is the same as the class name. The lack of a return type in front might cause a bit of confusion. Not mentioned so far is the rule that usually a function without a return type is assumed to return an integer. (Here is another example of a default.) In other words, if you declare any regular function and 'forget' to place a return type at the beginning of the function declaration, the C++ compiler will assume you mean for the function to return an integer. A constructor is the one exception to this. ( C++ knows to treat this function declaration in a special way because it notes that it has the same name as the class inside which it is being declared.) Adding a constructor to a class does not change anything else. Therefore, the code above can simply be added to the code we already have for the Contract class. Here is the complete declaration:
// Declaration of the class Contract
// File: contract.h
class Contract
{public:
Contract(int sqFootage, int numDesks, int numDays);
/*
Purpose: to instantiate (create) an instance of the class 'contract'.
Goal: a new instance of class 'contract' now exists
Receives: Square Footage, Number of Desks, Number of Days (all
integers)
Returns: NONE
*/
void ChangeSquareFootage(int sqFootage);
/*
Purpose: To change the value of the square footage data member
Goal: The square footage data member has a new value
Receives The new Square Footage (an integer)
Returns: NONE
*/
void ChangeNumDesks( int numDesks);
// Design comments go here.
void ChangeNumDays( int numDays);
// Design comments go here.
int ProvideSquareFootage();
/*
Purpose: To Return the Square Footage of an Office
Goal: The Square Footage is returned
Receives: NONE
Returns the Square Footage (an integer)
*/
int ProvideNumberOfDesks();
// Design comments go here.
int ProvideNumberOfDays();
// Design comments go here.
double ProvidePerWeekCharge ();
/*
Purpose: To calculate and return the per week charge for an office
Goal: The per week charge is returned
Receives: NONE
Returns: The Per Week Charge (a double)
*/
private:
int squareFootage;
int numberOfDaysPerWeek;
int numberOfDesks;
};
C. Defining a Constructor
Contract::Contract(int sqFootage, int numDesks, int numDays) As we learned earlier, the 'Contract::' at the front of this line tells the compiler that what follows is a function definition for a member function of the class 'contract'. The fact that the function being defined has the same name as the class, indicates to the compiler that it is a constructor for the class. Note that without 'Contract::', the function 'Contract' would not be seen by the compiler as associated with any class at all and certainly would not be seen as a constructor. It would be considered the same as the function definitions we have already seen. The rest of the code for the constructor is very simple. As we have already discussed, the purpose of the constructor is to create an instance and assign values to all or some of that instance's data members. According to our analysis, all the data members are to be initialized - that is the reason we have three 'receives' in the declaration. The code now uses those receives.
Contract::Contract(int sqFootage, int numDesks, int numDays)
{
squareFootage = sqFootage;
numberOfDesks = numDesks;
numberOfDaysPerWeek = numDays;
}
The code for most of the constructors you will work with in this course will be as simple as this one - essentially some set of assignment statements. One mistake that beginners often make is giving the same name to a parameter (a receive) that they give to a data member. Note that in this code 'squareFootage' is the symbolic name of the memory location holding the number of square feet involved in some specific contract, while 'sqFootage' is the symbolic name for the memory location used to pass the information to the constructor. The value in 'sqFootage' will be assigned to the memory location symbolized by 'squareFootage'. If both symbolic names were the same, how would the compiler know which memory location was to have its value assigned to which other memory location?We finish this section with the complete class definition:
D. Using the Constructor In the earlier code we declared instance, contract1, of class ‘Contract’ by writing:
We now know that this somehow calls the default constructor for the Contract class. Now you see why the constructor for a class has the same name as the class itself. The word 'Contract' (upper case 'C') in the line of code above is both a type name AND a very strange 'call' to the constructor for the class. Once we declare our own constructor with three receives, we need to provide the three 'receive' parameters in our call to the constructor. Here is how the declaration should look:
The numbers inside the parenthesis come from the table of initial values we developed in Section VI. We can, of course, use any values we like. When the system sees this line of code it:
Back to our instantiations of contract, here is our code after declaring the five instances.
// Program to test the functionality of the class 'contract'
// File: ch5tst1.cpp
#include <iostream.h >
#include <"contract.h">
void main()
{ Contract contract1(1000, 3, 5);
Contract contract2(500, 10, 3);
Contract contract3(200, 1, 4);
Contract contract4(2000, 20, 5); Contract contract5(300, 2, 2);
}
Below is the completed driver program, replacing the opening set of Change.... functions with calls to the constructor. Follow the instructions for your version of C++ to compile and link this program with the .cpp file containing the definitions for the class member functions.
// Program test the functionality of the class 'contract'
// File: ch5tst1.cpp
#include <iostream.h>
#include "contract.h"
void main()
{ // Code to create the five instances
Contract contract1(1000, 3, 5);
Contract contract2(500, 10, 3);
Contract contract3(200, 1, 4);
Contract contract4(2000, 20, 5);
Contract contract5(300, 2, 2);
int sqFootage;
int numDesks; int numDays;
double charge;
// Code to display the property values of the five instances to see if the constructor worked
// correctly
sqFootage = contract1.ProvideSquareFootage();
cout << "The square footage of contract1 is: " << sqFootage << endl;
numDesks = contract1.ProvideNumberOfDesks();
cout << "The number of desks of contract1 is: " << numDesks << endl;
numDays= contract1.ProvideNumberOfDays();
cout << "The number of days of contract1 is: " << numDays << endl;
charge = contract1.ProvidePerWeekCharge();
cout << "The per week charge for contract 1 is: " << charge << endl<< endl;
sqFootage = contract2.ProvideSquareFootage();
cout << "The square footage of contract2 is: " << sqFootage << endl;
numDesks = contract2.ProvideNumberOfDesks();
cout << "The number of desks of contract2 is: " << numDesks << endl;
numDays= contract2.ProvideNumberOfDays();
cout << "The number of days of contract2 is: " << numDays << endl;
charge = contract2.ProvidePerWeekCharge();
cout << "The per week charge for contract 2 is: " << charge << endl<< endl;
sqFootage = contract3.ProvideSquareFootage();
cout << "The square footage of contract3 is: " << sqFootage << endl;
numDesks = contract3.ProvideNumberOfDesks();
cout << "The number of desks of contract3 is: " << numDesks << endl;
numDays= contract3.ProvideNumberOfDays();
cout << "The number of days of contract3 is: " << numDays << endl;
charge = contract3.ProvidePerWeekCharge();
cout << "The per week charge for contract 3 is: " << charge <<
endl<< endl;
sqFootage = contract4.ProvideSquareFootage();
cout << "The square footage of contract4 is: " << sqFootage << endl; numDesks = contract4.ProvideNumberOfDesks();
cout << "The number of desks of contract4 is: " << numDesks << endl; numDays= contract4.ProvideNumberOfDays();
cout << "The number of days of contract4 is: " << numDays
<< endl;
charge = contract4.ProvidePerWeekCharge();
cout << "The per week charge for contract 4 is: " << charge << endl<< endl; sqFootage = contract5.ProvideSquareFootage();
cout <<
"The square footage of contract5 is: "<< sqFootage <<endl;
numDesks= contract5.ProvideNumberOfDesks();
cout << "The number of desks of contract5 is: " << numDesks
<< endl;
numDays = contract5.ProvideNumberOfDays();
cout << "The number of days of contract5 is: " << numDays << endl;
charge = contract5.ProvidePerWeekCharge();
cout << "The per week charge for contract 5 is: " << charge <<
endl<< endl;
// Code to make the desired changes to property values
contract1. ChangeNumDesks(4);
contract2. ChangeSquareFootage(600);
contract2. ChangeNumDays(5);
contract3. ChangeSquareFootage(300);
contract4. ChangeNumDesks(25);
contract4. ChangeNumDays(3); //
Code to check to see if the changes were handled correctly
sqFootage = contract1.ProvideSquareFootage();
cout <<
"The square footage of contract1 is: " << sqFootage<< endl;
numDesks = contract1.ProvideNumberOfDesks(); cout
<< "The number of desks of contract1 is: " << numDesks<< endl;
numDays = contract1.ProvideNumberOfDays();
cout << "The number of days of contract1 is: " <<
numDays<< endl;
charge = contract1.ProvidePerWeekCharge(); cout <<
"The per
week charge for contract 1 is: " << charge<< endl<< endl;
sqFootage= contract2.ProvideSquareFootage(); cout <<
"The square footage of contract2 is: " << sqFootage<< endl;
numDesks= contract2.ProvideNumberOfDesks();
cout <<
"The number of desks of contract2 is: " << numDesks<< endl;
numDays= contract2.ProvideNumberOfDays();
cout << "The number of days of contract2 is: " <<
numDays<< endl;
charge = contract2.ProvidePerWeekCharge(); cout << "The per week
charge for
contract 2 is: " << charge<< endl<< endl; sqFootage=
contract3.ProvideSquareFootage(); cout
<< "The square footage of contract3 is: " << sqFootage<< endl;
numDesks= contract3.ProvideNumberOfDesks();
cout << "The number of desks of contract3 is: " <<
numDesks<< endl;
numDays = contract3.ProvideNumberOfDays();
cout << "The number of days of contract3 is: " <<
numDays<< endl;
charge = contract3.ProvidePerWeekCharge(); cout << "The per week
charge for
contract 3 is: " << charge<< endl<< endl; sqFootage = contract4.ProvideSquareFootage();
cout << "The square footage of contract4 is: " << sqFootage<< endl; numDesks= contract4.ProvideNumberOfDesks();
cout <<
"The number of desks of contract4 is: " << numDesks<< endl; numDays=
contract4.ProvideNumberOfDays(); cout
<< "The number of days of contract4 is: " <<
numDays<< endl;
charge = contract4.ProvidePerWeekCharge(); cout << "The per
week charge
for contract 4 is: " << charge<< endl<< endl;
sqFootage= contract5.ProvideSquareFootage(); cout
<< "The square footage of contract5 is: " << sqFootage<< endl;
numDesks=
contract5.ProvideNumberOfDesks(); cout
<< "The number of desks of contract5 is: " <<
numDesks<< endl;
numDays = contract5.ProvideNumberOfDays(); cout <<
"The number
of days of contract5 is: " << numDays<< endl;
charge= contract5.ProvidePerWeekCharge();
cout <<
"The per week charge for contract 5 is: " << charge<< endl;
}
Topics Covered in the "Essentials of C++" Further Information on the Object-Oriented Paradigm
Other Explanations of Object-Oriented Programming and C++
| |