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

📄 ec7.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<P><A NAME="dingp13"></A>
The copy constructor generated by your compilers must initialize <CODE>no2.nameValue</CODE> and <CODE>no2.objectValue</CODE> using <CODE>no1.nameValue</CODE> and <CODE>no1.objectValue</CODE>, respectively. The type of <CODE>nameValue</CODE> is <CODE>string</CODE>, and <CODE>string</CODE> has a copy constructor (which you can verify by examining <CODE>string</CODE> in the standard library &#151; see <A HREF="#223029">Item 49</A>), so <CODE>no2.nameValue</CODE> will be initialized by calling the <CODE>string</CODE> copy constructor with <CODE>no1.nameValue</CODE> as its argument. On the other hand, the type of <CODE>NamedObject&lt;int&gt;::objectValue</CODE> is <CODE>int</CODE> (because <CODE>T</CODE> is <CODE>int</CODE> for this template instantiation), and no copy constructor is defined for <CODE>int</CODE>s, so <CODE>no2.objectValue</CODE> will be initialized by copying the bits over from <CODE>no1.objectValue</CODE>.<SCRIPT>create_link(13);</SCRIPT>

</P>
<A NAME="24879"></A>
<P><A NAME="dingp14"></A>
The compiler-generated assignment operator for <CODE>NamedObject&lt;int&gt;</CODE> would behave the same way, but in general, compiler-generated assignment operators behave as I've described only when the resulting code is both legal and has a reasonable chance of making sense. If either of these tests fails, compilers will refuse to generate an <CODE>operator=</CODE> for your class, and you'll receive some lovely diagnostic during <NOBR>compilation.<SCRIPT>create_link(14);</SCRIPT>

</NOBR></P>
<A NAME="23730"></A>
<P><A NAME="dingp15"></A>
For example, suppose <CODE>NamedObject</CODE> were defined like this, where <CODE>nameValue</CODE> is a <I>reference</I> to a string and <CODE>objectValue</CODE> is a <CODE><I>const</I></CODE> <CODE>T</CODE>:<SCRIPT>create_link(15);</SCRIPT>

</P>
<A NAME="222842"></A>
<UL><PRE><A NAME="p215"></A>template&lt;class T&gt;
class NamedObject {
public:
  // this ctor no longer takes a const name, because name-
  // Value is now a reference-to-non-const string. The char*
  // ctor is gone, because we must have a string to refer to
  NamedObject(string&amp; name, const T&amp; value);
</PRE>
</UL><A NAME="222844"></A>
<UL><PRE>
  ...                          // as above, assume no
                               // operator= is declared
private:
  string&amp; nameValue;           // this is now a reference
  const T objectValue;         // this is now const
};
</PRE>
</UL></P><A NAME="222845"></A>
<P><A NAME="dingp16"></A>
Now consider what should happen <NOBR>here:<SCRIPT>create_link(16);</SCRIPT>

</NOBR></P>
<A NAME="222846"></A>
<UL><PRE>string newDog("Persephone");
string oldDog("Satch");
</PRE>
</UL><A NAME="222848"></A>
<UL><PRE>
NamedObject&lt;int&gt; p(newDog, 2);      // as I write this, our dog
                                    // <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=pers" onMouseOver = "self.status = 'Persephone`s Link'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top"></NOBR>Persephone</A> is about to
                                    // have her second birthday
</PRE>
</UL><A NAME="222849"></A>
<UL><PRE>
NamedObject&lt;int&gt; s(oldDog, 29);     // the family dog Satch
                                    // (from my childhood)
                                    // would be 29 if she were
                                    // still alive
</PRE>
</UL><A NAME="222850"></A>
<UL><PRE>
p = s;                              // what should happen to
                                    // the data members in p?
</PRE>
</UL><A NAME="23521"></A>
<P><A NAME="dingp17"></A>
Before the assignment, <CODE>p.nameValue</CODE> refers to some <CODE>string</CODE> object and <CODE>s.nameValue</CODE> also refers to a <CODE>string</CODE>, though not the same one. How should the assignment affect <CODE>p.nameValue</CODE>? After the assignment, should <CODE>p.nameValue</CODE> refer to the <CODE>string</CODE> referred to by <CODE>s.nameValue</CODE>, i.e., should the reference itself be modified? If so, that breaks new ground, because C++ doesn't provide a way to make a reference refer to a different object (see <A HREF="../MEC/MC1_FR.HTM#11029" TARGET="_top">Item M1</A>). Alternatively, should the <CODE>string</CODE> object to which <CODE>p.nameValue</CODE> refers be modified, thus affecting other objects that hold pointers or references to that <CODE>string</CODE>, i.e., objects not directly involved in the assignment? Is that what the compiler-generated assignment operator should <NOBR>do?<SCRIPT>create_link(17);</SCRIPT>

</NOBR></P>
<A NAME="23503"></A>
<P><A NAME="dingp18"></A>
Faced with such a conundrum, C++ refuses to compile the code. If you want to support assignment in a class containing a reference member, you must define the assignment operator yourself. Compilers behave similarly for classes containing <CODE>const</CODE> members (such as <CODE>objectValue</CODE> in the modified class above); it's not legal to modify <CODE>const</CODE> members, so compilers are unsure how to treat them during an implicitly generated assignment function. Finally, compilers refuse to generate assignment operators for derived classes that inherit from base classes declaring the standard assignment operator <CODE>private</CODE>. After all, compiler-gener<A NAME="p216"></A>ated assignment operators for derived classes are supposed to handle base class parts, too (see Items <A HREF="./EC3_FR.HTM#2225" TARGET="_top">16</A> and <A HREF="../MEC/MC6_FR.HTM#10947" TARGET="_top">M33</A>), but in doing so, they certainly shouldn't invoke member functions the derived class has no right to <NOBR>call.<SCRIPT>create_link(18);</SCRIPT>

</NOBR></P>
<A NAME="24870"></A>
<P><A NAME="dingp19"></A>
All this talk of compiler-generated functions gives rise to the question, what do you do if you want to disallow use of those functions? That is, what if you deliberately don't declare, for example, an <CODE>operator=</CODE> because you never <I>ever</I> want to allow assignment of objects in your class? The solution to that little teaser is the subject of <A HREF="./EC4_FR.HTM#6406" TARGET="_top">Item 27</A>. For a discussion of the often-overlooked interactions between pointer members and compiler-generated copy constructors and assignment operators, check out <A HREF="./EC3_FR.HTM#2042" TARGET="_top">Item 11</A>.<SCRIPT>create_link(19);</SCRIPT>

</P>
<!-- SectionName="E46: Detect errors sooner rather than later" -->
<A NAME="195225"></A><A NAME="195226"></A>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#8160">Item 45: Know what functions C++ silently writes and calls.</A>
                            <BR>Continue to <A HREF="#8299">Item 47: Ensure that non-local static objects are initialized before they're used.</A></FONT></DIV>

<P><A NAME="dingp20"></A><FONT ID="eititle">Item 46: &nbsp;Prefer compile-time and link-time errors to runtime errors.</FONT><SCRIPT>create_link(20);</SCRIPT>

</P>

<P><A NAME="dingp21"></A>
Other than in the few situations that cause C++ to throw exceptions (e.g., running out of memory &#151; see <A HREF="./EC2_FR.HTM#1894" TARGET="_top">Item 7</A>), the notion of a runtime error is as foreign to C++ as it is to C. There's no detection of underflow, overflow, division by zero, no checking for array bounds violations, etc. Once your program gets past a compiler and linker, you're on your own &#151; there's no safety net of any consequence. Much as with skydiving, some people are exhilarated by this state of affairs, others are paralyzed with fear. The motivation behind the philosophy, of course, is efficiency: without runtime checks, programs are smaller and <NOBR>faster.<SCRIPT>create_link(21);</SCRIPT>

</NOBR></P>
<A NAME="8230"></A>
<P><A NAME="dingp22"></A>
There is a different way to approach things. Languages like Smalltalk and LISP generally detect fewer kinds of errors during compilation and linking, but they provide hefty runtime systems that catch errors during execution. Unlike C++, these languages are almost always interpreted, and you pay a performance penalty for the extra flexibility they <NOBR>offer.<SCRIPT>create_link(22);</SCRIPT>

</NOBR></P>
<A NAME="8232"></A>
<P><A NAME="dingp23"></A>
Never forget that you are programming in C++. Even if you find the Smalltalk/LISP philosophy appealing, put it out of your mind. There's a lot to be said for adhering to the party line, and in this case, that means eschewing runtime errors. Whenever you can, push the detection of an error back from runtime to link-time, or, ideally, to <NOBR>compile-time.<SCRIPT>create_link(23);</SCRIPT>

</NOBR></P>
<A NAME="8233"></A>
<P><A NAME="dingp24"></A>
Such a methodology pays dividends not only in terms of program size and speed, but also in terms of reliability. If your program gets through compilers and a linker without eliciting error messages, you may be confident there aren't any compiler- or linker-detectable errors in your <A NAME="p217"></A>program, period. (The other possibility, of course, is that there are bugs in your compilers or linkers, but let us not depress ourselves by admitting to such <NOBR>possibilities.)<SCRIPT>create_link(24);</SCRIPT>

</NOBR></P>
<A NAME="8235"></A>
<P><A NAME="dingp25"></A>
With runtime errors, the situation is very different. Just because your program doesn't generate any runtime errors during a particular run, how can you be sure it won't generate errors during a different run, when you do things in a different order, use different data, or run for a longer or shorter period of time? You can test your program until you're blue in the face, but you'll still never cover all the possibilities. As a result, detecting errors at runtime is simply less secure than is catching them during compilation or <NOBR>linking.<SCRIPT>create_link(25);</SCRIPT>

</NOBR></P>
<A NAME="8236"></A>
<P><A NAME="dingp26"></A>
Often, by making relatively minor changes to your design, you can catch during compilation what might otherwise be a runtime error. This frequently involves the addition of new types to the program. (See also <A HREF="../MEC/MC6_FR.HTM#10947" TARGET="_top">Item M33</A>.) For example, suppose you are writing a class to represent dates in time. Your first cut might look like <NOBR>this:<SCRIPT>create_link(26);</SCRIPT>

</NOBR></P>
<A NAME="8238"></A>
<UL><PRE>class Date {
public:
  Date(int day, int month, int year);
</PRE>
</UL><A NAME="8239"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="8240"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="8241"></A>
<P><A NAME="dingp27"></A>
If you were to implement this constructor, one of the problems you'd face would be that of sanity checking on the values for the day and the month. Let's see how you can eliminate the need to validate the value passed in for the <NOBR>month.<SCRIPT>create_link(27);</SCRIPT>

</NOBR></P>
<A NAME="8243"></A>
<P><A NAME="dingp28"></A>
One obvious approach is to employ an enumerated type instead of an <NOBR>integer:<SCRIPT>create_link(28);</SCRIPT>

</NOBR></P>
<A NAME="223050"></A>
<UL><PRE>enum Month { Jan = 1, Feb = 2, ... , Nov = 11, Dec = 12 };
</PRE>
</UL><A NAME="8246"></A>
<UL><PRE>class Date {
public:
  Date(int day, Month month, int year);
</PRE>
</UL><A NAME="8247"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="8248"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="8249"></A>
<P><A NAME="dingp29"></A>
Unfortunately, this doesn't buy you that much, because enums don't have to be <NOBR>initialized:<SCRIPT>create_link(29);</SCRIPT>

</NOBR></P>
<A NAME="8250"></A>
<UL><PRE>Month m;
Date d(22, m, 1857);      // m is undefined
</PRE>
</UL><A NAME="8251"></A>
<P><A NAME="dingp30"></A>
<A NAME="p218"></A>As a result, the <CODE>Date</CODE> constructor would still have to validate the value of the <CODE>month</CODE> <NOBR>parameter.<SCRIPT>create_link(30);</SCRIPT>

</NOBR></P>
<A NAME="8252"></A>
<P><A NAME="dingp31"></A>
To achieve enough security to dispense with runtime checks, you've got to use a class to represent months, and you must ensure that only valid months are <NOBR>created:<SCRIPT>create_link(31);</SCRIPT>

</NOBR></P>
<A NAME="223053"></A>

⌨️ 快捷键说明

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