⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ei40.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 40: Model "has-a" or "is-implemented-in-terms-of" through layering</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI40_DIR.HTM";
var dingtext = "Item E40, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>

<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E40: The meanings of layering" -->
<A NAME="7424"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI39_FR.HTM" TARGET="_top">Item 39: Avoid casts down the inheritance hierarchy.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI41_FR.HTM" TARGET="_top">Item 41: Differentiate between inheritance and templates.</A></FONT></DIV>

<P><A NAME="dingp1"></A><A NAME="p182"></A><FONT ID="eititle">Item 40: &nbsp;Model "has-a" or "is-implemented-in-terms-of" through layering.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="7426"></A>
<P><A NAME="dingp2"></A>
<I>Layering</I> is the process of building one class on top of another class by having the layering class contain an object of the layered class as a data member. For <NOBR>example:<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>
<A NAME="7428"></A>
<UL><PRE>
class Address { ... };           // where someone lives
</PRE>
</UL><A NAME="7429"></A>
<UL><PRE>class PhoneNumber { ... };
</PRE>
</UL><A NAME="7431"></A>
<UL><PRE>class Person {
public:
  ...
</PRE>
</UL><A NAME="28568"></A>
<UL><PRE>private:
  string name;                   // layered object
  Address address;               // ditto
  PhoneNumber voiceNumber;       // ditto
  PhoneNumber faxNumber;         // ditto
};
</PRE>
</UL><A NAME="7435"></A>
<P><A NAME="dingp3"></A>
In this example, the <CODE>Person</CODE> class is said to be layered on top of the <CODE>string</CODE>, <CODE>Address</CODE>, and <CODE>PhoneNumber</CODE> classes, because it contains data members of those types. The term <I>layering</I> has lots of synonyms. It's also known as <I>composition</I>, <I>containment</I>, and <I>embedding</I>.<SCRIPT>create_link(3);</SCRIPT>
</P>
<A NAME="7441"></A>
<P><A NAME="dingp4"></A>
<A HREF="./EI35_FR.HTM#6914" TARGET="_top">Item 35</A> explains that public inheritance means "isa." In contrast, layering means either "has-a" or <NOBR>"is-implemented-in-terms-of."<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="7443"></A>
<P><A NAME="dingp5"></A>
The <CODE>Person</CODE> class above demonstrates the has-a relationship. A <CODE>Person</CODE> object has a name, an address, and telephone numbers for voice and FAX communication. You wouldn't say that a person <I>is</I> a name or that a person <I>is</I> an address. You would say that a person <I>has</I> a name and <I>has</I> an address, etc. Most people have little difficulty with this distinction, so confusion between the roles of isa and has-a is relatively <NOBR>rare.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="7444"></A>
<P><A NAME="dingp6"></A>
Somewhat more troublesome is the difference between isa and is-implemented-in-terms-of. For example, suppose you need a template for classes representing sets of arbitrary objects, i.e., collections without duplicates. Because reuse is a wonderful thing, and because you wisely read <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A>'s overview of the standard C++ library, your first instinct is to employ the library's <CODE>set</CODE> template. After all, why write a new template when you can use an established one written by somebody <NOBR>else?<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="20665"></A>
<P><A NAME="dingp7"></A>
As you delve into <CODE>set</CODE>'s documentation, however, you discover a limitation your application can't live with: a <CODE>set</CODE> requires that the elements contained within it be <I>totally ordered</I>, i.e., for every pair of objects <CODE>a</CODE> <A NAME="p183"></A>and <CODE>b</CODE> in the set, it must be possible to determine whether <CODE>a&lt;b</CODE> or <CODE>b&lt;a</CODE>. For many types, this requirement is easy to satisfy, and having a total ordering among objects allows <CODE>set</CODE> to offer certain attractive guarantees regarding its performance. (See <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A> for more on performance guarantees in the standard library.) Your need, however, is for something more general: a <CODE>set</CODE>-like class where objects need not be totally ordered, they need only be what the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver = "self.status = 'The latest publicly-available version of the C++ standard'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standard</A> colorfully terms "EqualityComparable": it's possible to determine whether <CODE>a==b</CODE> for objects <CODE>a</CODE> and <CODE>b</CODE> of the same type. This more modest requirement is better suited to types representing things like colors. Is red less than green or is green less than red? For your application, it seems you'll need to write your own template after <NOBR>all.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="20726"></A>
<P><A NAME="dingp8"></A>
Still, reuse is a wonderful thing. Being the data structure maven you are, you know that of the nearly limitless choices for implementing sets, one particularly simple way is to employ linked lists. But guess what? The <CODE>list</CODE> template (which generates linked list classes) is just <I>sitting</I> there in the standard library! You decide to (re)use <NOBR>it.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="20738"></A>
<P><A NAME="dingp9"></A>
In particular, you decide to have your nascent <CODE>Set</CODE> template inherit from <CODE>list</CODE>. That is, <CODE>Set&lt;T&gt;</CODE> will inherit from <CODE>list&lt;T&gt;</CODE>. After all, in your implementation, a <CODE>Set</CODE> object will in fact <I>be</I> a <CODE>list</CODE> object. You thus declare your <CODE>Set</CODE> template like <NOBR>this:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="7472"></A>
<UL><PRE>// the wrong way to use list for Set
template&lt;class T&gt;
class Set: public list&lt;T&gt; { ... };
</PRE>
</UL><A NAME="7473"></A>
<P><A NAME="dingp10"></A>
Everything may seem fine and dandy at this point, but in fact there is something quite wrong. As <A HREF="./EI35_FR.HTM#6914" TARGET="_top">Item 35</A> explains, if D isa B, everything true of B is also true of D. However, a <CODE>list</CODE> object may contain duplicates, so if the value 3051 is inserted into a <CODE>list&lt;int&gt;</CODE> twice, that list will contain two copies of 3051. In contrast, a <CODE>Set</CODE> may not contain duplicates, so if the value 3051 is inserted into a <CODE>Set&lt;int&gt;</CODE> twice, the set contains only one copy of the value. It is thus a vicious lie that a <CODE>Set</CODE> isa <CODE>list</CODE>, because some of the things that are true for <CODE>list</CODE> objects are not true for <CODE>Set</CODE> <NOBR>objects.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="7477"></A>
<P><A NAME="dingp11"></A>
Because the relationship between these two classes isn't isa, public inheritance is the wrong way to model that relationship. The right way is to realize that a <CODE>Set</CODE> object can be <I>implemented in terms of</I> a <CODE>list</CODE> <NOBR>object:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="20826"></A>
<UL><PRE><A NAME="p184"></A>// the right way to use list for Set
template&lt;class T&gt;
class Set {
public:
  bool member(const T&amp; item) const;
</PRE>
</UL><A NAME="20829"></A>
<UL><PRE>  void insert(const T&amp; item);
  void remove(const T&amp; item);
</PRE>
</UL><A NAME="20830"></A>
<UL><PRE>  int cardinality() const;
</PRE>
</UL><A NAME="20835"></A>
<UL><PRE>private:
  list&lt;T&gt; rep;                       // representation for a set
};
</PRE>
</UL><A NAME="20840"></A>
<P><A NAME="dingp12"></A>
<CODE>Set</CODE>'s member functions can lean heavily on functionality already offered by <CODE>list</CODE> and other parts of the standard library, so the implementation is neither difficult to write nor thrilling to <NOBR>read:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="20856"></A>
<UL><PRE>template&lt;class T&gt;
bool Set&lt;T&gt;::member(const T&amp; item) const
{ return find(rep.begin(), rep.end(), item) != rep.end(); }
</PRE>
</UL><A NAME="70029"></A>
<UL><PRE>template&lt;class T&gt;
void Set&lt;T&gt;::insert(const T&amp; item)
{ if (!member(item)) rep.push_back(item); }
</PRE>
</UL><A NAME="70030"></A>
<UL><PRE>template&lt;class T&gt;
void Set&lt;T&gt;::remove(const T&amp; item)
{
  list&lt;T&gt;::iterator it =
    find(rep.begin(), rep.end(), item);
</PRE>
</UL><A NAME="20885"></A>
<UL><PRE>  if (it != rep.end()) rep.erase(it);
}
</PRE>
</UL><A NAME="20888"></A>
<UL><PRE>template&lt;class T&gt;
int Set&lt;T&gt;::cardinality() const
{ return rep.size(); }
</PRE>
</UL><A NAME="20899"></A>
<P><A NAME="dingp13"></A>
These functions are simple enough that they make reasonable candidates for inlining, though I know you'd want to review the discussion in <A HREF="./EI33_FR.HTM#6729" TARGET="_top">Item 33</A> before making any firm inlining decisions. (In the code above, functions like <CODE>find</CODE>, <CODE>begin</CODE>, <CODE>end</CODE>, <CODE>push_back</CODE>, etc., are part of the standard library's framework for working with container templates like <CODE>list</CODE>. You'll find an overview of this framework in <A HREF="./EI49_FR.HTM#8392" TARGET="_top">Item 49</A> and <A HREF="../MEC/MI35_FR.HTM#5473" TARGET="_top">M35</A>.)<SCRIPT>create_link(13);</SCRIPT>
</P>
<A NAME="7498"></A>
<P><A NAME="dingp14"></A>
It's worth remarking that the <CODE>Set</CODE> class interface fails the test of being complete and minimal (see <A HREF="./EI18_FR.HTM#17774" TARGET="_top">Item 18</A>). In terms of completeness, the primary omission is that of a way to iterate over the contents of a set, something that might well be necessary for many applications (and that is provided by all members of the standard library, including <CODE>set</CODE>). An additional drawback is that <CODE>Set</CODE> fails to follow the container class <A NAME="p185"></A>conventions embraced by the standard library (see Items <A HREF="./EI49_FR.HTM#8392" TARGET="_top">49</A> and <A HREF="../MEC/MI35_FR.HTM#5473" TARGET="_top">M35</A>), and that makes it more difficult to take advantage of other parts of the library when working with <CODE>Set</CODE>s.<SCRIPT>create_link(14);</SCRIPT>
</P>
<A NAME="20922"></A>
<P><A NAME="dingp15"></A>
Nits about <CODE>Set</CODE>'s interface, however, shouldn't be allowed to overshadow what <CODE>Set</CODE> got indisputably right: the relationship between <CODE>Set</CODE> and <CODE>list</CODE>. That relationship is not isa (though it initially looked like it might be), it's "is-implemented-in-terms-of," and the use of layering to implement that relationship is something of which any class designer may be justly <NOBR>proud.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="22297"></A>
<P><A NAME="dingp16"></A>
Incidentally, when you use layering to relate two classes, you create a compile-time dependency between those classes. For information on why this should concern you, as well as what you can do to allay your worries, turn to <A HREF="./EI34_FR.HTM#6793" TARGET="_top">Item 34</A>.<SCRIPT>create_link(16);</SCRIPT>
</P>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI39_FR.HTM" TARGET="_top">Item 39: Avoid casts down the inheritance hierarchy.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI41_FR.HTM" TARGET="_top">Item 41: Differentiate between inheritance and templates.</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -