C++ For Mathematicians (2006) [eng]
.pdf356 |
C++ for Mathematicians |
An alternative method for setting up a variable is to use new. If T is a type, we have the following statements,
T* xp;
xp = new T;
The new statement allocates sizeof(T) bytes of memory for a new object of type T. The zero-argument constructor (if any) is invoked when initializing the new object. Then, a pointer to that new object is assigned to xp. We may supply arguments to the type T constructor using this syntax:
xp = new T(arg1, arg2, ..., argN);
When we use new (as opposed to the ordinary method of declaring variables) the memory for the object does not reside on the stack. Instead, it resides in a different portion of the computer’s memory called the heap. The practical difference is that the object defined using new persists even after the procedure in which it was created ends.
Once the program is finished using an object created with new, the memory allocated to that object should be released. This is done with a delete statement:
delete xp;
Notice that the syntax here is slightly different from the situation in which we are freeing an array; in the latter case we use delete[]. The square brackets indicate that an entire of array of objects is being freed.
Once an object is created using new, it can be used just as any other object. However, we must remember that the variable xp is not of type T, but rather is a pointer to an object of type T. Therefore, if T has a method named, say, reset, it is an error to invoke this method with the expression xp.reset(). Instead, we should write (*xp).reset(). Likewise, to access a data member of this object (say, it has a data element named a), we do not use the expression xp.a; rather, we write (*xp).a.
There is an alternative way to write (*xp).reset() and (*xp).a. The C++ operator -> is defined as follows.
•xp->reset() means (*xp).reset(), and
•xp->a means (*xp).a.
The combination of a hyphen and a greater-than symbol is meant to look like an arrow pointing to the part of *xp we want.
15.6.5 Why use pointers?
There are a few situations in which pointers are useful, but in nearly all cases, one can do fine without them. Before call by reference was introduced into C++, procedures written in C that needed to modify their arguments used pointers instead of the values of the variables. For example, a procedure to swap the values of two double variables would be written like this:
Odds and Ends |
357 |
void swap(double* a, double* b) { double tmp = *a;
*a = *b; *b = tmp;
}
To use this swap procedure on variables x and y of type double, we would write swap(&x,&y);. The ampersands are necessary because this version of swap requires pointer-to-double arguments.
Similarly, suppose T is a class and that objects of type T are large (say, hundreds of bytes). Then each time we pass an object of type T to a procedure, the object is duplicated. Where possible, we can improve efficiency by passing a reference to the object instead of using call by value. Alternatively, we can declare the procedure’s argument to be of type pointer-to-T. Passing a pointer is efficient, but we need to remember to pass the address of the object, and not the object itself. This is summarized in the following chart:
Declaration |
Invocation |
Efficiency |
|
void proc(T x) {...} |
proc(x); |
slow |
|
void |
proc(const T& x) { ... } |
proc(x); |
fast |
void |
proc(T* x) { ... } |
proc(&x); |
fast |
There is a natural way to avoid using new to create new instances of objects in the heap. Recall that primary purpose for doing this is to avoid passing large objects to and from a procedure. For example, a graph theory package would naturally include a procedure for partitioning the vertex set into connected components. This hypothetical procedure, named components, would take a single argument (of type Graph) and return a partition. The standard way to define such a procedure is like this:
Partition components(Graph g) { Partition p;
// ...
return p;
}
Then, when we have the statement P = components(G); the computer would first make a copy of G (to be held in g inside the procedure). Then the Partition computed by the procedure (p) is copied back to the calling procedure to be saved in P. If the graph is large, this is highly inefficient.
Some programmers would solve this problem by using pointers. In that paradigm, the procedure would look like this:
Partition* components(Graph* gp) { Partition* pp;
pp = new Partition; // ...
return pp;
}
358 |
C++ for Mathematicians |
To invoke this procedure, we use a statement like this: P_ptr=components(&G);. Now G does not have to be copied; a pointer to G is all that needs to be sent to the procedure and a pointer to the newly calculated partition is all that needs to be sent back. It is then incumbent on the programmer to remember to delete pp; or else suffer a memory leak.
There is a better, third alternative. We can use call-by-reference parameters to prevent repeated copying, and avoid pointers altogether. In this case, we define the procedure like this:
void components(const Graph& g, Partition& p) { // calculate p from g
}
In this case, we call the procedure with a statement of the form components(G,P);. The procedure would begin by erasing any data that happen to be in P and then overwriting with the new partition.
Thankfully, there are hardly any situations in C++ that require the use of a pointer. You may be required to use pointers when using other people’s packages. An instance of this is the Standard Template Library’s sort procedure (see Section 7.4).
Every method in a class may use a pointer named this. The this pointer refers to the object that invoked the method; that is, if we call X.method(), then method may use a pointer named this that points to X. This is handy if method modifies X and then wants to return a copy of X. This behavior is desirable when defining operators such as += for a class. See Section 6.8.
15.7Exercises
15.1Create a Card class to represent cards from a standard 52-card deck. The class should have the following features.
•A zero-argument constructor that creates a default card (say, the ace of spades) and a two-argument constructor that sets the the value and suit of the card. The user should be able to create cards such as this:
Card boss(ACE,SPADES);
Card weak(2, CLUBS);
•Comparison operators <, ==, and !=.
•An operator<< for writing cards to the screen in English words, such as
ace of spades or four of diamonds.
15.2 What is the difference between the following two statements?
T* xp = new T(10);
T* xp = new T[10];
360 |
C++ for Mathematicians |
•A value() method that returns a complex<double> value giving the approximate decimal value of the constructible number.
•An operator<< procedure that writes the number to an output stream in a suitable format such as TEX
2 + \sqrt{5 - \sqrt{2}}
or Mathematica
2 + Sqrt[5 - Sqrt[2]]
or some sensible method of your choosing.
15.6Tautology Checker. Create a program to check if Boolean expressions are tautologies. A Boolean expression is an algebraic expression involving variables (a, b, c, . . . ) and logical operations (and, or, not, implies, iff, etc.). A tautology is a Boolean expression that evaluates to TRUE for all possible truth values of its variables. For example, ((x → y) x) → y is a tautology.
The program should be invoked either (a) with a command-line argument specifying a file that contains the Boolean expression to be tested, or (b) with no command-line argument, in which case the user is prompted to type in the Boolean expression.
The variable names may be any single lowercase letter (a to z). Use the following for operation symbols.
Symbol |
Meaning |
+ |
or |
* |
and |
- |
not |
> |
implies → |
< |
implied by ← |
= |
equivalent ↔ |
0 |
TRUE |
1 |
FALSE |
|
|
Use reverse Polish notation (RPN) for the expressions (these are easier to process than ordinary algebraic expressions). For example, the expression ((x → y) x) → y would be entered as x y > x * y >. Spaces are optional, so this may also be entered as xy>x*y>.
It may be convenient for the user to enter an expression over several lines. The user should indicate that the expression is finished by typing a period.
Part IV
Appendices
Your C++ Computing Environment |
365 |
Macintosh tools
Macintosh OS X comes with many of the tools you need. For example, the Terminal application launches a bash shell.
If X11 has not been installed (see the Utilities folder inside the Applications folder), then install it using the disks (CD/DVD-ROM) that came with your computer.
You need the Developer Tools. Check if there is a folder named Developer at the top level of your hard drive (and be sure that inside that folder there is a subfolder named Applications containing the Xcode application). Alternatively, open a Terminal window and type the command: g++ --version. If the computer complains g++: command not found then you need to install the Developer Tools, but if it responds with the version of g++ installed, then you know that the compiler is already installed on your computer. The make program should also be installed (try make --version).
If the Developer Tools are not on your computer, they are either available on the disks that came with your computer or you can download them (for free) from Apple’s Web site (developer.apple.com). Get a free membership to their Developer Connection, navigate to Developer Tools, and download Xcode.
Next you need a text editor such as emacs. You can find Macintosh-style versions of emacs for free on the Web; see:
http://home.att.ne.jp/alpha/z123/emacs-mac-e.html
Finally, install the Doxygen application, available for free from doxygen.org. This is a standard Macintosh application that you install simply by dragging its icon to your Applications folder. (See Appendix B.)
A.1.2 Editing program files
To create the files for your C++ project you need a text editor such as emacs. Do not use a word processor, such as Microsoft Word, for this purpose.
A good programming editor makes your life easier. As you type your program, the editor automatically indents lines of code the proper amount so you can see the structure. It also highlights keywords in color. If, for some reason, the editor does not indent your typing the distance you expect or a keyword does not appear in color, then you have a quick visual clue that you mistyped something. Smart text editors (such as emacs) detect what sort of file you are editing and place you into an appropriate mode1 for that sort of file.
Begin by creating a folder (directory) where you want to save your program. If you wish to use code you have already created for another project (or code from this book on the accompanying CD), you can copy the files you want into this directory.
1In emacs, editing a .h file places you in an editing mode meant for C, and not for C++. To switch to C++ mode, type: M-x c++-mode where M-x means “meta-x”.