C++笔记(to be cont'd)

最近在看这个:LearnCpp

主要是一些我自己容易忽视的地方

记一些笔记在下面,还有错漏地方请不吝赐教

CH1.10 Preprocessing

The preprocessor copies the contents of the included file into the including file at the point of the #include directive

Conditional Compilation:

#ifndef SOME_UNIQUE_NAME_HERE
#define SOME_UNIQUE_NAME_HERE
#endif
CH2.1 Fundamental variable definition, initialization, and assignment

Favor implicit initialization over explicit initialization

int nValue = 5; // explicit initialization
int nValue(5); // implicit initialization

Uniform(list) initialization in C++11

int value{}; // default initialization to 0
int value{4.5}; // error: an integer variable can not hold a non-integer value

If you’re using a C++11 compatible compiler, favor uniform initialization

CH2.4 Integers

While short int, long int, and long long int are valid, the shorthand versions short, long, and long long should be preferred. In addition to being less typing, adding the prefix int makes the type harder to distinguish from variables of type int. This can lead to mistakes if the short or long modifier is inadvertently missed.

long long int lli; // valid
long long ll; // preferred

All integer variables except char are signed by default. Char can be either signed or unsigned by default (but is usually signed for conformity).

Generally, the signed keyword is not used (since it’s redundant), except on chars (when necessary to ensure they are signed).
Favor signed integers over unsigned integers

CH2.4a Fixed-width integers
<cstdint>:fixed width integers: int8_t/uint8_t/...uint64_t

Until this is clarified by a future draft of C++, you should assume that int8_t and uint8_t may or may not behave like char types.

int can be used when the integer size doesn’t matter and isn’t going to be large.
Fixed-width integers should be used in all other cases.
Only use unsigned types if you have a compelling reason.

CH2.5 Floating point numbers

Summation precision: Kahan summation algorithm

#include <iomanip> // for std::setprecision()

Float values have between 6 and 9 digits of precision, with most float values having at least 7 significant digits. Double values have between 15 and 18 digits of precision, with most double values having at least 16 significant digits. Long double has a minimum precision of 15, 18, or 33 significant digits depending on how many bytes it occupies.

Favor double over float unless space is at a premium, as the lack of precision in a float will often lead to challenges.

Rounding Error matters 0.1+...+0.1 \neq 1.0. Ref. CH3.5 -- Relational operators

CH2.7 Chars

Note that even though cin will let you enter multiple characters, ch will only hold 1 character. Consequently, only the first input character is placed in ch. The rest of the user input is left in the input buffer that cin uses, and can be accessed with subsequent calls to cin.

\n and endl:
Use std::endl when you need to ensure your output is output immediately (e.g. when writing a record to a file, or when updating a progress bar). Note that this may have a performance cost, particularly if writing to the output device is slow (e.g. when writing a file to a disk).
Use ‘\n’ in other cases.

wchar_t should be avoided in almost all cases (except when interfacing with the Windows API). Its size is implementation defined, and is not reliable. It has largely been deprecated.

You won’t need to use char16_t(UTF-16) or char32_t(UTF-32) unless you’re planning on making your program Unicode compatible and you are using 16-bit or 32-bit Unicode characters.

CH2.9 Const, constexpr, and symbolic constants

Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

Any variable that should not change values after initialization should be declared as const (or constexpr in C++11).

Avoid using #define to create symbolic constants, but use const variables to provide a name and context for your magic numbers.

A recommended way:

  1. Create a header file to hold these constants
  2. Inside this header file, declare a namespace
  3. Add all your constants inside the namespace (make sure they’re const)
  4. include the header file wherever you need it

Use the scope resolution operator (::) to access your constants in .cpp files

CH3.2 Arithmetic operators

Prior to C++11, if either of the operands of integer division are negative, the compiler is free to round up or down! For example, -5 / 2 can evaluate to either -3 or -2, depending on which way the compiler rounds. However, most modern compilers truncate towards 0 (so -5 / 2 would equal -2). The C++11 specification changed this to explicitly define that integer division should always truncate towards 0 (or put more simply, the fractional component is dropped).

Also prior to C++11, if either operand of the modulus operator is negative, the results of the modulus can be either negative or positive! For example, -5 % 2 can evaluate to either 1 or -1. The C++11 specification tightens this up so that a % b always resolves to the sign of a.

CH3.3 Increment/decrement operators, and side effects

Be aware of undefined expressions like:x = x++;
Don’t use a variable that has a side effect (if it modifies some state) applied to it more than once in a given statement.

CH3.4 Sizeof, comma, and conditional operators

Avoid using the comma operator, except within for loops.

Only use the conditional operator for simple conditionals where it enhances readability.
It’s worth noting that the conditional operator evaluates as an expression, whereas if/else evaluates as statements. This means the conditional operator can be used in some places where if/else can not.
For example, when initializing a const variable:

bool inBigClassroom = false;
const int classSize = inBigClassroom ? 30 : 20;

There’s no satisfactory if/else statement for this, since const variables must be initialized when defined, and the initializer can’t be a statement.

CH3.5 Relational operators (comparisons)

Directly comparing floating point values using any of these operators is dangerous. This is because small rounding errors in the floating point operands may cause unexpected results.

Donald Knuth, a famous computer scientist, suggested the following method in his book “The Art of Computer Programming, Volume II: Seminumerical Algorithms (Addison-Wesley, 1969)”:
#include

// return true if the difference between a and b is within epsilon percent of the larger of a and b
bool approximatelyEqual(double a, double b, double epsilon)
{
return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); 
}

The author suggests the following approach
// return true if the difference between a and b is less than absEpsilon, or within relEpsilon percent of the larger of a and b
bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon)
{
// Check if the numbers are really close -- needed when comparing numbers near zero.
double diff = fabs(a - b);
if (diff <= absEpsilon)
return true;

// Otherwise fall back to Knuth's algorithm
return diff <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * relEpsilon);
}

Comparison of floating point numbers is a difficult topic, and there’s no “one size fits all” algorithm that works for every case.

CH3.6 Logical operators

Any non-zero integer value evaluates to true when used in a boolean context. Mixing integer and boolean operations can be very confusing, and should be avoided!

Short circuit evaluation presents another opportunity to show why operators that cause side effects should not be used.

CH3.8 Bitwise operators

When dealing with bit operators, use unsigned integers.
Note that the results of applying the bitwise shift operators to a signed integer are compiler dependent.

Bit mask and bit flags:

Bit flags are typically used in two cases:

  1. When you have many sets of identical bitflags.
  2. Set options easily especially there are a lot of options (consider f(arg1,arg2...arg100))

manage bitflags:std::bitset

Bit mask: one application: color channel

CH4.1 Blocks (compound statements)

Note that variables inside nested blocks can have the same name as variable inside outer blocks. When this happens, the nested variable “hides” the outer variable. This is called name hiding or shadowing.

CH4.2 Global variables and linkage

By convention, many developers prefix global variable names with “g_” to indicate that they are global. This both helps identify global variables as well as avoids naming conflicts with local variables.

By default, non-const variables declared outside of a block are assumed to be external. However, const variables declared outside of a block are assumed to be internal.

Encapsulate the global variable.

CH4.3 Static duration variables

Static variables offer some of the benefit of global variables (they don’t get destroyed until the end of the program) while limiting their visibility to block scope. This makes them much safer for use than global variables.

When applied to a global variable, the static keyword defines the global variable as having internal linkage, meaning the variable cannot be exported to other files.

When applied to a local variable, the static keyword defines the local variable as having static duration, meaning the variable will only be created once, and will not be destroyed until the end of the program.

Don’t use the “using” keyword in the global scope. This includes header files!

CH4.4 Implicit type conversion (coercion)

Implicit Conversion: Numeric Promotion (no data loss) and Numeric Conversion (might lose data)

Conversion in the compiler:

  1. If the operand is an integer, it undergoes integral promotion (as described above).
  2. If the operands still do not match, then the compiler finds the highest priority operand and converts the other operand to match.

Const casts and reinterpret casts should generally be avoided because they are only useful in rare cases and can be harmful if used incorrectly.

Because C-style casts are not checked by the compiler at compile time, C-style casts can be inherently misused, thus: Avoid C-style casts

The main advantage of static_cast is that it provides compile-time type checking, making it harder to make an inadvertent error. Static_cast is also (intentionally) less powerful than C-style casts, so you can’t inadvertently remove const or do other things you may not have intended to do.

Using casts to make implicit type conversions clear

Use std::getline() to input text of a whole line
To read a full line of input into a string, you’re better off using the std::getline() function instead.

If reading numeric values with std::cin, it’s a good idea to remove the extraneous newline using std::cin.ignore(). (std::cin.ignore(32767, '\n');)

CH4.5 Enumerated types

Best practice: Don’t assign specific values to your enumerators.
Rule: Don’t assign the same value to two enumerators in the same enumeration unless there’s a very good reason.

C++11 defines a new concept, the enum class (also called a scoped enumeration), which makes enumerations both strongly typed and strongly scoped.

CH4.6 Typedefs

Typedefs allow you to change the underlying type of an object without having to change lots of code.

One big advantage of typedefs is that they can be used to hide platform specific details. On some platforms, an integer is 2 bytes, and on others, it is 4. Thus, using int to store more than 2 bytes of information can be potentially dangerous when writing platform independent code.

CH4.7 Struct

Warning: One of the easiest mistakes to make in C++ is to forget the semicolon at the end of a struct declaration. This will cause a compiler error on the next line of code. Modern compilers like Visual Studio 2010 will give you an indication that you may have forgotten a semicolon, but older or less sophisticated compilers may not, which can make the actual error hard to find.

C++ supports a faster way to initialize structs using an initializer list

Employee joe = { 1, 32, 60000.0 }; // joe.id = 1, joe.age = 32, joe.wage = 60000.0
Employee frank = { 2, 28 }; // frank.id = 2, frank.age = 28, frank.wage = 0.0 (default initialization)

Uniform initialization:

Employee joe { 1, 32, 60000.0 }; // joe.id = 1, joe.age = 32, joe.wage = 60000.0
Employee frank { 2, 28 }; // frank.id = 2, frank.age = 28, frank.wage = 0.0 (default initialization)

A function can also return a struct, which is one of the few ways to have a function return multiple variables.

It turns out, we can only say that the size of a struct will be at least as large as the size of all the variables it contains. But it could be larger! For performance reasons, the compiler will sometimes add gaps into structures (this is called padding).

你可能感兴趣的:(C++笔记(to be cont'd))