|
My Training Period: xx hours
The source code for this tutorial is available in C++ Data Encapsulation source codes.
The C++ programming abilities that should be acquired in this session are:
13.1 An Introduction
13.2 Object Array
wall small, medium, large, group[4];
|
1. // program obarray.cpp
2. // object and an array
3. #include <iostream>
4. using namespace std;
5.
6. // a class declaration
7. class wall
8. {
9. int length;
10. int width;
11. static int extra_data;
12. // declaration of the extra_data static type
13. public:
14. wall(void);
15. void set(int new_length, int new_width);
16. int get_area(void);
17. int get_extra(void) { return extra_data++;} // an inline function
18. };
19.
20. // class implementation
21. int wall::extra_data; //Definition of extra_data
22.
23. // a constructor, assigning initial values
24. wall::wall(void)
25. {
26. length = 8;
27. width = 8;
28. extra_data = 1;
29. }
30.
31. // this method will set a wall size to the two input parameters
32. void wall::set(int new_length, int new_width)
33. {
34. length = new_length;
35. width = new_width;
36. }
37.
38. // this method will calculate and return the area of a wall instance
39. int wall::get_area(void)
40. {
41. return (length * width);
42. }
43.
44. // the main program
45. void main()
46. {
47. wall small, medium, large, group[4];
48. // 7 objects are instantiated, including an array
49.
50. small.set(5, 7); // assigning values
51. large.set(15, 20);
52.
53. for(int index=1; index<4; index++) // the group[0] uses default
54. group[index].set(index + 10, 10);
55.
56. cout<<"Sending message-->small.get_area()\n";
57. cout<<"Area of the small wall is "<<small.get_area()<<"\n\n";
58. cout<<"Sending message-->medium.get_area()\n";
59. cout<<"Area of the medium wall is "<<medium.get_area()<<"\n\n";
60. cout<<"Sending message-->large.get_area()\n";
61. cout<<"Area of the large wall is "<<large.get_area()<<"\n\n";
62.
63. cout<<"New length/width group[index].set(index + 10, 10)\n";
64. for(int index=0; index<4; index++)
65. {
66. cout<<"Sending message using an array
67. -->group"<<"["<<index<<"].get_area()\n";
68. cout<<"An array of wall area "<<index<<" is
69. "<<group[index].get_area()<<"\n\n";
70. }
71.
72. cout<<"extra_data = 1, extra_data++\n";
73. cout<<"Sending message using-->small.get_extra() or \n";
74. cout<<"array, group[0].get_extra()\n";
75.
76. cout<<"Extra data value is "<<small.get_extra()<<"\n";
77. cout<<"New Extra data value is "<<medium.get_extra()<<"\n";
78. cout<<"New Extra data value is "<<large.get_extra()<<"\n";
79. cout<<"New Extra data value is "<<group[0].get_extra()<<"\n";
80. cout<<"New Extra data value is "<<group[3].get_extra()<<"\n";
81.
82. // system("pause");
83. }
Each four wall objects will be initialized to the values defined within the constructor since the constructor will be executed for each wall as they are defined.
In order to define an array of objects, a constructor for that object with no parameters must be available.
Line 53 defines a for loop that begins with 1 instead of the normal starting index 0 for an array leaving the first object, named group[0], to use the default values stored when the constructor was called (length = 8, width = 8 and extra_data = 1).
for(int index=1; index<4; index++) // the group[0] uses default value
group[index].set(index + 10, 10);
Notice that sending a message to one of the array objects, uses the same construct as used for any object. The name of the array followed by its index in square brackets is used to send a message to one of the objects in the array as,
group[index].set(index + 10, 10)
The general form is:
The_object.the_method
This is illustrated again in line 68-69 as shown below.
cout<<"An array of wall area "<<index<<" is "<<group[index].get_area()<<"\n\n";
The other method get_extra() is called in the cout statement in lines 76 to 80 where the area of the four walls in the group array are displayed. The following is the code segment:
cout<<"Extra data value is "<<small.get_extra()<<"\n";
cout<<"New Extra data value is "<<medium.get_extra()<<"\n";
cout<<"New Extra data value is "<<large.get_extra()<<"\n";
cout<<"New Extra data value is "<<group[0].get_extra()<<"\n";
cout<<"New Extra data value is "<<group[3].get_extra()<<"\n";
An extra variable as shown below was included for illustration, named extra_data in line 11. Since the keyword static is used as variable modifier, it is an external variable and means only one copy of this variable will ever exist.
static int extra_data;
All seven objects defined in line 47 of this class, share a single copy of this variable which is global to the objects. The variable is actually only declared here which says it will exist somewhere, but it is not yet defined.
A declaration says the variable will exist and gives it a name, but the definition actually defines a place to store it somewhere in the computers memory.
By definition, a static variable can be declared in a class header but it cannot be defined there, so it is usually defined in the implementation program. In this case it is defined in line 21 as shown below and can then be used throughout the class.
int wall::extra_data; // an extra_data definition
Figure 13.1 is a graphical representation of some of the variables. Note that the objects named large, group[0], group[1], and group[2] are not shown but they also share the variable named extra_data.
Each object has its own personal length and width because they are not declared static.
----------------------------------------------------------------------------------------------------------------------------------------
Line 28 of the constructor sets this single global variable to 1 each time an object is declared. Only one assignment is necessary as follows.
extra_data = 1;
To illustrate that there is only one variable shared by all objects of this class, the method to read its value also increments it. Each time it is read in lines 76 through 80, it is incremented and the output of the execution proves that there is only a single variable shared by all objects of this class.
static is used when only one copy of the variable is needed in the program. This can be considered as an optimization. Understand this program, especially the static variable, then compile and run it.
Examine the program named obstring.cpp for our first example of an object with an embedded string. Actually, the object does not have an embedded string; it has an embedded pointer, but the two work so closely together.
1. // program obstring.cpp
2. #include <iostream>
3. using namespace std;
4.
5. // class declaration part
6. class wall
7. {
8. int length;
9. int width;
10. char *line_of_text; // a pointer variable
11. public:
12. wall(char *input_line); // a constructor declaration
13. void set(int new_length, int new_width);
14. int get_area(void);
15. };
16.
17. // class implementation part
18. wall::wall(char *input_line) // a constructor implementation
19. {
20. length = 8;
21. width = 8;
22. line_of_text = input_line;
23. }
24.
25. // this method will set a wall size to the two input parameters
26. void wall::set(int new_length, int new_width)
27. {
28. length = new_length;
29. width = new_width;
30. }
31.
32. // this method will calculate and return
33. // the area of a wall instance
34. int wall::get_area(void)
35. {
36. cout<<line_of_text<< "= ";
37. return (length * width);
38. }
39.
40. // the main program
41. void main()
42. {
43. // objects are instantiated with a string
44. // constant as an actual parameters
45. wall small("of small size "),
46. medium("of medium size "),
47. large("of large size ");
48.
49. small.set(5, 7);
50. large.set(15, 20);
51.
52. cout<<" Embedded string used as an object\n";
53. cout<<" ----------------------------------\n\n";
54. cout<<"Area of wall surface ";
55. cout<<small.get_area()<<"\n";
56. cout<<"Area of wall surface ";
57. cout<<medium.get_area()<<"\n";
58. // use default value of constructor
59. cout<<"Area of wall surface ";
60. cout<<large.get_area()<<"\n";
61.
62. // system("pause");
63. }
char *line_of_text; // a pointer variable
|
cout<<small.get_area()<<"\n";
...
cout<<medium.get_area()<<"\n";
// use a default value of constructor
...
cout<<large.get_area()<<"\n";
After you understand this program, compile and run.
Next, study program obinptr.cpp carefully. This is our first example, a program with an embedded pointer which will be used for dynamic data allocation discussion. Internal pointers refer to the pointer variables within the class itself.
1. // program opinptr.cpp
2. #include <iostream>
3. using namespace std;
4.
5. // class declaration part
6. class wall
7. {
8. int length;
9. int width;
10. int *point;
11. // a declaration of the pointer variable
12. public:
13. wall(void);
14. // a constructor declaration
15. void set(int new_length, int new_width, int stored_value);
16. int get_area(void) { return (length * width); }
17. // an inline function
18. int get_value(void) { return *point; }
19. // an inline function
20. ~wall();
21. // a destructor
22. };
23.
24. // class implementation part
25. wall::wall(void)
26. // a constructor implementation
27. {
28. length = 8;
29. width = 8;
30. point = new int; // notice a new keyword
31. *point = 112;
32. }
33.
34. // this method will set a wall size to the input parameters
35. void wall::set(int new_length, int new_width, int stored_value)
36. {
37. length = new_length;
38. width = new_width;
39. *point = stored_value;
40. }
41.
42. wall::~wall(void)
43. // a destructor
44. {
45. length = 0;
46. width = 0;
47. delete point; // notice a delete keyword
48. }
49.
50. // the main program
51. void main()
52. {
53. wall small, medium, large; // objects instance
54.
55. small.set(5, 7, 177);
56. large.set(15, 20, 999);
57.
58. cout<<"Area of the small wall surface is "<<small.get_area()<<"\n";
59. cout<<"Area of the medium wall surface is "<<medium.get_area()<<"\n";
60. cout<<"Area of the large wall surface is "<<large.get_area()<<"\n\n";
61. cout<<"Third variable in class wall, pointer *point\n";
62. cout<<"----------------------------------------------\n";
63. cout<<"Stored value of the small wall surface is "<<small.get_value()<<"\n";
64. cout<<"Stored value of the medium wall surface is
65. "<<medium.get_value()<<"\n";
66. cout<<"Stored value of the large wall surface is "<<large.get_value()<<"\n";
67.
68. // system("pause");
69. }
In line 10 we declare a pointer named point to an integer variable, but it is only a pointer, there is no storage associated with it. The constructor therefore allocates storage for an integer type variable on the heap (free memory space) for use with this pointer in line 30 as shown below. The compiler automatically determines a proper size of the object.
int *point;
...
point = new int; // notice a new keyword
Three objects instantiated in line 53 each contain a pointer which points into the heap to three different locations as shown below. Each object has its own dynamically storage allocated variable for its own private use.
wall small, medium, large; // objects instance
Moreover each has a value of 112 stored in it dynamically because line 31 stores that value in each of the three locations, once for each call to the constructor as shown below:
*point = 112;
In a real program production, it would be mandatory to test that the value of the return pointer is not NULL (in C++ .NET the nullptr keyword is used instead) to assure that the data actually did get allocated. For example:
if (*point = NULL)
do something...
else
do something...
The method named set() has three parameters associated with it and the third parameter is used to set the value of the new dynamically allocated variable. There are two messages passed, one to the small wall and one to the large wall. As before, the medium wall is left with its default values.
The three areas are displayed followed by the three stored values in the dynamically allocated variables, and we finally have a program that requires a destructor in order to do a clean up.
If we simply leave the scope of the objects as we do when leave the main() program, we will leave the three dynamically allocated variables on the heap with nothing pointing to them.
They will be inaccessible and will therefore represent wasted storage on the heap. For that reason, as shown below, the destructor is used to delete the variable which the pointer named point is referencing, as each object goes out of existence as in line 47.
delete point; // delete operator
In this case, lines 45 and 46 as shown below assign zero to variables that will be automatically deleted. Even though these lines of code really do no good, they are legal statements.
length = 0;
width = 0;
Actually, in this particular case, the variables will be automatically reclaimed when we return to the operating system because all program cleanups are done for us at that time.
This is an example of good programming practice that of cleaning up when you no longer need the dynamically allocated variables to free up the heap.
Compile and run this program.
Examine the program named obdyna.cpp carefully for our first look at dynamically allocated object. Notice that the pointer is in the main program instead of within the class as in previous example.
1. // program obdyna.cpp
2. // dynamically allocated object
3.
4. #include <iostream>
5. using namespace std;
6.
7. // class declaration part
8. class wall
9. {
10. int length;
11. int width;
12. // two member variables
13. public:
14. wall(void);
15. // constructor declaration
16. void set(int new_length, int new_width);
17. int get_area(void);
18. // two methods
19. };
20.
21. // class implementation part
22. wall::wall(void)
23. // constructor implementation
24. {
25. length = 8;
26. width = 8;
27. }
28.
29. // this method will set a wall size to the input parameters
30. void wall::set(int new_length, int new_width)
31. {
32. length = new_length;
33. width = new_width;
34. }
35.
36. // this method will calculate and return the area of a wall instance
37. int wall::get_area(void)
38. {
39. return (length * width);
40. }
41.
42. // main program
43. void main()
44. {
45. wall small, medium, large;
46. // objects are instantiated of type class wall
47. wall *point;
48. // a pointer to a class wall
49.
50. small.set(5, 7);
51. large.set(15, 20);
52.
53. point = new wall; // new operator
54. // use the defaults value supplied by the constructor
55.
56. cout<<"Use small.set(5, 7)\n";
57. cout<<"-------------------\n";
58. cout<<"Area of the small wall surface is "<<small.get_area()<<"\n\n";
59. cout<<"Use default/constructor value medium.set(8, 8)\n";
60. cout<<"-----------------------------------------------\n";
61. cout<<"Area of the medium wall surface is "<<medium.get_area()<<"\n\n";
62. cout<<"Use large.set(15, 20)\n";
63. cout<<"----------------------\n";
64. cout<<"Area of the large wall surface is "<<large.get_area()<<"\n\n";
65. cout<<"Use default/constructor value, point->get_area()\n";
66. cout<<"------------------------------------------------\n";
67. cout<<"New surface area of wall "<<point->get_area()<<"\n\n";
68. cout<<"Use new value, point->set(12, 12)\n";
69. cout<<"---------------------------------\n";
70. point->set(12, 12);
71. cout<<"New surface area of wall "<<point->get_area()<<"\n";
72. delete point; // delete operator
73.
74. // system("pause");
75. }
In line 47 as shown below, we defined a pointer to an object of type wall named point and it is only a pointer with nothing to point to.
wall *point;
The following code means, we dynamically allocate object storage for it in line 53, with the object being instantiated on the heap just like any other dynamically allocated variable using the new keyword.
point = new wall; // new keyword, assigns point a wall object
When the object is created in line 53, the constructor is called automatically to assign values to the two internal storage variables. Note that the constructor is not called when the pointer is defined since there is nothing to initialize. It is called when the object is allocated.
Reference to the components of the object are handled in much the same way that structure references are made, through the use of the pointer operator as illustrated in lines 67, 70 and 71 as shown below.
cout<<"New surface area of wall "<<point->get_area()<<"\n\n";
...
point->set(12, 12);
cout<<"New surface area of wall "<<point->get_area()<<"\n";
Alternatively, you can use the pointer dereferencing method without the arrow such as (refer to C & C++ pointers).
(*point).set(12, 12);
Same as:
point->set(12, 12)
Finally, the object is deleted in line 72 as shown below and the program terminates. If there were a destructor for this class, it would be called automatically as part of the delete statement to clean up the object and release all storage resources back to the system.
delete point; // delete keyword
Notice that the use of objects is not much different from the use of structure. Compile and run this program after you have studied it thoroughly.
Tenouk C++ encapsulation object oriented tutorial: principles and implementations