您的位置:首页 > 编程语言 > C语言/C++

C++ Primer Week2

2015-11-01 20:04 375 查看

C++ Primer Week 2

标签(空格分隔): c++

Chapter 4. Expressions

Section 4.1 Fundamentals

4.1.1. Basic Concepts

Key Words: unary operator; binary operator; operand; precedence and associativity; Overloaded Operators; Lvalues and Rvalues

Overloaded Operators: e.g,The IO library >> and << operators and the operators we used with strings, vectors, and iterators are all overloaded operators.

Lvalues and Rvalues: These names are inherited from C and originally had a simple mnemonic purpose: lvalues could stand on the left-hand side of an assignment whereas rvalues could not. In another way, we can say, we can use an lvalue when an rvalue is required, but we cannot use an rvalue when an lvalue (i.e., a location) is required.

SOMETHING DONT UNDERSTAND: Lvalues and rvalues also differ when used with decltype (§ 2.5.3, p. 70). When we apply decltype to an expression (other than a variable), the result is a reference type if the expression yields an lvalue. As an example, assume p is an int*. Because dereference yields an lvalue, decltype(*p) is int&. On the other hand, because the address-of operator yields an rvalue, decltype(&p) is int**, that is, a pointer to a pointer to type int.

4.1.2. Precedence and Associativity

compound expression

Key Points:

Parentheses Override Precedence and Associativity

When Precedence and Associativity Matter

for example:

int ia[] = {0,2,4,6,8}; // array with five elements of type int
int last = *(ia + 4); // initializes last to 8, the value of ia [4]
last = *ia + 4; // last = 4, equivalent to ia [0] + 4


If we want to access the element at the location ia+4, then the parentheses around the addition are essential. Without parentheses, *ia is grouped first and 4 is added to the value in *ia.

Table 4.4. Operator Precedence





4.1.3. Order of Evaluation

an example:

int i = 0;
cout << i << " " << ++i << endl; // undefined


Because this program is undefined, we cannot draw any conclusions about how it might be have. The compiler might evaluate ++i before evaluating i, in which case the output will be 1 1. Or the compiler might evaluate i first, in which case the output will be 0 1. Or the compiler might do something else entirely. Because this expression has undefined behavior, the program is in error, regardless of what code the compiler generates.

Key Point: Order of Evaluation, Precedence, and Associativity

Order of operand evaluation is independent of precedence and associativity. In an expression such as f() + g() * h() + j():

• Precedence guarantees that the results of g() and h() are multiplied.

• Associativity guarantees that the result of f() is added to the product of g() and h() and that the result of that addition is added to the value of j().

• There are no guarantees as to the order in which these functions are called.

If f, g, h, and j are independent functions that do not affect the state of the same objects or perform IO, then the order in which the functions are called is irrelevant. If any of these functions do affect the same object, then the expression is in error and has undefined behavior.

Section 4.2 Arithmetic Operators

Table 4.1. Arithmetic Operators (Left Associative)



Caution: Overflow and Other Arithmetic Exceptions

Consider a machine on which shorts are 16 bits. In that case, the maximum short is 32767. On such a machine, the following compound assignment overflows:

short short_value = 32767; // max value if shorts are 16 bits
short_value += 1; // this calculation overflows
cout << "short_value: " << short_value << endl;


The assignment to short_value is undefined. Representing a signed value of 32768 requires 17 bits, but only 16 are available. On many systems, there is no compile-time or run-time warning when an overflow occurs. As with any undefined behavior, what happens is unpredictable. On our system the program completes and writes

short_value: -32768


The value “wrapped around”: The sign bit, which had been 0, was set to 1, resulting in a negative value. On another system, the result might be different, or the program might behave differently, including crashing entirely.

C++11 for modulus operator:

The modulus operator is defined so that if m and n are integers and n is nonzero, then (m/n)*n + m%n is equal to m. By implication, if m%n is nonzero, it has the same sign as m. Earlier versions of the language permitted m%n to have the same sign as n on implementations in which negative m/n was rounded away from zero, but such implementations are now prohibited. Moreover, except for the obscure case where -m overflows, (-m)/n and m/(-n) are always equal to -(m/n), m%(-n) is equal to m%n, and (-m)%n is equal to -(m%n). More concretely:

21 % 6; /* result is 3 */
21 % 7; /* result is 0 */
-21 % -8; /* result is -5 */
21 % -5; /* result is 1 */
21 / 6; /* result is 3 */
21 / 7; /* result is 3 */
-21 / -8; /* result is 2 */
21 / -5; /* result is -4 */


Exercise 4.6: Write an expression to determine whether an int value is even or odd.

int x;
if (x % 2 == 1)
cout<<"x is an odd"<<endl;
else
cout<<"x is an even"<<endl;


Section 4.3 Logical and Relational Operators

Table 4.2. Logical and Relational Operators



Logical AND and OR Operators

short-circuit evaluation:

• The right side of an && is evaluated if and only if the left side is true.

• The right side of an || is evaluated if and only if the left side is false.

Exercise 4.11: Write an expression that tests four values, a, b, c, and d, and ensures that a is greater than b, which is greater than c, which is greater than d.

if (a > b && b > c && c > d )


Section 4.4 Assignment Operators

If the types of the left and right operands differ, the right-hand operand is converted to the type of the left:

k = 0; // result: type int, value 0
k = 3.14159; // result: type int, value 3


C++11

k = {3.14}; // error: narrowing conversion
vector<int> vi; // initially empty
vi = {0,1,2,3,4,5,6,7,8,9}; // vi now has ten elements, values 0 through 9


If the left-hand operand is of a built-in type, the initializer list may contain at most one value, and that value must not require a narrowing conversion (§ 2.2.1, p. 43). For class types, what happens depends on the details of the class. In the case of vector, the vector template defines its own version of an assignment operator that can take an initializer list. This operator replaces the elements of the left-hand side with the elements in the list on the right-hand side. Regardless of the type of the left-hand operand, the initializer list may be empty. In this case, the compiler generates a value-initialized (§ 3.3.1, p. 98) temporary and assigns that value to the left-hand operand.

Assignment Has Low Precedence

int i;
// a better way to write our loop---what the condition does is now clearer
while ((i = get_value()) != 42) {
// do something ...
}


Because assignment has lower precedence than the relational operators, parentheses are usually needed around assignments in conditions.

Beware of Confusing Equality and Assignment Operators

Compound Assignment Operators

+= -= *= /= %= // arithmetic operators
<<= >>= &= ^= |= // bitwise operators; see § 4.8 (p. 152)


Section 4.5 Increment and Decrement Operators

increment (++) and decrement (–) operators

Advice: Use Postfix Operators only When Necessary

Combining Dereference and Increment in a Single Expression

The precedence of postfix increment is higher than that of the dereference

operator

auto pbeg = v.begin();
// print elements up to the first negative value
while (pbeg != v.end() && *beg >= 0)
cout << *pbeg++ << endl; // print the current value and advance pbeg


The precedence of postfix increment is higher than that of the dereference operator, so pbeg++ is equivalent to (pbeg++). The subexpression pbeg++ increments pbeg and yields a copy of the previous value of pbeg as its result. Accordingly, the operand of * is the unincremented value of pbeg. Thus, the statement prints the element to which pbeg originally pointed and increments pbeg.

This usage relies on the fact that postfix increment returns a copy of its original, unincremented operand. (这种用法的根据在于后自增操作返回其操作数原值(没有加1)的副本) If it returned the incremented value, we’d dereference the incremented value, with disastrous results. We’d skip the first element. Worse, if the sequence had no negative values, we would attempt to dereference one too many elements.

Advice: Brevity Can Be a Virtue

please use

cout << *iter++ << endl;


instead of

cout << *iter << endl;
++iter;


Remember That Operands Can Be Evaluated in Any Order

remenber to read again

Section 4.6 The Member Access Operators

The dot (§ 1.5.2, p. 23) and arrow (§ 3.4.1, p. 110) operators provide for member

access.

string s1 = "a string", *p = &s1;
auto n = s1.size(); // run the size member of the string s1
n = (*p).size(); // run size on the object to which p points
n = p->size(); // equivalent to (*p).size()


Because dereference has a lower precedence than dot, we must parenthesize the dereference subexpression. If we omit the parentheses, this code means something quite different:

// run the size member of p, then dereference the result!
*p.size(); // error: p is a pointer and has no member named size


Section 4.7 The Conditional Operator

The conditional operator (the ?: operator) lets us embed simple if-else logic inside an expression. The conditional operator has the following form:

cond ? expr1 : expr2;


Nesting Conditional Operations

(Nested conditionals quickly become unreadable. It’s a good idea to nest no more than two or three.)

e.g,

finalgrade = (grade > 90) ? "high pass": (grade < 60) ? "fail" : "pass";


Using a Conditional Operator in an Output Expression

Key Point: The conditional operator has fairly low precedence.

cout << ((grade < 60) ? "fail" : "pass"); // prints pass or fail
cout << (grade < 60) ? "fail" : "pass"; // prints 1 or 0!
cout << grade < 60 ? "fail" : "pass"; // error: compares cout to 60


The second expression uses the comparison between grade and 60 as the operand to the << operator. The value 1 or 0 is printed, depending on whether grade < 60 is true or false. The << operator returns cout, which is tested as the condition for the conditional operator. That is, the second expression is equivalent to

cout << (grade < 60); // prints 1 or 0
cout ? "fail" : "pass"; // test cout and then yield one of the two literals
// depending on whether cout is true or false


The last expression is an error because it is equivalent to

cout << grade; // less-than has lower precedence than shift, so print grade first
cout < 60 ? "fail" : "pass"; // then compare cout to 60!


Section 4.8 The Bitwise Operators

Table 4.3. Bitwise Operators (Left Associative)



Bitwise Shift Operators

an example:



The left-shift operator (the << operator) inserts 0-valued bits on the right. The behavior of the right-shift operator (the >> operator) depends on the type of the left-hand operand: If that operand is unsigned, then the operator inserts 0-valued bits on the left; if it is a signed type, the result is implementation defined—either copies of the sign bit or 0-valued bits are inserted on the left.

Bitwise NOT Operator (the ~ operator)

Bitwise AND, OR, and XOR Operators

Using Bitwise Operators

–remember to read P214

Section 4.9 The sizeof Operator

The sizeof operator returns the size, in bytes, of an expression or a type name.

The operator takes one of two forms:

sizeof (type)
sizeof expr


Sales_data data, *p;
sizeof(Sales_data); // size required to hold an object of type Sales_data
sizeof data; // size of data's type, i.e., sizeof(Sales_data)
sizeof p; // size of a pointer
sizeof *p; // size of the type to which p points, i.e., sizeof(Sales_data)
sizeof data.revenue; // size of the type of Sales_data's revenue member
sizeof Sales_data::revenue; // alternative way to get the size of revenue


C++11

The result of applying sizeof depends in part on the type involved:

• sizeof char or an expression of type char is guaranteed to be 1.

• sizeof a reference type returns the size of an object of the referenced type.

• sizeof a pointer returns the size needed hold a pointer.

• sizeof a dereferenced pointer returns the size of an object of the type to

which the pointer points; the pointer need not be valid.

• sizeof an array is the size of the entire array. It is equivalent to taking the

sizeof the element type times the number of elements in the array. Note that

sizeof does not convert the array to a pointer.

• sizeof a string or a vector returns only the size of the fixed part of these types; it does not return the size used by the object’s elements.

One Method to calculate the numbers of array:

// sizeof(ia)/sizeof(*ia) returns the number of elements in ia
constexpr size_t sz = sizeof(ia)/sizeof(*ia);
int arr2[sz]; // ok sizeof returns a constant expression § 2.4.4 (p. 65)


Section 4.10 Comma Operator

The left-hand expression is evaluated and its result is discarded. The result of a comma expression is the value of its right-hand expression. The result is an lvalue if the right-hand operand is an lvalue.

逗号表达式的结果通常是其最右边表达式的值。如果最右边的操作数是左值,则逗号表达式的值也是左值。

Section 4.11 Type Conversions

Key Words: Converseion; implicit type conversion

4.11.1. The Arithmetic Conversions

Key Words: integral promotion; Operands of Unsigned Type

The integral promotions convert the small integral types to a larger integral type. The types bool, char, signed char, unsigned char, short, and unsigned short are promoted to int if all possible values of that type fit in an int.

Operands of Unsigned Type

remember to read

Understanding the Arithmetic Conversions

remember to read

4.11.2. Other Implicit Conversions

Key Words: Array to Pointer Conversions; Pointer Conversions; Conversions to bool; Conversion to const; Conversions Defined by Class Types

remember to review

4.11.3. Explicit Conversions (cast)

Although necessary at times, casts are inherently dangerous constructs.

Key Words: Named Casts;{static_cast; dynamic_cast; const_cast; reinterpret_cast}

Named Casts

cast-name<type>(expression);


Section 4.12 Operator Precedence Table

Table 4.4(前面)

Chapter 5. Statements

Section 5.1 Simple Statements

Null Statements

; // null statement


an example:

// read until we hit end-of-file or find an input equal to sought
while (cin >> s && s != sought)
; // null statement


Beware of Missing or Extraneous Semicolons

无关的空语句并非总是无害的。

Compound Statements (Blocks)

We also can define an empty block by writing a pair of curlies with no statements.An empty block is equivalent to a null statement:

while (cin >> s && s != sought)
{ } // empty block


Section 5.2 Statement Scope

Variables defined in the control structure are visible only within that statement and are out of scope after the statement ends.

Section 5.3 Conditional Statements

5.3.1. The if Statement

Dangling else

solution:Statements do not span block boundaries, so the inner if ends at the close curly before the else. The else cannot be part of the inner if. Now, the nearest unmatched if is the outer if, which is what we intended all along.(每个if后面都加上花括号)

5.3.2. The switch Statement

an example:

// if ch is a vowel, increment the appropriate counter
switch (ch) {
case 'a': case 'e': case 'i': case 'o': case 'u':
++vowelCnt;
break;
default:
++otherCnt;
break;
}


Section 5.4 Iterative Statements

5.4.1. The while Statement

5.4.2. Traditional for Statement

5.4.3. Range for Statement

C++11:

for (declaration : expression)
statement


an example:

vector<int> v = {0,1,2,3,4,5,6,7,8,9};
// range variable must be a reference so we can write to the elements
for (auto &r : v) // for each element in v
r *= 2; // double the value of each element in v


Section 5.5 Jump Statements

5.5.1. The break Statement

5.5.2. The continue Statement

5.5.3. The goto Statement

Section 5.6 try Blocks and Exception Handling(need to review)

In C++, exception handling involves:

• throw expressions, which the detecting part uses to indicate that it encountered something it can’t handle. We say that a throw raises an exception.

• try blocks, which the handling part uses to deal with an exception. A try block starts with the keyword try and ends with one or more catch clauses. Exceptions thrown from code executed inside a try block are usually handled by one of the catch clauses. Because they “handle” the exception, catch clauses are also known as exception handlers.

• A set of exception classes that are used to pass information about what happened between a throw and an associated catch.

5.6.1. A throw Expression

5.6.2. The try Block

#include <stdexcept>
using std::runtime_error;

#include <iostream>
using std::cin; using std::cout; using std::endl;

#include "Sales_item.h"

int main()
{
Sales_item item1, item2;

while (cin >> item1 >> item2) {
try {
// execute code that will add the two Sales_items
// if the addition fails, the code throws a runtime_error exception
// first check that the data are for the same item
if (item1.isbn() != item2.isbn())
throw runtime_error("Data must refer to same ISBN");

// if we're still here, the ISBNs are the same
cout << item1 + item2 << endl;
} catch (runtime_error err) {
// remind the user that the ISBNs must match
// and prompt for another pair
cout << err.what()
<< "\nTry Again?  Enter y or n" << endl;
char c;
cin >> c;
if (!cin || c == 'n')
break;      // break out of the while loop
}  // ends the catch clause
}  // ends the while loop

return 0;   // indicate success
}


5.6.3. Standard Exceptions

The C++ library defines several classes that it uses to report problems encountered in the functions in the standard library.

These classes are defined in four headers:

• The exception header defines the most general kind of exception class named exception. It communicates only that an exception occurred but provides no additional information.

• The stdexcept header defines several general-purpose exception classes, which are listed in Table 5.1.

Table 5.1. Standard Exception Classes Defined in
<stdexcept>




• The new header defines the bad_alloc exception type, which we cover in §12.1.2 (p. 458).

• The type_info header defines the bad_cast exception type, which we cover in § 19.2 (p. 825).

Chapter 6. Functions

Section 6.1 Function Basics

Parameters and Arguments:

fact(3.14); // 3.14 is an argument


int fact(int val) //val is a parameter
{
int ret = 1; // local variable to hold the result as we calculate it
while (val > 1)
ret *= val--; // assign ret * val to ret and decrement val
return ret; // return the result
}


Section 6.2 Argument Passing

Key Words: passed by reference; passed by value

Using Reference Parameters to Return Additional Information

Section 6.3 Return Types and the return Statement

Section 6.4 Overloaded Functions

Functions that have the same name but different parameter lists and that appear in the same scope are overloaded.

6.4.1. Overloading and Scope

Section 6.5 Features for Specialized Uses

Section 6.6 Function Matching

Section 6.7 Pointers to Functions

// compares lengths of two strings
bool lengthCompare(const string &, const string &);


has type bool(const string&, const string&). To declare a pointer that can point at this function, we declare a pointer in place of the function name:

// pf points to a function returning bool that takes two const string references
bool (*pf)(const string &, const string &); // uninitialized


pf is a pointer which points to a function that has two const string& parameters and returns bool.

note: without using parentheses, the meaning of pf is totally different

// declares a function named pf that returns a bool*
bool *pf(const string &, const string &);


Using Function Pointers

pf = lengthCompare; // pf now points to the function named lengthCompare
pf = &lengthCompare; // equivalent assignment: address-of operator is optional


Moreover, we can use a pointer to a function to call the function to which the pointer points. We can do so directly—there is no need to dereference the pointer:

bool b1 = pf("hello", "goodbye"); // calls lengthCompare
bool b2 = (*pf)("hello", "goodbye"); // equivalent call
bool b3 = lengthCompare("hello", "goodbye"); // equivalent call


There is no conversion between pointers to one function type and pointers to another function type. However, as usual, we can assign nullptr (§ 2.3.2, p. 53) or a zero-valued integer constant expression to a function pointer to indicate that the pointer does not point to any function:

string::size_type sumLength(const string&, const string&);
bool cstringCompare(const char*, const char*);
pf = 0; // ok: pf points to no function
pf = sumLength; // error: return type differs
pf = cstringCompare; // error: parameter types differ
pf = lengthCompare; // ok: function and pointer types match exactly


Pointers to Overloaded Functions

void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff; // pf1 points to ff(unsigned)

void (*pf2)(int) = ff; // error: no ff with a matching parameter list
double (*pf3)(int*) = ff; // error: return type of ff and pf3 don't match


the compiler uses the type of the pointer to determine which overloaded function to

use.

Function Pointer Parameters use function pointer as a parameter

// third parameter is a function type and is automatically treated as a pointer to function
void useBigger(const string &s1, const string &s2,
bool pf(const string &, const string &));
// equivalent declaration: explicitly define the parameter as a pointer to function
void useBigger(const string &s1, const string &s2,
bool (*pf)(const string &, const string &));


Returning a Pointer to Function

Using auto or decltype for Function Pointer Types
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: