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

📄 chapter14.html

📁 think like a computer scientist
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<PRE>  Complex c1 (2.0, 3.0);  Complex c2 (3.0, 4.0);  Complex sum = add (c1, c2);  sum.printCartesian();</PRE><P>The output of this program is</P><PRE>5 + 7i</PRE><BR><BR><H3>14.7 Another function on <TT>Complex</TT> numbers</H3><P>Another operation we might want is multiplication. Unlike addition, multiplication is easy if the numbers are in polar coordinates and hard if theyare in Cartesian coordinates (well, a little harder, anyway).</P><P>In polar coordinates, we can just multiply the magnitudes and add the angles.As usual, we can use the accessor functions without worrying about the representation of the objects.</P><PRE>Complex mult (Complex& a, Complex& b){  double mag = a.getMag() * b.getMag()  double theta = a.getTheta() + b.getTheta();  Complex product;  product.setPolar (mag, theta);  return product;}</PRE><P>A small problem we encounter here is that we have no constructor that accepts polar coordinates. It would be nice to write one, but remember that we can only overload a function (even a constructor) if the different versions take different parameters. In this case, we would like a second constructor that also takes two <TT>double</TT>s, and we can't have that.</P><P>An alternative it to provide an accessor function that <I>sets</I> the instance variables. In order to do that properly, though, we have to make sure that when <TT>mag</TT> and <TT>theta</TT> are set, we also set the <TT>polar</TT> flag. At the same time, we have to make sure that the <TT>cartesian</TT> flag is unset. That's because if we change the polar coordinates, the cartesian coordinates are no longer valid.</P><PRE>void Complex::setPolar (double m, double t){  mag = m;  theta = t;  cartesian = false;  polar = true;}</PRE><P>As an exercise, write the corresponding function named <TT>setCartesian</TT>.</P><P>To test the <TT>mult</TT> function, we can try something like:</P><PRE>  Complex c1 (2.0, 3.0);  Complex c2 (3.0, 4.0);  Complex product = mult (c1, c2);  product.printCartesian();</PRE><P>The output of this program is</P><PRE>-6 + 17i</PRE><P>There is a lot of conversion going on in this program behind the scenes. When we call <TT>mult</TT>, both arguments get converted to polar coordinates.The result is also in polar format, so when we invoke <TT>printCartesian</TT> it has to get converted back. Really, it's amazing that we get the right answer!</P><BR><BR><H3>14.8 Invariants</H3><P>There are several conditions we expect to be true for a proper <TT>Complex</TT> object. For example, if the <TT>cartesian</TT> flag is set then we expect <TT>real</TT> and <TT>imag</TT> to contain valid data. Similarly, if <TT>polar</TT> is set, we expect <TT>mag</TT> and <TT>theta</TT> to be valid. Finally, if both flags are set then we expect the other four variables to be consistent; that is, they should be specifying the same point in two different formats.</P><P>These kinds of conditions are called <TT>invariants</TT>, for the obvious reason that they do not vary---they are always supposed to be true. One of the ways to write good quality code that contains few bugs is to figure out what invariants are appropriate for your classes, and write code that makes it impossible to violate them.</P><P>One of the primary things that data encapsulation is good for is helping to enforce invariants. The first step is to prevent unrestricted access to the instance variables by making them private. Then the only way to modify the object is through accessor functions and modifiers. If we examine all the accessors and modifiers, and we can show that every one of them maintains the invariants, then we can prove that it is impossible for an invariant to be violated.</P><P>Looking at the <TT>Complex</TT> class, we can list the functions that make assignments to one or more instance variables:</P><PRE>the second constructorcalculateCartesiancalculatePolarsetCartesiansetPolar</PRE><P>In each case, it is straightforward to show that the function maintains eachof the invariants I listed.  We have to be a little careful, though. Notice that I said ``maintain'' the invariant. What that means is ``If the invariant is true when the function is called, it will still be true when the function iscomplete.''</P><P>That definition allows two loopholes. First, there may be some point in the middle of the function when the invariant is not true. That's ok, and in some cases unavoidable. As long as the invariant is restored by the end of the function, all is well.</P><P>The other loophole is that we only have to maintain the invariant if it was true at the beginning of the function. Otherwise, all bets are off. If the invariant was violated somewhere else in the program, usually the best we can do is detect the error, output an error message, and exit.</P><BR><BR><H3>14.9 Preconditions</H3><P>Often when you write a function you make implicit assumptions about the parameters you receive. If those assumptions turn out to be true, then everything is fine; if not, your program might crash.</P><P>To make your programs more robust, it is a good idea to think about your assumptions explicitly, document them as part of the program, and maybe write code that checks them.</P><P>For example, let's take another look at <TT>calculateCartesian</TT>. Is there an assumption we make about the current object? Yes, we assume that the <TT>polar</TT> flag is set and that <TT>mag</TT> and <TT>theta</TT> contain valid data. If that is not true, then this function will produce meaningless results.</P><P>One option is to add a comment to the function that warns programmers about the <B>precondition</B>.</P><PRE>void Complex::calculateCartesian ()// precondition: the current object contains valid polar coordinates	and the polar flag is set// postcondition: the current object will contain valid Cartesian	coordinates and valid polar coordinates, and both the cartesian	flag and the polar flag will be set{  real = mag * cos (theta);  imag = mag * sin (theta);  cartesian = true;}</PRE><P>At the same time, I also commented on the <B>postconditions</B>, the things we know will be true when the function completes.</P><P>These comments are useful for people reading your programs, but it is an even better idea to add code that <I>checks</I> the preconditions, so that we can print an appropriate error message:</P><PRE>void Complex::calculateCartesian (){  if (polar == false) {    cout <<    "calculateCartesian failed because the polar representation is invalid"	 << endl;    exit (1);  }  real = mag * cos (theta);  imag = mag * sin (theta);  cartesian = true;}</PRE><P>The <TT>exit</TT> function causes the program to quit immediately. The return value is an error code that tells the system (or whoever executed the program) that something went wrong.</P><P>This kind of error-checking is so common that C++ provides a built-in function to check preconditions and print error messages. If you include the <TT>assert.h</TT> header file, you get a function called <TT>assert</TT> that takes a boolean value (or a conditional expression) as an argument. As long as the argument is true, <TT>assert</TT> does nothing. If the argument is false, assert prints an error message and quits.  Here's how to use it:</P><PRE>void Complex::calculateCartesian (){  assert (polar);  real = mag * cos (theta);  imag = mag * sin (theta);  cartesian = true;  assert (polar && cartesian);}</PRE><P>The first <TT>assert</TT> statement checks the precondition (actually just part of it); the second <TT>assert</TT> statement checks the postcondition.</P><P>In my development environment, I get the following message when I violate anassertion:</P><PRE>Complex.cpp:63: void Complex::calculatePolar(): Assertion `cartesian' failed.Abort</PRE><P>There is a lot of information here to help me track down the error, including the file name and line number of the assertion that failed, the function name and the contents of the assert statement.</P><BR><BR><H3>14.10 Private functions</H3><P>In some cases, there are member functions that are used internally by a class, but that should not be invoked by client programs. For example, <TT>calculatePolar</TT> and <TT>calculateCartesian</TT> are used by the accessor functions, but there is probably no reason clients should call them directly (although it would not do any harm). If we wanted to protect these functions, we could declare them <TT>private</TT> the same way we do with instance variables. In that case the complete class definition for <TT>Complex</TT> would look like:</P><PRE>class Complex{private:  double real, imag;  double mag, theta;  bool cartesian, polar;  void calculateCartesian ();  void calculatePolar ();public:  Complex () { cartesian = false;  polar = false; }  Complex (double r, double i)  {    real = r;  imag = i;    cartesian = true;  polar = false;  }  void printCartesian ();  void printPolar ();  double getReal ();  double getImag ();  double getMag ();  double getTheta ();  void setCartesian (double r, double i);  void setPolar (double m, double t);};</PRE><P>The <TT>private</TT> label at the beginning is not necessary,but it is a useful reminder.</P><BR><BR><H3>14.11 Glossary</H3><DL>  <DT>class:</DT><DD> In general use, a class is a user-defined type with   member functions. In C++, a class is a structure with private instance   variables.</DD>  <DT>accessor function:</DT><DD> A function that provides access (read or   write) to a private instance variable.</DD>  <DT>invariant:</DT><DD> A condition, usually pertaining to an object, that   should be true at all times in client code, and that should be maintained by   all member functions.</DD>  <DT>precondition:</DT><DD> A condition that is assumed to be true at the   beginning of a function. If the precondition is not true, the function may   not work. It is often a good idea for functions to check their preconditions,  if possible.</DD>  <DT>postcondition:</DT><DD> A condition that is true at the end of a   function.</DD> </DL><BR><DIV CLASS=navigation><HR>  <TABLE ALIGN=center WIDTH="100%" CELLPADDING=0 CELLSPACING=2>  <TR>    <TD><A HREF="chapter15.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/chapter15.html">      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT="next"       SRC="images/next.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/next.gif"></A>    </TD>    <TD><A HREF="index.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/index.html">      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT="up"       SRC="images/up.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/up.gif"></A>    </TD>    <TD><A HREF="chapter13.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/chapter13.html">      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT="previous"      SRC="images/previous.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/previous.gif"></A>    </TD>    <TD ALIGN=center BGCOLOR="#99CCFF" WIDTH="100%">      <B CLASS=title>How to Think Like a Computer Scientist: Chapter 14</B>    </TD>    <TD><A HREF="index.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/index.html">      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT="contents"      SRC="images/contents.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/contents.gif"></A>    </TD>    <TD>      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT=""      SRC="images/blank.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/blank.gif">    </TD>    <TD>      <IMG WIDTH=32 HEIGHT=32 ALIGN=bottom BORDER=0 ALT=""      SRC="images/blank.gif" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/blank.gif">    </TD>  </TR>  </TABLE>  <B CLASS=navlabel>Next:</B>  <SPAN CLASS=sectref><A HREF="chapter15.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/chapter15.html">Chapter 15</A></SPAN>  <B CLASS=navlabel>Up:</B>  <SPAN CLASS=sectref><A HREF="index.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/index.html">Index</A></SPAN>  <B CLASS=navlabel>Previous:</B>  <SPAN CLASS=sectref><A HREF="chapter13.html" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/chapter13.html">Chapter 13</A></SPAN>  <HR></DIV></BODY></HTML>

⌨️ 快捷键说明

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