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

📄 mi17.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 17: Consider using lazy evaluation</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>
var dingbase = "MI17_DIR.HTM";
var dingtext = "Item M17, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="M17: Consider using lazy evaluation" -->
<A NAME="41011"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI16_FR.HTM" TARGET="_top">Item 16: Remember the 80-20 rule</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI18_FR.HTM" TARGET="_top">Item 18: Amortize the cost of expected computations</A></FONT></DIV>


<P><A NAME="dingp1"></A><font ID="mititle">Item 17: &nbsp;Consider using lazy evaluation.</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="72144"></A>

<A NAME="41012"></A>
<P><A NAME="dingp2"></A>
From the perspective of efficiency, the best computations are those you never perform at all. That's fine, but if you don't need to do something, why would you put code in your program to do it in the first place? And if you do need to do something, how can you possibly avoid executing the code that does <NOBR>it?<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P><A NAME="41013"></A>
<P><A NAME="dingp3"></A>
The key is to be <NOBR>lazy.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P><A NAME="41014"></A>
<P><A NAME="dingp4"></A>
Remember when you were a child and your parents told you to clean your room? If you were anything like me, you'd say "Okay," then promptly go back to what you were doing. You would <I>not</I> clean your room. In fact, cleaning your room would be the last thing on your mind &#151; <I>until</I> you heard your parents coming down the hall to confirm that your room had, in fact, been cleaned. Then you'd sprint to your room and get to work as fast as you possibly could. If you were lucky, your parents would never check, and you'd avoid all the work cleaning your room normally <NOBR>entails.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P><A NAME="41015"></A>
<P><A NAME="dingp5"></A>
It turns out that the same delay tactics that work for a five year old work for a C++ programmer. In Computer Science, however, we dignify such procrastination with the name <I>lazy evaluation</I>. When you employ lazy evaluation, you write your classes in such a way that they defer computations until the <I>results</I> of those computations are required. If the results are never required, the computations are never performed, and neither your software's clients nor your parents are any the <NOBR>wiser.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P><A NAME="41016"></A>
<P><A NAME="dingp6"></A>
Perhaps you're wondering exactly what I'm talking about. Perhaps an example would help. Well, lazy evaluation is applicable in an enormous variety of application areas, so I'll describe <NOBR>four.<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp7"></A><font ID="mhtitle">Reference Counting</font><SCRIPT>create_link(7);</SCRIPT>
</P>

<P><A NAME="dingp8"></A><A NAME="41017"></A>
Consider this <NOBR>code:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="41018"></A>
<UL><PRE>
class String { ... };                        // a string class (the standard
                                             // string type may be implemented
                                             // as described below, but it
                                             // doesn't have to be)
<A NAME="41019"></A>
<A NAME="p86"></A>String s1 = "Hello";
<A NAME="41020"></A>
String s2 = s1;                              // call String copy ctor
</PRE>
</UL>
<A NAME="41021"></A>
<P><A NAME="dingp9"></A>
A common implementation for the <CODE>String</CODE> copy constructor would result in <CODE>s1</CODE> and <CODE>s2</CODE> each having its own copy of <CODE>"Hello"</CODE> after <CODE>s2</CODE> is initialized with <CODE>s1</CODE>. Such a copy constructor would incur a relatively large expense, because it would have to make a copy of <CODE>s1</CODE>'s value to give to <CODE>s2</CODE>, and that would typically entail allocating heap memory via the <CODE>new</CODE> operator (see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>) and calling <CODE>strcpy</CODE> to copy the data in <CODE>s1</CODE> into the memory allocated by <CODE>s2</CODE>. This is <i>eager evaluation:</i> making a copy of <CODE>s1</CODE> and putting it into <CODE>s2</CODE> just because the <CODE>String</CODE> copy constructor was <i>called</i>. At this point, however, there has been no real <i>need</i> for <CODE>s2</CODE> to have a copy of the value, because <CODE>s2</CODE> hasn't been used <NOBR>yet.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P><A NAME="41025"></A>

<P><A NAME="dingp10"></A>
The lazy approach is a lot less work. Instead of giving <CODE>s2</CODE> a copy of <CODE>s1</CODE>'s value, we have <CODE>s2</CODE><i> share </i><CODE>s1</CODE>'s value. All we have to do is a little bookkeeping so we know who's sharing what, and in return we save the cost of a call to <CODE>new</CODE> and the expense of copying anything. The fact that <CODE>s1</CODE> and <CODE>s2</CODE> are sharing a data structure is transparent to clients, and it certainly makes no difference in statements like the following, because they only read values, they don't write <NOBR>them:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="41026"></A>
<UL><PRE>
cout &lt;&lt; s1;                              // read s1's value
<A NAME="41027"></A>
cout &lt;&lt; s1 + s2;                         // read s1's and s2's values
</PRE>
</UL>
<A NAME="41028"></A>
<P><A NAME="dingp11"></A>
In fact, the only time the sharing of values makes a difference is when one or the other string is <I>modified</I>; then it's important that only one string be changed, not both. In this <NOBR>statement,<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>

<A NAME="41029"></A>
<UL><PRE>s2.convertToUpperCase();
</PRE>
</UL>
<A NAME="41030"></A>
<P><A NAME="dingp12"></A>it's crucial that only <CODE>s2</CODE>'s value be changed, not <CODE>s1</CODE>'s <NOBR>also.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="41031"></A>

<P><A NAME="dingp13"></A>To handle statements like this, we have to implement <CODE>String</CODE>'s <CODE>convertToUpperCase</CODE> function so that it makes a copy of <CODE>s2</CODE>'s value and makes that value private to <CODE>s2</CODE> before modifying it. Inside <CODE>convertToUpperCase</CODE>, we can be lazy no longer: we have to make a copy of <CODE>s2</CODE>'s (shared) value for <CODE>s2</CODE>'s private use. On the other hand, if <CODE>s2</CODE> is never modified, we never have to make a private copy of its value. It can continue to share a value as long as it exists. If we're lucky, <CODE>s2</CODE> will never be modified, in which case we'll never have to expend the effort to give it its own <NOBR>value.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P><A NAME="41032"></A>

<P><A NAME="dingp14"></A>
The details on making this kind of value sharing work (including all the code) are provided in <A HREF="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, but the idea is lazy evaluation: don't bother to make a copy of something until you really need one. Instead, be lazy &#151; use someone else's copy as long as you can get away with it. In some application areas, you can often get away with it <NOBR>forever.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="p87"></A>
<P><A NAME="dingp15"></A><font ID="mhtitle">Distinguishing Reads from Writes</font><SCRIPT>create_link(15);</SCRIPT>
</P>

<P><A NAME="dingp16"></A><A NAME="63347"></A>
Pursuing the example of reference-counting strings a bit further, we come upon a second way in which lazy evaluation can help us. Consider this <NOBR>code:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>

<A NAME="63354"></A>
<UL><PRE>
String s = "Homer's Iliad";                  // Assume s is a
                                             // reference-counted string
...
<A NAME="63355"></A>
cout &lt;&lt; s[3];                         // call operator[] to read s[3]
s[3] = 'x';                           // call operator[] to write s[3]
</PRE>
</UL>

<P><A NAME="dingp17"></A><A NAME="63326"></A>
The first call to <CODE>operator[]</CODE> is to read part of a string, but the second call is to perform a write. We'd like to be able to distinguish the read call from the write, because reading a reference-counted string is cheap, but writing to such a string may require splitting off a new copy of the string's value prior to the <NOBR>write.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp18"></A><A NAME="63327"></A>
This puts us in a difficult implementation position. To achieve what we want, we need to do different things inside <CODE>operator[]</CODE> (depending on whether it's being called to perform a read or a write). How can we determine whether <CODE>operator[]</CODE> has been called in a read or a write context? The brutal truth is that we can't. By using lazy evaluation and proxy classes as described in <A HREF="./MI30_FR.HTM#6074" TARGET="_top">Item 30</A>, however, we can defer the decision on whether to take read actions or write actions until we can determine which is <NOBR>correct.<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp19"></A><font ID="mhtitle">Lazy Fetching</font><SCRIPT>create_link(19);</SCRIPT>
</P>

<P><A NAME="dingp20"></A><A NAME="41036"></A>
As a third example of lazy evaluation, imagine you've got a program that uses large objects containing many constituent fields. Such objects must persist across program runs, so they're stored in a database. Each object has a unique object identifier that can be used to retrieve the object from the <NOBR>database:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>

<A NAME="41037"></A>
<UL><PRE>
class LargeObject {                        // large persistent objects
public:
  LargeObject(ObjectID id);                // restore object from disk
<A NAME="41039"></A>
  const string&amp; field1() const;            // value of field 1
  int field2() const;                      // value of field 2
  double field3() const;                   // ...
  const string&amp; field4() const;
  const string&amp; field5() const;
  ...
<A NAME="57552"></A>
};
</PRE>
</UL>
<A NAME="41042"></A>
<P><A NAME="dingp21"></A>Now consider the cost of restoring a <CODE>LargeObject</CODE> from <NOBR>disk:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="41043"></A>
<UL><PRE><A NAME="p88"></A>void restoreAndProcessObject(ObjectID id)
{
  LargeObject object(id);                  // restore object
<A NAME="41044"></A>
  ...
<A NAME="41045"></A>
}
</PRE>
</UL>
<A NAME="41046"></A>

<P><A NAME="dingp22"></A>Because <CODE>LargeObject</CODE> instances are big, getting all the data for such an object might be a costly database operation, especially if the data must be retrieved from a remote database and pushed across a network. In some cases, the cost of reading all that data would be unnecessary. For example, consider this kind of <NOBR>application:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>

<A NAME="41047"></A>
<UL><PRE>void restoreAndProcessObject(ObjectID id)
{
  LargeObject object(id);
<A NAME="41048"></A>
  if (object.field2() == 0) {
    cout &lt;&lt; "Object " &lt;&lt; id &lt;&lt; ": null field2.\n";
  }
}
</PRE>
</UL>

<P><A NAME="dingp23"></A><A NAME="41049"></A>Here only the value of <CODE>field2</CODE> is required, so any effort spent setting up the other fields is <NOBR>wasted.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp24"></A><A NAME="41050"></A>
The lazy approach to this problem is to read no data from disk when a <CODE>LargeObject</CODE> object is created. Instead, only the "shell" of an object is created, and data is retrieved from the database only when that particular data is needed inside the object. Here's one way to implement this kind of "demand-paged" object <NOBR>initialization:<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>

<A NAME="41051"></A>
<UL><PRE>class LargeObject {
public:
  LargeObject(ObjectID id);
<A NAME="41054"></A>
  const string&amp; field1() const;
  int field2() const;
  double field3() const;
  const string&amp; field4() const;
  ...
<A NAME="57563"></A>

⌨️ 快捷键说明

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