LIMITS.H and FLOAT.H

Chapter 17 - Classes in C++

Visual C++ 6: The Complete Reference
Chris H. Pappas and William H. Murray, III
  Copyright 1998 The McGraw-Hill Companies

Features Specific to Classes
The syntax for correctly creating an elementary C++ class was illustrated in the previous chapter. However, classes have extended capabilities that go far beyond this simple syntax. This section is devoted to exploring these capabilities with an eye toward object-oriented programming. In the next chapter, class objects will be woven into more complicated object-oriented programs.
Working with a Simple Class
In this section, we’ll present a short review of a simple class based on the definitions from Chapter 16. Remember that a class starts with the keyword class followed by a class name (tag). In the following example, the class tag name is car. If the class contains member variables, they are defined at the start of the class. Their declaration type is private, by default. This example defines three member variables: mileage, tire_pressure, and speed. Class member functions follow the member variable list. Typically, the member functions are declared public. A private declaration limits the member variables to member functions within the class. This is often referred to as data hiding. A public declaration makes the member functions available outside of the class:
class car {
 int   mileage;
 int   tire_pressure;
 float speed;

public:
 int maintenance(int);
 int wear_record(int);
 int air_resistance(float);
} mycar;
Notice that three member functions are prototyped within the class definition. They are maintenance( ), wear_record( ), and air_resistance( ). All three return an int type. Typically, however, the contents of the member functions are defined outside the class definition—usually, immediately after the class itself.
Let’s continue the study of classes with a look at additional class features.
Nesting Classes
In Chapter 13, you learned that structures can be nested. This also turns out to be true for C++ classes. When using nested classes, you must take care not to make the resulting declaration more complicated than necessary. The following examples illustrate the nesting concept.
Nesting Structures Within a Class
The next listing is a simple example of how two structures can be nested within a class definition. Using nesting in this fashion is both common and practical. You can also use the class keyword in this manner.
//
//  wages.cpp
//  C++ program illustrates the use of nesting concepts
//  in classes. This program calculates the wages for
//  the named employee.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <iostream.h>

char newline;
class employee {
 struct emp_name {
   char firstname[20];
   char middlename[20];
   char lastname[20];
 } name;
 struct emp_hours {
   double hours;
   double base_sal;
   double overtime_sal;
 } hours;

public:
 void emp_input(void);
 void emp_output(void);
};
void employee::emp_input( )
{
 cout << “Enter first name of employee: ”;
 cin >> name.firstname;
 cin.get(newline);    // flush carriage return
 cout << “Enter middle name of employee: ”;
 cin >> name.middlename;
 cin.get(newline);
 cout << “Enter last name of employee:  ”;
 cin >> name.lastname;
 cin.get(newline);

 cout << “Enter total hours worked:  ”;
 cin >> hours.hours;
 cout << “Enter hourly wage (base rate):   ”;
 cin >> hours.base_sal;
 cout << “Enter overtime wage (overtime rate): ”;
 cin >> hours.overtime_sal;
 cout << “\n\n”;
}
void employee::emp_output( )
{
 cout << name.firstname << “ ” << name.middlename
      << “ ” << name.lastname << endl;
 if (hours.hours <= 40)
   cout << “Base Pay:  $”
        << hours.hours
* hours.base_sal << endl;
   else {
     cout << “Base Pay:  $”
          << 40
* hours.base_sal << endl;
     cout << “Overtime Pay: $”
          << (hours.hours-40)
* hours.overtime_sal
          << endl;
     }
}
main( )
{
 employee acme_corp;    // associate acme_corp with class

 acme_corp.emp_input( );
 acme_corp.emp_output( );
 return (0);
}
In the next example, two classes are nested within the employee class definition. The use of nesting, as you can see, can be quite straightforward:
class employee {
 class emp_name {
   char firstname[20];
   char middlename[20];
   char lastname[20];
 } name;
 class emp_hours {
   double hours;
   double base_salary;
   double overtime_sal;
 } hours;

public:
 void emp_input(void);
 void emp_output(void);
};
The employee class includes two nested classes, emp_name and emp_hours. The nested classes, while part of the private section of the employee class, are actually available outside the class. In other words, the visibility of the nested classes is the same as if they were defined outside the employee class. The individual member variables, for this example, are accessed through the member functions (public, by default) emp_input( ) and emp_output( ).
The member functions, emp_input( ) and emp_output( ), are of type void and do not accept arguments. The emp_input( ) function prompts the user for employee data that will be passed to the nested structures (classes). The data collected includes the employee’s full name, the total hours worked, the regular pay rate, and the overtime pay rate. Output is generated when the emp_output( ) function is called. The employee’s name, base pay, and overtime pay will be printed to the screen:
Enter first name of employee: Peter
Enter middle name of employee: Harry
Enter last name of employee: Jones
Enter total hours worked: 52
Enter hourly wage (base rate): 7.50
Enter overtime wage (overtime rate): 10.00

Peter Harry Jones
Base Pay:  $300.00
Overtime Pay: $120.00
The main( ) function in this program is fairly short. This is because most of the work is being done by the member functions of the class:
employee acme_corp;    // associate acme_corp with class

acme_corp.emp_input( );
acme_corp.emp_output( );
The variable acme_corp, representing the Acme Computer Corporation, is associated with the employee class. To request a member function, the dot operator is used. Next, acme_corp.emp_input( ) is called to collect the employee information, and then acme_corp.emp_output( ) is used to calculate and print the payroll results.
An Alternate Nesting Form
There is an alternate way to perform nesting. The following form of nesting is also considered acceptable syntax:
class cars {
 int mileage;
public:
 void trip(int t);
 int speed(float s);
};

class contents {
 int count;
public:
 cars mileage;
 void rating(void);
{
Here, cars becomes nested within the contents class. Nested classes, whether inside or outside, have the same scope.
Working with Constructors and Destructors
A constructor is a class member function. Constructors are useful for initializing class variables or allocating memory storage. The constructor always has the same name as the class it is defined within. Constructors have additional versatility: they can accept arguments and be overloaded. A constructor is executed automatically when an object of the class type is created. Free store objects are objects created with the new operator and serve to allocate memory for the objects created. Constructors are generated by the Visual C++ compiler if they are not explicitly defined.
A destructor is a class member function typically used to return memory allocated from free store memory. The destructor, like the constructor, has the same name as the class it is defined in, preceded by the tilde character (~). Destructors are the complement to their constructor counterparts. A destructor is automatically called when the delete operator is applied to a class pointer or when a program passes beyond the scope of a class object. Destructors, unlike their constructor counterparts, cannot accept an argument and may not be overloaded. Destructors are also generated by the Visual C++ compiler if they are not explicitly defined.
A Simple Constructor and Destructor
The following listing represents the first example involving the use of constructors and destructors. Here a constructor and destructor are used to signal the start and end of a coin conversion example. This program illustrates that constructors and destructors are called automatically:
//
//  coins.cpp
//  C++ program illustrates the use of constructors and
//  destructors in a simple program.
//  This program converts cents into appropriate coins:
//  (quarters, dimes, nickels, and pennies).
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//
#include <iostream.h>

const int QUARTER=25;
const int DIME=10;
const int NICKEL=5;

class coins {
 int number;

public:
 coins( ) {cout << “Begin Conversion!\n”;}      // constructor
 ~coins( ) {cout << “\nFinished Conversion!”;}  // destructor
 void get_cents(int);
 int quarter_conversion(void);
 int dime_conversion(int);
 int nickel_conversion(int);
};

void coins::get_cents(int cents)
{
 number=cents;
 cout << number << “ cents, converts to:”
      << endl;
}
int coins::quarter_conversion( )
{
 cout << number/QUARTER << “ quarter(s), ”;
 return(number%QUARTER);
}

int coins::dime_conversion(int d)
{
 cout << d/DIME << “ dime(s), ”;
 return(d%DIME);
}

int coins::nickel_conversion(int n)
{
 cout << n/NICKEL << “ nickel(s), and ”;
 return(n%NICKEL);
}
main( )
{
 int c,d,n,p;

 cout << “Enter the cash, in cents, to convert: ”;
 cin >> c;

 // associate cash_in_cents with coins class.
 coins cash_in_cents;

 cash_in_cents.get_cents(c);
 d=cash_in_cents.quarter_conversion( );
 n=cash_in_cents.dime_conversion(d);
 p=cash_in_cents.nickel_conversion(n);
 cout << p << “ penny(ies).”;
 return (0);
}
This program uses four member functions. The first function passes the number of pennies to the private class variable number. The remaining three functions convert cash, given in cents, to the equivalent cash in quarters, dimes, nickels, and pennies. Notice in particular the placement of the constructor and destructor in the class definition. The constructor and destructor function descriptions contain nothing more than a message that will be printed to the screen. Constructors are not specifically called by a program. Their appearance on the screen is your key that the constructor and destructor were automatically called when the object was created and destroyed.
class coins {
 int number;

public:
 coins( ) {cout << “Begin Conversion!\n”;}      // constructor
 ~coins( ) {cout << “\nFinished Conversion!”;}  // destructor
 void get_cents(int);
 int quarter_conversion(void);
 int dime_conversion(int);
 int nickel_conversion(int);
};
Here is an example of the output from this program:
Enter the cash, in cents, to convert: 159
Begin Conversion!
159 cents, converts to:
6 quarter(s), 0 dime(s), 1 nickel(s), and 4 penny(ies).
Finished Conversion!
In this example, the function definition is actually included within the constructor and destructor. When the function definition is included with member functions, it is said to be implicitly defined. Member functions can be defined in the typical manner or declared explicitly as inline functions.
Why not expand this example to include dollars and half-dollars!
Initializing Member Variables with Constructors
Another practical use for constructors is for initialization of private class variables. In the previous examples, class variables were set using separate member functions. In the next example, the original class of the previous program is modified slightly to eliminate the need for user input. In this case, the variable number will be initialized to 431 pennies:
class coins {
 int number;

public:
 coins( ) {number=431;}                         // constructor
 ~coins( ) {cout << “\nFinished Conversion!”;}  // destructor
 int quarter_conversion(void);
 int dime_conversion(int);
 int nickel_conversion(int);
};
The route to class variables is always through class member functions. Remember that the constructor is considered a member function.
Creating and Deleting Free Store Memory
Perhaps the most significant reason for using a constructor is in utilizing free store memory. In the next example, a constructor is used to allocate memory for the string1 pointer with the new operator. A destructor is also used to release the allocated memory back to the system when the object is destroyed. This is accomplished with the use of the delete operator.
class string_operation {
 char *string1;
 int  string_len;
 
public:
 string_operation(char *) {string1=new char[string_len];}
 ~string_operation( ) {delete string1;}
 void input_data(char *);
 void output_data(char *);
};
The memory allocated by new to the pointer string1 can only be deallocated with a subsequent call to delete. For this reason, you will usually see memory allocated to pointers in constructors and deallocated in destructors. This also ensures that if the variable assigned to the class passes out of its scope, the allocated memory will be returned to the system. These operations make memory allocation dynamic and are most useful in programs that utilize linked lists.
The memory used by data types, such as int and float, is automatically restored to the system.
Overloading Class Member Functions
Class member functions can be overloaded just like ordinary C++ functions. Overloading functions means that more than one function can have the same function name in the current scope. It becomes the compiler’s responsibility to select the correct function based upon the number and type of arguments used during the function call. The first example in this section illustrates the overloading of a class function named number( ). This overloaded function will return the absolute value of an integer or double with the use of the math functions abs( ), which accepts and returns integer values, and fabs( ), which accepts and returns double values. With an overloaded function, the argument types determine which member function will actually be used.
//
//  absol.cpp
//  C++ program illustrates member function overloading.
//  Program determines the absolute value of an integer
//  and a double.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <iostream.h>
#include <math.h>
#include <stdlib.h>

class absolute_value {
public:
 int number(int);
 double number(double);
};
int absolute_value::number(int test_data)
{
 int answer;

 answer=abs(test_data);
 return (answer);
}

double absolute_value::number(double test_data)
{
 double answer;

 answer=fabs(test_data);
 return (answer);
}

main( )
{
 absolute_value neg_number;

 cout << “The absolute value is ”
      << neg_number.number(-583) << endl;
 cout << “The absolute value is ”
      << neg_number.number(-583.1749) << endl;
 return (0);
}
Notice that the dot operator is used in conjunction with the member function name to pass a negative integer and negative double values. The program selects the proper member function based upon the type (integer or double) of argument passed along with the function name. The positive value returned by each function is printed to the screen:
the absolute value is 583
the absolute value is 583.1749
In another example, angle information is passed to member functions in one of two formats—a double or a string. With member function overloading, it is possible to process both types:
//
//  overld.cpp
//  C++ program illustrates overloading two class member
//  functions. The program allows an angle to be entered
//  in decimal or deg/min/sec format. One member function
//  accepts data as a double, the other as a string. The
//  program returns the sine, cosine, and tangent.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <iostream.h>
#include <math.h>
#include <string.h>

const double DEG_TO_RAD=0.0174532925;
class trigonometric {
 double angle;
 double answer_sine;
 double answer_cosine;
 double answer_tangent;

public:
 void trig_calc(double);
 void trig_calc(char *);
};

void trigonometric::trig_calc(double degrees)
{
 angle=degrees;
 answer_sine=sin(angle * DEG_TO_RAD);
 answer_cosine=cos(angle * DEG_TO_RAD);
 answer_tangent=tan(angle * DEG_TO_RAD);
 cout << “\nFor an angle of ” << angle
      << “ degrees.” << endl;
 cout << “The sine is ” << answer_sine << endl;
 cout << “The cosine is ” << answer_cosine << endl;
 cout << “The tangent is ” << answer_tangent << endl;
}
void trigonometric::trig_calc(char *dat)
{
 char *deg,*min,*sec;

 deg=strtok(dat,"d “);  
 min=strtok(0,”m “);
 sec=strtok(0,”s");
 angle=atof(deg)+((atof(min))/60.0)+((atof(sec))/360.0);
 answer_sine=sin(angle * DEG_TO_RAD);
 answer_cosine=cos(angle * DEG_TO_RAD);
 answer_tangent=tan(angle * DEG_TO_RAD);
 cout << “\nFor an angle of ” << angle
      << “ degrees.” << endl;
 cout << “The sine is ” << answer_sine << endl;
 cout << “The cosine is ” << answer_cosine << endl;
 cout << “The tangent is ” << answer_tangent << endl;
}

main()
{
 trigonometric data;

 data.trig_calc(75.0);
 char str1[] = “35d 75m 20s”;
 data.trig_calc(str1);
 
 data.trig_calc(145.72);

 char str2[] = “65d 45m 30s”;
 data.trig_calc(str2);
 
 return (0);
}
This program makes use of a very powerful built-in function, strtok( ), prototyped in STRING.H. The syntax for using strtok( ) is straightforward:
char *strtok(string1,string2);  //locates token in string1
char *string1;                 //string that has token(s)
const char *string2;           //string with delimiter chars
The strtok( ) function will scan the first string, string1, looking for a series of character tokens. For this example, the tokens representing degrees, minutes, and seconds are used. The actual length of the tokens can vary. The second string, string2, contains a set of delimiters. Spaces, commas, or other special characters can be used for delimiters. The tokens in string1 are separated by the delimiters in string2. Because of this, all of the tokens in string1 can be retrieved with a series of calls to the strtok( ) function. strtok( ) alters string1 by inserting a null character after each token is retrieved. The function returns a pointer to the first token the first time it is called. Subsequent calls return a pointer to the next token, and so on. When there are no more tokens in the string, a null pointer is returned.
This example permits angle readings formatted as decimal values or in degrees, minutes, and seconds of arc. For the latter case, strtok( ) uses the symbol (d) to find the first token. For minutes, a minute symbol (m) will pull out the token containing the number of minutes. Finally, the (s) symbol is used to retrieve seconds.
This program produces the following formatted output:
For an angle of 75 degrees.
The sine is 0.965926
The cosine is 0.258819
The tangent is 3.732051

For an angle of 36.305556 degrees.
The sine is 0.592091
The cosine is 0.805871
The tangent is 0.734722

For an angle of 145.72 degrees.
The sine is 0.563238
The cosine is -0.826295
The tangent is -0.681642

For an angle of 65.833333 degrees.
The sine is 0.912358
The cosine is 0.409392
The tangent is 2.228568
Class member function overloading gives programs and programmers flexibility when dealing with different data formats. If you are not into math or engineering programs, can you think of any applications that interest you where this feature might be helpful? Consider this possibility: if you are the cook in your household, you could develop an application that modifies recipes. You could write a program that would accept data as a decimal value or in mixed units. For example, the program might allow you to enter “1 pint 1.75 cups” or “1 pint 1 cup 2 tbsp”.
Friend Functions
Classes have another important feature—the ability to hide data. Recall that member data is private by default in classes—that is, sharable only with member functions of the class. It is almost ironic, then, that there exists a category of functions specifically designed to override this feature. Functions of this type, called friend functions, allow the sharing of private class information with nonmember functions. Friend functions, not defined in the class itself, can share the same class resources as member functions.
Friend functions offer the advantage that they are external to the class definition, as shown here:
//
//  secs.cpp
//  C++ program illustrates the use of friend functions.
//  Program will collect a string of date and time
//  information from system. Time information will
//  be processed and converted into seconds.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <iostream.h>
#include <time.h>    // for tm & time_t structure
#include <string.h>  // for strtok function prototype
#include <stdlib.h>  // for atol function prototype
class time_class {
 long secs;
 friend char * present_time(time_class);  //friend
public:
 time_class(char *);
};

time_class::time_class(char *tm)
{
 char *hours,*minutes,*seconds;

 // data returned in the following string format:
 // (day month date hours:minutes:seconds year)
 // Thus, need to skip over three tokens, i.e.,
 // skip day, month and date
 hours=strtok(tm," “);
 hours=strtok(0,” “);
 hours=strtok(0,” “);
 // collect time information from string
 hours=strtok(0,”:");
 minutes=strtok(0,":");
 seconds=strtok(0," “);

 // convert data to long type and accumulate seconds.
 secs=atol(hours)*3600;
 secs+=atol(minutes)*60;
 secs+=atol(seconds);
}

char * present_time(time_class);  // prototype

main( )
{
 // get the string of time & date information
 struct tm *ptr;
 time_t ltime;
 ltime=time(NULL);
 ptr=localtime(&ltime);

 time_class tz(asctime(ptr));
 cout << ”The date/time string information: “
      << asctime(ptr) << endl;
 cout << ”The time converted to seconds: “
      << present_time(tz) << endl;
 return (0);
}

char * present_time(time_class tz)
{
 char *ctbuf;
 ctbuf=new char[40];
 long int seconds_total;

 seconds_total=tz.secs;
 ltoa(seconds_total,ctbuf,10);
 return (ctbuf);
}
Notice in the class definition the use of the keyword friend along with the description of the present_time( ) function. When you examine the program listing, you will notice that this function, external to the class, appears after the main( ) function description. In other words, it is written as a traditional C++ function, external to member functions of the defined class.
This program has a number of additional interesting features. In the function main( ), the system’s time is obtained with the use of time_t and its associated structure tm. In this program, ltime is the name of the variable associated with time_t. Local time is initialized and retrieved into the pointer, ptr, with the next two lines of code. By using asctime(ptr), the pointer will point to an ASCII string of date and time information.
struct tm *ptr;
time_t ltime;
ltime=time(NULL);
ptr=localtime(&ltime);

time_class tz(asctime(ptr));
The date and time string is formatted in this manner:
day month date hours:minutes:seconds year \n \0
For example:
Mon Aug 10 13:12:21 1998
There is a more detailed discussion of built-in functions, including those prototyped in TIME.H, in Chapter 15.
The string information that is retrieved is sent to the class by associating tz with the class time_class:
time_class tz(asctime(ptr));
A constructor, time_class(char *), is used to define the code required to convert the string information into integer data. This is accomplished by using the strtok( ) function. The date/time information is returned in a rather strange format. To process this information, strtok( ) must use a space as the delimiter in order to skip over the day, month, and date information in the string. In this program, the variable hours initially serves as a junk collector for unwanted tokens. The next delimiter is a colon (:), which is used in collecting both hour and minute tokens from the string. Finally, the number of seconds can be retrieved by reading the string until another space is encountered. The string information is then converted to a long type and converted to the appropriate number of seconds. The variable secs is private to the class but accessible to the friend function.
The friend function takes the number of accumulated seconds, tz.seconds, and converts it back to a character string. The memory for storing the string is allocated with the new operator. This newly created string is a result of using the friend function.
The program prints two pieces of information:
The date/time string information: Mon Aug 10 09:31:14 1998

The time converted to seconds: 34274
First, cout sends the string produced by asctime( ) to the screen. This information is obtainable from time_t( ) and is available to the main( ) function. Second, the system time is printed by passing present_time to the cout stream.
While friend functions offer some interesting programming possibilities when programming with C++ classes, they should be used with caution.
The this Pointer
The keyword this is used to identify a self-referential pointer that is implicitly declared in C++, as follows:
class_name *this;   //class_name is class type.
The this pointer is used to point to the object for which the member function is invoked and is only accessible in member functions of the class (struct or union) type. It can also be used to include a link on a doubly linked list or when writing constructors involving memory allocations, as you see in the following example:
class class_name {
 int x,y,z;
 char chr;
 
public:
 class_name(size) {this=new(size);}
 ~class_name(void);
};

Books24x7.com, Inc 2000 –  


Visual C++ 6(c) The Complete Reference
Visual Studio 6: The Complete Reference
ISBN: B00007FYGA
EAN: N/A
Year: 1998
Pages: 207

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net