📄 calc.html
字号:
int num2 = _stack.Pop ();
int num1;
// Special case, when only one number on the stack:
// use this number for both operands.
if (_stack.IsEmpty ())
num1 = num2;
else
num1 = _stack.Pop ();
_stack.Push (Calculate (num1, num2, token));
status = true;
}
}
return status;
}
</pre>
</table>
<!-- End Code -->
<p>
We have introduced the extension of the conditional--the <var>else</var> clause. The body of the <i>else</i> statement is executed only if the preceding <i>if</i> statement hasn't been executed (because the condition was false).
<p>
<i>If</i>/<i>else</i> statement can be staggered like this:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>if (A)
...
else if (B)
...
else if (C)
...
else
...</pre>
</table>
<!-- End Code -->
<p>
<var>Calculate</var> is the new method of <var>Calculator</var>. Since it is used only inside the implementation of <var>Calculator</var>, it is made private. It takes two numbers and a token and it returns the result of the operation. I have separated this code into a method mostly because the <var>Execute</var> method was getting too large. <var>Calculate</var> has well defined functionality, quite independent from the rest of the code. It is implemented as one large staggered <i>if</i>/<i>else</i> construct.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>int <span class=method>Calculator::Calculate</span> (int num1, int num2, int token) const
{
int result;
if (token == '+')
result = num1 + num2;
else if (token == '-')
result = num1 - num2;
else if (token == '*')
result = num1 * num2;
else if (token == '/')
{
if (num2 == 0)
{
cout << "Division by zero\n";
result = 0;
}
else
result = num1 / num2;
}
return result;
}
</pre>
</table>
<!-- End Code -->
Notice the use of character literals such as
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>'+', '-', '*', '/'.</pre>
</table>
<!-- End Code -->
Unlike string literals, character literals are surrounded by <i>single</i> quotes. Instead of assigning special values to <i>operator</i> tokens, we just used their character literal (ASCII) values.
<p>
Let's have a look at the modified class definition of the calculator.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>class <span class=method>Calculator</span>
{
public:
bool Execute ( Input& input );
// give access to the stack
IStack const & GetStack () const { return _stack; }
private:
int Calculate (int n1, int n2, int token) const;
IStack _stack;
};</pre>
</table>
<!-- End Code -->
<p>
We may now add our old implementation of <var>IStack</var> and <var>StackSeq</var>, extend the dummy <var>Input</var> by adding dummy implementations of the methods <var>Token</var> and <var>Number</var>, recompile and test.
<h4>Input: Implementation</h4>
<p>
Now it's the time to implement the <var>Input</var> class. It's also time to split our project into separate files. We should have three header files--<var>calc.h</var>, <var>stack.h</var> and <var>input.h</var>--as well as three implementation files--<var>calc.cpp</var>, <var>stack.cpp</var> and <var>input.cpp</var>. To build an executable program out of multiple source files one needs the help of a <i>linker</i>. Fortunately, in an integrated environment you can create a project to which you add all the implementation files and let the environment <i>build</i> the program for you. We don't want to get too involved in such details here.
<p>
The more header files we have, the more likely it is that we will include the same file twice. How can this happen? Suppose that we include <var>input.h</var> inside <var>calc.h</var>. Then we include both <var>calc.h</var> and <var>input.h</var> inside <var>calc.cpp</var>, and we're in trouble. The compiler will see two definitions of class <var>Input</var> (they're identical, but so what). The way to protect ourselves from such situations is to guard the header files with conditional compilation directives. Let's enclose the whole body of <var>input.h</var> with the pair
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>#if !defined input_h
...
#endif</pre>
</table>
<!-- End Code -->
<p>
If the symbol <var>input_h</var> is not defined, the compiler will process the code between these two <i>preprocessor directives</i> (as they are called). In the beginning, this symbol is not defined by anybody. So the first time the compiler encounters the <var>#include "input.h"</var> directive, it will go ahead processing <var>input.h</var>. However, the first directive inside the <i>if</i>/<i>endif</i> pair defines the symbol <var>input_h</var>
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>#define input_h</pre>
</table>
<!-- End Code -->
<p>
Next time the <var>#include "input.h"</var> directive is encountered, the compiler will skip everything between <var>#if !defined input_h</var> and <var>#endif</var> since <var>input_h</var> has already been defined.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>#if !defined input_h // prevent multiple inclusions
#define input_h
const int maxBuf = 100;
// Tokens are tokNumber, tokError, +, -, *, /.
const int tokNumber = 1;
const int tokError = 2;
// Gets input from stdin, converts to token.
class <span class=method>Input</span>
{
public:
Input ();
int Token () const { return _token; }
int Number () const;
private:
int _token;
char _buf [maxBuf];
};
#endif // input_h</pre>
</table>
<!-- End Code -->
<p>
Notice that the methods <var>Token</var> and <var>Number</var> are declared <var>const</var>. The <var>Calculator</var> who has read-only access to input (through a <i>const</i> reference) will still be able to call these methods. The buffer <var>_buf</var> is where the string obtained from the user will be stored.
<p>
The implementation file <var>input.cpp</var> includes two new standard headers.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>#include <cctype>
#include <cstdlib></pre>
</table>
<!-- End Code -->
<p>
The file <var>cctype</var> contains the definitions of (very efficient) macros to recognize character type. The <var>isdigit</var> macro, for instance, returns true if the character is one of the digits between '0' and '9' and false otherwise. The other file, <var>cstdlib</var>, is needed for the declaration of the function <var>atoi</var> that converts an ASCII string representing a decimal number to an integer. You can find out more about functions like that by studying the standard library. You can either use online help or read the compiler manuals (or some other books).
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre><span class=method>Input::Input</span> ()
{
cin >> _buf;
// first char of input is usually enough to decide
// what token it is
int c = _buf [0];
if (isdigit (c))
_token = tokNumber;
else if (c == '+' || c == '*' || c == '/')
_token = c;
else if (c == '-') // allow entering negative numbers
{
if (isdigit (_buf [1])) // peek at next char
_token = tokNumber;
else
_token = c;
}
else
_token = tokError;
}</pre>
</table>
<!-- End Code -->
<p>
The constructor of <var>Input</var> reads a line of text from the standard input into a character buffer. This is yet another amazing trick performed by our friend <var>cin</var>. (By the way, for the time being we are not even thinking about what could happen if the buffer overflows. We are still at the level of <i>weekend programming</i>.)
<p>
Depending on the first character in the buffer, we decide what token to make out of it. Notice the special treatment of the minus sign. It could be a binary minus, or it could be a unary minus in front of a number. To find out which one it is, we peek at the next character in the buffer. Notice the use of one of the character classification macros I mentioned before. <var>isdigit</var> returns true when the character is a digit (that is one of '0', '1', '2'... '9'). The character classification macros are implemented using a very efficient <i>table lookup</i> method. It is more efficient to call <var>isdigit</var> than execute code like this
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>if ( c >= '0' && c <= '9' )</pre>
</table>
<!-- End Code -->
<p>
The header <var>cctype</var> contains other useful macros, besides <var>isdigit</var>, like <var>isspace</var>, <var>islower</var> and <var>isupper</var>.
<p>
If the token is <var>tokNumber</var>, the <var>Calculator</var> needs to know its value. The method <var>Number</var> converts the string in the buffer into an <var>int</var> value using the library function <var>atoi</var> declared in <var>cstdlib</var>.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>int <span class=method>Input::Number</span> () const
{
assert (_token == tokNumber);
return atoi (_buf); // convert string to integer
}
</pre>
</table>
<!-- End Code -->
<h4>The Makefile</h4>
<p>
One final word for those of us who, for one reason or another, don't use an integrated environment to develop their code. When the project grows beyond a single file, it is time to start using the <i>make</i> utility. Below is a very simplified <i>makefile</i> that establishes the dependencies between various files in the project and provides the recipes to generate them:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>calc.exe : calc.obj stack.obj input.obj
cl calc.obj stack.obj input.obj
calc.obj : calc.cpp calc.h input.h stack.h
cl -c calc.cpp
stack.obj : stack.cpp stack.h
cl -c stack.cpp
input.obj : input.cpp input.h
cl -c input.cpp</pre>
</table>
<!-- End Code -->
<p>
If the <i>makefile</i> is called <var>calc.mak</var>, you just call <var>make calc.mak</var> (or <var>nmake calc.mak</var>) and all the necessary compilation and linking steps will be executed for you to create the executable <var>calc.exe</var>.
Again, use online help or read manuals to find out more about <i>make</i> (or <i>nmake</i>).
</table>
<!-- End Main Table -->
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -