《C++ Without Fear》學習筆記
書名:C++ Without Fear
A Beginner’s Guide That Makes You Feel Smart
作者:Brian Overland
閱讀時間:2018/10/6-2018/10/20
書摘+書評:
An important rule of good programming style is that variables should usually be initialized, which means giving them a value as soon as you declare them.
C++ also stores floating-point constants in double precision unless you specify otherwise (for example, by using the notation 12.5F instead of 12.5).
Type double stands for “double-precision floating point.”
building is the process of compiling and linking a program.
To print output and advance to the next line, use the cout object and send a newline character (endl). For example:
cout << "Never fear, C++ is here!" << endl;
Variables that may store a fractional portion should have type double. This stands for “double-precision floating point.” The single-precision type (float) should be used only when storing large amounts of floating-point data on disk.
You can assign values between integer and floating-point variables, but if you assign a floating-point value to an integer, the compiler complains about loss of data.
int n = 5;
double x = n; // Ok: convert 5 to floating-pt
n = 3.7; // Warning: convert from double to int
n = 3.0;/* This also gets a warning, because the presence of a decimal point (.) causes a value to be stored in floating-point (double) format.*/
What if, instead of executing one statement in response to a condition, you want to do a series of things? The answer is you can use a compound statement (or block):
if (x == y) {
cout << "x and y are equal." << endl;
cout << "Isn't that nice?";
they_are_equal = true;
}
The significance of the block is that either all these statements are executed or none of them is executed. If the condition is not true, the program jumps past the end of the block. A block can be plugged into the if statement syntax because of another cardinal rule:
✱ Anywhere you can use a statement in C++ syntax, you can use a compound statement (or block).
The block itself is not terminated by a semicolon (;)—only the statements inside are terminated by a semicolon. This is one of the few exceptions to the general rule that statements end with a semicolon in C++.
C++ treats “x = y” as an expression that produces a value. And there’d be nothing wrong with that, except that almost any valid numeric expression can be used as a condition. Therefore, the compiler does not complain if you write this, even though it’s usually wrong:
if (x = y)
// Do action... (Probably should have used ==)
The following statement divides by 2 and gets the remainder. This is called modulus or remainder division. The result is stored in a variable named (appropriately enough) “remainder.”
int remainder = n % 2;
Dividing two integers produces a quotient and a remainder.
int my_quotient = 17/3; // Result is 5.
int my_remainder = 17%3; // Result is 2.
Every Boolean (relational) operator returns 1 or 0. Also, any nonzero value fed to a condition is interpreted as true.
Operator var++ ++var var-- --var |
Action ACTION Pass along the current value of var; then add 1 to var. Add 1 to var; then pass along the result. Pass along the current value of var; then subtract 1 from it. Subtract 1 from var; then pass along the result. |
In many contexts, you can use either “i++” or “++i” and the effect will be the same. For example, there is no difference in results when the expression appears alone:
++i;
But while the postfix version (i++) used to be the preferred style for cases like this, the prefix version (++i) is now preferred by most C++ experts. When you’re working with integers, the difference between postfix and prefix increment is not significant, but when you work with more complex types, the prefix version potentially results in a more efficient program.
In general, you recognize a statement by its terminating semicolon (;).
cout << ++i << " ";
A simple statement such as this is usually one line of a C++ program. But remember that a semicolon terminates a statement, so it’s legal (though not recommended) to put two statements on a line:
cout << i << " "; ++i;
So, what’s an expression? An expression usually produces a value (with a few notable exceptions). You terminate an expression to get a simple statement. Here’s a sample list of expressions, along with descriptions of what value each produces:
x // Produces value of x
12 // Produces 12
x + 12 // Produces x + 12
x == 33 // Test for equality: true or false
x = 33 // Assignment: produces value assigned
++num // Produces value before incrementing
i = num++ + 2 // Complex expression; produces new value of i
Because these are expressions, any of them can be used as part of a larger expression, including assignment (=). The last three have side effects. x = 33 alters the value of x, and num++ alters the value of num. The last example changes the value of both num and i. Remember, any expression can be turned into a statement by using a semicolon (;).
++num;
The fact that any expression can be turned into a statement this way makes some strange statements possible. You could, for example, turn a literal constant into a statement, but such a statement would do exactly nothing.
12;
This is a legal statement in C++. Why would anyone write such a statement? The answer is they wouldn’t. It just illustrates the point that any expression in C++ (with a few exceptions) can be turned into a statement.
But that’s not to say that statements aren’t significant. By putting expressions into different statements, you guarantee that everything in one statement is evaluated before everything in the next statement. The danger of putting everything into a single statement is that it becomes difficult to predict the order of execution in extremely complex expressions.
j = i++; // Assign i to j, then inc. i.
Logical operators have lower precedence than the relational operators (<, >, >=, <=, !, and ==), which in turn have lower precedence than arithmetic operators (such as + and *). Consequently, the following statement does what you’d probably expect:
if (x + 2 > y && a == b) {
cout << "The data passes the test";
}
This means “If x + 2 is greater than y, and a is equal to b, then print the message.” You can, if you choose, make the program clearer for humans who read it by using parentheses—even though this doesn’t change what it does.
if (((x + 2) > y) && (a == b)) {
cout << "The data passes the test";
}
You can use any of the C++ operators (such as +, *, -, /, and %) without library support because operators are intrinsic to the language itself. But to use math functions, you need to include this line:
#include <cmath>
This #include directive brings in declarations for all the math functions, so you don’t have to prototype them yourself.
The values true and false are predefined in all but the most ancient versions of C++.
cout << n++; // Print n and then add 1 to n.
The call to srand, setting a random seed, must be made from within a function, such as main, but it need only be done
once in any program that is going to generate one or more random numbers.
type time_t is a long integer.
The case and default labels are just special cases of label names. Because a
labeled statement is itself a statement, it’s legal to have a statement that has many
labels. For example:
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
cout << "Char. Is a vowel.";
break;
cout << rand() % n; // Print random 0 to n - 1
Wherever you can use a statement in C++, you can also use a block.
The for statement does not require you to use all three expressions (initializer, condition, and increment). If initializer or increment is missing, it’s ignored. If the condition is omitted, it’s considered “true” by default, setting up an infinite loop.
for(;;) {
// Infinite loop!
}
Note that in a program, only main is guaranteed to be executed. Other functions run only when called.
C++ requires that a function be declared before being used: such a declaration can be either a prototype or a definition.
A function prototype provides type information only.
If the function does not return a value, use void.
Technically, you don’t need the argument names in a prototype, but it is a good programming practice.
double avg(double x, double y) {
return (x + y) / 2;
}
The return statement causes immediate exit and it specifies that the function returns the amount (x + y) / 2. Functions with no return value can still use the return statement, but only to exit early.
The general rule is that functions must be declared before being called. (They do not, however, have to be defined before being called.)
You can declare global—that is, nonlocal—variables by declaring them outside of any function definition. It’s usually best to put all global declarations near the beginning of the program, before the first function. A variable is recognized only from the point it is declared, to the end of the file.
Note that modification of global variables is another way for a function to change data that can affect the rest of the program. But in general, it’s best to have as few global variables as possible.
Since C++11, compilers have been required to support the nullptr keyword, which has a zero value but has pointer type.
Local variables are declared inside a function definition; global variables are declared outside all function definitions, preferably before main. If a variable is local, it is not shared with other functions; two functions can each have a variable named i (for example) without interfering with each other.
The elements of the array range from array_name[0] to array_name[size-1].
If a variable or an array is global, then by default C++ initializes it to zero. (In the case of arrays, C++ initializes every element to zero.) But local variables not initialized contain random meaningless values, also known as “garbage.”
The index number in a C or C++ array is not an ordinal number (that is, a position) as much as it is an offset. That is, the index number of an element is a measure of the distance from the beginning of the array.
And the first element, of course, is zero positions away from the beginning. The index of the first element is therefore 0.
A pointer is just a variable that stores the location of another piece of data.
When p points to n, referring to *p has the same effect as referring to n.
indirection operator (*) is often called the at operator.
A for statement is just another kind of statement; therefore, it can be put inside another if, while, or for statement, and so on, to any degree of complexity.
C++ interprets all array names as address expressions. arr[2] translates into the following:
*(arr + 2)
A C-string is a simple array of char.
In reality, the computer doesn’t actually store alphanumeric characters; it stores only numbers.
As programmers, we can think of C++ storing text characters in memory, one byte per character. (Exception: The international standard, Unicode, uses more than one byte per character.)
A string is an array of base type char.
Technically, char is an integer type, one byte wide, large enough to store 256 different values (ranging from 0 to 255).
The character \0 is C++ notation for a null character: it means the actual value 0 is stored in this byte (as opposed to value 48, the ASCII code for the digit “0”).
char s[] = "Hello!";
char *p = "Hello!";
The effect of these two statements is roughly the same, but there are some differences: s is considered to name an array; therefore s itself is a constant that cannot change. But technically speaking, p is a pointer rather than an array, and it can be reassigned to point to other locations.
strlen(s) | Return length of string s (not counting terminating null) |
As a general rule, using any standard-library function that begins with the three letters str requires you to include <cstring>.
In C++ source code, when the compiler reads a backslash (\), the very next character is interpreted as having a special meaning.
Think of an object as a data structure that comes with built-in knowledge of how to do certain things. The way you call upon an object’s abilities is to call a member function.
“white space” being programmer-ese for a blank space, tab, or newline.
'A' and "A" are different because one is converted to an integer value, whereas the other represents a string and so is converted to an address.
Expressions in double quotation marks (such as "A") are arrays of char and, as such, are translated into addresses.
the word token means a substring containing a single word.
The stream operator (>>), used with the cin object, provides only limited control over input. When you use it to send data to a string address, it only gets the characters up to the first white space (blank, tab, or newline).
File-stream objects provide a way to communicate with files.
In OOP design you begin by asking: what are the principal data structures you’re working on, and what actions need to be performed on each?
The term object refers to an instance in C++, especially an instance of a class.
A class declaration requires a semicolon after the closing brace (}), whereas a function definition should not be followed by a semicolon. (At best, you’d be producing a null statement.)
The dot (.) syntax says that a certain function applies to a particular object.
function definitions within the class can refer to class members directly, whether private or not.
When a function is inlined, the program does not transfer control to a separate block of code. Instead, the compiler replaces the function call with the body of the function.
You can make functions inline by placing their function definitions in the class declaration itself.
If a function definition is short, you can improve efficiency by writing it as an inline function. Remember, this is done just by simply including the function’s definition inside the class declaration itself, so it doesn’t need to be defined anywhere else.
In C++, the struct keyword is equivalent to the class keyword, except that in classes declared with struct, members are public by default.
for backward compatibility, C++ had to supply an automatic default constructor.
The crucial thing that references and pointers have in common here is that they just create another way to refer to an existing data item; they do not allocate new data.
Literals and array names can never be lvalues.
The break statement transfers execution out of the nearest enclosing while, do-while, for, or switch-case statement. Execution is transferred to the first statement past the end of the block.
the variable declaration
int **ptr;
means that **ptr, when it appears in executable code, is an item of type int; ptr itself is therefore a pointer to a pointer to an integer, because it would have to be dereferenced twice to produce an integer.
A pointer to a function needs to be assigned the address of a function before being called.
double (*fPointer)(double);
fPointer = &sqrt;
int x = (*fPointer)(5.0); // Call sqrt(5.0)
static: When used with a local variable or data member, it indicates there is only one copy of the variable. In the case of local variables, this means that the function “remembers” the value between function calls. It’s incompatible with recursive functions.