📄 ec7.htm
字号:
</NOBR></P>
<A NAME="24419"></A>
<P><A NAME="dingp53"></A>
In the field of Chaos Theory, there is a principle known as the "Butterfly Effect." This principle asserts that the tiny atmospheric disturbance caused by the beating of a butterfly's wings in one part of the world can lead to profound changes in weather patterns in places far distant. Somewhat more rigorously, it asserts that for some types of systems, minute perturbations in inputs can lead to radical changes in <NOBR>outputs.<SCRIPT>create_link(53);</SCRIPT>
</NOBR></P>
<A NAME="24095"></A>
<P><A NAME="dingp54"></A>
The development of software systems can exhibit a Butterfly Effect of its own. Some systems are highly sensitive to the particulars of their requirements, and small changes in requirements can significantly af<A NAME="p222"></A>fect the ease with which a system can be implemented. For example, <A HREF="./EC5_FR.HTM#6490" TARGET="_top">Item 29</A> describes how changing the specification for an implicit conversion from <CODE>String</CODE>-to-<CODE>char*</CODE> to <CODE>String</CODE>-to-<CODE><I>const</I></CODE>-<CODE>char*</CODE> makes it possible to replace a slow or error-prone function with a fast, safe <NOBR>one.<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<A NAME="24585"></A>
<P><A NAME="dingp55"></A>
The problem of ensuring that non-local static objects are initialized before use is similarly sensitive to the details of what you want to achieve. If, instead of demanding access to non-local static objects, you're willing to settle for access to objects that <I>act</I> like non-local static objects (except for the initialization headaches), the hard problem vanishes. In its stead is left a problem so easy to solve, it's hardly worth calling a problem any <NOBR>longer.<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>
<A NAME="24581"></A>
<P><A NAME="dingp56"></A>
The technique — sometimes known as the <I>Singleton pattern</I> — is simplicity itself. First, you move each non-local static object into its own function, where you declare it <CODE>static</CODE>. Next, you have the function return a reference to the object it contains. Clients call the function instead of referring to the object. In other words, you replace non-local static objects with objects that are <CODE>static</CODE> inside functions. (See also <A HREF="../MEC/MC5_FR.HTM#5350" TARGET="_top">Item M26</A>.)<SCRIPT>create_link(56);</SCRIPT>
</P>
<A NAME="30738"></A>
<P><A NAME="dingp57"></A>
The basis of this approach is the observation that although C++ says next to nothing about when a non-local static object is initialized, it specifies quite precisely when a static object inside a function (i.e. a <I>local</I> static object) is initialized: it's when the object's definition is first encountered during a call to that function. So if you replace direct accesses to non-local static objects with calls to functions that return references to local static objects inside them, you're guaranteed that the references you get back from the functions will refer to initialized objects. As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object, something that can't be said for true non-local static <NOBR>objects.<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>
<A NAME="30648"></A>
<P><A NAME="dingp58"></A>
Here's the technique applied to both <CODE>theFileSystem</CODE> and <CODE>tempDir</CODE>:<SCRIPT>create_link(58);</SCRIPT>
</P><A NAME="30650"></A>
<UL><PRE>
class FileSystem { ... }; // same as before
</PRE>
</UL><A NAME="30654"></A>
<UL><PRE>
FileSystem& theFileSystem() // this function replaces
{ // the theFileSystem object
</PRE>
</UL><A NAME="30741"></A>
<UL><PRE>
static FileSystem tfs; // define and initialize
// a local static object
// (tfs = "the file system")
</PRE>
</UL><A NAME="30655"></A>
<UL><PRE>
return tfs; // return a reference to it
}
</PRE>
</UL><A NAME="30659"></A>
<UL><PRE>
class Directory { ... }; // same as before
</PRE>
</UL><A NAME="30752"></A>
<UL><PRE><A NAME="p223"></A>Directory::Directory()
{
<I>same as before, except references to</I> theFileSystem <I>are
replaced by references to</I> theFileSystem<I>();</I>
}
</PRE>
</UL><A NAME="30745"></A>
<UL><PRE>
Directory& tempDir() // this function replaces
{ // the tempDir object
</PRE>
</UL><A NAME="30755"></A>
<UL><PRE>
static Directory td; // define/initialize local
// static object
</PRE>
</UL><A NAME="30794"></A>
<UL><PRE>
return td; // return reference to it
}
</PRE>
</UL><A NAME="30682"></A>
<P><A NAME="dingp59"></A>
Clients of this modified system program exactly as they used to, except they now refer to <CODE>theFileSystem()</CODE> and <CODE>tempDir()</CODE> instead of <CODE>theFileSystem</CODE> and <CODE>tempDir</CODE>. That is, they refer only to functions returning references to those objects, never to the objects <NOBR>themselves.<SCRIPT>create_link(59);</SCRIPT>
</NOBR></P>
<A NAME="24703"></A>
<P><A NAME="dingp60"></A>
The reference-returning functions dictated by this scheme are always simple: define and initialize a local static object on line 1, return it on line 2. That's it. Because they're so simple, you may be tempted to declare them <CODE>inline</CODE>. <A HREF="./EC5_FR.HTM#6729" TARGET="_top">Item 33</A> explains that late-breaking revisions to the C++ language specification make this a perfectly valid implementation strategy, but it also explains why you'll want to confirm your compilers' conformance with this aspect of <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</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">the</NOBR> standard</A> before putting it to use. If you try it with a compiler not yet in accord with the relevant parts of the standard, you risk getting multiple copies of both the access function and the static object defined within it. That's enough to make a grown programmer <NOBR>cry.<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P>
<A NAME="8375"></A>
<P><A NAME="dingp61"></A>
Now, there's no magic going on here. For this technique to be effective, it must be possible to come up with a reasonable initialization order for your objects. If you set things up such that object A must be initialized before object B, and you also make A's initialization dependent on B's having already been initialized, you are going to get in trouble, and frankly, you deserve it. If you steer shy of such pathological situations, however, the scheme described in this Item should serve you quite <NOBR>nicely.<SCRIPT>create_link(61);</SCRIPT>
</NOBR></P>
<!-- SectionName="E48: Pay attention to compiler warnings" -->
<A NAME="8378"></A><A NAME="8379"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#8299">Item 47: Ensure that non-local static objects are initialized before they're used.</A>
<BR>Continue to <A HREF="#8392">Item 49: Familiarize yourself with the standard library.</A></FONT></DIV>
<P><A NAME="dingp62"></A><FONT ID="eititle">Item 48: Pay attention to compiler warnings.</FONT><SCRIPT>create_link(62);</SCRIPT>
</P>
<P><A NAME="dingp63"></A>
Many programmers routinely ignore compiler warnings. After all, if the problem were serious, it'd be an error, right? This kind of thinking may be relatively harmless in other languages, but in C++, it's a good bet compiler writers have a better grasp of what's going on than you do. For example, here's an error everybody makes at one time or <NOBR>another:<SCRIPT>create_link(63);</SCRIPT>
</NOBR></P>
<A NAME="8380"></A>
<UL><PRE><A NAME="p224"></A>class B {
public:
virtual void f() const;
};
class D: public B {
public:
virtual void f();
};
</PRE>
</UL></P><A NAME="8381"></A>
<P><A NAME="dingp64"></A>
The idea is for <CODE>D::f</CODE> to redefine the virtual function <CODE>B::f</CODE>, but there's a mistake: in <CODE>B</CODE>, <CODE>f</CODE> is a <CODE>const</CODE> member function, but in <CODE>D</CODE> it's not declared <CODE>const</CODE>. One compiler I know says this about <NOBR>that:<SCRIPT>create_link(64);</SCRIPT>
</NOBR></P>
<A NAME="8382"></A>
<UL><PRE>warning: D::f() hides virtual B::f()
</PRE>
</UL></P>
<A NAME="8383"></A>
<P><A NAME="dingp65"></A>
Too many inexperienced programmers respond to this message by saying to themselves, "Of <I>course</I> <CODE>D::f</CODE> hides <CODE>B::f</CODE> — that's what it's <I>supposed</I> to do!" Wrong. What this compiler is trying to tell you is that the <CODE>f</CODE> declared in <CODE>B</CODE> has not been redeclared in <CODE>D</CODE>, it's been hidden entirely (see <A HREF="#8569">Item 50</A> for a description of why this is so). Ignoring this compiler warning will almost certainly lead to erroneous program behavior, followed by a lot of debugging to find out about something that this compiler detected in the first <NOBR>place.<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P><A NAME="8387"></A>
<P><A NAME="dingp66"></A>
After you gain experience with the warning messages from a particular compiler, of course, you'll learn to understand what the different messages mean (which is often very different from what they <I>seem</I> to mean, alas). Once you have that experience, there may be a whole range of warnings you'll choose to ignore. That's fine, but it's important to make sure that before you dismiss a warning, you understand exactly what it's trying to tell <NOBR>you.<SCRIPT>create_link(66);</SCRIPT>
</NOBR></P><A NAME="8388"></A>
<P><A NAME="dingp67"></A>
As long as we're on the topic of warnings, recall that warnings are inherently implementation-dependent, so it's not a good idea to get sloppy in your programming, relying on compilers to spot your mistakes for you. The function-hiding code above, for instance, goes through a different (but widely used) compiler with nary a squawk. Compilers are supposed to translate C++ into an executable format, not act as your personal safety net. You want that kind of safety? Program in <NOBR>Ada.<SCRIPT>create_link(67);</SCRIPT>
</NOBR></P>
<!-- SectionName="E49: Be familiar with the standard library" -->
<A NAME="8392"></A><A NAME="26771"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#8378">Item 48: Pay attention to compiler warnings.</A>
<BR>Continue to <A HREF="#8569">Item 50: Improve your understanding of C++.</A></FONT></DIV>
<P><A NAME="dingp68"></A><FONT ID="eititle">Item 49: Familiarize yourself with the standard library.</FONT><SCRIPT>create_link(68);</SCRIPT>
</P>
<P><A NAME="dingp69"></A>
C++'s standard library is big. Very big. Incredibly big. How big? Let me put it this way: the specification takes over 300 closely-packed pages in the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</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>, and that all but excludes the standard C library, <A NAME="p225"></A>which is included in the C++ library "by reference." (That's the term they use, <NOBR>honest.)<SCRIPT>create_link(69);</SCRIPT>
</NOBR></P>
<A NAME="26772"></A>
<P><A NAME="dingp70"></A>
Bigger isn't always better, of course, but in this case, bigger <I>is</I> better, because a big library contains lots of functionality. The more functionality in the standard library, the more functionality you can lean on as you develop your applications. The C++ library doesn't offer <I>everything</I> (support for concurrency and for graphical user interfaces is notably absent), but it does offer a lot. You can lean almost anything against <NOBR>it.<SCRIPT>create_link(70);</SCRIPT>
</NOBR></P>
<A NAME="26831"></A>
<P><A NAME="dingp71"></A>
Before summarizing what's in the library, I need to tell you a bit about how it's organized. Because the library has so much in it, there's a reasonable chance you (or someone like you) may choose a class or function name that's the same as a name in the standard library. To shield you from the name conflicts that would result, virtually everything in the standard library is nestled in the namespace <CODE>std</CODE> (see <A HREF="./EC4_FR.HTM#6429" TARGET="_top">Item 28</A>). But that leads to a new problem. Gazillions of lines of existing C++ rely on functionality in the pseudo-standard library that's been in use for years, e.g., functionality declared in the headers <CODE><iostream.h></CODE>, <CODE><complex.h></CODE>, <CODE><limits.h></CODE>, etc. That existing software isn't designed to use namespaces, and it would be a shame if wrapping the standard library by <CODE>std</CODE> caused the existing code to break. (Authors of the broken code would likely use somewhat harsher language than "shame" to describe their feelings about having the library rug pulled out from underneath <NOBR>them.)<SCRIPT>create_link(71);</SCRIPT>
</NOBR></P>
<A NAME="26873"></A>
<P><A NAME="dingp72"></A>
Mindful of the destructive power of rioting bands of incensed programmers, the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver = "self.status = 'Link to the C++ Standardization Committee'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">standardization</NOBR> committee</A> decided to create new header names for the <CODE>std</CODE>-wrapped components. The algorithm they chose for generating the new header names is as trivial as the results it produces are jarring: the <CODE>.h</CODE> on the existing C++ headers was simply dropped. So <CODE><iostream.h></CODE> became <CODE><iostream></CODE>, <CODE><complex.h></CODE> became <CODE><complex></CODE>, etc. For C headers, the same algorithm was applied, but a <CODE>c</CODE> was prepended to each result. Hence C's <CODE><string.h></CODE> became <CODE><cstring></CODE>, <CODE><stdio.h></CODE> became <CODE><cstdio></CODE>, etc. For a final twist, the old C++ headers were officially <I>deprecated</I> (i.e., listed as no longer supported), but the old C headers were not (to maintain C compatibility). In practice, compiler vendors have no incentive to disavow their customers' legacy software, so you can expect the old C++ headers to be supported for many <NOBR>years.<SCRIPT>create_link(72);</SCRIPT>
</NOBR></P>
<A NAME="28169"></A>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -