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

📄 ch17.htm

📁 Why C++ is the emerging standard in software development. The steps to develop a C++ program. How
💻 HTM
📖 第 1 页 / 共 5 页
字号:
Enter a number (0 to quit): 4You entered: 4.  Square(4): 16. Cube(4): 64.Enter a number (0 to quit): 5You entered: 5.  Square(5): 25. Cube(5): 125.Enter a number (0 to quit): 6You entered: 6.  Square(6): 36. Cube(6): 216.Enter a number (0 to quit): 0</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On lines 3 and 4, two inlinefunctions are declared: <TT>Square()</TT> and <TT>Cube()</TT>. Each is declared tobe inline, so like a macro function these will be expanded in place for each call,and there will be no function call overhead.<BR>As a reminder, expanded inline means that the content of the function will be placedinto the code wherever the function call is made (for example, on line 16). Becausethe function call is never made, there is no overhead of putting the return addressand the parameters on the stack.</P><P>On line 16, the function <TT>Square</TT> is called, as is the function <TT>Cube</TT>.Again, because these are inline functions, it is exactly as if this line had beenwritten like this:</P><PRE><FONT COLOR="#0066FF">16:          cout &lt;&lt; &quot;.  Square(&quot; &lt;&lt; x &lt;&lt; &quot;): &quot;  &lt;&lt; x * x &lt;&lt; &quot;. 	Cube(&quot; &lt;&lt; x &lt;&lt; &#194;&quot;): &quot; &lt;&lt; x * x * x &lt;&lt;&quot;.&quot; &lt;&lt; endl;</FONT></PRE><P><PRE><FONT COLOR="#0066FF"></FONT></PRE><H3 ALIGN="CENTER"><A NAME="Heading24"></A><FONT COLOR="#000077">String Manipulation</FONT></H3><P>The preprocessor provides two special operators for manipulating strings in macros.The stringizing operator (<TT>#</TT>) substitutes a quoted string for whatever followsthe stringizing operator. The concatenation operator bonds two strings together intoone.<H4 ALIGN="CENTER"><A NAME="Heading25"></A><FONT COLOR="#000077">Stringizing</FONT></H4><P>The stringizing operator puts quotes around any characters following the operator,up to the next white space. Thus, if you write</P><PRE><FONT COLOR="#0066FF">#define WRITESTRING(x) cout &lt;&lt; #x</FONT></PRE><P>and then call</P><PRE><FONT COLOR="#0066FF">WRITESTRING(This is a string);</FONT></PRE><P>the precompiler will turn it into</P><PRE><FONT COLOR="#0066FF">cout &lt;&lt; &quot;This is a string&quot;;</FONT></PRE><P>Note that the string <TT>This is a string</TT> is put into quotes, as requiredby <TT>cout</TT>.<H4 ALIGN="CENTER"><A NAME="Heading26"></A><FONT COLOR="#000077">Concatenation</FONT></H4><P>The concatenation operator allows you to bond together more than one term intoa new word. The new word is actually a token that can be used as a class name, avariable name, an offset into an array, or anywhere else a series of letters mightappear.</P><P>Assume for a moment that you have five functions, named <TT>fOnePrint</TT>, <TT>fTwoPrint</TT>,<TT>fThreePrint</TT>, <TT>fFourPrint</TT>, and <TT>fFivePrint</TT>. You can thendeclare:</P><PRE><FONT COLOR="#0066FF">#define fPRINT(x) f ## x ## Print</FONT></PRE><P>and then use it with <TT>fPRINT(Two)</TT> to generate <TT>fTwoPrint</TT> and with<TT>fPRINT(Three)</TT> to generate <TT>fThreePrint.</TT></P><P>At the conclusion of Week 2, a <TT>PartsList</TT> class was developed. This listcould only handle objects of type <TT>List</TT>. Let's say that this list works well,and you'd like to be able to make lists of animals, cars, computers, and so forth.</P><P>One approach would be to create <TT>AnimalList</TT>, <TT>CarList</TT>, <TT>ComputerList</TT>,and so on, cutting and pasting the code in place. This will quickly become a nightmare,as every change to one list must be written to all the others.</P><P>An alternative is to use macros and the concatenation operator. For example, youcould define</P><PRE><FONT COLOR="#0066FF">#define Listof(Type)  class Type##List \{ \public: \Type##List(){} \private:          \int itsLength; \};</FONT></PRE><P>This example is overly sparse, but the idea would be to put in all the necessarymethods and data. When you were ready to create an <TT>AnimalList</TT>, you wouldwrite</P><PRE><FONT COLOR="#0066FF">Listof(Animal)</FONT></PRE><P>and this would be turned into the declaration of the <TT>AnimalList</TT> class.There are some problems with this approach, all of which are discussed in detailon Day 19, when templates are discussed.<H3 ALIGN="CENTER"><A NAME="Heading27"></A><FONT COLOR="#000077">Predefined Macros</FONT></H3><P>Many compilers predefine a number of useful macros, including <TT>__DATE__</TT>,<TT>__TIME__</TT>, <TT>__LINE__</TT>, and <TT>__FILE__</TT>. Each of these namesis surrounded by two underscore characters to reduce the likelihood that the nameswill conflict with names you've used in your program.</P><P>When the precompiler sees one of these macros, it makes the appropriate substitutes.For <TT>__DATE__</TT>, the current date is substituted. For <TT>__TIME__</TT>, thecurrent time is substituted. <TT>__LINE__</TT> and <TT>__FILE__</TT> are replacedwith the source code line number and filename, respectively. You should note thatthis substitution is made when the source is precompiled, not when the program isrun. If you ask the program to print <TT>__DATE__</TT>, you will not get the currentdate; instead, you will get the date the program was compiled. These defined macrosare very useful in debugging.<H3 ALIGN="CENTER"><A NAME="Heading28"></A><FONT COLOR="#000077">assert()</FONT></H3><P>Many compilers offer an <TT>assert()</TT> macro. The <TT>assert()</TT> macro returns<TT>TRUE</TT> if its parameter evaluates <TT>TRUE</TT> and takes some kind of actionif it evaluates <TT>FALSE</TT>. Many compilers will abort the program on an <TT>assert()</TT>that fails; others will throw an exception (see Day 20, &quot;Exceptions and ErrorHandling&quot;).</P><P>One powerful feature of the <TT>assert()</TT> macro is that the preprocessor collapsesit into no code at all if <TT>DEBUG</TT> is not defined. It is a great help duringdevelopment, and when the final product ships there is no performance penalty norincrease in the size of the executable version of the program.</P><P>Rather than depending on the compiler-provided <TT>assert()</TT>, you are freeto write your own <TT>assert()</TT> macro. Listing 17.5 provides a simple <TT>assert()</TT>macro and shows its use.</P><P><A NAME="Heading29"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 17.5. A simpleassert() macro.</B></FONT></P><PRE><FONT COLOR="#0066FF">1:     // Listing 17.5 ASSERTS2:     #define DEBUG3:     #include &lt;iostream.h&gt;4:5:     #ifndef DEBUG6:        #define ASSERT(x)7:     #else8:        #define ASSERT(x) \9:                 if (! (x)) \10:                { \11:                   cout &lt;&lt; &quot;ERROR!! Assert &quot; &lt;&lt; #x &lt;&lt; &quot; failed\n&quot;; \12:                   cout &lt;&lt; &quot; on line &quot; &lt;&lt; __LINE__  &lt;&lt; &quot;\n&quot;; \13:                   cout &lt;&lt; &quot; in file &quot; &lt;&lt; __FILE__ &lt;&lt; &quot;\n&quot;;  \14:                }15:    #endif16:17:18:    int main()19:    {20:       int x = 5;21:       cout &lt;&lt; &quot;First assert: \n&quot;;22:       ASSERT(x==5);23:       cout &lt;&lt; &quot;\nSecond assert: \n&quot;;24:       ASSERT(x != 5);25:       cout &lt;&lt; &quot;\nDone.\n&quot;;26:     return 0;<TT>27: }</TT></FONT><FONT COLOR="#0066FF">Output: First assert:Second assert:ERROR!! Assert x !=5 failed on line 24 in file test1704.cppDone.</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On line 2, the term <TT>DEBUG</TT>is defined. Typically, this would be done from the command line (or the IDE) at compiletime, so you can turn this on and off at will. On lines 8-14, the <TT>assert()</TT>macro is defined. Typically, this would be done in a header file, and that header(<TT>ASSERT.HPP</TT>) would be included in all your implementation files.<BR><BR>On line 5, the term <TT>DEBUG</TT> is tested. If it is not defined, <TT>assert()</TT>is defined to create no code at all. If <TT>DEBUG</TT> is defined, the functionalitydefined on lines 8-14 is applied.</P><P>The <TT>assert()</TT> itself is one long statement, split across seven sourcecode lines, as far as the precompiler is concerned. On line 9, the value passed inas a parameter is tested; if it evaluates <TT>FALSE</TT>, the statements on lines11-13 are invoked, printing an error message. If the value passed in evaluates <TT>TRUE</TT>,no action is taken.<H4 ALIGN="CENTER"><A NAME="Heading31"></A><FONT COLOR="#000077">Debugging with assert()</FONT></H4><P>When writing your program, you will often know deep down in your soul that somethingis true: a function has a certain value, a pointer is valid, and so forth. It isthe nature of bugs that what you know to be true might not be so under some conditions.For example, you know that a pointer is valid, yet the program crashes. <TT>assert()</TT>can help you find this type of bug, but only if you make it a regular practice touse <TT>assert()</TT> liberally in your code. Every time you assign or are passeda pointer as a parameter or function return value, be sure to assert that the pointeris valid. Any time your code depends on a particular value being in a variable, <TT>assert()</TT>that that is true.</P><P>There is no penalty for frequent use of <TT>assert()</TT>; it is removed fromthe code when you undefine debugging. It also provides good internal documentation,reminding the reader of what you believe is true at any given moment in the flowof the code.<H4 ALIGN="CENTER"><A NAME="Heading32"></A><FONT COLOR="#000077">assert() VersusExceptions</FONT></H4><P>On Day 20, you will learn how to work with exceptions to handle error conditions.It is important to note that <TT>assert()</TT> is not intended to handle runtimeerror conditions such as bad data, out-of-memory conditions, unable to open file,and so forth. <TT>assert()</TT> is created to catch programming errors only. Thatis, if an <TT>assert()</TT> &quot;fires,&quot; you know you have a bug in your code.</P><P>This is critical, because when you ship your code to your customers, instancesof <TT>assert()</TT> will be removed. You can't depend on an <TT>assert()</TT> tohandle a runtime problem, because the <TT>assert()</TT> won't be there.</P><P>It is a common mistake to use <TT>assert()</TT> to test the return value froma memory assignment:</P><PRE><FONT COLOR="#0066FF">Animal *pCat = new Cat;Assert(pCat);   // bad use of assertpCat-&gt;SomeFunction();</FONT></PRE><P>This is a classic programming error; every time the programmer runs the program,there is enough memory and the <TT>assert()</TT> never fires. After all, the programmeris running with lots of extra RAM to speed up the compiler, debugger, and so forth.The programmer then ships the executable, and the poor user, who has less memory,reaches this part of the program and the call to <TT>new</TT> fails and returns <TT>NULL</TT>.The <TT>assert()</TT>, however, is no longer in the code and there is nothing toindicate that the pointer points to <TT>NULL</TT>. As soon as the statement <TT>pCat-&gt;SomeFunction()</TT>is reached, the program crashes.</P><P>Getting <TT>NULL</TT> back from a memory assignment is not a programming error,although it is an exceptional situation. Your program must be able to recover fromthis condition, if only by throwing an exception. Remember: The entire <TT>assert()</TT>statement is gone when <TT>DEBUG</TT> is undefined. Exceptions are covered in detailon Day 20.<H4 ALIGN="CENTER"><A NAME="Heading33"></A><FONT COLOR="#000077">Side Effects</FONT></H4><P>It is not uncommon to find that a bug appears only after the instances of <TT>assert()</TT>are removed. This is almost always due to the program unintentionally depending onside effects of things done in <TT>assert()</TT> and other debug-only code. For example,if you write</P><PRE><FONT COLOR="#0066FF">ASSERT (x = 5)</FONT></PRE><P>when you mean to test whether <TT>x == 5</TT>, you will create a particularlynasty bug.</P><P>Let's say that just prior to this <TT>assert()</TT> you called a function thatset <TT>x</TT> equal to 0. With this <TT>assert()</TT> you think you are testingwhether <TT>x</TT> is equal to 5; in fact, you are setting <TT>x</TT> equal to 5.The test returns <TT>TRUE</TT>, because <TT>x = 5</TT> not only sets <TT>x</TT> to5, but returns the value <TT>5</TT>, and because 5 is non-zero it evaluates as <TT>TRUE</TT>.</P><P>Once you pass the <TT>assert()</TT> statement, <TT>x</TT> really is equal to 5(you just set it!). Your program runs just fine. You're ready to ship it, so youturn off debugging. Now the <TT>assert()</TT> disappears, and you are no longer setting<TT>x</TT> to 5. Because <TT>x</TT> was set to 0 just before this, it remains at0 and your program breaks.</P><P>In frustration, you turn debugging back on, but hey! Presto! The bug is gone.Once again, this is rather funny to watch, but not to live through, so be very carefulabout side effects in debugging code. If you see a bug that only appears when debuggingis turned off, take a look at your debugging code with an eye out for nasty sideeffects.<H4 ALIGN="CENTER"><A NAME="Heading34"></A><FONT COLOR="#000077">Class Invariants</FONT></H4><P>Most classes have some conditions that should always be true whenever you arefinished with a class member function. These class invariants are the sine qua nonof your class. For example, it may be true that your <TT>CIRCLE</TT> object shouldnever have a radius of zero, or that your <TT>ANIMAL</TT> should always have an agegreater than zero and less than 100.</P><P>It can be very helpful to declare an <TT>Invariants()</TT> method that returns<TT>TRUE</TT> only if each of these conditions is still true. You can then <TT>ASSERT(Invariants())</TT>at the start and completion of every class method. The exception would be that your<TT>Invariants()</TT> would not expect to return <TT>TRUE</TT> before your constructorruns or after your destructor ends. Listing 17.6 demonstrates the use of the <TT>Invariants()</TT>method in a trivial class.</P><P><A NAME="Heading35"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 17.6. UsingInvariants().</B></FONT></P><PRE><FONT COLOR="#0066FF">0:    #define DEBUG1:    #define SHOW_INVARIANTS2:    #include &lt;iostream.h&gt;3:    #include &lt;string.h&gt;4:    5:    #ifndef DEBUG6:    #define ASSERT(x)7:    #else8:    #define ASSERT(x) \9:                if (! (x)) \10:                { \11:                   cout &lt;&lt; &quot;ERROR!! Assert &quot; &lt;&lt; #x &lt;&lt; &quot; failed\n&quot;; \12:                   cout &lt;&lt; &quot; on line &quot; &lt;&lt; __LINE__  &lt;&lt; &quot;\n&quot;; \13:                   cout &lt;&lt; &quot; in file &quot; &lt;&lt; __FILE__ &lt;&lt; &quot;\n&quot;;  \14:                }15:    #endif16:    17:    18:    const int FALSE = 0;19:    const int TRUE = 1;20:    typedef int BOOL;21: 22:    23:    class String24:    {25:       public:26:          // constructors27:          String();28:          String(const char *const);29:          String(const String &amp;);30:          ~String();31:    32:          char &amp; operator[](int offset);33:          char operator[](int offset) const;34:    35:          String &amp; operator= (const String &amp;);36:          int GetLen()const { return itsLen; }37:          const char * GetString() const { return itsString; }38:          BOOL Invariants() const;39:    40:       private:41:          String (int);         // private constructor42:          char * itsString;

⌨️ 快捷键说明

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