[笔记]C++笔记(1)

最近在看这个: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).


CH5.1-5.7 Control Flow

Rule: If defining variables used in a case statement, do so in a block inside the case (or before the switch if appropriate)

Rule: Avoid use of goto statements unless necessary

for (init-statement; condition-expression; end-expression)
statement;

The easiest way to understand a for loop

for (init-statement; condition-expression; end-expression)
statement;

is to convert it into an equivalent while loop:

{ // **note the block here**
init-statement;
while (condition-expression)
{
statement;
end-expression;
}
} // variables defined inside the loop go out of scope here

This is the only place in C++ where the comma operator typically gets used.

 int iii, jjj;
 for (iii=0, jjj=9; iii < 10; ++iii, --jjj)
 cout << iii << " " << jjj << endl;

For loops in old code

In older versions of C++, variables defined as part of the init-statement did not get destroyed at the end of the loop. This meant that you could have something like this:

for (int count=0; count < 10; ++count)
std::cout << count << " ";
 
// count is not destroyed in older compilers
 
std::cout << "\n";
std::cout << "I counted to: " << count << "\n"; // so you can still use it here
This use has been disallowed, but you may still see it in older code.

CH5.10 std::in

The following code will test for and fix failed extractions:

if (std::cin.fail()) // has a previous extraction failed?
{
// yep, so let's handle the failure
std::cin.clear(); // put us back in 'normal' operation mode
std::cin.ignore(32767,'\n'); // and remove the bad input
}

The following will also clear any extraneous input:

std::cin.ignore(32767,'\n'); // and remove the bad input

CH6.1 Size of

The sizeof operator can be used on arrays, and it will return the total size of the array (array length multiplied by element size). Note that due to the way C++ passes arrays to functions, this will not work properly for arrays that have been passed to functions!

void printSize(int array[])
{
std::cout << sizeof(array) << '\n'; // prints the size of a pointer, not the size of the array!
}

int main()
{
int array[] = { 1, 1, 2, 3, 5, 8, 13, 21 };
std::cout << sizeof(array) << '\n'; // will print the size of the array
printSize(array);
 
return 0;
}

CH6.7 Pointers (1)

A pointer is a variable that holds a memory address as its value.

* It is part of the pointer declaration syntax

Dereferencing a null pointer can cause bad things to happen. Deleting a null pointer is okay.

Syntactically, C++ will accept the asterisk next to the data type, next to the variable name, or even in the middle.

However, when declaring multiple pointer variables, the asterisk has to be included with each variable. It’s easy to forget to do this if you get used to attaching the asterisk to the type instead of the variable name!


int* iPtr6, iPtr7; // iPtr6 is a pointer to an int, but iPtr7 is just a plain int!
Initialize your pointers to a null value if you’re not giving them another value by:

int *ptr(0);  // ptr is now a null pointer

or

int *ptr2; // ptr2 is uninitialized
ptr2 = 0; // ptr2 is now a null pointer

Starting with C++11, this should be favored instead of 0 when we want a null pointer:
int *ptr = nullptr; // note: ptr is still an integer pointer, just set to a null value (0)

C++11 also introduces a new type called std::nullptr_t(in header <cstddef>). std::nullptr_t can only hold one value: nullptr! While this may seem kind of silly, it’s useful in one situation. If we want to write a function that accepts a nullptr argument, what type do we make the parameter? The answer is std::nullptr_t.

Pointers and arrays

It’s a common fallacy in C++ to believe an array and a pointer to the array are identical. They’re not. Although both point to the first element of the array. The confusion is primary caused by the fact that in many cases, when evaluated, a fixed array will “decay” (be implicitly converted) into a pointer to the first element of the array (essentially, losing its type information).
Differences: 1. While using the sizeof() operator. When used on a fixed array, sizeof returns the size of the entire array (array length * element size). When used on a pointer, sizeof returns the size of a memory address (in bytes). 2. While using the address-of operator (&). Taking the address of a pointer yields the memory address of the pointer variable. Taking the address of the array returns a pointer to the entire array. This pointer also points to the first element of the array, but the type information is different.

In most cases, because the pointer doesn’t know how large the array is, you’ll need to pass in the array size as a separate parameter.

Favor the pointer syntax (*) over the array syntax ([]) for array function parameters.

Arrays in structs and classes don’t decay.

Pointer arithmetic

Pointer arithmetic returns address of next element of the same type.


CH6.8 Symbolic constant for strings

Feel free to use C-style string symbolic constants if you need read-only strings in your program, but always make them const!

#include

int main()
{
char myName[] = "Alex";
std::cout << myName;
 
return 0;
}

The memory for example above is editable. This array will be deleted when it goes out of scope.

#include <iostream>
int main()
{
const char *myName = "Alex";
std::cout << myName;
 
return 0;
}

In the symbolic constant case, what usually happens is that this memory is read-only, and it will not go out of scope (can be returned).

Feel free to use C-style string symbolic constants if you need read-only strings in your program, but always make them const!

Side note for std::cout:

std::cout makes some assumptions about your intent. If you pass it a non-char pointer, it will simply print the contents of that pointer (the address that the pointer is holding). However, if you pass it an object of type char* or const char*, it will assume you’re intending to print a string.

#include

int main()
{
char c = 'Q';
std::cout << &c;
 
return 0;
}

In this case, the programmer is intending to print the address of variable c. However, &c has type char*, so std::cout tries to print this as a string. Use cout << (void *) c; instead.


CH6.9 Dynamic memory allocation

C++ memory allocations:

  • Static memory allocation happens for static and global variables. Memory for these types of variables is allocated once when your program is run and persists throughout the life of your program.
  • Automatic memory allocation happens for function parameters and local variables. Memory for these types of variables is allocated when the relevant block is entered, and freed when the block is exited, as many times as necessary.
  • Dynamic memory allocation allocate as user's request.

​ Assign memory size of type int and return a pointer.

int *ptr = new int
int *ptr1 = new int (5); // use direct initialization
int *ptr2 = new int { 6 }; // use uniform initialization, C++ 11

The user is responsible for dynamically allocated memory:

delete ptr; // return the memory pointed to by ptr to the operating system
ptr = 0; // set ptr to be a null pointer (use nullptr instead of 0 in C++11)

Do not delete a pointer that is not pointing to dynamically allocated memory.

To avoid dangling pointers, after deleting memory, set all pointers pointing to the deleted memory to 0 (or nullptr in C++11)

int *value = new (std::nothrow) int; // value will be set to a null pointer if the integer allocation fails

To check if above memory was successfully allocated, use if (!value).

Dynamically allocated memory effectively has no scope(but will be released after program terminates). However, the pointers used to hold dynamically allocated memory addresses follow the scoping rules of normal variables.  This will cause potentially memory leaks. Memory leaks also happen when alter pointer pointing to dynamically allocated memory to another piece of memory without freeing previous memory.

int *prt = new int[n] essentially called new [], that's why deleting this array should use delete []

sizeof cannot be used to dynamically allocated array or pointer to a fixed array. For-each also doesn't work in this case.

int fixedArray[5] = { 9, 7, 5, 3, 1 }; // initialize a fixed array in C++03
int *array = new int[5] { 9, 7, 5, 3, 1 }; // initialize a dynamic array **in C++11**
char *array = new char[14] { "Hello, world!" }; // doesn't work in C++11, works in C++14

CH6.10 Pointers (2)
Pointers and const

A pointer to a constant variable can point to a non-constant variable, but if the variable is accessed using this pointer, it cannot be modified. But this pointer can still point to another variable.

int value1 = 5;
const int *ptr = &value1; // ptr points to a const int

const pointer is a pointer whose value can not be changed after initialization. (The address it points to is fixed)

int value = 5;
int *const ptr = &value;
Reference variables

reference (&) is a type of C++ variable that acts as an alias to another variable.

int value = 5; // normal integer
int &ref = value; // reference to variable value

In this context, the ampersand does not mean “address of”, it means “reference to”.

References are treated as const, meaning that they must be initialized (cannot be initialized with const variable, unless using const reference const type&) and once initialized can not be reassigned.

References are used as function parameters to pass references and modify variables directly.

Rule: Pass non-pointer, non-fundamental data type variables by (const) reference.

A secondary use of references is to provide easier access to nested data. Consider the following struct.

// Define struct first
int &ref = other.something.value1;
// ref can now be used in place of other.something.value1

A reference acts like a const pointer that is implicitly dereferenced when accessed(ref and ptr are identical).

int value = 5;
int *const ptr = &value;
int &ref = value;

The reference should generally be preferred over pointers unless pointers are the only solution.

Rule: When using a pointer to access the value of a member, use operator >instead of operator.

For-each loop

Copy array values:

for (type var : array_or_any_list_structures)
   statement;

or

for (auto var : array_or_any_list_structures)
   statement;

Using references(preferred)

for (auto &var : array_or_any_list_structures)
	statement;

Use references or const references for your element declaration in for-each loops for performance reasons.

Note: For-each doesn't work with pointers (array size unknown).

Void (generic) pointers

A void pointer can point to objects of any data type. Henceforth, the void pointer must first be explicitly cast to another pointer type (static_cast<type_to_dereference*>) before it is dereferenced.

Undefined behaviors for void pointers: delete, pointer arithmetic.

Pointers to pointers

Example:

int value = 5;
 
int *ptr = &value;
std::cout << *ptr; // dereference pointer to int to get int value
 
int **ptrptr = &ptr;
std::cout << **ptrptr; // first dereference to get pointer to int, second dereference to get int value

The address of operator (operator&) requires an lvalue, but &value is an rvalue:

int value = 5;
int **ptrptr = &&value; // not valid

However, following is valid:

int **ptrptr = nullptr; // use 0 instead prior to C++11

A common use for pointers to pointers is to facilitate dynamically allocated multidimensional arrays:

  1. Compile-time constant:

    int (*array)[5] = new int[10][5]; or auto array = new int[10][5]; // C++11

  2. Variable size:

    int **array = new int*[n]; // allocate an array of n int pointers — rows
    for (int count = 0; count < n; ++count)
        array[count] = new int[m]; // columns
    

    De-allocate:

    for (int count = 0; count < n; ++count)
        delete[] array[count];
    delete[] array; // **this needs to be done last**
    

    If the array is regular, use one dimensional array and playing with indices.


CH6.15 std::array and std::vector

Always pass std::array by reference or const reference (to prevent the compiler from making a copy of the array when the array was passed to the function (for performance reasons))

std::array.size():array length; sizeof() size of an element multiplied by the array length

std::vector/array.at(index): does bound checking

·std::vector has a special implementation: can compact 8 booleans into a byte.


CH7.1 Function parameters and arguments
  1. Passing by value: used when passing fundamental data type and enumerators, do not use when passing arrays, structs, or classes.

  2. Passing by reference: when passing an argument by reference, always use a const references unless you need to change the value of the argument. Advantages:can be used to return multiple values from a function; must be initialized, so there’s no worry about null values. Disadvantages:  An argument passed by value and passed by reference looks the same in function declaration. (It is hard to tell if the passed value will be changed)

  3. Passing by address: Remember to check null pointers before dereference. Passing by address is COPYING address, i.e., the pointer will not be modified in the function. To change the address being passed, use

    type func(type *&prt_to_change, ptr_to_change is a reference to the pointer being passed. (Copying address of pointer being passed. Then any modification to ptr_to_change will apply to the original pointer.)

  4. So, to sum up: There is only pass by value (either value of a variable, or value of address, etc.)

Prefer pass by reference to pass by address whenever applicable. (as the later one is slower, i.e., needs dereference)

3 ways of return: value, address, reference.

Do NOT return by address if:

  • When returning variables that were declared inside the function (use return by value), otherwise might produce dangling pointers. (This rule also applies to return by reference)
  • When returning a large struct or class (use return by reference)

An interesting example:

int returnByValue()
{
    return 5;
}
 
int& returnByReference()
{
     static int x = 5; // static ensures x isn't destroyed when it goes out of scope
     return x;
}
 
int main()
{
    int value = returnByReference(); // case A -- ok, treated as return by value
    int &ref = returnByValue(); // case B -- compile error
    const int &cref = returnByValue(); // case C -- ok, the lifetime of return value is extended to the lifetime of cref
}

CH7.5 Inline functions

Inline is only a recommendation for compiler, the compiler may choose to ignore it for a lengthy function.

inline usually applies to small functions that are typically called inside loops and do not branch.


CH7.6 Function overloading and default parameters

Function overloading is a feature of C++ that allows us to create multiple functions with the same name, so long as they have different parameters.

The function’s return type is NOT considered when overloading functions; typedefs are not distinct

Function overloading works in the following way:

  1. Try to find exact match (by function arguments, NOT return type)

  2. If 1. failed, do promotion (internal type conversion)

    1. Char, unsigned char, and short -> int.
    2. Unsigned short -> int or unsigned int, depending on the size of an int
    3. Float -> double
    4. Enum -> int
  3. If 2. failed, do standard conversion

    1. Any numeric type -> any other numeric type, including unsigned (eg. int to float)
    2. Enum -> the formal type of a numeric type (eg. enum to float)
    3. Zero -> pointer type and numeric type (eg. 0 to char*, or 0 to float)
    4. A pointer -> a void pointer
  4. If 3. failed, find a match through user-defined conversion

Note: all standard conversions and user-defined conversions are considered equal, i.e., no priority. Default parameters do NOT count towards the parameters that make the function unique.

Another trick: floating numbers always follow with an "f". So print(3.14159); is print (double) NOT print(float).

To eliminate ambiguous function calls, either create a new function that accepts exactly the arguments or do explicit type conversion, e.g., static_cast.

All default parameters must be the rightmost parameters.

If the function has a forward declaration, put the default parameters there.


CH7.8 Function pointers

reinterpret_cast<void*>func force convert function to function address. func is a pointer to the function func and func() lets program jump to function's address.

Declare: Type (* pointer_name)(args).

Assign: pointer_name = func, or type (*pointer_name) (args) = func (return type, args should be exactly matching).

Call: (*pointer_name)(args)(explicit dereference). or pointer_name(args) (implicit dereference).

Pass default arguments to functions when using function parameters. This is because default parameters are resolved at compile-time but function pointers are dereferenced at run-time.

Typedef:

typedef bool (*validateFcn)(int, int);
bool validate(int x, int y, bool (*fcnPtr)(int, int)); // ugly
bool validate(int nX, int nY, validateFcn pfcn) // clean

Type alias (C++11)

using validateFcn = bool(*)(int, int); // type alias
bool validate(int nX, int nY, validateFcn pfcn) // clean

std::function (C++11)

#include <functional>
bool validate(int x, int y, std::function<bool(int, int)> fcn);
// std::function method that returns a bool and takes two int parameters

CH7.9 Stack, heap, recursion and vector

Program in the memory:

  • The code segment (also called a text segment), where the compiled program sits in memory, is typically read-only.
  • The bss segment (also called the uninitialized data segment), where zero-initialized global and static variables are stored.
  • The data segment (also called the initialized data segment), where initialized global and static variables are stored.
  • The heap, where dynamically allocated variables are allocated from.
  • The call stack, where function parameters, local variables, and other function-related information are stored.

The heap segment (also known as the “free store”) keeps track of memory used for dynamic memory allocation.

  • Allocated memory stays allocated until it is specifically deallocated (beware memory leaks) or the application ends (at which point the OS should clean it up).
  • Dynamically allocated memory must be accessed through a pointer. Dereferencing a pointer is slower than accessing a variable directly.

CPU register stores pointer to top of stack. Accessing stack is faster than heap.

std::vectorlength is how many elements are being used in the array, whereas capacity is how many elements were allocated in memory. (length <= capacity). resize()changes both length and capacity.

Array subscripts "[]" and "at()" are based on length, not capacity.

Vector stack operations:

  • push_back() pushes an element on the stack.
  • back() returns the value of the top element on the stack.
  • pop_back() pops an element off the stack.

Rule: Generally favor iteration over recursion (for performance and potential stack overflow)


CH7.11 Assert and NODEBUG

An assert statement is a preprocessor macro that evaluates a conditional expression. It lies in <cassert>. Error output contains the conditional expression that failed, along with line number of broken piece.

A trick:

assert(statement && "Informative error output");

Remember to check release option in your IDE before setting up preprocessor macros.

#define NDEBUG
 
// all assert() calls will now be ignored to the end of the file

Deactivate assert/exit in release model as this terminates program without doing clean-ups.


CH7.13 Command line arguments
int main(int argc, char *argv[] // preferred
or
int main(int argc, char** argv

argv[0] is (on most OS) the path and name of the current program. So argc>=1.

Use std::stringstream to parse string to int.

std::stringstream convert(argv[1]); // set up a stringstream variable named convert, initialized with the input from argv[1]
 
int myint;
if (!(convert >> myint)) // do the conversion
	myint = 0; // if conversion fails, set myint to a default value

posted on 2016-05-04 11:29  小硕鼠  阅读(272)  评论(0)    收藏  举报

导航