|
|
Variable Data Type | Values |
unsigned short int | 0 to 65,535 |
short int | -32,768 to 32,767 |
unsigned long int | 0 to 4,294,967,925 |
long int | -2,147,483,648 to 2,147,483,647 |
int | -2,147,483,648 to 2,147,483,647 (32 bit) |
unsigned int | 0 to 4,294,967,295 (32 bit) |
char | 256 character values |
float | 1.2e-38 to 3.4e38 |
double | 2.2e-308 to 1.8e308 |
To define a variable, you first define its type, followed by the name. You may also assign values to variables by using the assignment (=) operator, as in these two examples:
double base = 5; unsigned long int base =5;
In C++, you may also define your own type definition. You do this by using the keyword typedef, followed by the existing type and name:
typedef unsigned long int ULONG; ULONG base =5;
Defining your own type does save you the trouble of typing the entire declaration.
The next line of the code, line 9, defines the prototype of your function:
double Area (double,double);
This function has a type double, a name Area, and a parameter list of two variables of type double. When you define the prototype, it is not necessary to define the parameters, but it is a good practice to do so. This program takes two inputs from the user, namely base and height of the triangle, and calculates the area of the triangle. The base, height, and area are all variables. The Helloworld.cpp example used the insertion (<<) operator. In this example, you use the extraction (>>) operator. The program queries the user to enter a value for the height of the triangle on line 13. When the user enters a value for height, the data from the screen is extracted and placed into the variable height. The process is repeated for the base of the triangle on lines 15 and 16. After accepting the input from the user, the function main() passes execution to the function Area(base,height) along with the parameter values for base and height. When main() passes the execution to the function Area(base, height), it expects a value of type double in return from the function. The calculation of the area of the triangle is conducted on line 27:
area = (0.5*base*height);
NOTE: Area is the name of a function, and area is a variable name. Because C++ is case sensitive, it clearly distinguishes these two names.
This statement uses the standard operators, the assignment operator (=), and the multiplication operator (*). The assignment operator assigns the result of (0.5*base*height) to the variable area. The multiplication operator (*) calculates the resulting values of (0.5*base*height). The assignment operator (=) has an evaluation order from right-to-left. Hence, the multiplication is carried out prior to assigning the values to area. The five basic mathematical operators are addition (+), subtraction (-), multiplication (*), division (/), and modulus (%).
Line 28 of the Area function returns the value of the variable area to the main() function. At this point, the control of the program is returned to line 18 of the main() function. The remainder of the program displays the result of area to the screen.
While programming large complex programs, it is often necessary to query the user and provide direction to the program based on his input. This is accomplished by using the if statement. The next example demonstrates the application of an if statement. The format of the if statement is
if (this expression) do this;
The if statement is often used in conjunction with relational operators. Another format of the if statement is
if (this expression) do this; else do this;
Because if statements often use relational operators, let's review relational operators. Relational operators are used to determine if two expressions or numbers are equal. If the two expressions or numbers are not equal, the statement will evaluate to either 0 or false. Table A.2 lists the six relational operators defined in C++.
Operator | Name |
== | Comparative |
!= | Not equal |
> | Greater than |
< | Less than |
>= | Greater than or equal to |
<= | Less than or equal to |
C++ also has logical operators. The advantage of logical operators is the ability to compare two individual expressions and conclude whether they are true or false. Table A.3 lists the three logical operators.
Symbol | Operator |
&& | AND |
|| | OR |
! | NOT |
An important and powerful feature of C++ is function overloading, or polymorphism. Polymorphism is the ability to have more than one function with the same name that differ in their parameter lists. The next example is an extension of the previous triangle code. In this program, you will calculate the area of a triangle and a circle. You will be asked whether you want to calculate the area of a triangle or a circle. Depending upon your response, 1 for triangle and 2 for circle, the program collects your input and calculates the area. In Listing A.3, the Area function is overloaded. The same function name is used to calculate the area of the triangle or the circle. The functions differ only in their parameter lists.
1: // Workspace Name: Overload 2: // Program Name: Overload.cpp 3: 4: # include <iostream.h> 5: 6: double base,height,radius; // Global variables 7: double Area_of_triangle,Area_of_circle; // Global variables 8: int choice; // Global variable 9: 10: double Area (double,double); // Function prototype 11: double Area (double); // Function prototype 12: 13: const double pi = 3.14; // Constant variable 14: 15: void main() // main function 16: 17: { 18: cout << "To find the area of a Triangle, input 1 \n"; 19: cout << "To find the area of a Circle, input 2 \n"; 20: cin >> choice; 21: 22: if (choice == 1) 23:
24: {
25: cout << "Enter the base of the triangle: "; 26: cin >> base; 27: cout << "Enter the height of the triangle: "; 28: cin >> height; 29: 30: Area_of_triangle = Area(base,height); 31: 32: cout << "The Area of the Triangle is: "<<Area_of_triangle<<endl; 33: } 34: 35: if (choice == 2) 36: 37: { 38: cout << "Enter radius of the Circle: "; 39: cin >> radius; 40: Area_of_circle = Area(radius); 41: cout << "The area of the Circle is: "<<Area_of_circle<<endl; 42: } 43: 44: if (choice != 1 && choice != 2) 45: 46: { 47: cout << "Sorry! You must enter either 1 or 2 \n"; 48: } 49: } 50: 51: double Area (double base, double height) 52: { 53: return (0.5*base*height) 54: } 55: 56: double Area(double radius) 57: { 58: return (pi*radius*radius); 59: }
In all of the preceding examples, the variables have been declared at the beginning of the program, prior to defining the main() function. Declaring variables in this fashion is more akin to C programs than C++. They are global variables and can be accessed by all the functions. However, you may also define local variables that have a scope only in a particular function. Local variables can have the same names as the global variables, but they do not change the global variables. Local variables refer only to the function in which they are defined. This difference can be confusing and lead to erratic results.
The program in Listing A.4 clearly shows the difference between global and local variables. You will calculate the area of a circle using global variables and local variables.
1: // Workspace: Variable 2: // Program name: Global.cpp 3: 4: #include <iostream.h> 5: 6: double area; 7: double Area (double); 8: const double pi = 3.14; 9: double radius = 5; 10: 11: int main() 12: 13: { 14: cout<<"This Program Calculates The Area Of A Circle \n"; 15: area = Area (radius); 16: cout << "The Area of the Circle is: "<<area<<endl; 17: cout << "The Radius In the Main() Function is: "<<radius<<endl; 18: return 0; 19: } 20: 21: double Area (double radius) 22: { 23: area = (pi*radius*radius); 24: cout<<"The Radius In the Area() Function is: "<<radius<<endl; 25: return area; 26: }
The variable radius is accessible in the main() function and also the Area() function, and it is the same. The result of executing this program is shown in Figure A.6.
FIGURE A.6. Global.cpp--using a global variable.
As the program executes, it shows the value of the variable radius in the different functions. You will now modify the global variable to be a local variable. Add an additional line to the Area function defining a local variable:
double radius = 2;
Compile and execute this program. The results are shown in Figure A.7.
FIGURE A.7. Global.cpp--global and local variables.
You will notice that the value of the variable radius remains unchanged in the main() function and changes locally in the Area() function. The area of the circle is calculated based on the value of the local variable, whereas at the same time, the value of the global variable is not changed but is hidden from the function Area().
NOTE: It is always advisable to differentiate your global and local variables by prefixing them with a g for global and l for local.
Pointers are one of the most important features of C++, and they are always confusing to new programmers of C++. Pointers work by providing access to the original data directly, which increases efficiency. Pointers primarily work with two operators, the indirection operator (*) and the address-of operator (&). It is common practice to add a p to the beginning of a pointer variable's name to distinguish it from other variables. A pointer is just another variable, but the difference is it holds a memory address. You declare a pointer by putting an asterisk (*) in front of the pointer name. To access the address of the variable, you put the & operator in front of the variable name.
To understand pointers, you need a brief overview of how variables are stored. You covered different variable types in Table A.1. Table A.4 shows the size of the variable types.
Variable Type | Size in Bytes |
unsigned short int | 2 bytes |
short int | 2 bytes |
unsigned long int | 4 bytes |
long int | 4 bytes |
int | 4 bytes (32 bit) |
unsigned int | 2 bytes(32 bit) |
char | 1 byte |
float | 4 bytes |
double | 8 bytes |
In the program address.cpp in Listing A.5, the two variables base and radius each occupy 8 and 4 bytes. Assume that your computer memory has a certain space to store these variables, they are sequentially numbered from 1 through 12, and each space is 1 byte. When you declare the variable base of type double, it occupies 8 bytes. Assume these 8 bytes reside at locations beginning from 1 through 8. You also declared another variable radius of type int, which occupies 4 bytes and its location is byte 9 through byte 12. The location of each of these variables is termed as its address. Hence, the variable base has an address beginning at address 1 and ending at address 8. Similarly, the variable radius has an address beginning at address 9 and ending at address 12. When you use the address-of operator (&) on a variable, this is the address returned. The variable base has an address from 1 through 8, but the address-of operator returns its address as 1. Internally, the system already knows that the total addresses occupied are 8 because you defined its type as double.
NOTE: The byte size shown in Table A.4 is not fixed. It can be different depending on your compiler and the hardware on which it runs. To determine the size of the variable for your individual compiler and hardware settings, use the sizeof() function as implemented in Listing A.5 on lines 13 and 16.
The program in Listing A.5 shows how to access the memory address of variables.
1: // Workspace: Pointers 2: // Program name: Address.cpp 3: 4: #include <iostream.h> 5: 6: double base = 5.0; 7: int radius = 2; 8: 9: void main() 10: { 11: cout<<"The VALUE of base is: "<<base<<endl; 12: cout<<"The ADDRESS of base is: "<<&base<<endl; 13: cout<<"The SIZE of double base is: "<<sizeof(double)<< "bytes \n"; 14: cout<<"The VALUE of radius is: "<<radius<<endl; 15: cout<<"The ADDRESS of radius is: "<<&radius<<endl; 16: cout<<"The SIZE of integer radius is: "<<sizeof(int)<<" bytes \n"; 17: }
The address of the variables is accessed directly on lines 12 and 15 by using the address-of operator (&).The addresses of the variables base and radius are shown in Figure A.8. The addresses of the variables depend on your system, so they might not be the same.
FIGURE A.8. Using the address-of operator.
The indirection operator (*) operates by providing access to the value stored in the address of the variable. When a pointer is declared for a specific variable type (such as int), it should not be used with any other type unless it is recast to a new type. You should remember that a pointer is a variable, and like all other variables, it should be declared and initialized. A pointer that is not initialized could be dangerous. The program in Listing A.5 is modified to access the values of the variables radius and base. The modified program is provided in Listing A.6.
1: // Workspace: Pointers
2: // Program name: Address.cpp 3: 4: #include <iostream.h> 5: 6: double base =5.0; 7: int radius =2; 8: 9: double *pBase =0; // Initialize the pointer variable 10: int *pRadius =0; // Initialize the pointer variable 11: 12: void main() 13: { 14: pBase = &base; // Assign the address of base 15: pRadius = &radius; // Assign the address of radius 16: cout<<"The VALUE of base is: "<<base<<endl; // Output value of base 17: cout<<"The ADDRESS of base is: "<<&base<<endl; // Output address of Âbase 18: cout<<"The SIZE of double base is: "<<sizeof(double)<< "bytes \n 19: cout<<"The VALUE of pBase is: "<<*pBase<<endl; Â // Output redirected value of base 20: 21: cout<<"The VALUE of radius is: "<<radius<<endl; Â// Output value of radius 22: cout<<"The ADDRESS of radius is: "<<&radius<<endl; Â// Output address of base 23: cout<<"The SIZE of integer radius is: "<<sizeof(int)<<" bytes \n"; 24: cout<<"The VALUE of pRadius is: "<<*pRadius<<endl; Â// Output redirected value of radius 25: 26: }
An important feature in C++ that is used often with function parameters is references. Reference is simply a synonym for variable. Until now, you have passed parameters in functions by value. You will learn how to pass parameters by reference. You create a reference variable by specifying its type and preceding the name with the reference operator (&). If you have a variable float radius, you create a reference with
void functionname (float &rfradius);
You can give the reference variable any name you want; in the following example, the reference variable names have an rf prefix. The advantage of a reference is that you can pass it as a parameter, like any other variable. However, unlike regular parameters, changes made to the reference's value while in a function are stored in the original variable. The example in Listing A.7 shows how the reference changes the value of the variable in the main() function.
1: // Workspace: Reference 2: // Program name: Refer.cpp 3: 4: #include <iostream.h> 5: 6: void squareit (float &num); 7: int main() 8: 9: { 10: float num=5.0; 11: 12: cout<<"In Main: before squaring number: "<<num*num<<"\n"; 13: 14: squareit (num); 15: cout<<"In Main: after squaring number: "<<num*num<<"\n"; 16: return 0; 17: 18: } 19: 20: void squareit (float &rfnum) 21: { 22: 23: cout<<"In Squareit: before squaring number: "<<rfnum*rfnum<<"\n"; 24: 25: rfnum = rfnum+5; 26: 27: cout<<"In Squareit: after squaring number: "<<rfnum*rfnum<<"\n"; 28: 29: }
You define a function squareit on line 6, and its parameters are references. This is the function prototype. On line 10, the variable num is given a value of 5. The square of the number is displayed to the screen on line 15. On line 14, you call the squareit function.
NOTE: You pass the variable num and not its address.
Only when execution jumps to line 20 from line 14 are the variables identified as references. On line 27, the references are squared and displayed. They should be the same as the variables because they are just like aliases for the variables. On line 25, you add 5 to the reference, which in turn changes the variable num. The incremented value is squared and displayed to the screen. Execution returns to main() on line 15, where the display confirms the variable was changed. The output for this program is shown in Figure A.9.
FIGURE A.9. Passing parameters by reference.
In the previous sections, you used data types (int, float, and so on) that are inherently built into C++. In large complex programs, it is easier to define your own type, which could be a combination of the inherent types. Classes were added to C++ primarily for this purpose--to enable the programmer to be able to define custom data types and methods. The concept of classes in C++ evolved due to certain limitations of the concept of structures in C. To thoroughly understand classes, you have to step back into C and understand structures first.
A structure in C/C++ is a way of representing your own custom data. When you defined variables, you first defined their data types, followed by their names:
int radius;
To define your own data types, you use the keyword struct. The syntax for declaring a structure is
struct [structure_name] { data_members }
The data_members of a structure are variables and functions. When functions are associated with classes, they are more appropriately referred to as methods. From now on, you use the term function for program code that is not a part of a structure or class. A reference to methods indicates that the function is associated with a class structure. To understand how structures are used, review the example in Listing A.8.
1: // Workspace Name: Class1 2: // Program Name: Struct.cpp 3: #include <iostream.h> 4: 5: struct farm_house 6: { 7: int pig_values; 8: }; 9: 10: int main() 11: { 12: farm_house pig1, pig2, pig3; 13: 14: pig1.pig_values = 12; 15: pig2.pig_values = 13; 16: pig3.pig_values = 14; 17: 18: cout << "The value of pig1 is " << pig1.pig_values<< "\n"; 19: cout << "The value of pig2 is " << pig2.pig_values << "\n"; 20: cout << "The value of pig3 is " << pig3.pig_values << "\n"; 21: 22: return 0; 23: }.
On line 5, the struct keyword is followed by the name of the structure. The actual definition of the structure is enclosed in the curly brackets. This particular structure defines a data member of type int and name pig_value. If you remember, I mentioned earlier that when you define a structure, you basically define a custom-made data type. All data types end with a semicolon, so the structure should also end with a semicolon. On line 12, you define three instances of the same type of farm_house, each of which contains a single int type variable.
NOTE: If you strictly use C, then to define instances on line 12, you must use the keyword struct:struct farm_house pig1, pig2, pig3;
This is no longer required in C++.
On lines 14 through 16, you assign values to the member variables of each structure. The structure member operator (.), also called the dot operator, is used to access member variables of the structure. On lines 18 through 20, the assigned values are output to the screen. Figure A.10 shows the output from this program.
FIGURE A.10. Structure output.
The most important concept of object-oriented programming is encapsulation. Encapsulation can involve one or more classes. Encapsulation promotes safeguards and data hiding. The struct.cpp program had no encapsulation or classes. What do encapsulation and classes mean in object-oriented programming?
Let's start with describing the syntax and components of a class:
class class_name { public: class_name_constructor; ~class_name_destructor; class_method_prototypes(); class_member_variables; private: class_method_prototypes(); class_member_variables; };
The words in bold are keywords. You declare a class by using the class keyword. This is followed by the name of the class. The data and methods of a class are enclosed in curly brackets ({ }). The methods of a class are function prototypes. They determine the behavior of the objects of your class. The member variables are the variables in your class. Classes have constructors and destructors. The methods and variables can be classified as either public or private.
You will now re-create the previous example of Struct.cpp in Listing A.8, employing the class and encapsulation methodology. The output from this program in Listing A.9 is identical to the previous example, Struct.cpp.
1: // Workspace: Class2 2: // Program Name: Clasfarm.cpp 3: #include <iostream.h> 4: 5: class farm_house 6: { 7: int pig_values; 8: public: 9: void set(int input); 10: int get(void); 11: }; 12: 13: void farm_house::set(int input) 14: { 15: pig_values = input; 16: } 17: 18: int farm_house::get(void) 19: { 20: return pig_values; 21: } 22: 23: int main() 24: { 25: farm_house pig1, pig2, pig3; 26: 27: 28: pig1.set(12); 29: pig2.set(13); 30: pig3.set(14); 31: 32: cout << "The value of pig1 is " << pig1.get() << "\n"; 33: cout << "The value of pig2 is " << pig2.get() << "\n"; 34: cout << "The value of pig3 is " << pig3.get() << "\n"; 35: 36: return 0; 37: 38: }
Compare the struct declaration of the Struct.cpp program in Listing A.8 (lines 5 through 7) to the class declaration of the Clasfarm.cpp program in Listing A.9 (lines 5 through 11). The difference is in the private and public portions of their declarations. In the struct declaration, everything is public, whereas in the class declaration, you begin with a private section. All data and methods at the beginning of a class are private. This means the member variable
int pig_values;
is private and hidden to methods outside the class. This means that the variable pig_ values is not accessible inside main(). In other words, this member variable is hidden. This member variable is accessible to the methods of its class, mainly
void set (int input); int get(void);
These methods are defined to be public. Because they are public, these methods can be accessed by any objects of this class. On line 25, you defined pig1, pig2, and pig3 to be instances or objects of the class. What? I am sure you are wondering why pig1 is an object.
You defined on line 5 a class farm_house. Remember when you declare a class, all you are doing is declaring a new type. When you declare a variable, you declare its type and then the variable name, as shown here:
long somevariable, anotherone, onemore;
Similarly, to define an object of a class, you declare the type, which in this case is farm_house, and the object name, which is pig1:
farm_house pig1,pig2,pig3;
On line 28, you set the value of pig1 to 12. This is done using the dot operator (.). The object pig1 has access to the method set(). The set() method is a method of the class farm_house, so it has access to its private data. The implementation of the set() method is shown on line 13. For the program to know that the set() method is within the scope of the class farm_house, you use the scope (::) operator. On line 15, the variable input is set to the variable pig_values.
The class farm_house declared two public methods. The other method is the get() method. The get() method is implemented on line 18. The get() method takes no parameters but only returns the pig_values because it also is within the scope of the class farm_house.
On line 32, the get() method is again called by the objects pig1, pig2, and pig3 to return the pig_values to the screen.
If you compare the two programs struct.cpp and clasfarm.cpp, you notice that one is about 23 lines, whereas the other is 38 lines. The code just got longer by implementing classes! This is true. The big benefits of using classes are really seen in more complex and larger programs. Also, because you hide critical data from the user, using classes is safer and less error prone. It enables the compiler to find mistakes before they become bugs.
Earlier, I defined the syntax of a class. In the syntax, I mentioned constructors and destructors. However, in the example clasfarm.cpp, you did not define any constructors or destructors. If a constructor or a destructor is not defined, the compiler creates one for you.
A constructor is a class initialization function that is executed automatically when a class instance is created. A constructor must abide by the following rules:
class farm_house { public: farm_house(); //constructor ..... ..... }
A destructor function is the opposite of a constructor function, which is executed automatically when the block in which the object is initialized is exited. A destructor releases the object and hence frees up the memory that was allocated. A destructor must abide by the following rules:
class farm_house { public: farm_house (); // Constructor function ~farm_house(); // Destructor function ..... }
Methods and members that are declared private are accessible only to that part of the program that is part of the class. However, a function outside the class or another class may be defined as a friend class or function. You can declare an entire class or individual functions as friends. You must follow some critical rules when declaring friend functions:
Whenever you use classes, they have their own private and public member variables and methods. As you saw in the previous Clasfarm.cpp example, the program is getting lengthy. There are no hard rules, but there are some standard practices followed by almost all programmers. The procedure is to put all class declarations in the header files. A header file is a file with an .h or .hpp extension. All the class definitions are placed in the .cpp file. The beginning of the .cpp file has an include directive for the header file. For example, the clasfarm program would be separated into clasfarm.h and Clasfarm.cpp. The Clasfarm.h file would look like Listing A.10.
1: // Workspace: Class2 2: // Program Name: Clasfarm.hpp 3: #include <iostream.h> 4: 5: class farm_house 6: { 7: int pig_values; 8: public: 9: void set(int input); 10: int get(void); 11: };
The Clasfarm.cpp file is in Listing A.11.
1: #include <clasfarm.h> 2: void farm_house::set(int input) 3: { 4: pig_values = input; 5: } 6: 7: int farm_house::get(void) 8: { 9: return pig_values; 10: } 11: 12: int main() 13: { 14: farm_house pig1, pig2, pig3; 15: 16: 17: pig1.set(12); 18: pig2.set(13); 19: pig3.set(14); 20: 21: cout << "The value of pig1 is " << pig1.get() << "\n"; 22: cout << "The value of pig2 is " << pig2.get() << "\n"; 23: cout << "The value of pig3 is " << pig3.get() << "\n"; 24: 25: return 0; 26: 27: };
It is perfectly legal to have another class declaration within a given class. This is often referred to as nesting classes. The following example declares two classes, Lot_size and Tax_assessment. The Tax_assessment class object taxes is defined within the Lot_size class. The main() method has no objects of the Tax_assessment class, so the methods or members of the Tax_assessment class cannot be directly accessed from the main() function. Let's review the program in Listing A.12.
1: // Workspace Name: Class3 2: // Program Name: Class3.cpp 3: #include <iostream.h> 4: 5: class Tax_assessment 6: { 7: int city_tax; 8: int prop_tax; 9: public:
10: void set(int in_city, int in_prop)
11: {city_tax = in_city; prop_tax = in_prop; } 12: int get_prop_tax(void) {return prop_tax;} 13: int get_city_tax(void) {return city_tax;} 14: }; 15: 16: 17: class Lot_size { 18: int length; 19: int width; 20: Tax_assessment taxes; 21: public: 22: void set(int l, int w, int s, int p) { 23: length = l; 24: width = w; 25: taxes.set(s, p); } 26: int get_area(void) {return length * width;} 27: int get_data(void) {return taxes.get_prop_tax() ;} 28: int get_data2(void) {return taxes.get_city_tax() ;} 29: }; 30: 31: 32: int main() 33: { 34: Lot_size small, medium, large; 35: 36: small.set(5, 5, 5, 25); 37: medium.set(10, 10, 10, 50); 38: large.set(20, 20, 15, 75); 39: 40: 41: cout << "For a small lot of area "<< small.get_area ()<< "\n"; 42: cout << "the city taxes are $ "<< small.get_data2 () << "\n"; 43: cout << "and property taxes are $ " << small.get_data ()<< "\n"; 44: 45: cout << "For a medium lot of area "<< medium.get_area ()<< "\n"; 46: cout << "the city taxes are $ "<< medium.get_data2 () << "\n"; 47: cout << "and property taxes are $ " << medium.get_data ()<< "\n"; 48: 49: cout << "For a Large lot of area "<< large.get_area ()<< "\n"; 50: cout << "the city taxes are $ "<< large.get_data2 () << "\n"; 51: cout << "and property taxes are $ " << large.get_data ()<< "\n"; 52: return 0; 53: }
When you execute this program, it outputs the area of an rectangle and also the hypothetical taxes on rectangular area. The output is shown in Figure A.11.
FIGURE A.11. Output from Class3.cpp.
In lines 5 through 14, the class Tax_assessment is defined. It consists of two private data members, int city_tax and int prop_tax. The class has three public methods. It is important to note the declaration and definition of these methods. In the earlier examples, you only declared the methods in the class. The function definitions were accessed using the scope (::) operator. In this example, you declare the method and also write its definition. This technique is referred to as inline implementation of the function. If a function definition is small and concise, this is a good technique to employ. This technique is also used to increase program efficiency (speed of execution) because the program does not have to jump in and out of a function definition.
The data members city_tax and prop_tax are private so they can only be accessed via their member methods--namely, set(), get_prop_tax(), and get_city_tax().
Lines 17 through 29 declare the class Lot_size with its data members and methods. On line 20, the class Tax_assessment is embedded in this class. The object taxes is also declared on this line, and it is under the privacy of the class Lot_size. The only methods that would be able to access this object are the ones belonging to the Lot_size class. The Lot_size class has four public methods declared and defined on line 22 and lines 26 through 28. Line 25 of the set() method has another set() method defined. This is not a recursive method but rather another example of function overloading. The set() method on line 10 and line 22 differ in the number of parameters. The set() method on line 25 can access the object taxes because it is defined under the class Tax_assessment on line 20.
The main() function begins on line 32 and has a return type int. On line 34, the objects of class Lot_size are declared. On lines 36 through line 38, the values of the objects are set using the set() method. An important point to note is that the class Tax_assessment has no objects in the main() method, so you cannot access any data member or method of this class from main().
On line 41, the area of the Lot_size is output by operating the get_area() method on an object of class Lot_size. On line 42, the city taxes are output by operating the method get_data2 on an object of Lot_size. This approach is required because the city_tax is a member data of class Tax_assessment, which cannot be operated on directly in the main() method. You use the method get_data2, which is a method of Lot_size and has access to the object taxes, which in turn can be accessed via get_city_tax.
One of the advantages of programming in C++ or any other object-oriented language is taking a global to local approach. Suppose you need to develop a program that comprehends all metals and their characteristics. If you take the class approach of the previous section, you would probably have one class named metals. The data members of metals would probably be density and volume. You could have another class named gold and one for aluminum. The data members describing gold and aluminum would need all the properties of metals in addition to their own data members such as color and shine. If you could devise a hierarchy of classes such that the classes for gold and aluminum would have only their individual data members but inherit the generic properties from the parent metals class--then you would be using inheritance.
Inheritance is also called derivation. The new class inherits the functionality of an existing class. The existing class is called the base class, and the new class is called the derived class. A similar inheritance can be derived for animals, mammals, and dogs.
To derive a new class from a base class, you use the colon (:) operator:
class human : public mammal
In this example, the new class human is derived from the base class mammal. The derived class human would have all the functionality of the base class mammal. In addition, the human class can have other functionality, such as the ability to drive a car and work for food. The example in Listing A.13 shows how to create the objects of type human and access its data and functions.
1: // Workspace Name: Inherit 2: // Program Name : Inherit1.cpp 3: 4: #include <iostream.h> 5: enum GENDER { MALE, FEMALE }; 6: 7: class Mammal 8: { 9: public: 10: // constructors 11: Mammal():itsAge(35), itsWeight(180){} 12: ~Mammal(){} 13: 14: int GetAge()const { return itsAge; } 15: void SetAge(int age) { itsAge = age; } 16: int GetWeight() const { return itsWeight; }
17: void SetWeight(int weight) { itsWeight = weight; }
18: 19: 20: protected: 21: int itsAge; 22: int itsWeight; 23: }; 24: 25: class Human : public Mammal 26: { 27: public: 28: 29: // Constructors 30: Human():itsGender(MALE){} 31: ~Human(){} 32: 33: GENDER GetGender() const { return itsGender; } 34: void SetGender(GENDER gender) { itsGender = gender; } 35: 36: void Drive() { cout << "Driving to work...\n"; } 37: void Work() { cout << "working...\n"; } 38: 39: private: 40: GENDER itsGender; 41: }; 42: 43: void main() 44: { 45: Human John_doe; 46: John_doe.Drive(); 47: John_doe.Work(); 48: cout << "John_doe is " << John_doe.GetAge() << " years old\n"; 49: cout << "And weighs " <<John_doe.GetWeight() << " lbs \n"; 50: }
The output from Listing A.13 is shown in Figure A.12.
FIGURE A.12. Inheritance output.
On line 5, a new keyword enum is defined. Enumerate defines a new data type with a list of identifiers. The identifiers are fixed values that increment automatically. In this example, the variable MALE has a value 0 and the variable FEMALE has a value 1 by default. You could also specify a value:
enum Alphabets ( A, B, C=5, D=1)
In this case, A has a value 0, B is 1, C is 5, and D is 1. If you did not specify any values, then
A is 0
B is 1
C is 2
D is 3
On line 20, another new keyword protected is defined in the class Mammal. You covered public and private in the section on classes where all the data members were defined under the private keyword. When the data member is defined as private, the derived class cannot access them. For the derived classes to be able to access the data members and methods of a class, they must be defined as protected. The protected keyword restricts the access only to the derived classes. Another alternative is to define these methods and members as public, in which case all classes have free access. Although this is a solution, it is not a desired solution because it moves you away from encapsulation.
Line 7 declares the base class Mammal. The constructor is on line 11, and the destructor is on line 12. In classes, whenever an object of the class is created, the class constructor is called. The constructor class performs an additional function of initializing its member data, itsAge(35) and itsWeight(180). This could have been accomplished by initializing the member data in the body of the constructor, as shown in the following:
Mammal() { itsAge = 35; itsWeight = 180; };
The technique of initializing the data members in the constructor declaration (as shown on line 11 of Listing A.14) is far more efficient due to the internal initialization of classes in C++. Use this technique whenever possible because it increases code efficiency.
With derived classes, when an object is created in the derived class, the constructor of the base class is called first and then the constructor of the derived class is called. In this example, when the object John_doe is created for the first time, the constructor of the base class Mammal is called. The object John_doe is not created until both the base constructor and derived class constructor are called. With destructors, the reverse order is followed; when the object John_doe ceases to exist, the derived class destructor is called before the base class destructor. On line 25, you define the name of the derived class and its relevant base class.
Line 48 and line 49 are critical in terms of how the data is accessed and output. On lines 48 and 49, the Human object John_doe accesses information directly from the base class of Mammal. Remember from the example class3.cpp, to output data from a nested class, you had to use indirect access to the class Tax_assessment.
Inheritance is a significant tool in object-oriented programming, and if it's used effectively, it provides code reusability. The inherit1.cpp program gave you an overall flavor of inheritance and its properties. However, when programs are written in real life, they are structured more efficiently. The next program involves a more logical and formal process of writing a program.
Assume you are writing a program for the automobile market. The automobile market consists of cars, trucks, minivans, and SUVs (sport utility vehicles). automobile is the parent or base class, and the others are the derived classes. Let's start by defining the automobile class. Listing A.14 shows the code.
1: // Workspace name: Inherit2 2: // Program name: Auto.h 3: 4: #ifndef AUTO_H 5: #define AUTO_H 6: 7: class automobile 8: { 9: protected: 10: int miles_per_gallon;
11: float fuel_capacity;
12: public: 13: void initialize(int in_mpg, float in_fuel); 14: int get_mpg(void); 15: float get_fuel(void); 16: float travel_distance(void); 17: } 18: 19: #endif
Lines 4 and 5 include directives to the preprocessor. The directive on line 4 is covered in detail toward the end of this section. On line 7, the class automobile is defined. This class has two data members and four methods. The class is included in the header file only. The definition of the methods of this class are contained in the Auto.cpp file in Listing A.15.
1: // Workspace name : Inherit2 2: // Program name : Auto.cpp 3: #include "auto.h" 4: 5: 6: void automobile::initialize(int in_mpg, float in_fuel) 7: { 8: miles_per_gallon = in_mpg; 9: fuel_capacity = in_fuel; 10: } 11: 12: // Get the rated fuel economy - miles per gallon 13: int automobile::get_mpg() 14: { 15: return miles_per_gallon; 16: } 17: 18: // Get the fuel tank capacity 19: float automobile::get_fuel() 20: { 21: return fuel_capacity; 22: } 23: 24: // Return the travel distance possible 25: float automobile::travel_distance() 26: { 27: return miles_per_gallon * fuel_capacity; 28: }
The method get_mpg provides the value for the miles per gallon for a particular vehicle. The get_fuel method provides the gas tank capacity. Next, you define the first derived class, a car, in Listing A.16.
1: // Workspace name: Inherit2 2: // Program name: Car.h 3: 4: #ifndef CAR_H 5: #define CAR_H 6: 7: #include "auto.h" 8: 9: class car : public automobile 10: { 11: int Total_doors; 12: public: 13: void initialize(int in_mpg, float in_fuel, int door = 4); 14: int doors(void); 15: }; 16: 17: #endif
The class car is a derived class from the automobile class. Because it is a derived class, it has access to all of the methods of the base class automobile. In addition, this class has a data member for the number of doors in the car. The methods of this class are defined in Car.cpp in Listing A.17.
1: // Workspace name: Inherit2 2: // Program name: Car.cpp 3: 4: #include "car.h" 5: 6: void car::initialize(int in_mpg, float in_fuel, int door) 7: { 8: Total_doors = door;
9: miles_per_gallon = in_mpg;
10: fuel_capacity = in_fuel; 11: } 12: 13: 14: int car::doors(void) 15: { 16: return Total_doors; 17: }
The initialization method is defined in lines 6 through 11. It is important to note that the base class of the automobile (auto.h) also had an initialization method. The initialization in the car class overrides the base class initialization. Last but not least is the main() definition. The main() method is defined in Allauto.cpp in Listing A.18.
1: // Workspace name: Inherit2 2: // Program name: Allauto.cpp 3: 4: #include <iostream.h> 5: #include "auto.h" 6: #include "car.h" 7: 8: int main() 9: { 10: 11: car sedan; 12: 13: sedan.initialize(24, 20.0, 4); 14: cout << "The sedan can travel " << sedan.travel_distance() << 15: " miles.\n"; 16: cout << "The sedan has " << sedan.doors() << " doors.\n"; 17: 18: return 0; 19: }
The main() definition has only one object defined. On line 11, an object of class car is declared. The initialization is on line 13. The initialization passes the fuel efficiency of the car (miles per gallon) and the tank capacity. This information is used to access the method travel_distance in the base class define in auto.cpp. The derived class has access to the methods of the base class. Additionally, the derived class passes information to its own data member about the number of doors in the vehicle. The result of executing this program is shown in Figure A.13.
FIGURE A.13. Vehicle class results.
You can now add more classes for other vehicle types. You can make your own classes for a truck and minivan and derive them from the base class exactly like the car class.
If you add another class for trucks, it is important to include the preprocessor directives from Listing A.14's lines 4 and 19. These lines are listed again in the following:
4 #ifndef AUTO.H 5 #define AUTO.H . . . 19 #endif
Because the truck class is derived from the parent class automobile, it must include the file Auto.h in Truck.h. The header of the car class, Car.h, already includes Auto.h for the same reason. Now, if you create a method that uses both the truck and car classes, you could potentially include Auto.h twice, which would generate in a compiler error. To prevent this, you add lines 4 and 5 of Listing A.14. Line 4 issues a command to the compiler to verify whether the class AUTO.H has been defined; if it hasn't been defined, the program jumps to line 5 and defines it, and if it has been defined, the program jumps to line 19 and ends.
Congratulations! You have covered almost all of the features and properties of C++. You should now have a solid footing to take full advantage of Visual C++ and object-oriented programming.
© Copyright, Macmillan Computer Publishing. All rights reserved.