home *** CD-ROM | disk | FTP | other *** search
-
-
-
- Chapter 6
- MORE ENCAPSULATION
-
- WHY BOTHER WITH ENCAPSULATION?
- -----------------------------------------------------------------
- We asked this question earlier, but now that we have a little
- experience, we can provide a much better answer. Encapsulation
- protects data from accidental corruption, and constructors
- guarantee proper initialization. Both prevent errors that we are
- very prone to make since we are thinking only about the internals
- of the class when we are writing it. Later, when we are actually
- using the class, we have no need to concern ourselves with the
- internal structure or operation, but can spend our energies using
- the class to solve the overall problem we are working on. As you
- may guess, there is a lot more to learn about the use and
- benefits of classes so we will dive right into some new topics.
-
- The purpose of this chapter is to illustrate how to use some of
- the traditional aspects of C or C++ with classes and objects.
- Pointers to an object as well as pointers within an object will
- be illustrated. Arrays embedded within an object, and an array
- of objects will be illustrated. Since objects are simply another
- C++ data construct, all of these things are possible and can be
- used if needed.
-
- In order to have a systematic study, we will use the program
- named BOXES1.CPP from the last chapter as a starting point and we
- will add a few new constructs to it for each example program.
- You will recall that it was a very simple program with the class
- definition, the class implementation, and the main program all in
- one file. This was selected as a starting point because we will
- eventually make changes to all parts of the program and it will
- be convenient to have it all in a single file for illustrative
- purposes. It must be kept in mind however that the proper way to
- use these constructs is to separate them into the three files as
- was illustrated in BOX.H, BOX.CPP, and BOXES2.CPP in the last
- chapter. This allows the implementor of box to supply the user
- with only the interface, namely BOX.H. Not giving him the
- implementation file named BOX.CPP, is practicing the technique of
- information hiding. As we have said many times, it seems silly
- to break up such a small program into three separate files, and
- it is sort of silly. The last chapter of this tutorial will
- illustrate a program large enough to require dividing the program
- up into many separate files.
-
-
- AN ARRAY OF OBJECTS
- -----------------------------------------------------------------
- Examine the file named OBJARRAY.CPP for our ==================
- first example of an array of objects. This OBJARRAY.CPP
- file is nearly identical to the file named ==================
- BOX1.CPP until we come to line 44 where an
-
- Page 6-1
-
- Chapter 6 - More Encapsulation
-
- array of 4 boxes are declared. Recalling the operation
- of the constructor you will remember that each of the four box
- objects will be initialized to the values defined within the
- constructor since each box will go through the constructor as
- they are declared. In order to declare an array of objects, a
- constructor for that object must not require any parameters. (We
- have not yet illustrated a constructor with initializing
- parameters, but we will in the next program.) This is an
- efficiency consideration since it would probably be an error to
- initialize all elements of an array of objects to the same value.
- We will see the results of executing the constructor when we
- compile and execute the file later.
-
- Line 49 defines a for loop that begins with 1 instead of the
- normal starting index for an array leaving the first object,
- named group[0], to use the default values stored when the
- constructor was called. You will observe that sending a message
- to one of the objects uses the same construct as is 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. This is illustrated in line 50 and the operation of that
- code should be clear to you. The other method is called in the
- output statement in lines 57 and 58 where the area of the four
- boxes in the group array are listed on the monitor.
-
- Another fine point should be mentioned. The integer variable
- named index is declared in line 49 and is still available for use
- in line 56 since we have not yet left the enclosing block which
- begins in line 43 and extends to line 65.
-
-
- DECLARATION AND DEFINITION OF A VARIABLE
- -----------------------------------------------------------------
- An extra variable was included for illustration, the one named
- extra_data in line seven. Since the keyword static is used to
- modify this variable in line 7, it is an external variable and
- only one copy of this variable will ever exist. All seven
- objects of this class share a single copy of this variable which
- is global to the objects defined in line 44.
-
- The variable is actually only declared here which says it will
- exist somewhere, but it is not 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 space. 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 file. In this case it is
- defined in line 16 and can then be used throughout the class.
-
- Figure 6-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
-
-
- Page 6-2
-
- Chapter 6 - More Encapsulation
-
- variable named extra_data. They are not shown in order to
- simplify the picture and enhance the clarity.
-
- Line 23 of the constructor sets the single global variable to 1
- each time an object is declared. Only one assignment is
- necessary so the other six are actually wasted code. 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 60 through 64, it is incremented
- and the result of the execution proves that there is only a
- single variable shared by all objects of this class. You will
- also note that the method named get_extra() is defined within
- the class declaration so it will be assembled into the final
- program as inline code.
-
- You will recall the 2 static variables we declared in lines 16
- and 17 of DATE.H in chapter 5 of this tutorial. We defined them
- in lines 9 and 10 of DATE.CPP and overlooked a complete
- explanation of what they did at that time. The declaration and
- definition of these variables should be considered a good example
- of the proper place to put these constructs in your classes.
-
- Be sure you understand this program and especially the static
- variable, then compile and execute it to see if you get the same
- result as listed at the end of the program.
-
-
- A STRING WITHIN AN OBJECT
- -----------------------------------------------------------------
- Examine the program named OBJSTRNG.CPP for ===================
- our first example of an object with an OBJSTRING.CPP
- embedded string. Actually, the object does ===================
- not have an embedded string, it has an
- embedded pointer, but the two work so closely together that we
- can study one and understand both. You will notice that line 7
- contains a pointer to a string named line_of_text. The
- constructor contains an input parameter which is a pointer to a
- string which will be copied to the string named line_of_text
- within the constructor. We could have defined the variable
- line_of_text as an actual array in the class, then used strcpy()
- to copy the string into the object and everything would have
- worked the same, but we will leave that as an exercise for you at
- the end of this chapter. It should be pointed out that we are
- not limited to passing a single parameter to a constructor. Any
- number of parameters can be passed, as will be illustrated later.
-
- You will notice that when the three boxes are declared this time,
- we supply a string constant as an actual parameter with each
- declaration which is used by the constructor to assign the string
- pointer some data to point to. When we call get_area() in lines
- 48 through 53, we get the message displayed and the area
- returned. It would be prudent to put these operations in
- separate methods since there is no apparent connection between
-
- Page 6-3
-
- Chapter 6 - More Encapsulation
-
- printing the message and calculating the area, but it was written
- this way to illustrate that it can be done. What this really
- says is that it is possible to have a method that has a side
- effect, the message output to the monitor, and a return value,
- the area of the box. However, as we discussed in chapter 4 when
- we studied DEFAULT.CPP, the order of evaluation is sort of funny,
- so we broke each line into two lines.
-
- After you understand this program, compile and execute it.
-
-
- AN OBJECT WITH AN INTERNAL POINTER
- -----------------------------------------------------------------
- The program named OBJINTPT.CPP is our first ==================
- example program with an embedded pointer OBJINTPT.CPP
- which will be used for dynamic allocation of ==================
- data. In line 7 we declare a pointer to an
- integer variable, but it is only a pointer, there is no storage
- associated with it. The constructor therefore allocates an
- integer type variable on the heap for use with this pointer in
- line 21. It should be clear to you that the three objects
- created in line 45 each contain a pointer which points into the
- heap to three different locations. Each object has its own
- dynamically allocated variable for its own private use. Moreover
- each has a value of 112 stored in its dynamically allocated data
- because line 22 stores that value in each of the three locations,
- once for each call to the constructor.
-
- In such a small program, there is no chance that we will exhaust
- the heap, so no test is made for unavailable memory. In a real
- production program, it would be expedient to test that the value
- of the returned pointer is not NULL to assure that the data
- actually did get allocated.
-
- 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 box and one to the large box. As before, the
- medium box 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 be completely
- proper. If we simply leave the scope of the objects as we do
- when we 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, the destructor is
- used to delete the variable which the pointer named point is
- referencing, as each object goes out of existence. In this case,
- lines 37 and 38 assign zero to variables that will be
- automatically deleted. Even though these lines of code really do
- no good, they are legal statements.
-
- Page 6-4
-
- Chapter 6 - More Encapsulation
-
- Actually, in this particular case, the variables will be
- automatically reclaimed when we return to the operating system
- because all program cleanup is done for us at that time. If this
- were a function that was called by another function however, the
- heap space would be wasted. This is an illustration of good
- programming practice, that of cleaning up after yourself when you
- no longer need some dynamically allocated variables.
-
- One other construct should be mentioned again, that of the inline
- method implementations in line 11 and 12. As we mentioned in
- chapter 5 and repeated earlier in this chapter, inline functions
- can be used where speed is of the utmost in importance since the
- code is assembled inline rather than by actually making a method
- call. Since the code is defined as part of the declaration, the
- system will assemble it inline, and a separate implementation for
- these methods is not needed. If the inline code is too involved,
- the compiler is allowed to ignore the inline request and will
- actually assemble it as a separate method, but it will do it
- invisibly to you and will probably not even tell you about it.
-
- Remember that we are interested in using information hiding and
- inline code prevents hiding of the implementation, putting it out
- in full view. Many times you will be more interested in speeding
- up a program than you are in hiding a trivial implementation.
- Since most inline methods are trivial, you should feel free to
- use the inline code construct wherever it is expedient. Be sure
- to compile and execute this program.
-
-
- A DYNAMICALLY ALLOCATED OBJECT
- -----------------------------------------------------------------
- Examine the file named OBJDYNAM.CPP for our ==================
- first look at a dynamically allocated object. OBJDYNAM.CPP
- This is not any different than any other ==================
- dynamically allocated object, but an example
- is always helpful. In line 39 we declare a pointer to an object
- of type box and since it is only a pointer with nothing to point
- to, we dynamically allocate an object for it in line 44, with the
- object being created on the heap just like any other dynamically
- allocated variable. When the object is created in line 44, 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 declared 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 use of the
- pointer operator as illustrated in lines 50 through 52. Of
- course you can use the pointer dereferencing method without the
- arrow such as (*point).set(12, 12); as a replacement for line 51
- but the arrow notation is much more universal and should be used.
- Finally, the object is deleted in line 54 and the program
- terminates. If there were a destructor for this class, it would
-
- Page 6-5
-
- Chapter 6 - More Encapsulation
-
- be called as part of the delete statement to clean up the object
- prior to deletion.
-
- You have probably noticed by this time that the use of objects is
- not much different from the use of structures. Be sure to
- compile and execute this program after you have studied it
- thoroughly.
-
-
- AN OBJECT WITH A POINTER TO ANOTHER OBJECT
- -----------------------------------------------------------------
- The program named OBJLIST.CPP contains an =================
- object with an internal reference to another OBJLIST.CPP
- object of its own class. This is the =================
- standard structure used for a singly linked
- list and we will keep the use of it very simple in this program.
-
- The constructor contains the statement in line 21 which assigns
- the pointer the value of NULL to initialize the pointer. This is
- a good idea for all of your programming, don't allow any pointer
- to point off into space, but initialize all pointers to something.
- By assigning the pointer within the constructor, you guarantee
- that every object of this class will automatically have its
- pointer initialized. It will be impossible to overlook the
- assignment of one of these pointers.
-
- Two additional methods are declared in lines 12 and 13 with the
- one in line 13 having a construct we have not yet mentioned in
- this tutorial. This method returns a pointer to an object of the
- box class. As you are aware, you can return a pointer to a
- struct in standard C, and this is a parallel construct in C++.
- The implementation in lines 48 through 51 returns the pointer
- stored within the object. We will see how this is used when we
- get to the actual program.
-
- An extra pointer named box_pointer is declared in the main
- program for use later and in line 66 we make the embedded pointer
- within the small box point to the medium box. Line 67 makes the
- embedded pointer within the medium box point to the large box.
- We have effectively generated a linked list with three elements.
- In line 69 we make the extra pointer point to the small box.
- Continuing in line 70 we use it to refer to the small box and
- update it to the value contained in the small box which is the
- address of the medium box. We have therefore traversed from one
- element of the list to another by sending a message to one of the
- objects. If line 70 were repeated exactly as shown, it would
- cause the extra pointer to refer to the large box, and we would
- have traversed the entire linked list which is only composed of
- three elements. Figure 6-2 is a graphical representation of the
- data space following execution of line 69. Note that only a
- portion of each object is actually depicted here to keep it
- simple.
-
-
- Page 6-6
-
- Chapter 6 - More Encapsulation
-
- ANOTHER NEW KEYWORD this
- -----------------------------------------------------------------
- Another new keyword is available in C++, the keyword this. The
- word this is defined within any object as being a pointer to the
- object in which it is contained. It is implicitly declared as
- class_name *this; and is initialized to point to the object for
- which the member function is invoked. This pointer is most
- useful when working with pointers and especially with a linked
- list when you need to reference a pointer to the object you are
- inserting into the list. The keyword this is available for this
- purpose and can be used in any object. Actually the proper way
- to refer to any variable within a list is through use of the
- predefined pointer this, by writing this->variable_name, but the
- compiler assumes the pointer is used, and we can simplify every
- reference by omitting the pointer. Use of the keyword this is
- not illustrated in a program at this point, but will be used in
- one of the larger example programs later in this tutorial.
-
- You should study this program until you understand it completely
- then compile and execute it in preparation for our next example
- program.
-
-
- A LINKED LIST OF OBJECTS
- -----------------------------------------------------------------
- The next example program in this chapter is =================
- named OBJLINK.CPP and is a complete example OBJLINK.CPP
- of a linked list written in object oriented =================
- notation. This program is very similar to
- the last one. In fact it is identical until we get to the main
- program. You will recall that in the last program the only way
- we had to set or use the embedded pointer was through use of the
- two methods named point_at_next() and get_next() which are listed
- in lines 40 through 51 of the present program. We will use these
- to build up our linked list then traverse and print the list.
- Finally, we will delete the entire list to free the space on the
- heap.
-
- In lines 56 through 58 we declare three pointers for use in the
- program. The pointer named start will always point to the
- beginning of the list, but temp will move down through the list
- as we create it. The pointer named box_pointer will be used for
- the creation of each object. We execute the loop in lines 61
- through 69 to generate the list where line 62 dynamically
- allocates a new object of the box class and line 63 fills it
- with nonsense data for illustration. If this is the first
- element in the list, the start pointer is set to point to this
- element, but if elements already exist, the last element in the
- list is assigned to point to the new element. In either case,
- the temp pointer is assigned to point to the last element of the
- list, in preparation for adding another element if there is
- another element to be added.
-
-
- Page 6-7
-
- Chapter 6 - More Encapsulation
-
- In line 72, the pointer named temp is caused to point to the
- first element and it is used to increment its way through the
- list by updating itself in line 75 during each pass through the
- loop. When temp has the value of NULL, which it gets from the
- last element of the list, we are finished traversing the list.
-
- Finally, we delete the entire list by starting at the beginning
- and deleting one element each time we pass through the loop in
- lines 79 through 84.
-
- A careful study of the program will reveal that it does indeed
- generate a linked list of ten elements, each element being an
- object of class box. The length of this list is limited by the
- practicality of how large a list we desire to print out, but it
- could be lengthened to many thousands of these simple elements
- provided you have enough memory available to store them all.
-
- Once again, the success of the dynamic allocation is not checked
- as it should be in a correctly written program. Be sure to
- compile and execute this example program.
-
-
- NESTING OBJECTS
- -----------------------------------------------------------------
- Examine the program named NESTING.CPP for an =================
- example of nesting classes which results in NESTING.CPP
- nested objects. A nested object could be =================
- illustrated with your computer in a rather
- simple manner. The computer itself is composed of many items
- which work together but work entirely differently, such as a
- keyboard, a disk drive, and a power supply. The computer is
- composed of these very dissimilar items and it is desirable to
- discuss the keyboard separately from the disk drive because they
- are so different. A computer class could be composed of several
- objects that are dissimilar by nesting the dissimilar classes
- within the computer class.
-
- If however, we wished to discuss disk drives, we may wish to
- examine the characteristics of disk drives in general, then
- examine the details of a hard disk, and the differences of floppy
- disks. This would involve inheritance because much of the data
- about both drives could be characterized and applied to the
- generic disk drive then used to aid in the discussion of the
- other three. We will study inheritance in the next three
- chapters, but for now we will look at the embedded or nested
- class.
-
- This example program contains a class named box which contains an
- object of another class embedded within it in line 16, the
- mail_info class. It is depicted graphically in figure 6-3. This
- object is available for use only within the class implementation
- of box because that is where it is defined. The main program has
- objects of class box defined but no objects of class mail_info,
-
- Page 6-8
-
- Chapter 6 - More Encapsulation
-
- so the mail_info class cannot be referred to in the main program.
- In this case, the mail_info class object is meant to be used
- internally to the box class and one example is given in line 21
- where a message is sent to the label.set() method to initialize
- the variables. Additional methods could be used as needed, but
- these are given as an illustration of how they can be called.
-
- Of prime importance is the fact that there are never any objects
- of the mail_info class declared directly in the main program,
- they are inherently declared when the enclosing objects of class
- box are declared. Of course objects of the mail_info class could
- be declared and used in the main program if needed, but they are
- not in this example program. In order to be complete, the box
- class should have one or more methods to use the information
- stored in the object of the mail_info class. Study this program
- until you understand the new construct, then compile and
- execute it.
-
- If the class and the nested classes require parameter lists for
- their respective constructors an initialization list can be
- given. This will be discussed and illustrated later in this
- tutorial.
-
-
- OPERATOR OVERLOADING
- -----------------------------------------------------------------
- The example file named OPOVERLD.CPP contains ==================
- examples of overloading operators. This OPOVERLD.CPP
- allows you to define a class of objects and ==================
- redefine the use of the normal operators.
- The end result is that objects of the new class can be used in as
- natural a manner as the predefined types. In fact, they seem to
- be a part of the language rather than your own add-on.
-
- In this case we overload the + operator and the * operator, with
- the declarations in lines 10 through 12, and the definitions in
- lines 16 through 40. The methods are declared as friend
- functions so we can use the double parameter functions as listed.
- If we did not use the friend construct, the function would be a
- part of one of the objects and that object would be the object to
- which the message was sent. Including the friend construct
- allows us to separate this method from the object and call the
- method with infix notation. Using this technique, it can be writ-
- ten as object1 + object2 rather than object1.operator+(object2).
- Also, without the friend construct we could not use an
- overloading with an int type variable for the first parameter
- because we can not send a message to an integer type variable
- such as int.operator+(object). Two of the three operator
- overloadings use an int for the first parameter so it is
- necessary to declare them as friend functions.
-
-
-
-
- Page 6-9
-
- Chapter 6 - More Encapsulation
-
- There is no upper limit to the number of overloadings for any
- given operator. Any number of overloadings can be used provided
- the parameters are different for each particular overloading.
-
- The header in line 16 illustrates the first overloading where the
- + operator is overloaded by giving the return type followed by
- the keyword operator and the operator we wish to overload. The
- two formal parameters and their types are then listed in the
- parentheses and the normal function operations are given in the
- implementation of the function in lines 18 through 21. The
- observant student will notice that the implementation of the
- friend functions are not actually a part of the class because the
- class name is not prepended onto the method name in line 16.
- There is nothing unusual about this implementation, it should be
- easily understood by you at this point. For purposes of
- illustration, some silly mathematics are performed in the method
- implementation, but any desired operations can be done.
-
- The biggest difference occurs in line 56 where this method is
- called by using the infix notation instead of the usual message
- sending format. Since the variables small and medium are objects
- of the box class, the system will search for a way to use the
- + operator on two objects of class box and will find it in the
- overloaded operator+ method we have just discussed. The
- operations within the method implementation can be anything we
- need them to be, and they are usually much more meaningful than
- the silly math included here.
-
- In line 58 we ask the system to add an int type constant to an
- object of class box, so the system finds the other overloading of
- the + operator beginning in line 25 to perform this operation.
- Also in line 60 we ask the system to use the * operator to do
- something to an int constant and an object of class box, which it
- satisfies by finding the method in lines 34 through 40. Note
- that it would be illegal to attempt to use the * operator the
- other way around, namely large * 4 since we did not define a
- method to use the two types in that order. Another overloading
- could be given with reversed types, and we could use the reverse
- order in a program.
-
- You will notice that when using operator overloading, we are also
- using function name overloading since some of the function names
- are the same.
-
- When we use operator overloading in this manner, we actually make
- our programs look like the class is a natural part of the
- language since it is integrated into the language so well. C++
- is therefore an extendible language and can be molded to fit the
- mechanics of the problem at hand.
-
-
-
-
-
- Page 6-10
-
- Chapter 6 - More Encapsulation
-
- OPERATOR OVERLOADING CAVEATS
- -----------------------------------------------------------------
- Each new topic we study has its pitfalls which must be warned
- against and the topic of operator overloading seems to have the
- record for pitfalls since it is so prone to misuse and has
- several problems. The overloading of operators is only available
- for classes, you cannot redefine the operators for the predefined
- simple types. This would probably be very silly anyway since the
- code could be very difficult to read if you changed some of them
- around.
-
- The logical and "&&" and the logical or "||" operators can be
- overloaded for the classes you define, but they will not operate
- as short circuit operators. All members of the logical
- construction will be evaluated with no regard concerning the
- outcome. Of course the normal predefined logical operators will
- continue to operate as short circuit operators as expected, but
- not the overloaded ones.
-
- If the increment "++" or decrement "--" operators are overloaded,
- the system has no way of telling whether the operators are used
- as preincrement or postincrement (or predecrement or
- postdecrement) operators. Which method is used is
- implementation dependent, so you should use them in such a way
- that it doesn't matter which is used.
-
- Be sure to compile and execute OPOVERLD.CPP before continuing on
- to the next example program.
-
-
- FUNCTION OVERLOADING IN A CLASS
- -----------------------------------------------------------------
- Examine the program named FUNCOVER.CPP for an ==================
- example of function name overloading within a FUNCOVER.CPP
- class. In this program the constructor is ==================
- overloaded as well as one of the methods to
- illustrate what can be done.
-
- This file illustrates some of the uses of overloaded names and a
- few of the rules for their use. You will recall that the
- function selected is based on the number and types of the formal
- parameters only. The type of the return value is not significant
- in overload resolution.
-
- In this case there are three constructors. The constructor which
- is actually called is selected by the number and types of the
- parameters in the definition. In line 77 of the main program the
- three objects are declared, each with a different number of
- parameters and inspection of the results will indicate that the
- correct constructor was called based on the number of parameters.
-
- In the case of the other overloaded methods, the number and type
- of parameters is clearly used to select the proper method. You
-
- Page 6-11
-
- Chapter 6 - More Encapsulation
-
- will notice that one method uses a single integer and another
- uses a single float type variable, but the system is able to
- select the correct one. As many overloadings as desired can be
- used provided that all of the parameter patterns are unique.
-
- You may be thinking that this is a silly thing to do but it is,
- in fact, a very important topic. Throughout this tutorial we
- have been using an overloaded operator and you haven't been the
- least confused over it. It is the cout operator which operates
- as an overloaded function since the way it outputs data is a
- function of the type of its input variable or the field we ask
- it to display. Many programming languages have overloaded
- output functions so you can output any data with the same
- function name.
-
- Be sure to compile and execute this program.
-
-
- SEPARATE COMPILATION
- -----------------------------------------------------------------
- Separate compilation is available with C++ and it follows the
- identical rules as given for ANSI-C separate compilation. As
- expected, separately compiled files can be linked together.
- However, since classes are used to define objects, the nature of
- C++ separate compilation is considerably different from that used
- for ANSI-C. This is because the classes used to create the
- objects are not considered as external variables, but as included
- classes. This makes the overall program look different from a
- pure ANSI-C program. Your programs will take on a different
- appearance as you gain experience in C++.
-
-
- YOU GET SOME METHODS BY DEFAULT
- -----------------------------------------------------------------
- Even if you include no constructors or ==================
- operator overloadings you get a few defined DEFMETHS.CPP
- automatically by the compiler. Examine the ==================
- file named DEFMETHS.CPP which will illustrate
- those methods provided by the compiler, and why you sometimes
- can't use the defaults but need to write your own to do the job
- the defaults were intended to do for you.
-
- Before we actually look at the program, we will list a few rules
- that all compiler writers must follow in order to deliver a
- useful implementation of C++. First we will state the rules,
- then take a closer look at them and the reason for their
- existence.
-
- 1. If no constructors are defined by the writer of a class, the
- compiler will automatically generate a default constructor
- and a copy constructor. Both of these constructors will be
- defined for you shortly.
-
-
- Page 6-12
-
- Chapter 6 - More Encapsulation
-
- 2. If the class author includes any constructor in the class,
- the default constructor will not be supplied by the
- constructor.
-
- 3. If the class author does not include a copy constructor, the
- compiler will generate one, but if the writer includes a copy
- constructor, the compiler will not generate one
- automatically.
-
- 4. If the class author includes an assignment operator, the
- compiler will not include one automatically, otherwise it
- will generate a default assignment operator.
-
- Any class declared and used in a C++ program must have some way
- to construct an object because the compiler, by definition, must
- call a constructor when we define an object. If we don't provide
- a constructor, the compiler itself will generate one that it can
- call during construction of the object. This is the default
- constructor and we have used it unknowingly in a lot of our
- example programs. The default constructor does not initialize
- any of the member variables, but it sets up all of the internal
- class references it needs, and calls the base constructor or
- constructors if they exist. We haven't studied inheritance yet,
- but we will in the next chapter of this tutorial so we will know
- then what base classes are all about. Line 11 of the present
- program contains a default constructor which is called when you
- define an object with no parameters. In this case, the
- constructor is necessary because we have an embedded string in
- the class that requires a dynamic allocation and an
- initialization of the string to the null string. It will take
- little thought to see that our constructor is much better than
- the default constructor which would leave us with an
- uninitialized pointer.
-
- The default constructor is used in line 78 of this example
- program.
-
-
- THE COPY CONSTRUCTOR
- -----------------------------------------------------------------
- The copy constructor is generated automatically for you by the
- compiler if you fail to define one yourself. It is used to copy
- the contents of an object to a new object during construction of
- that new object. If the compiler generates it for you, it will
- simply copy the contents of the original into the new object as a
- byte by byte copy, which may not be what you want. For simple
- classes with no pointers, that is usually sufficient, but in the
- present example program, we have a pointer as a class member so a
- byte by byte copy would copy the pointer from one to the other
- and they would both be pointing to the same allocated member.
- For this program, we declared our own copy constructor in line 14
- and implemented it in lines 34 to 39. A careful study of the
- implementation will reveal that the new class will indeed be
-
- Page 6-13
-
- Chapter 6 - More Encapsulation
-
- identical to the original, but the new class has its own string
- to work with. Since both constructors contain dynamic
- allocation, we must assure that the allocated data is destroyed
- when we are finished with the objects, so a destructor is
- mandatory as implemented in lines 50 through 53 of the present
- example program. The copy constructor is used in line 84 of the
- current example program.
-
-
- THE ASSIGNMENT OPERATOR
- -----------------------------------------------------------------
- It is not too obvious, but an assignment operator is required for
- this program also, because the default assignment operator simply
- copies the source object to the destination object byte by byte.
- This would result the same problem we had with copy constructor.
- The assignment operator is declared in line 17 and defined in
- lines 41 through 49 where we deallocate the old string in the
- existing object prior to allocating room for the new text and
- copying the text from the source object into the new object. The
- assignment operator is used in line 91.
-
- It should be fairly obvious to the student that when a class is
- defined which includes any sort of dynamic allocation, the above
- three methods should be included in addition to the proper
- destructor. If any of the four entities are omitted, the program
- may have terribly erratic behavior. Be sure to compile and
- execute this example program.
-
-
- A PRACTICAL EXAMPLE
- -----------------------------------------------------------------
- Using the inline keyword with a class member ==================
- can cause a bit of difficulty unless you PHRASE.H
- understand how the compiler uses the inline ==================
- code definition to perform the inline code
- insertion. Examine the header file named PHRASE.H which includes
- some inline methods. These are included as an illustration of
- one means of defining the inline methods in a clean way that the
- compiler can use efficiently. When any implementation uses this
- class, it must have access to the inline implementation in order
- to insert the proper inline code for the member functions. One
- way to do this is to put all of the inline methods in a separate
- file named with the INL extension, then including that file into
- the end of the .H file as shown here. This makes all of the
- inline code available for the compiler while compiling files
- that use this class.
-
- The example file named PHRASE.INL contains ==================
- all of the inline code for this class. If PHRASE.INL
- this class had methods that were not ==================
- inlined, they could be packaged into a file
- named PHRASE.CPP in the usual manner. Note that for illustrative
-
-
- Page 6-14
-
- Chapter 6 - More Encapsulation
-
- purposes, all of the methods were declared inline, so there is no
- implementation file for this class.
-
- The file named USEPHRAS.CPP uses the phrase ==================
- class defined in the last two example files. USEPHRAS.CPP
- It is plain to see that this class is no ==================
- different than any others we have studied.
- It simply illustrates a way to package inline code in a simple
- and very efficient manner.
-
-
- ANOTHER PRACTICAL EXAMPLE
- -----------------------------------------------------------------
- We come again to the practical part of this lesson where we study
- a practical class that can actually be used in a program but is
- still simple enough for the student to completely understand.
-
- In the last chapter we studied the date ================
- class and in this chapter we will study a TIME.H
- simple time class. You should begin by ================
- studying the file named TIME.H which will
- look very similar to the date class header. The only major
- difference in this class from the date class is the overloaded
- constructors and methods. The program is a very practical
- example that illustrates very graphically that many constructor
- overloadings are possible.
-
- The implementation for the time class is ==================
- given in the file named TIME.CPP. Once TIME.CPP
- again, the code is very simple and you ==================
- should have no problem understanding this
- example in its entirety. It should be pointed out that three of
- the four overloadings actually call the fourth so that the code
- did not have to be repeated four times. This is a perfectly good
- coding practice and illustrates that other member functions can
- be called from within the implementation.
-
- The example program named USETIME.CPP is a =================
- very simple program that uses the time class USETIME.CPP
- in a very rudimentary way as an illustration =================
- for you. You should be able to understand
- this program in a very short time. It will be to your advantage
- to completely understand the practical example programs given at
- the end of the last chapter and the end of this chapter. As
- mentioned above, we will use the time class and the date class
- as the basis for both single and multiple inheritance in the
- next three chapters.
-
-
- WHAT SHOULD BE THE NEXT STEP?
- -----------------------------------------------------------------
- At this point you have learned enough C++ to write meaningful
- programs and it would be to your advantage to stop studying and
-
- Page 6-15
-
- Chapter 6 - More Encapsulation
-
- begin using the knowledge you have gained. Because C++ is an
- extension to ANSI-C, it can be learned in smaller pieces than
- would be required if you are learning a completely new language.
- You have learned enough to study and completely understand the
- example program given in chapter 12, the Flyaway adventure game.
- You should begin studying this program now.
-
- One of your biggest problems is learning to think in terms of
- object oriented programming. It is not a trivial problem if you
- have been programming in procedural languages for any significant
- length of time. However, it can be learned by experience, so you
- should begin trying to think in terms of classes and objects
- immediately. Your first project should use only a small number
- of objects and the remainder of code can be completed in standard
- procedural programming techniques. As you gain experience, you
- will write more of the code for any given project using classes
- and objects but every project will eventually be completed in
- procedural code.
-
- After you have programmed for a while using the techniques
- covered up to this point in the tutorial, you can continue on to
- the next few chapters which will discuss inheritance and virtual
- functions.
-
-
- PROGRAMMING EXERCISES
- -----------------------------------------------------------------
- 1. Modify OBJDYNAM.CPP to make the objects named small and
- medium pointers, then dynamically allocate them prior to
- using them.
-
- 2. Modify the loop in line 61 of OBJLINK.CPP so that the loop
- will store 1000 elements in the list before stopping. You
- will probably wish to remove the printout from line 74 so the
- program will stop in a reasonable time. You may also get an
- integer overflow indicated by wrong answers if you send a
- message to get_area() with such large numbers. That will
- depend upon your compiler. You should also add a test to
- assure that the memory did not become exhausted after each
- dynamic allocation.
-
- 3. Write a program that uses both the date and time classes in a
- meaningful manner. No answer will be given in the ANSWERS
- directory for this exercise since it is so straight forward.
- These classes can be used in all of your future C++ programs
- to time stamp the time and date of execution.
-
-
-
-
-
-
-
-
- Page 6-16
-