|
|
In this laboratory assignment we will consider the following:
A. Review of C++ Class Construct
B. Using C++ Constructors
C. Overloaded Functions
D. Overloading Constructors in a C++ Class
E. Overloaded Operators
In Lab 6, we introduced the C++ class construct which is used to define an object. The general form of the class specification section could be set up as follows:
class SomeClass { public: //Data items and function prototypes are //included in this section to provide the public interface //of the object (the data and operations //which are needed to use the object effectively) . . . private: //This section contains data items and function //prototypes which can only be accessed by members //of the class. . . };
We also saw that the implementation section for the class contains member function definitions of the class. Short functions can be completely defined in the class specification. However, it is usually considered better if the class specification is precisely that -- the specification of the class. The actual implementation of the class should be contained in a separate implementation section. We learned in Lab 4 to declare an instance of the class as follows:
SomeClass object1;
class LoanClass
{
1:
void printRate();
float payoff();
void schedule();
void LoanSettings (char t, float r, int m);
2:
char type;
float rate;
int months;
float simplePayment();
float compoundPayment();
};
class LoanClass
{
public:
void printRate();
float payoff();
void schedule();
void LoanSettings(char t, float r, int m);
private:
char type;
float rate;
int months;
float simplePayment();
float compoundPayment();
};
the main function of the program can make a call to simplePayment() for any
LoanClass instance.
With that brief review, we will introduce some addititonal features of the C++ class. Consider the Clock class which we discussed in Lab 6. If we wished to declare a Clock object and then initialize the data members in the object, we would first have to declare the object:
The Clock object, yourClock, could now be initialized by calling the setClock() member function. Initializing yourClock to the time 10:15:00 am could be accomplished as follows:Clock yourClock;
yourClock.setClock (10,15,0,AM);
There is nothing wrong with this method of declaring and initializing an object. However, there is a more convenient way to initialize the data members of an object when defining the object. C++ includes special provisions for such initializations. When a class is defined, a special member function called a constructor can be used. A constructor is a member function that is automatically called when an object of that class is declared. A constructor may be used to initialize the values of data members and to do any other initialization that may be needed.
For example, consider the ifstream class. When we declare an input file stream as:
an instance of the ifstream class is created. The statementifstream ins;
is a call to the member function "open" which connects the input stream ins to the file "myfile.dat". The following shows an alternate way to declare an input stream and to open the stream at declaration time:ins.open ("myfile.dat");
ifstream ins("myfile.dat");
This second form of declaration and initialization uses a "constructor"
of the class ifstream.
Suppose we wish to declare an instance of a Clock and initialize the Clock to the time 10:15:00 am at declaration time. If we had an appropriate constructor, we could type:
Clock yourClock (10,15,0,AM);
We now need to learn how to define a constructor (a special member function) to allow us to use the above statement. A constructor is defined the same way as other member functions, except for two rules:
Let us consider a modified version of the Clock class declaration:
class Clock { public: //function prototypes of member functions (methods) //class constructor which initializes the clock to //h hours, m minutes, s seconds and aOrP am or pm. Clock(int h, int m, int s, int aOrP); //Set the clock to the specified time void setClock (int h, int m, int s, int aOrP); //Display the time in standard notation void displayStandard(); //Display the time in military notation void displayMilitary(); //Increment the time by one second void incrementClock(); private: //declarations of data members that are private int hr, //an hour in the range 1 - 12 min, //a minute in the range 0 - 59 sec, //a second in the range 0 - 59 ampm; //is the time AM (0) or PM (1) };
Note that the name of the constructor is Clock(), the same as the name of the class. Also note that the prototype for the constructor Clock does not start with void or with any other type. Finally note that the constructor is placed in the public section of the class specification. Normally, you should make your constructors public member functions so all clients may access the constructor.
With the redefined Clock class, two objects of type Clock can be declared and initialized as follows:
Assuming that the implementation of the constructor performs the initializing actions promised, the declaration above would declare yourClock to have the time 10:15:00 am and myClock to have the time 1:30:45 pm.Clock yourClock (10, 15, 0, AM);Clock myClock (1, 30, 45, PM);
The implementation of a constructor is given in the same way as any other member function. The implementation of the Clock constructor would appear as follows:
//Class constructor which initializes the clock to //h hours, m minutes, s seconds and am or pm. Clock::Clock(int h, int m, int s, int aOrP) { //hr, min, sec, and ampm are declared in the private //section of the class hr = h; min = m; sec = s; ampm = aOrP; }
Since the class and the constructor function have the same name, the name
Clock
occurs twice in the function heading. The Clock before
the scope resolution operator :: is the name of the class
and
Clock after the scope resolution operator is the name
of the constructor function. Note that there is no return type
specified in the function heading.
A. There is no type
B. void
C. Bool because it returns true or false based on whether or not the initialization was successful.
D. The type is dependent upon the actual class.
class LoanClass
{
public:
LoanClass(char t, float r, int m);
void printRate();
float payoff();
void schedule();
private:
char type;
float rate;
int months;
float simplePayment();
float compoundPayment();
};
A. public member function
B. private member function
C. public data member
D. private data member
Constructors will be considered again after we discuss a feature
of C++ which allows a function to have multiple definitions.
In the English language, a word may have more than one meaning. We determine the meaning of a word by considering the context in which the word appears. For example, the word string has two meanings -- an object possibly made from twine which might be used to tie around another object or (in computer science) just a sequence of characters. We have no problem determining which meaning is intended when we consider the following sentences:
Similarly, functions may have more than one meaning in C++. When this is the case, we say we have an overloaded function. Thus, the same function name may be defined more than once with different formal parameters.
In C++, we can overload functions and operators by providing multiple definitions for the function or operator. The compiler determines which definition is intended by the context in which the function or operator occurs. Suppose we wished to have two meanings for a function called ave () which would average numbers and we provide the following definitions:
and//Find the average of two integers float ave (int a, int b) { return float (a+b)/2.0; }
The main function might have the following statements//Find the average of an array of n integers float ave (int a[], int n) { int sum = 0; for (int i = 0; i < n; i++) sum += a[i]; return float (sum) / n; }
int a = 2, b = 3; int x[5] = {10, 20, 30, 40, 50}; cout << "The average of 2 and 3 is " << ave (a, b) << endl; cout << "The average of the integers 10, 20, 30, 40," << " 50 is" << ave (x, 5) << endl;
As you can see the meaning of the function ave() is completely determined by the context (what arguments are sent to the function). This property of functions with multiple meanings is called overloading. Overloading is an example of polymorphism, a term derived from a Greek word meaning "many forms". The use of the same function name to mean different things is called polymorphism. We will see that polymorphism is very important in defining objects.
int a = 10, b = 6;
int x[4] = {5, 6, 7, 8};
cout << sum(a, b) << endl;
cout << sum(x, 4) << endl;
B. Which of the following determines which definition of an overloaded
function is intended based on the context?
A. The user
B. The compiler
C. The linker
D. The processor
A. The program must add 3 numbers. It must also add 3 arrays.
B. The program must multiply two arrays. It must also add two arrays.
C. A and B
D. None of the above
A constructor is called automatically when an object is declared. In Lab 6, the Clock class and the Date class did not include a constructor. When a class does not include a constructor, the compiler automatically creates a default constructor for the class. Thus when the statement
was used, the compiler provided a default constructor.Date christmas;
Using constructors is an all or nothing situation. If the class contains a constructor, then every time an object of that type is declared, C++ looks for an appropriate constructor definition in the class. Thus the following declaration would be illegal for the modified Clock class:
Clock newClock; //ILLEGAL declaration
The reason this statement is illegal is that since the Clock class contains a constructor, the compiler will not create a default constructor. Instead the Clock class is searched for a constructor which requires no arguments. Since the class only has one constructor which requires four arguments, this is an illegal declaration. To allow a statement such as the one above, a constructor must be added (an overloaded function) which requires no arguments.
A constructor with no arguments is called a default constructor because it applies in the default case when an object is declared without specifying any arguments. Since it is likely that an object will be declared without giving any arguments, a default constructor should almost always be included with the class. The default constructor for the Clock class might be:
Clock::Clock () {}
Notice that this default constructor does nothing. All data values are left uninitialized. In some cases, the default constructor would be used to initialize appropriate data members to default values. If the overloaded constructor above were added to the Clock class and the statement:
appeared in a C++ program, it would be a legal statement and the default constructor would be called. In summary, two points can be made about supplying default constructors:Clock newClock;
As a second example of using overloaded constructors in a class definition, suppose we wished to have a default constructor to set the time of a Clock object to 12:00 am so that any of the following declarations would work.
Clock myClock(10, 15, 0, PM); Clock yourClock;
The default constructor would now be:
//Class constructor which initializes the clock to
//12:00 am.
Clock::Clock()
{
hr = 12;
min = 0;
sec = 0;
ampm = 0;
}
It should be noted that you cannot have two default constructors.
Not only can functions be overloaded but operators, such as ==, !=, +, etc., can also be overloaded. In fact this feature has been used anytime the same operator is used for different types. For example:
int num1, num2; Clock clock1, clock2; //assume that the integer num2 and the Clock clock2 have //been initialized //first use of the operator "=" between integer operands num1 = num2; //second use of the operator "=" between Clock operands clock1 = clock2;
The operator "=" is used twice -- once between two integers and once between two Clock objects. The operator is used to replace the integer num1 by the integer num2 in the first instance. In the second instance, the hr, min, sec, and ampm data in clock1 is replaced by the hr, min, sec and ampm data of clock2.
We might also wish to overload other operators such as !=, <, > so that we could do the following for example:
Clock yourClock, myClock; . . . if (yourClock != myClock) cout << "Your clock is wrong.\n";
We cannot use the operator != between two Clock objects without first defining what this would mean. C++ does provide a default definition of the operator = but does not provide a definition for other operators. When the operator = is used between two objects of a class, C++ copies the data member of the class to the right of the = to the class on the left.
C++ considers an operator to be a function, so an operator can be overloaded also. Overloading an operator, such as !=, is very similar to overloading a function. To overload the != operator, the following function prototype would be included in the Clock specification section.
bool operator != (Clock rhs);
As can be seen, an operator function named "operator op" overloads the operator op. Thus the function operator !=( ) overloads the operator !=, the function operator < ( ) would overload the operator < and so on. There are some restrictions:
The implementation of the function operator != for the Clock class follows below:
bool Clock::operator != (Clock rhs) { bool notequal = false; //hr, min, sec, ampm are private data members of the lhs clock if ((hr != rhs.hr)||(min != rhs.min)||(sec != rhs.sec)||(ampm != rhs.ampm)) notequal = true; return notequal; }