📄 ch17.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD><!-- This document was created from RTF source by rtftohtml version 3.0.1 --> <META NAME="GENERATOR" Content="Symantec Visual Page 1.0"> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <TITLE>Teach Yourself C++ in 21 Days</TITLE></HEAD><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><H1 ALIGN="CENTER"><A HREF="ch16.htm"><IMG SRC="../buttons/BLANPREV.GIF"WIDTH="37" HEIGHT="37" ALIGN="BOTTOM" BORDER="0"></A><A HREF="http://www.mcp.com/sams"><IMGSRC="../buttons/BLANHOME.GIF" WIDTH="37" HEIGHT="37" ALIGN="BOTTOM"BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../buttons/BLANTOC.GIF"WIDTH="37" HEIGHT="37" ALIGN="BOTTOM" BORDER="0"></A><A HREF="ch18.htm"><IMG SRC="../buttons/BLANNEXT.GIF"WIDTH="37" HEIGHT="37" ALIGN="BOTTOM" BORDER="0"></A></H1><H1></H1><UL> <LI><A HREF="#Heading1">Day 17</A> <UL> <LI><A HREF="#Heading2">The Preprocessor</A> <UL> <LI><A HREF="#Heading3">The Preprocessor and the Compiler</A> <LI><A HREF="#Heading4">Seeing the Intermediate Form</A> <LI><A HREF="#Heading5">Using #define</A> <UL> <LI><A HREF="#Heading6">Using #define for Constants</A> <LI><A HREF="#Heading7">Using #define for Tests</A> <LI><A HREF="#Heading8">The #else Precompiler Command</A> </UL> <LI><A HREF="#Heading9">Listing 17.1. Using #define.</A> <LI><A HREF="#Heading10">Inclusion and Inclusion Guards</A> <UL> <LI><A HREF="#Heading11">Defining on the Command Line</A> <LI><A HREF="#Heading12">Undefining</A> </UL> <LI><A HREF="#Heading13">Listing 17.2. Using #undef</A><A HREF="#Heading14">.</A> <UL> <LI><A HREF="#Heading15">Conditional Compilation</A> </UL> <LI><A HREF="#Heading16">Macro Functions</A> <UL> <LI><A HREF="#Heading17">Why All the Parentheses?</A> </UL> <LI><A HREF="#Heading18">Listing 17.3. Using parentheses in macros</A><A HREF="#Heading19">.</A> <UL> <LI><A HREF="#Heading20">Macros Versus Functions and Templates</A> </UL> <LI><A HREF="#Heading21">Inline Functions</A> <LI><A HREF="#Heading22">Listing 17.4. Using</A> <LI><A HREF="#Heading23">inline rather than a macro.</A> <LI><A HREF="#Heading24">String Manipulation</A> <UL> <LI><A HREF="#Heading25">Stringizing</A> <LI><A HREF="#Heading26">Concatenation</A> </UL> <LI><A HREF="#Heading27">Predefined Macros</A> <LI><A HREF="#Heading28">assert()</A> <LI><A HREF="#Heading29">Listing 17.5. A simple assert() macro</A><A HREF="#Heading30">.</A> <UL> <LI><A HREF="#Heading31">Debugging with assert()</A> <LI><A HREF="#Heading32">assert() Versus Exceptions</A> <LI><A HREF="#Heading33">Side Effects</A> <LI><A HREF="#Heading34">Class Invariants</A> </UL> <LI><A HREF="#Heading35">Listing 17.6. Using Invariants().</A> <UL> <LI><A HREF="#Heading36">Printing Interim Values</A> </UL> <LI><A HREF="#Heading37">Listing 17.7. Printing values in DEBUG mode</A><A HREF="#Heading38">.</A> <UL> <LI><A HREF="#Heading39">Debugging Levels</A> </UL> <LI><A HREF="#Heading40">Listing 17.8. Levels of debugging</A><A HREF="#Heading41">.</A> <LI><A HREF="#Heading42">Summary</A> <LI><A HREF="#Heading43">Q&A</A> <LI><A HREF="#Heading44">Workshop</A> <UL> <LI><A HREF="#Heading45">Quiz</A> <LI><A HREF="#Heading46">Exercises</A> </UL> </UL> </UL></UL><P><HR SIZE="4"><H2 ALIGN="CENTER"><A NAME="Heading1"></A><FONT COLOR="#000077">Day 17</FONT></H2><H2 ALIGN="CENTER"><A NAME="Heading2"></A><FONT COLOR="#000077">The Preprocessor</FONT></H2><P>Most of what you write in your source code files is C++. These are interpretedby the compiler and turned into your program. Before the compiler runs, however,the preprocessor runs, and this provides an opportunity for conditional compilation.Today you will learn<UL> <LI>What conditional compilation is and how to manage it. <P> <LI>How to write macros using the preprocessor. <P> <LI>How to use the preprocessor in finding bugs.</UL><H3 ALIGN="CENTER"><A NAME="Heading3"></A><FONT COLOR="#000077">The Preprocessorand the Compiler</FONT></H3><P>Every time you run your compiler, your preprocessor runs first. The preprocessorlooks for preprocessor instructions, each of which begins with a pound symbol (<TT>#</TT>).The effect of each of these instructions is a change to the text of the source code.The result is a new source code file, a temporary file that you normally don't see,but that you can instruct the compiler to save so that you can examine it if youwant to.</P><P>The compiler does not read your original source code file; it reads the outputof the preprocessor and compiles that file. You've seen the effect of this alreadywith the <TT>#include</TT> directive. This instructs the preprocessor to find thefile whose name follows the <TT>#include</TT> directive, and to write it into theintermediate file at that location. It is as if you had typed that entire file rightinto your source code, and by the time the compiler sees the source code, the includedfile is there.<H3 ALIGN="CENTER"><A NAME="Heading4"></A><FONT COLOR="#000077">Seeing the IntermediateForm</FONT></H3><P>Just about every compiler has a switch that you can set either in the integrateddevelopment environment (IDE) or at the command line, and that instructs the compilerto save the intermediate file. Check your compiler manual for the right switchesto set for your compiler, if you'd like to examine this file.<H3 ALIGN="CENTER"><A NAME="Heading5"></A><FONT COLOR="#000077">Using #define</FONT></H3><P>The <TT>#define</TT> command defines a string substitution. If you write</P><PRE><FONT COLOR="#0066FF">#define BIG 512</FONT></PRE><P>you have instructed the precompiler to substitute the string <TT>512</TT> whereverit sees the string <TT>BIG</TT>. This is not a string in the C++ sense. The characters<TT>512</TT> are substituted in your source code wherever the token <TT>BIG</TT>is seen. A token is a string of characters that can be used wherever a string orconstant or other set of letters might be used. Thus, if you write</P><PRE><FONT COLOR="#0066FF">#define BIG 512int myArray[BIG];</FONT></PRE><P>The intermediate file produced by the precompiler will look like this:</P><PRE><FONT COLOR="#0066FF">int myArray[512];</FONT></PRE><P>Note that the <TT>#define</TT> statement is gone. Precompiler statements are allremoved from the intermediate file; they do not appear in the final source code atall.<H4 ALIGN="CENTER"><A NAME="Heading6"></A><FONT COLOR="#000077">Using #define forConstants</FONT></H4><P>One way to use <TT>#define</TT> is as a substitute for constants. This is almostnever a good idea, however, as <TT>#define</TT> merely makes a string substitutionand does no type checking. As explained in the section on constants, there are tremendousadvantages to using the <TT>const</TT> keyword rather than <TT>#define</TT>.<H4 ALIGN="CENTER"><A NAME="Heading7"></A><FONT COLOR="#000077">Using #define forTests</FONT></H4><P>A second way to use <TT>#define,</TT> however, is simply to declare that a particularcharacter string is defined. Therefore, you could write</P><PRE><FONT COLOR="#0066FF">#define BIG</FONT></PRE><P>Later, you can test whether <TT>BIG</TT> has been defined and take action accordingly.The precompiler commands to test whether a string has been defined are <TT>#ifdef</TT>and <TT>#ifndef</TT>. Both of these must be followed by the command <TT>#endif</TT>before the block ends (before the next closing brace).</P><P><TT>#ifdef</TT> evaluates to <TT>TRUE</TT> if the string it tests has been definedalready. So, you can write</P><PRE><FONT COLOR="#0066FF">#ifdef DEBUGcout << "Debug defined";#endif</FONT></PRE><P>When the precompiler reads the <TT>#ifdef</TT>, it checks a table it has builtto see if you've defined <TT>DEBUG</TT>. If you have, the <TT>#ifdef</TT> evaluatesto <TT>TRUE</TT>, and everything to the next <TT>#else</TT> or <TT>#endif</TT> iswritten into the intermediate file for compiling. If it evaluates to <TT>FALSE</TT>,nothing between <TT>#ifdef DEBUG</TT> and <TT>#endif</TT> will be written into theintermediate file; it will be as if it were never in the source code in the firstplace.</P><P>Note that <TT>#ifndef</TT> is the logical reverse of <TT>#ifdef</TT>. <TT>#ifndef</TT>evaluates to <TT>TRUE</TT> if the string has not been defined up to that point inthe file.<H4 ALIGN="CENTER"><A NAME="Heading8"></A><FONT COLOR="#000077">The #else PrecompilerCommand</FONT></H4><P>As you might imagine, the term <TT>#else</TT> can be inserted between either <TT>#ifdef</TT>or <TT>#ifndef</TT> and the closing <TT>#endif</TT>. Listing 17.1 illustrates howthese terms are used.</P><P><A NAME="Heading9"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 17.1. Using #define.</B></FONT></P><PRE><FONT COLOR="#0066FF">1: #define DemoVersion2: #define DOS_VERSION 53: #include <iostream.h>4:5:6: int main()7: {8:9: cout << "Checking on the definitions of DemoVersion, DOS_VERSION Â _and WINDOWS_VERSION...\n";10:11: #ifdef DemoVersion12: cout << "DemoVersion defined.\n";13: #else14: cout << "DemoVersion not defined.\n";15: #endif16:17: #ifndef DOS_VERSION18: cout << "DOS_VERSION not defined!\n";19: #else20: cout << "DOS_VERSION defined as: " << DOS_VERSION << endl;21: #endif22:23: #ifdef WINDOWS_VERSION24: cout << "WINDOWS_VERSION defined!\n";25: #else26: cout << "WINDOWS_VERSION was not defined.\n";27: #endif28:29: cout << "Done.\n";30: return 0;<TT>31: }</TT></FONT><FONT COLOR="#0066FF">Output: Checking on the definitions of DemoVersion, DOS_VERSION Â _and WINDOWS_VERSION...\n";DemoVersion defined.DOS_VERSION defined as: 5WINDOWS_VERSION was not defined.Done.</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On lines 1 and 2, <TT>DemoVersion</TT>and <TT>DOS_VERSION</TT> are defined, with <TT>DOS_VERSION</TT> defined with thestring <TT>5</TT>. On line 11, the definition of <TT>DemoVersion</TT> is tested,and because <TT>DemoVersion</TT> is defined (albeit with no value), the test is trueand the string on line 12 is printed.<BR>On line 17 is the test that <TT>DOS_VERSION</TT> is not defined. Because <TT>DOS_VERSION</TT>is defined, this test fails and execution jumps to line 20. Here the string <TT>5</TT>is substituted for the word <TT>DOS_VERSION</TT>; this is seen by the compiler as</P><PRE><FONT COLOR="#0066FF">cout << "DOS_VERSION defined as: " << 5 << endl;</FONT></PRE><P>Note that the first word <TT>DOS_VERSION</TT> is not substituted because it isin a quoted string. The second <TT>DOS_VERSION</TT> is substituted, however, andthus the compiler sees <TT>5</TT> as if you had typed <TT>5</TT> there.</P><P>Finally, on line 23, the program tests for <TT>WINDOWS_VERSION</TT>. Because youdid not define <TT>WINDOWS_VERSION</TT>, the test fails and the message on line 24is printed.<H3 ALIGN="CENTER"><A NAME="Heading10"></A><FONT COLOR="#000077">Inclusion and InclusionGuards</FONT></H3><P>You will create projects with many different files. You will probably organizeyour directories so that each class has its own header file (HPP) with the classdeclaration, and its own implementation file (CPP) with the source code for the classmethods.</P><P>Your <TT>main()</TT> function will be in its own CPP file, and all the CPP fileswill be compiled into OBJ files, which will then be linked together into a singleprogram by the linker.</P><P>Because your programs will use methods from many classes, many header files willbe included in each file. Also, header files often need to include one another. Forexample, the header file for a derived class's declaration must include the headerfile for its base class.</P><P>Imagine that the <TT>Animal</TT> class is declared in the file <TT>ANIMAL.HPP</TT>.The <TT>Dog</TT> class (which derives from <TT>Animal</TT>) must include the file<TT>ANIMAL.HPP</TT> in <TT>DOG.HPP</TT>, or <TT>Dog</TT> will not be able to derivefrom <TT>Animal</TT>. The <TT>Cat</TT> header also includes <TT>ANIMAL.HPP</TT> forthe same reason.</P><P>If you create a method that uses both a <TT>Cat</TT> and a <TT>Dog</TT>, you willbe in danger of including <TT>ANIMAL.HPP</TT> twice. This will generate a compile-timeerror, because it is not legal to declare a class (<TT>Animal</TT>) twice, even thoughthe declarations are identical. You can solve this problem with inclusion guards.At the top of your <TT>ANIMAL</TT> header file, you write these lines:</P><PRE><FONT COLOR="#0066FF">#ifndef ANIMAL_HPP#define ANIMAL_HPP... // the whole file goes here#endif</FONT></PRE><P>This says, if you haven't defined the term <TT>ANIMAL_HPP</TT>, go ahead and defineit now. Between the <TT>#define</TT> statement and the closing <TT>#endif</TT> arethe entire contents of the file.</P><P>The first time your program includes this file, it reads the first line and thetest evaluates to <TT>TRUE</TT>; that is, you have not yet defined <TT>ANIMAL_HPP</TT>.So, it goes ahead and defines it and then includes the entire file.</P><P>The second time your program includes the <TT>ANIMAL.HPP</TT> file, it reads thefirst line and the test evaluates to <TT>FALSE</TT>; <TT>ANIMAL.HPP</TT> has beendefined. It therefore skips to the next <TT>#else</TT> (there isn't one) or the next<TT>#endif</TT> (at the end of the file). Thus, it skips the entire contents of thefile, and the class is not declared twice.</P><P>The actual name of the defined symbol (<TT>ANIMAL_HPP</TT>) is not important,although it is customary to use the filename in all uppercase with the dot (<TT>.</TT>)changed to an underscore. This is purely convention, however.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -