?? ch15.htm
字號:
66: }
67:
68: int main()
69: {
70: Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);
71: Edie.SetSalary(50000);
72: String LastName("Levine");
73: Edie.SetLastName(LastName);
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 passed
in 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. This
string 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 with
yet another string constant. However, if you are paying close attention, you will
notice that <TT>Employee</TT> does not have a function <TT>SetFirstName()</TT> that
takes a character string as its argument; <TT>SetFirstName()</TT> requires a constant
string reference.</P>
<P>The compiler resolves this because it knows how to make a string from a constant
character string. It knows this because you told it how to do so on line 9 of Listing
15.1.
<H4 ALIGN="CENTER"><A NAME="Heading8"></A><FONT COLOR="#000077">Accessing Members
of 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 the
member 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 accessor
functions 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 about
how the integer variable, <TT>itsSalary</TT>, stores its information.
<H4 ALIGN="CENTER"><A NAME="Heading9"></A><FONT COLOR="#000077">Filtering Access
to Contained Members</FONT></H4>
<P>Note that the <TT>String</TT> class provides the <TT>operator+</TT>. The designer
of the <TT>Employee</TT> class has blocked access to the <TT>operator+</TT> being
called on <TT>Employee</TT> objects by declaring that all the string accessors, such
as <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 called
on), 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 function
itself is no longer <TT>const</TT>. Changing the return value is not sufficient to
overload 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 price
of 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 driver
program to add <TT>print</TT> statements indicating where in the program objects
are 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. Contained
class 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*) constructor
3: String(char*) constructor
4: String(char*) constructor
5: Calling SetFirstName with char *...
6: String(char*) constructor
7: String destructor
8: Creating temporary string LstName...
9: String(char*) constructor
10: Name: Edythe Levine
11: Address: 1461 Shore Parkway
12: Salary: 20000
13: String destructor
14: String destructor
15: String destructor
16: String destructor
</FONT></PRE>
<P><B>Analysis: </B>Listing 15.3 uses the same class declarations as Listings 15.1
and 15.2. However, the <TT>cout</TT> statements have been uncommented. The output
from 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 that
the 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. Here
the programmer is doing explicitly what the compiler did implicitly on the previous
statement. 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, is
destroyed 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 caused
five string constructor calls. Listing 15.4 again rewrites the driver program. This
time 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 incremented
each 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 by
value. <TT>ConstructorCount</TT> keeps track of how many string objects are created
when 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. Passing
by 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*) constructor
Constructor count: 5
Name: Edythe Levine
Address: 1461 Shore Parkway
Salary: 20000
Constructor count: 5
String(String&) constructor
String(String&) constructor
String(String&) constructor
Name: Edythe Levine.
Address: 1461 Shore Parkway.
Salary: 20000
String destructor
String destructor
String destructor
Constructor count: 8
String destructor
String destructor
String destructor
String destructor
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>The output shows that five
string objects were created as part of creating one <TT>Employee</TT> object. When
the <TT>Employee</TT> object is passed to <TT>rPrintFunc()</TT> by reference, no
additional <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 objects
are created (by calls to the copy constructor).
<H3 ALIGN="CENTER"><A NAME="Heading16"></A><FONT COLOR="#000077">Implementation in
Terms 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 specification
you've been given defines a <TT>PartsCatalog</TT> as a collection of parts; each
part has a unique part number. The <TT>PartsCatalog</TT> does not allow duplicate
entries, 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 on
that technology when making your <TT>PartsCatalog</TT>, rather than inventing it
from 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 whether
a <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 the
derived 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 collections
and each member of the collection can repeat. The <TT>PartsCatalog</TT> has unique
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -