📄 mc4.htm
字号:
<P><A NAME="dingp31"></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(31);</SCRIPT>
</NOBR></P>
<A NAME="41026"></A>
<UL><PRE>
cout << s1; // read s1's value
<A NAME="41027"></A>
cout << s1 + s2; // read s1's and s2's values
</PRE>
</UL>
<A NAME="41028"></A>
<P><A NAME="dingp32"></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(32);</SCRIPT>
</NOBR></P>
<A NAME="41029"></A>
<UL><PRE>s2.convertToUpperCase();
</PRE>
</UL>
<A NAME="41030"></A><P><A NAME="dingp33"></A>
it's crucial that only <CODE>s2</CODE>'s value be changed, not <CODE>s1</CODE>'s <NOBR>also.<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P><A NAME="41031"></A>
<P><A NAME="dingp34"></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(34);</SCRIPT>
</NOBR></P><A NAME="41032"></A>
<P><A NAME="dingp35"></A>
The details on making this kind of value sharing work (including all the code) are provided in <A HREF="./MC5_FR.HTM#6073" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 29'; return true" onMouseOut = "self.status = self.defaultStatus">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 — 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(35);</SCRIPT>
</NOBR></P>
<A NAME="p87"></A>
<P><A NAME="dingp36"></A><font ID="mhtitle">Distinguishing Reads from Writes</font><SCRIPT>create_link(36);</SCRIPT>
</P>
<A NAME="63347"></A>
<P><A NAME="dingp37"></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(37);</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 << s[3]; // call operator[] to read s[3]
s[3] = 'x'; // call operator[] to write s[3]
</PRE>
</UL>
<A NAME="63326"></A>
<P><A NAME="dingp38"></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(38);</SCRIPT>
</NOBR></P><A NAME="63327"></A>
<P><A NAME="dingp39"></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="./MC5_FR.HTM#6074" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 30'; return true" onMouseOut = "self.status = self.defaultStatus">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(39);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp40"></A><font ID="mhtitle">Lazy Fetching</font><SCRIPT>create_link(40);</SCRIPT>
</P>
<A NAME="41036"></A>
<P><A NAME="dingp41"></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(41);</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& field1() const; // value of field 1
int field2() const; // value of field 2
double field3() const; // ...
const string& field4() const;
const string& field5() const;
...
<A NAME="57552"></A>
};
</PRE>
</UL>
<A NAME="41042"></A>
<P><A NAME="dingp42"></A>
Now consider the cost of restoring a <CODE>LargeObject</CODE> from <NOBR>disk:<SCRIPT>create_link(42);</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="dingp43"></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(43);</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 << "Object " << id << ": null field2.\n";
}
}
</PRE>
</UL>
<A NAME="41049"></A>
<P><A NAME="dingp44"></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(44);</SCRIPT>
</NOBR></P><A NAME="41050"></A>
<P><A NAME="dingp45"></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(45);</SCRIPT>
</NOBR></P>
<A NAME="41051"></A>
<UL><PRE>class LargeObject {
public:
LargeObject(ObjectID id);
<A NAME="41054"></A>
const string& field1() const;
int field2() const;
double field3() const;
const string& field4() const;
...
<A NAME="57563"></A>
private:
ObjectID oid;
<A NAME="57564"></A>
mutable string *field1Value; // see below for a
mutable int *field2Value; // discussion of "mutable"
mutable double *field3Value;
mutable string *field4Value;
...
<A NAME="75179"></A>
};
<A NAME="41056"></A>
<A NAME="p89"></A>LargeObject::LargeObject(ObjectID id)
: oid(id), field1Value(0), field2Value(0), field3Value(0), ...
{}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -