?? ch17.htm
字號:
<PRE><FONT COLOR="#0066FF">0: enum LEVEL { NONE, LOW, MEDIUM, HIGH };
1: const int FALSE = 0;
2: const int TRUE = 1;
3: typedef int BOOL;
4:
5: #define DEBUGLEVEL HIGH
6:
7: #include <iostream.h>
8: #include <string.h>
9:
10: #if DEBUGLEVEL < LOW // must be medium or high
11: #define ASSERT(x)
12: #else
13: #define ASSERT(x) \
14: if (! (x)) \
15: { \
16: cout << "ERROR!! Assert " << #x << " failed\n"; \
17: cout << " on line " << __LINE__ << "\n"; \
18: cout << " in file " << __FILE__ << "\n"; \
19: }
20: #endif
21:
22: #if DEBUGLEVEL < MEDIUM
23: #define EVAL(x)
24: #else
25: #define EVAL(x) \
26: cout << #x << ":\t" << x << endl;
27: #endif
28:
29: #if DEBUGLEVEL < HIGH
30: #define PRINT(x)
31: #else
32: #define PRINT(x) \
33: cout << x << endl;
34: #endif
35:
36:
37: class String
38: {
39: public:
40: // constructors
41: String();
42: String(const char *const);
43: String(const String &);
44: ~String();
45:
46: char & operator[](int offset);
47: char operator[](int offset) const;
48:
49: String & operator= (const String &);
50: int GetLen()const { return itsLen; }
51: const char * GetString() const
52: { return itsString; }
53: BOOL Invariants() const;
54:
55: private:
56: String (int); // private constructor
57: char * itsString;
58: unsigned short itsLen;
59: };
60:
61: BOOL String::Invariants() const
62: {
63: PRINT("(String Invariants Checked)");
64: return ( (BOOL) (itsLen && itsString) ||
65: (!itsLen && !itsString) );
66: }
67:
68: class Animal
69: {
70: public:
71: Animal():itsAge(1),itsName("John Q. Animal")
72: {ASSERT(Invariants());}
73:
74: Animal(int, const String&);
75: ~Animal(){}
76:
77: int GetAge()
78: {
79: ASSERT(Invariants());
80: return itsAge;
81: }
82:
83: void SetAge(int Age)
84: {
85: ASSERT(Invariants());
86: itsAge = Age;
87: ASSERT(Invariants());
88: }
89: String& GetName()
90: {
91: ASSERT(Invariants());
92: return itsName;
93: }
94:
95: void SetName(const String& name)
96: {
97: ASSERT(Invariants());
98: itsName = name;
99: ASSERT(Invariants());
100: }
101:
102: BOOL Invariants();
103: private:
104: int itsAge;
105: String itsName;
106: };
107:
108: BOOL Animal::Invariants()
109: {
110: PRINT("(Animal Invariants Checked)");
111: return (itsAge > 0 && itsName.GetLen());
112: }
113:
114: int main()
115: {
116: const int AGE = 5;
117: EVAL(AGE);
118: Animal sparky(AGE,"Sparky");
119: cout << "\n" << sparky.GetName().GetString();
120: cout << " is ";
121: cout << sparky.GetAge() << " years old.";
122: sparky.SetAge(8);
123: cout << "\n" << sparky.GetName().GetString();
124: cout << " is ";
125: cout << sparky.GetAge() << " years old.";
126: return 0;
<TT>127: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: AGE: 5
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
(String Invariants Checked)
Sparky is (Animal Invariants Checked)
5 Years old. (Animal Invariants Checked)
(Animal Invariants Checked)
(Animal Invariants Checked)
Sparky is (Animal Invariants Checked)
8 years old. (String Invariants Checked)
(String Invariants Checked)
// run again with DEBUG = MEDIUM
AGE: 5
Sparky is 5 years old.
Sparky is 8 years old.
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>On lines 10 to 20, the <TT>assert()</TT>
macro is defined to be stripped if <TT>DEBUGLEVEL</TT> is less than <TT>LOW</TT>
(that is, <TT>DEBUGLEVEL</TT> is <TT>NONE</TT>). If any debugging is enabled, the
<TT>assert()</TT> macro will work. On line 23, <TT>EVAL</TT> is declared to be stripped
if <TT>DEBUG</TT> is less than <TT>MEDIUM</TT>; if <TT>DEBUGLEVEL</TT> is <TT>NONE</TT>
or <TT>LOW</TT>, <TT>EVAL</TT> is stripped.<BR>
Finally, on lines 29-34, the <TT>PRINT</TT> macro is declared to be stripped if <TT>DEBUGLEVEL</TT>
is less than <TT>HIGH</TT>. <TT>PRINT</TT> is used only when <TT>DEBUGLEVEL</TT>
is <TT>HIGH</TT>; you can eliminate this macro by setting <TT>DEBUGLEVEL</TT> to
<TT>MEDIUM</TT> and still maintain your use of <TT>EVAL</TT> and <TT>assert()</TT>.</P>
<P><TT>PRINT</TT> is used within the <TT>Invariants()</TT> methods to print an informative
message. <TT>EVAL</TT> is used on line 117 to evaluate the current value of the constant
integer <TT>AGE</TT>.
<BLOCKQUOTE>
<P>
<HR>
<B>DO</B> use CAPITALS for your macro names. This is a pervasive convention, and
other programmers will be confused if you don't. <B>DON'T</B> allow your macros to
have side effects. Don't increment variables or assign values from within a macro.
<B>DO</B> surround all arguments with parentheses in macro functions.
<HR>
</BLOCKQUOTE>
<H3 ALIGN="CENTER"><A NAME="Heading42"></A><FONT COLOR="#000077">Summary</FONT></H3>
<P>Today you learned more details about working with the preprocessor. Each time
you run the compiler, the preprocessor runs first and translates your preprocessor
directives such as <TT>#define</TT> and <TT>#ifdef</TT>.</P>
<P>The preprocessor does text substitution, although with the use of macros these
can be somewhat complex. By using <TT>#ifdef</TT>,<TT> #else</TT>, and <TT>#ifndef</TT>,
you can accomplish conditional compilation, compiling in some statements under one
set of conditions and in another set of statements under other conditions. This can
assist in writing programs for more than one platform and is often used to conditionally
include debugging information.</P>
<P>Macro functions provide complex text substitution based on arguments passed at
compile time to the macro. It is important to put parentheses around every argument
in the macro to ensure the correct substitution takes place.</P>
<P>Macro functions, and the preprocessor in general, are less important in C++ than
they were in C. C++ provides a number of language features, such as <TT>const</TT>
variables and templates, that offer superior alternatives to use of the preprocessor.
<H3 ALIGN="CENTER"><A NAME="Heading43"></A><FONT COLOR="#000077">Q&A</FONT></H3>
<DL>
<DD><B>Q. If C++ offers better alternatives than the preprocessor, why is this option
still available?<BR>
</B><BR>
<B>A.</B> First, C++ is backward-compatible with C, and all significant parts of
C must be supported in C++. Second, there are some uses of the preprocessor that
are still used frequently in C++, such as inclusion guards.<BR>
<BR>
<B>Q. Why use macro functions when you can use a regular function?<BR>
</B><BR>
<B>A.</B> Macro functions are expanded inline and are used as a substitute for repeatedly
typing the same commands with minor variations. Again, though, templates offer a
better alternative.<BR>
<BR>
<B>Q. How do you know when to use a macro versus an inline function?<BR>
</B><BR>
<B>A.</B> Often it doesn't matter much; use whichever is simpler. However, macros
offer character substitution, stringizing, and concatenation. None of these is available
with functions.<BR>
<BR>
<B>Q. What is the alternative to using the preprocessor to print interim values during
debugging?</B><BR>
<B><BR>
A.</B> The best alternative is to use <TT>watch</TT> statements within a debugger.
For information on <TT>watch</TT> statements, consult your compiler or debugger documentation.<BR>
<BR>
<B>Q. How do you decide when to use an assert() and when to throw an exception?<BR>
</B><BR>
<B>A.</B> If the situation you're testing can be true without your having committed
a programming error, use an exception. If the only reason for this situation to ever
be true is a bug in your program, use an <TT>assert()</TT>.
</DL>
<H3 ALIGN="CENTER"><A NAME="Heading44"></A><FONT COLOR="#000077">Workshop</FONT></H3>
<P>The Workshop provides quiz questions to help you solidify your understanding of
the material covered and exercises to provide you with experience in using what you've
learned. Try to answer the quiz and exercise questions before checking the answers
in Appendix D, and make sure you understand the answers before continuing to the
next chapter.
<H4 ALIGN="CENTER"><A NAME="Heading45"></A><FONT COLOR="#000077">Quiz</FONT></H4>
<DL>
<DD><B>1.</B> What is an inclusion guard?<BR>
<B><BR>
2. </B>How do you instruct your compiler to print the contents of the intermediate
file showing the effects of the preprocessor?<BR>
<B><BR>
3.</B> What is the difference between <TT>#define debug 0</TT> and <TT>#undef debug</TT>?<BR>
<B><BR>
4.</B> Name four predefined macros.<BR>
<B><BR>
5.</B> Why can't you call <TT>Invariants()</TT> as the first line of your constructor?
</DL>
<H4 ALIGN="CENTER"><A NAME="Heading46"></A><FONT COLOR="#000077">Exercises</FONT></H4>
<DL>
<DD><B>1.</B> Write the inclusion guard statements for the header file <TT>STRING.H</TT>.<BR>
<B><BR>
2.</B> Write an <TT>assert()</TT> macro that prints an error message and the file
and line number if debug level is 2, just a message (without file and line number)
if the level is 1, and does nothing if the level is 0.<BR>
<B><BR>
3.</B> Write a macro <TT>DPrint</TT> that tests if <TT>DEBUG</TT> is defined and,
if it is, prints the value passed in as a parameter.<BR>
<B><BR>
4.</B> Write a function that prints an error message. The function should print the
line number and filename where the error occurred. Note that the line number and
filename are passed in to this function.<BR>
<B><BR>
5.</B> How would you call the preceding error function?<BR>
<B><BR>
6.</B> Wr
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -