?? ch15.htm
字號:
321:
322: if (!choice)
323: break;
324:
325: cout << "New PartNumber?: ";
326: cin >> PartNumber;
327:
328: if (choice == 1)
329: {
330: cout << "Model Year?: ";
331: cin >> value;
332: pPart = new CarPart(value,PartNumber);
333: }
334: else
335: {
336: cout << "Engine Number?: ";
337: cin >> value;
338: pPart = new AirPlanePart(value,PartNumber);
339: }
340: pc.Insert(pPart);
341: }
342: pc.ShowAll();
343: return 0;
<TT>344: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: (0)Quit (1)Car (2)Plane: 1
New PartNumber?: 1234
Model Year?: 94
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4434
Model Year?: 93
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 1234
Model Year?: 94
1234 was the first entry. Rejected!
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2345
Model Year?: 93
(0)Quit (1)Car (2)Plane: 0
Part Number: 1234
Model Year: 94
Part Number: 2345
Model Year: 93
Part Number: 4434
Model Year: 93
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 15.7 reproduces the
interface to the <TT>Part</TT>, <TT>PartNode</TT>, and <TT>PartList</TT> classes
from Week 2 in Review, but to save room it does not reproduce the implementation
of their methods.</P>
<P>A new class, <TT>PartsCatalog</TT>, is declared on lines 260-270. <TT>PartsCatalog</TT>
has a <TT>PartsList</TT> as its data member, to which it delegates list management.
Another way to say this is that the <TT>PartsCatalog</TT> is implemented in terms
of this <TT>PartsList</TT>.</P>
<P>Note that clients of the <TT>PartsCatalog</TT> do not have access to the <TT>PartsList</TT>
directly. The interface is through the <TT>PartsCatalog</TT>, and as such the behavior
of the <TT>PartsList</TT> is dramatically changed. For example, the <TT>PartsCatalog::Insert()</TT>
method does not allow duplicate entries into the <TT>PartsList</TT>.</P>
<P>The implementation of <TT>PartsCatalog::Insert()</TT> starts on line 272. The
<TT>Part</TT> that is passed in as a parameter is asked for the value of its <TT>itsPartNumber</TT>
member variable. This value is fed to the <TT>PartsList</TT>'s <TT>Find()</TT> method,
and if no match is found the number is inserted; otherwise an informative error message
is printed.</P>
<P>Note that <TT>PartsCatalog</TT> does the actual insert by calling <TT>Insert()</TT>
on its member variable, <TT>pl</TT>, which is a <TT>PartsList</TT>. The mechanics
of the actual insertion and the maintenance of the linked list, as well as searching
and retrieving from the linked list, are maintained in the contained <TT>PartsList</TT>
member of <TT>PartsCatalog</TT>. There is no reason for <TT>PartsCatalog</TT> to
reproduce this code; it can take full advantage of the well-defined interface.</P>
<P>This is the essence of reusability within C++: <TT>PartsCatalog</TT> can reuse
the <TT>PartsList</TT> code, and the designer of <TT>PartsCatalog</TT> is free to
ignore the implementation details of <TT>PartsList</TT>. The interface to <TT>PartsList</TT>
(that is, the class declaration) provides all the information needed by the designer
of the <TT>PartsCatalog</TT> class.
<H3 ALIGN="CENTER"><A NAME="Heading20"></A><FONT COLOR="#000077">Private Inheritance</FONT></H3>
<P>If <TT>PartsCatalog</TT> needed access to the protected members of <TT>LinkedList</TT>
(in this case there are none), or needed to override any of the <TT>LinkedList</TT>
methods, then <TT>PartsCatalog</TT> would be forced to inherit from <TT>PartsList</TT>.</P>
<P>Since a <TT>PartsCatalog</TT> is not a <TT>PartsList</TT> object, and since you
don't want to expose the entire set of functionality of <TT>PartsList</TT> to clients
of <TT>PartsCatalog</TT>, you need to use private inheritance.</P>
<P>The first thing to know about private inheritance is that all of the base member
variables and functions are treated as if they were declared to be private, regardless
of their actual access level in the base. Thus, to any function that is not a member
function of <TT>PartsCatalog</TT>, every function inherited from <TT>PartsList</TT>
is inaccessible. This is critical: private inheritance does not involve inheriting
interface, just implementation.</P>
<P>To clients of the <TT>PartsCatalog</TT> class, the <TT>PartsList</TT> class is
invisible. None of its interface is available: you can't call any of its methods.
You can call <TT>PartsCatalog</TT> methods, however, and they can access all of <TT>LinkedLists</TT>,
because they are derived from <TT>LinkedLists</TT>.</P>
<P>The important thing here is that the <TT>PartsCatalog</TT> isn't a <TT>PartsList</TT>,
as would have been implied by public inheritance. It is implemented in terms of a
<TT>PartsList</TT>, just as would have been the case with containment. The private
inheritance is just a convenience.</P>
<P>Listing 15.6 demonstrates the use of private inheritance by rewriting the <TT>PartsCatalog</TT>
class as privately derived from <TT>PartsList</TT>.
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>To compile this program, replace
lines 260-344 of Listing 15.5 with Listing 15.6 and recompile.
<HR>
</BLOCKQUOTE>
<P><A NAME="Heading21"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 15.6. Private
inheritance.</B></FONT>
<PRE><FONT COLOR="#0066FF">1: //listing 15.6 demonstrates private inheritance
2:
3: //rewrites PartsCatalog from listing 15.5
4:
5: //see attached notes on compiling
6:
7: class PartsCatalog : private PartsList
8: {
9: public:
10: void Insert(Part *);
11: ULONG Exists(ULONG PartNumber);
12: Part * Get(int PartNumber);
13: operator+(const PartsCatalog &);
14: void ShowAll() { Iterate(Part::Display); }
15: private:
16: };
17:
18: void PartsCatalog::Insert(Part * newPart)
19: {
20: ULONG partNumber = newPart->GetPartNumber();
21: ULONG offset;
22:
23: if (!Find(offset, partNumber))
24: PartsList::Insert(newPart);
25: else
26: {
27: cout << partNumber << " was the ";
28: switch (offset)
29: {
30: case 0: cout << "first "; break;
31: case 1: cout << "second "; break;
32: case 2: cout << "third "; break;
33: default: cout << offset+1 << "th ";
34: }
35: cout << "entry. Rejected!\n";
36: }
37: }
38:
39: ULONG PartsCatalog::Exists(ULONG PartNumber)
40: {
41: ULONG offset;
42: Find(offset,PartNumber);
43: return offset;
44: }
45:
46: Part * PartsCatalog::Get(int PartNumber)
47: {
48: ULONG offset;
49: return (Find(offset, PartNumber));
50:
51: }
52:
53: int main()
54: {
55: PartsCatalog pc;
56: Part * pPart = 0;
57: ULONG PartNumber;
58: USHORT value;
59: ULONG choice;
60:
61: while (1)
62: {
63: cout << "(0)Quit (1)Car (2)Plane: ";
64: cin >> choice;
65:
66: if (!choice)
67: break;
68:
69: cout << "New PartNumber?: ";
70: cin >> PartNumber;
71:
72: if (choice == 1)
73: {
74: cout << "Model Year?: ";
75: cin >> value;
76: pPart = new CarPart(value,PartNumber);
77: }
78: else
79: {
80: cout << "Engine Number?: ";
81: cin >> value;
82: pPart = new AirPlanePart(value,PartNumber);
83: }
84: pc.Insert(pPart);
85: }
86: pc.ShowAll();
87: return 0;
<TT>88: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: (0)Quit (1)Car (2)Plane: 1
New PartNumber?: 1234
Model Year?: 94
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4434
Model Year?: 93
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 1234
Model Year?: 94
1234 was the first entry. Rejected!
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2345
Model Year?: 93
(0)Quit (1)Car (2)Plane: 0
Part Number: 1234
Model Year: 94
Part Number: 2345
Model Year: 93
Part Number: 4434
Model Year: 93
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 15.6 shows only the
changed interface to <TT>PartsCatalog</TT> and the rewritten driver program. The
interfaces to the other classes are unchanged from Listing 15.5.</P>
<P>On line 7 of Listing 15.6, <TT>PartsCatalog</TT> is declared to derive privately
from <TT>PartsList</TT>. The interface to <TT>PartsCatalog</TT> doesn't change from
Listing 15.5, though of course it no longer needs an object of type <TT>PartsList</TT>
as member data.</P>
<P>The <TT>PartsCatalog</TT> <TT>ShowAll()</TT> function calls <TT>PartsList</TT>
<TT>Iterate()</TT> with the appropriate pointer to member function of class <TT>Part</TT>.
<TT>ShowAll()</TT> acts as a public interface to <TT>Iterate()</TT>, providing the
correct information but preventing client classes from calling <TT>Iterate()</TT>
dir-ectly. Although <TT>PartsList</TT> might allow other functions to be passed to
<TT>Iterate()</TT>, <TT>PartsCatalog</TT> does not.</P>
<P>The <TT>Insert()</TT> function has changed as well. Note, on line 23, that <TT>Find()</TT>
is now called directly, because it is inherited from the base class. The call on
line 24 to <TT>Insert()</TT> must be fully qualified, of course, or it would endlessly
recurse into itself.</P>
<P>In short, when methods of <TT>PartsCatalog</TT> want to call <TT>PartsList</TT>
methods, they may do so directly. The only exception is when <TT>PartsCatalog</TT>
has overridden the method and the <TT>PartsList</TT> version is needed, in which
case the function name must be qualified fully.</P>
<P>Private inheritance allows the <TT>PartsCatalog</TT> to inherit what it can use,
but still provide mediated access to <TT>Insert</TT> and other methods to which client
classes should not have direct access.
<BLOCKQUOTE>
<P>
<HR>
<B>DO </B>inherit publicly when the derived object is a kind of the base class. <B>DO</B>
use containment when you want to delegate functionality to another class, but you
don't need access to its protected members.<B> DO</B> use private inheritance when
you need to implement one class in terms of another, and you need access to the base
class's protected members. <B>DON'T </B>use private inheritance when you need to
use more than one of the base class. You must use containment. For example, if <TT>PartsCatalog</TT>
needed two <TT>PartsLists</TT>, you could not have used private inheritance. <B>DON'T</B>
use public inheritance when members of the base class should not be available to
clients of the derived class.
<HR>
</BLOCKQUOTE>
<H3 ALIGN="CENTER"><A NAME="Heading24"></A><FONT COLOR="#000077">Friend Classes</FONT></H3>
<P>Sometimes you will create classes together, as a set. For example, <TT>PartNode</TT>
and <TT>PartsList</TT> were tightly coupled, and it would have been convenient if
<TT>PartsList</TT> could have read <TT>PartNode</TT>'s <TT>Part</TT> pointer, <TT>itsPart</TT>,
directly.</P>
<P>You wouldn't want to make <TT>itsPart</TT> public, or even protected, because
this is an implementation detail of <TT>PartNode</TT> and you want to keep it private.
You do want to expose it to <TT>PartsList</TT>, however.</P>
<P>If you want to expose your private member data or functions to another class,
you must declare that class to be a friend. This extends the interface of your class
to include the friend class.</P>
<P>Once <TT>PartsNode</TT> declares <TT>PartsList</TT> to be a friend, all of <TT>PartsNode</TT>'s
member data and functions are public as far as <TT>PartsList</TT> is concerned.</P>
<P>It is important to note that friendship cannot be transferred. Just because you
are my friend and Joe is your friend doesn't mean Joe is my friend. Friendship is
not inherited either. Again, just because you are my friend and I'm willing to share
my secrets with you doesn't mean I'm willing to share my secrets with your children.</P>
<P>Finally, friendship is not commutative. Assigning <TT>Class One</TT> to be a friend
of <TT>Class Two</TT> does not make <TT>Class Two</TT> a friend of <TT>Class One</TT>.
Just because you are willing to tell me your secrets doesn't mean I am willing to
tell you mine.</P>
<P>Listing 15.7 illustrates friendship by rewriting the example from Listing 15.6,
making <TT>PartsList</TT> a friend of <TT>PartNode</TT>. Note that this does not
make <TT>PartNode</TT> a friend of <TT>PartsList</TT>.</P>
<P><A NAME="Heading25"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 15.7. Friend
class illustrated.</B></FONT>
<PRE><FONT COLOR="#0066FF">0: #include <iostream.h>
1:
2: typedef unsigned long ULONG;
3: typedef unsigned short USHORT;
4:
5:
6: // **************** Part ************
7:
8: // Abstract base class of parts
9: class Part
10: {
11: public:
12: Part():itsPartNumber(1) {}
13: Part(ULONG PartNumber):
14: itsPartNumber(PartNumber){}
15: virtual ~Part(){}
16: ULONG GetPartNumber() const
17: { return itsPartNumber; }
18: virtual void Display() const =0;
19: private:
20: ULONG itsPartNumber;
21: };
22:
23: // implementation of pure virtual function so that
24: // derived classes can chain up
25: void Part::Display() const
26: {
27: cout << "\nPart Number: ";
28: cout << itsPartNumber << endl;
29: }
30:
31: // **************** Car Part ************
32:
33: class CarPart : public Part
34: {
35: public:
36: CarPart():itsModelYear(94){}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -