Footnotes:
Footnote on raw C++ arrays and why not to use
them.
Arrays cannot be declared without bounds in C++
the way they can in Java, and the brackets must follow the variable.
That is, "int[32]
arr;", "int
arr[32];", and
"int[]
arr;" for an unbounded array are all legal
Java declarations, but only the middle one is legal in C++. Java
prefers the first and third because it likes to think of
"int[]"
and "int[32]"
as separate types. The unbounded array "int[]
arr" could be translated
"int*
arr" in C++, because native C/C++ arrays "are"
pointers and can be coded that way, but we will not do so.
Instead, we shall use the ANSI Standard C++
vector
class and write "vector<int>
arr;" The vector
class has an "operator[]"
in it so that you can still write array references the familiar way,
e.g. as arr[17],
and you get a second option of arr.at(17)
that we will prefer only for strings. The angle brackets are the
syntax of a templated class in C++, which is a language
feature that Java lacks (but will add within a year or so, using the
Ada-inspired keyword generic).
The Java String
type is lowercased in C++ as string; we shall NOT be using
char arrays with their "\0"
terminators and the zillion bugs they cause for C and pre-ANSI C++
programmers, no-way Jose'! The ANSI classes provide the safety net of
automatic subscript range checking, which has proved to be far more
important (certainly during initial software development) than the
efficiency advantage of forgoing it.
Footnote on C++ zoo of types:
Indeed, C++ gives you a zoo of types associated to what you'd think would be one type, Foo:
All of these count as completely different types for the purpose of dispatching function calls in C++! For contrast, one reason Java rejected "const" was so that there would be one and only one "Foo type" in a program.
Footnote on const versus #define and on C++
namespaces.
The "#" is the standard notation for "C/C++ preprocessor directives. Before ANSI C and C++ standardized the const keyword, it was common to define constants via lines typified by #define PI 3.14159 (must be on a line by itself, with no semicolon). Better now, however, is the usage const double PI = 3.14159; This is safer because it makes PI a real program variable and gives it a type. This line CAN float freely in global file scope above main, and we will permit such usage, but let us mention a Java headache and its C++ solution. In Java one cannot import member fields of a class, just classes in a package. Hence pi must be called Math.PI in a program, and the square-root function must be Math.sqrt, and so on. ANSI C++ introduced a stripped-down kind of class called a namespace that is supposed to collect together related constants and names that don't really belong to a class but would be hard to keep track of if they just floated freely in global scope. The syntax is simply namespace Noo { followed by the declaration-initialization lines and a closing }. To eliminate the need to write e.g. Noo.PI, another file can have the lines "#include Noo.h" and "using namespace Noo;"---and then the constants etc. in that namespace are imported by their simple names. Indeed, the standard ANSI library functions are in a namespace called std and are imported by an (automatic?) line "using namespace std;". You will probbaly not need to care about namespaces in this course.
(Footnote on "int main()" and
"argc,argv".)
(By the way, requiring main to return an int is a latter-day ANSI rule, but most compilers look the other way on it---void main(...) compiles. The cryptic args/argc/argv are for doing "command-line arguments", and nicely illustrate the difference between Java and raw C/C++ arrays: Java arrays store their size with them, whereas C/C++ arrays do not, so that the separate arg-count variable is needed [and C/C++ programmers get into the habit when they pass an array to a function of also passing its length as a separate parameter]. The size of a Java array arr is available by calling arr.length()---weirdly, for a Java string str you use the public data field str.length :-(. The double-* in "char** argv" is because argv is an array (v is for vector, but it is not an ANSI vector) of char*, and a char*-style string is an array of char. For those of you who have used command-line arguments in Java, C and C++ insert the name of the file main is in as an "extra" element of argv, so indexes are off by one compared to the args array in Java. In any event, we will not have much need for command-line arguments in this course, and maybe the new ANSI standard provides a Java-like way of not having to muck with a "char**" here!)
Most aspects of coding style should be the same as in Java. There is a great debate in both languages about how to handle braces { ... }. What I regard as a classic case of a well-motivated idea that goes awry because of human temptation and random varying is the insistence on having { and } line up in the same column (or row). Thus a function gets coded as
void Foo(int arg) { statement1(); statement2(); }
The problems are that one gets tempted to write the following:
void Foo(int arg) { //comment is good after function header but not on same line as { statement1(); statement2(); } or void Foo(int arg) { statement1(); statement2(); } or void Foo(int arg) { statement1(); statement2(); } or even void Foo(int arg) { statement1(); statement2(); }
wondering in the last case how many statements one can "get away with" on the same line. These temptations are even worse with if (...) {...} else {...} blocks! The real problem is that the resulting lines become cumbersome to edit---you may delete a { and get a zillion error messages!
The "swish style" gives you ONE recommended way to code everything that maximizes later editing comfort.
void Foo(int arg) { //comment here has a line to itself. statement1(); statement2(); } void Bar(int x, int y) { if (x < y) { statement1(); } else { statement2(); } }
There is still the temptation to omit braces when the if-block is a single statement, but that always makes "else" look uneven right underneath the "if". The "} else {" looks weird at first sight but it "grows on you"---it is actually quite distinctive. And it is now easy to edit both blocks to add more statements. Finally, the swish style encourages you not to vary from your indenting increment---and then the indent itself becomes a nice check on whether you have the right number of closing braces. This also helps you minimize the number of indents you do---I absolutely cannot understand why some texts advocate writing
void Foo(int arg) { statement1(); statement2(); }
---yuck!
I like the "swish style" so much that I've even adopted it for ML, where it works nicely to solve indenting and line-chewing nasties of the if-then-else and let-in-end constructs, e.g.
fun f(x) = let val y = x*(x - 3) in if y > 7 then (y-7)*x else let val z = x*y in z*z - 4*x*y end
end;