📄 ch15.htm
字号:
74: Edie.SetFirstName("Edythe");75:76: cout << "Name: ";77: cout << Edie.GetFirstName().GetString();78: cout << " " << Edie.GetLastName().GetString();79: cout << ".\nAddress: ";80: cout << Edie.GetAddress().GetString();81: cout << ".\nSalary: " ;82: cout << Edie.GetSalary();83: return 0;<TT>84: }</TT></FONT></PRE><BLOCKQUOTE> <P><HR><FONT COLOR="#000077"><B>NOTE: </B></FONT>Put the code from Listing 15.1 into a file called <TT>STRING.HPP</TT>. Then any time you need the <TT>String</TT> class you can include Listing 15.1 by using <TT>#include</TT>. For example, at the top of Listing 15.2 add the line <TT>#include String.hpp</TT>. This will add the <TT>String</TT> class to your program. <HR></BLOCKQUOTE><PRE><FONT COLOR="#0066FF">Output: Name: Edythe Levine.Address: 1461 Shore Parkway.Salary: 50000</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 15.2 shows the <TT>Employee</TT>class, which contains three string objects: <TT>itsFirstName</TT>,<TT> itsLastName</TT>,and <TT>itsAddress</TT>.</P><P>On line 70, an <TT>Employee</TT> object is created, and four values are passedin to initialize the <TT>Employee</TT> object. On line 71, the <TT>Employee</TT>access function <TT>SetSalary()</TT> is called, with the constant value <TT>50000</TT>.Note that in a real program this would either be a dynamic value (set at runtime)or a constant.</P><P>On line 72, a string is created and initialized using a C++ string constant. Thisstring object is then used as an argument to <TT>SetLastName()</TT> on line 73.</P><P>On line 74, the <TT>Employee</TT> function <TT>SetFirstName()</TT> is called withyet another string constant. However, if you are paying close attention, you willnotice that <TT>Employee</TT> does not have a function <TT>SetFirstName()</TT> thattakes a character string as its argument; <TT>SetFirstName()</TT> requires a constantstring reference.</P><P>The compiler resolves this because it knows how to make a string from a constantcharacter string. It knows this because you told it how to do so on line 9 of Listing15.1.<H4 ALIGN="CENTER"><A NAME="Heading8"></A><FONT COLOR="#000077">Accessing Membersof the Contained Class</FONT></H4><P><TT>Employee</TT> objects do not have special access to the member variables of<TT>String</TT>. If the <TT>Employee</TT> object <TT>Edie</TT> tried to access themember variable <TT>itsLen</TT> of its own <TT>itsFirstName</TT> member variable,it would get a compile-time error. This is not much of a burden, however. The accessorfunctions provide an interface for the <TT>String</TT> class, and the <TT>Employee</TT>class need not worry about the implementation details, any more than it worries abouthow the integer variable, <TT>itsSalary</TT>, stores its information.<H4 ALIGN="CENTER"><A NAME="Heading9"></A><FONT COLOR="#000077">Filtering Accessto Contained Members</FONT></H4><P>Note that the <TT>String</TT> class provides the <TT>operator+</TT>. The designerof the <TT>Employee</TT> class has blocked access to the <TT>operator+</TT> beingcalled on <TT>Employee</TT> objects by declaring that all the string accessors, suchas <TT>GetFirstName()</TT>, return a constant reference. Because <TT>operator+</TT>is not (and can't be) a <TT>const</TT> function (it changes the object it is calledon), attempting to write the following will cause a compile-time error:</P><PRE><FONT COLOR="#0066FF">String buffer = Edie.GetFirstName() + Edie.GetLastName();</FONT></PRE><P><TT>GetFirstName()</TT> returns a constant <TT>String</TT>, and you can't call<TT>operator+</TT> on a constant object.</P><P>To fix this, overload <TT>GetFirstName()</TT> to be non-<TT>const</TT>:</P><PRE><FONT COLOR="#0066FF">const String & GetFirstName() const { return itsFirstName; }String & GetFirstName() { return itsFirstName; }</FONT></PRE><P>Note that the return value is no longer <TT>const</TT> and that the member functionitself is no longer <TT>const</TT>. Changing the return value is not sufficient tooverload the function name; you must change the constancy of the function itself.<H4 ALIGN="CENTER"><A NAME="Heading10"></A><FONT COLOR="#000077">Cost of Containment</FONT></H4><P>It is important to note that the user of an <TT>Employee</TT> class pays the priceof each of those string objects each time one is constructed, or a copy of the <TT>Employee</TT>is made.</P><P>Uncommenting the <TT>cout</TT> statements in Listing 15.1, lines 38, 51, 63, 75,84, and 100, reveals how often these are called. Listing 15.3 rewrites the driverprogram to add <TT>print</TT> statements indicating where in the program objectsare being created:<BLOCKQUOTE> <P><HR><FONT COLOR="#000077"><B>NOTE: </B></FONT>To compile this listing, follow these steps: 1. Uncomment lines 38, 51, 63, 75, 84, and 100 in Listing 15.1. 2. Edit Listing 15.2. Remove lines 64-80 and substitute Listing 15.3. 3. Add <TT>#include string.hpp</TT> as previously noted. <HR></BLOCKQUOTE><P><A NAME="Heading11"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 15.3. Containedclass constructors.</B></FONT><PRE><FONT COLOR="#0066FF">1: int main()2: {3: cout << "Creating Edie...\n";4: Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);5: Edie.SetSalary(20000);6: cout << "Calling SetFirstName with char *...\n";7: Edie.SetFirstName("Edythe");8: cout << "Creating temporary string LastName...\n";9: String LastName("Levine");10: Edie.SetLastName(LastName);11:12: cout << "Name: ";13: cout << Edie.GetFirstName().GetString();14: cout << " " << Edie.GetLastName().GetString();15: cout << "\nAddress: ";16: cout << Edie.GetAddress().GetString();17: cout << "\nSalary: " ;18: cout << Edie.GetSalary();19: cout << endl;20: return 0;<TT>21: }</TT></FONT><FONT COLOR="#0066FF">Output: 1: Creating Edie...2: String(char*) constructor3: String(char*) constructor4: String(char*) constructor5: Calling SetFirstName with char *...6: String(char*) constructor7: String destructor8: Creating temporary string LstName...9: String(char*) constructor10: Name: Edythe Levine11: Address: 1461 Shore Parkway12: Salary: 2000013: String destructor14: String destructor15: String destructor16: String destructor</FONT></PRE><P><B>Analysis: </B>Listing 15.3 uses the same class declarations as Listings 15.1and 15.2. However, the <TT>cout</TT> statements have been uncommented. The outputfrom Listing 15.3 has been numbered to make analysis easier.</P><P>On line 3 of Listing 15.3, the statement <TT>Creating Edie...</TT> is printed,as reflected on line 1 of the output. On line 4 an <TT>Employee</TT> object, <TT>Edie</TT>,is created with four parameters. The output reflects the constructor for <TT>String</TT>being called three times, as expected.</P><P>Line 6 prints an information statement, and then on line 7 is the statement <TT>Edie.SetFirstName("Edythe")</TT>.This statement causes a temporary string to be created from the character string<TT>"Edythe"</TT>, as reflected on lines 6 and 7 of the output. Note thatthe temporary is destroyed immediately after it is used in the assignment statement.</P><P>On line 9, a <TT>String</TT> object is created in the body of the program. Herethe programmer is doing explicitly what the compiler did implicitly on the previousstatement. This time you see the constructor on line 9 of the output, but no destructor.This object will not be destroyed until it goes out of scope at the end of the function.</P><P>On lines 13-19, the strings in the employee object are destroyed as the <TT>Employee</TT>object falls out of scope, and the string <TT>LastName</TT>, created on line 9, isdestroyed as well when it falls out of scope.<H4 ALIGN="CENTER"><A NAME="Heading13"></A><FONT COLOR="#000077">Copying by Value</FONT></H4><P>Listing 15.3 illustrates how the creation of one <TT>Employee</TT> object causedfive string constructor calls. Listing 15.4 again rewrites the driver program. Thistime the <TT>print</TT> statements are not used, but the string static member variable<TT>ConstructorCount</TT> is uncommented and used.</P><P>Examination of Listing 15.1 shows that <TT>ConstructorCount</TT> is incrementedeach time a string constructor is called. The driver program in 15.4 calls the <TT>print</TT>functions, passing in the <TT>Employee</TT> object, first by reference and then byvalue. <TT>ConstructorCount</TT> keeps track of how many string objects are createdwhen the employee is passed as a parameter.<BLOCKQUOTE> <P><HR><FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>To compile this listing: 1. Uncomment lines 23, 39, 52, 64, 76, and 152 in Listing 15.1. 2. Edit Listing 15.2. Remove lines 68-84 and substitute Listing 15.4. 3. Add <TT>#include string.hpp</TT> as previously noted. <HR></BLOCKQUOTE><P><A NAME="Heading14"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 15.4. Passingby value</B></FONT><PRE><FONT COLOR="#0066FF">1: void PrintFunc(Employee);2: void rPrintFunc(const Employee&);3:4: int main()5: {6: Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);7: Edie.SetSalary(20000);8: Edie.SetFirstName("Edythe");9: String LastName("Levine");10: Edie.SetLastName(LastName);11:12: cout << "Constructor count: " ;13: cout << String::ConstructorCount << endl;14: rPrintFunc(Edie);15: cout << "Constructor count: ";16: cout << String::ConstructorCount << endl;17: PrintFunc(Edie);18: cout << "Constructor count: ";19: cout << String::ConstructorCount << endl;20: return 0;21: }22: void PrintFunc (Employee Edie)23: {24:25: cout << "Name: ";26: cout << Edie.GetFirstName().GetString();27: cout << " " << Edie.GetLastName().GetString();28: cout << ".\nAddress: ";29: cout << Edie.GetAddress().GetString();30: cout << ".\nSalary: " ;31: cout << Edie.GetSalary();32: cout << endl;33:34: }35:36: void rPrintFunc (const Employee& Edie)37: {38: cout << "Name: ";39: cout << Edie.GetFirstName().GetString();40: cout << " " << Edie.GetLastName().GetString();41: cout << "\nAddress: ";42: cout << Edie.GetAddress().GetString();43: cout << "\nSalary: " ;44: cout << Edie.GetSalary();45: cout << endl;<TT>46: }</TT>Output: String(char*) constructor String(char*) constructor String(char*) constructor String(char*) constructor String destructor String(char*) constructorConstructor count: 5Name: Edythe LevineAddress: 1461 Shore ParkwaySalary: 20000Constructor count: 5 String(String&) constructor String(String&) constructor String(String&) constructorName: Edythe Levine.Address: 1461 Shore Parkway.Salary: 20000 String destructor String destructor String destructorConstructor count: 8String destructor String destructor String destructor String destructor</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>The output shows that fivestring objects were created as part of creating one <TT>Employee</TT> object. Whenthe <TT>Employee</TT> object is passed to <TT>rPrintFunc()</TT> by reference, noadditional <TT>Employee</TT> objects are created, and so no additional <TT>String</TT>objects are created. (They too are passed by reference.)</P><P>When, on line 14, the <TT>Employee</TT> object is passed to <TT>PrintFunc()</TT>by value, a copy of the <TT>Employee</TT> is created, and three more string objectsare created (by calls to the copy constructor).<H3 ALIGN="CENTER"><A NAME="Heading16"></A><FONT COLOR="#000077">Implementation inTerms of Inheritance/Containment Versus Delegation</FONT></H3><P>At times, one class wants to draw on some of the attributes of another class.For example, let's say you need to create a <TT>PartsCatalog</TT> class. The specificationyou've been given defines a <TT>PartsCatalog</TT> as a collection of parts; eachpart has a unique part number. The <TT>PartsCatalog</TT> does not allow duplicateentries, and does allow access by part number.</P><P>The listing for the Week in Review for Week 2 provides a <TT>LinkedList</TT> class.This <TT>LinkedList</TT> is well-tested and understood, and you'd like to build onthat technology when making your <TT>PartsCatalog</TT>, rather than inventing itfrom scratch.</P><P>You could create a new <TT>PartsCatalog</TT> class and have it contain a <TT>LinkedList</TT>.The <TT>PartsCatalog</TT> could delegate management of the linked list to its contained<TT>LinkedList</TT> object.</P><P>An alternative would be to make the <TT>PartsCatalog</TT> derive from <TT>LinkedList</TT>and thereby inherit the properties of a <TT>LinkedList</TT>. Remembering, however,that public inheritance provides an is-a relationship, you should question whethera <TT>PartsCatalog</TT> really is a type of <TT>LinkedList</TT>.</P><P>One way to answer the question of whether <TT>PartsCatalog</TT> is a <TT>LinkedList</TT>is to assume that <TT>LinkedList</TT> is the base and <TT>PartsCatalog</TT> is thederived class, and then to ask these other questions:<DL> <DD><B>1.</B> Is there anything in the base class that should not be in the derived? For example, does the <TT>LinkedList</TT> base class have functions that are inappropriate for the <TT>PartsCatalog</TT> <BR> class? If so, you probably don't want public inheritance.<BR> <BR> <B>2.</B> Might the class you are creating have more than one of the base? For example, might a <TT>PartsCatalog</TT> need two <TT>LinkedList</TT>s in each object? If it might, you almost certainly want to use containment.<BR> <BR> <B>3.</B> Do you need to inherit from the base class so that you can take advantage of virtual functions or access protected members? If so, you must use inheritance, public or private.</DL><P>Based on the answers to these questions, you must chose between public inheritance(the is-a relationship) and either private inheritance or containment.<DL> <DD><HR><FONT COLOR="#000077"><B>New Term:</B></FONT> <UL> <P> </UL> <DD><B>Contained --</B>An object declared as a member of another class contained by that class.<BR> <BR> <B>Delegation -</B>-Using the attributes of a contained class to accomplish functions not otherwise available to the containing class.<BR> <BR> <B>Implemented in terms of --</B>Building one class on the capabilities of another without using public inheritance. <HR></DL><H4 ALIGN="CENTER"><A NAME="Heading17"></A><FONT COLOR="#000077">Delegation</FONT></H4><P>Why not derive <TT>PartsCatalog</TT> from <TT>LinkedList</TT>? The <TT>PartsCatalog</TT>isn't a <TT>LinkedList</TT> because <TT>LinkedList</TT>s are ordered collectionsand each member of the collection can repeat. The <TT>PartsCatalog</TT> has uniqueentries that are not ordered. The fifth member of the <TT>PartsCatalog</TT> is notpart number 5.</P><P>Certainly it would have been possible to inherit publicly from <TT>PartsList</TT>and then override <TT>Insert()</TT> and the offset operators (<TT>[]</TT>) to dothe right thing, but then you would have changed the essence of the <TT>PartsList</TT>class. Instead you'll build a <TT>PartsCatalog</TT> that has no offset operator,does not allow duplicates, and defines the <TT>operator+</TT> to combine two sets.</P><P>The first way to accomplish this is with containment. The <TT>PartsCatalog</TT>will delegate list management to a contained <TT>LinkedList</TT>. Listing 15.5 illustratesthis approach.</P><P><A NAME="Heading18"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 15.5. Delegatingto a contained LinkedList.</B></FONT><PRE><FONT COLOR="#0066FF">0: #include <iostream.h>1: 2: typedef unsigned long ULONG;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -