Skip to content

Instantly share code, notes, and snippets.

@xu20160924
Last active April 9, 2022 13:55
Show Gist options
  • Save xu20160924/cfd1d8debe05f03b731ff05830e3838b to your computer and use it in GitHub Desktop.
Save xu20160924/cfd1d8debe05f03b731ff05830e3838b to your computer and use it in GitHub Desktop.

C++ 5nth primer

线程

优先使用线程池

  • 线程池是线程的更高层的抽象
  • 更容易扩展

处理错误

  • C++编码规范不鼓励异常,除非已有项目/底层库使用了异常,这时候必须要catch所有异常

特性

  • C++11 通过enable_if 可以实现重载返回值

代码规范

  • include 的头文件保持代码目录

高内聚

  • 如果每个成员函数都需要访问成员变量(以每个函数访问成员变量的个数作为判断标准),那么类就是高内聚的

Basics

  • Scope operator, when the scope operator has an empty left-hand side, it is a request to fetch the name on the right-hand side from the global scope

Identifiers

  • The identifiers we define in our own programs may not contain two consecutive underscores, nor can an identifier begin with an underscore followed immediately by an uppercase letter. In addition, identifiers defined outside a function may not begin with an underscore.

Pointer && Reference

  • The value (i.e., the address) stored in a pointer can be in one of four states:

    1. It can point to an object.
    2. It can point to the location just immediately past the end of an object.
    3. It can be a null pointer, indicating that it is not bound to any object.
    4. It can be invalid; values other than the preceding three are invalid.
  • Our recommendation to initialize all variables is particularly important for pointers. If possible, define a pointer only after the object to which it should point has been defined. If there is no object to bind to a pointer, then initialize the pointer to nullptr or zero. That way, the program can detect that the pointer does not point to an object.

  • Reference is not an object. Once we have defined a reference, there is no way to make that reference refer to a different object. When we use a reference, we always get the object to which the reference was initially bound. There is no such identity between a pointer and the address that it holds. As with any other (nonreference) variable, when we assign to a pointer, we give the pointer itself a new value. Assignment makes the pointer point to a different object

  • Any nonzero pointer evaluates as true in condition.

  • To understand complicated pointer or reference declarations which read them from right to left.

  • Two exceptions to the rule that the type of a reference must match the type of the object to which it refers.

    1. Reference to const
  • Two exceptions to the rule that the types of a pointer and the object to which it points must match

    1. Pointer to const to point to a nonconst object
  • decltyuupe((variable)) is always a reference type, but decltype(variable) is a reference type only if variable is a reference.

const

  • To share a const object among multiple files, you must define the variable as extern
  • there are no const references. A reference is not an object, so we cannot make a reference itself const. Indeed, because there is no way to make a reference refer to a different object, in some sense all references are const.
  • const in reference types is always low-level
  • const reference can receive literals, but non const reference not

array

  • It can be easier to understand array declarations by starting with the array’s name and reading them from the inside out.
  • To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references
  • Array Parameters
    • We cannot copy an array, and when we use an array it is (usually) converted to a pointer (§ 3.5.3, p. 117). Because we cannot copy an array, we cannot pass an array by value. Because arrays are converted to pointers, when we pass an array to a function, we are actually passing a pointer to the array’s first element.

tmp

  • 动态库 静态库
  • References "rvalue reference" "lvalue reference"
  • void * pointer
  • Code inside headers ordinarily should not use using declarations.

variable define

  • The value of an object of built-in type that is not explicitly initialized depends on where it is defined. Variables defined outside any function body are initialized to zero. With one exception, which we cover in § 6.1.1 (p. 205), variables of built-in type defined inside a function are uninitialized.

Expressions

Advice: Managing Compound Expressions

  1. When in doubt, parenthesize expressions to force the grouping that the logic of your program requires.
  2. If you change the value of an operand, don’t use that operand elsewhere in the same expresion.
  • Because assignment has lower precedence than the relational operators, parentheses are usually needed around assignments in conditions.
  • Dereference has a lower precedence than dot

Type Conversions

  • When we use an old-style cast where a static_cast or a const_cast would be legal, the old-style cast does the same conversion as the respective named cast. If neither cast is legal, then an old-style cast performs a reinterpret_cast

Summary

  • Precedence determines how operators are grouped in a compound expression. Associativity determines how operators at the same precedence level are grouped.

class

class VS struct

  • The difference between struct and class is the default access level
    • If we use the struct keyword, the members defined before the first access specifier are public
    • If we use class, then the members are private.

unclassified

  • A const member function that returns *this as a reference should have a return type that is a reference to const

lambda

  • As a rule, we should avoid potential problems with captures by minimizing the data we capture. Moreover, if possible, avoid capturing pointers or references.

Iterator

  • The * and ++ operators do nothing on an ostream_iterator
  • The iterator categories are input, output, forward, bidirectional, and random access.

Container

  • Excepting array, swap does not copy, delete, or insert any elements and is guaranteed to run in constant time.
  • COntainer operations may invalidate iterators、pointers or references, for the more details
  • All the containers (except array) provide efficient dynamic memory management.

unordered container

  • Use an unordered container if the key type is inherently unordered or if performance testing reveals problems that hashing might solve.

Dqueue

  • A deque guarantees constant-time insert and delete of elements at the beginning and end of the container. As with vector, inserting elements other than at the front or back of a deque is a potentially expensive operation.
associative container
  • Subscripting a map behaves quite differently from subscripting an array or vector: Using a key that is not already present adds an element with that key to the map. Because the subscript operator on map might insert an element, we may use subscript only on a map that is not const.

Dynamic memory

  • Local, automatic objects are created and destroyed when the block in which they are defined is entered and exited
  • Local static objects are allocated before their first use and are destroyed when the program ends.
  • If you put shared_ptrs in a container, and you subsequently need to use some, but not all, of the elements, remember to erase the elements you no longer need. If do not, the shared_ptrs will not freed.
  • Programs tend to use dynamic memory for one of three purposes
  1. They don’t know how many objects they’ll need
  2. They don’t know the precise type of the objects they need
  3. They want to share data between several objects
  • Dynamic memory managed through built-in pointers (rather than smart pointers) exists until it is explicitly freed.

  • It is dangerous to use a built-in pointer to access an object owned by a smart pointer, because we may not know when that object is destroyed.

  • Use get only to pass access to the pointer to code that you know will not delete the pointer. In particular, never use get to initialize or assign to another smart pointer.

  • Smart Pointer Pitfalls

  1. Don’t use the same built-in pointer value to initialize (or reset) more than one smart pointer.
  2. Don’t delete the pointer returned from get().
  3. Don’t use get() to initialize or reset another smart pointer.
  4. If you use a pointer returned by get(), remember that the pointer will become invalid when the last corresponding smart pointer goes away.
  5. If you use a smart pointer to manage a resource other than memory allocated by new, remember to pass a deleter

C++ 基础

变量和基础类型

  • 在算数表达式中不要使用char或bool, 只有在存放字符或布尔值时才使用它们。因为类型char在一些机器上是有符号的,而在另一些机器上又是无符号的,所以如果使用char进行运算特别容易出问题。如果你需要使用一个不大的整数,那么明确指定它的类型是signed char 或者 unsigned char.

Generic algorithm

  • Algorithms never directly change the size of the sequences on which they operate. They may copy elements from one position to another but cannot directly add or remove elements.

Advice: Deciding which Type to Use

  • Use int for integer arithmetic. short is usually too small and, in practice long often has the same size as int. I f your data values are larger than the minimum guaranteed size of an int, then use long long.
  • Do not use plain char or bool in arithmetic expressions. Use them only to hold characters or truth values. Computations using char are especially problematic because char is signed on some machines and unsigned on others. If you need a tiny integer, explicitly specify either signed char or unsigned char.
  • Use double for floating-point computations; float usually does not have enough precision, and the cost of double-precision calculations versus single-precision is negligible. In fact, on some machines, double-precision operations are faster than single. The precision offered by long double usually is unnecessary and often entails considerable run-time cost.
  • If a \ is followed by more than three octal digits, only the first three are associated with the

Chapter 13

Copy initialization happens not only when we define variables using an =, but also when we

  • Pass an object as an argument to a parameter of nonreference type
  • Return an object from a function that has a nonreference return type
  • Brace initialize the elements in an array or the members of an aggregate class

Rule of thumb about copy-assignmetn operator、 copy constructor、 destructor

  • If a class needs a destructor, it almost surely also needs the copy-assignment operator and a copy constructor.
  • If a class needs a copy constructor, it almost surely needs a copy-assignment operator. And vice versa—if the class needs an assignment operator, it almost surely needs a copy constructor as well. Nevertheless, needing either the copy constructor or the copy-assignment operator does not (necessarily) indicate the need for a destructor.

deleted

  • In essence, the copy-control members are synthesized as deleted when it is impossible to copy, assign, or destroy a member of the class.

Copy state

  • The library containers and string class have valuelike behavior.

  • Not surprisingly, the shared_ptr class provides pointerlike behavior

  • The IO types and unique_ptr do not allow copying or assignment, so they provide neither valuelike nor pointerlike behavior.

  • Assignment operators that use copy and swap are automatically exception safe and correctly handle self-assignment.

  • rvalue reference: An rvalue reference is a reference that must be bound to an rvalue.

  • Lvalues Persist; Rvalues Are Ephemeral

    • Lvalues have persistent state, whereas rvalues are either literals or temporary objects created in the course of evaluating expressions.
  • Move constructors and move assignment operators that cannot throw exceptions should be marked as noexcept.

  • The compiler synthesizes the move constructor and move assignment only if a class does not define any of its own copy-control members and only if all the data members can be moved constructed and move assigned, respectively.

  • Classes that define a move constructor or move-assignment operator must also define their own copy operations. Otherwise, those members are deleted by default.

  • The move constructor and move-assignment operator take a (usually nonconst) rvalue reference; the copy versions take a (usually const) ordinary lvalue reference.

Chapter 14

  • Ordinarily, the comma, address-of, logical AND, and logical OR operators should not be overloaded.
  • If a single logical definition for < exists, classes usually should define the < operator. However, if the class also has ==, define < only if the definitions of < and == yield consistent results.
  • To be consistent with the built-in operators, the prefix operators should return a reference to the incremented or decremented object.
  • To be consistent with the built-in operators, the postfix operators should return the old (unincremented or undecremented) value. That value is returned as a value, not a reference.
  • Operator arrow must be a member. The dereference operator is not required to be a member but usually should be a member as well.
  • The overloaded arrow operator must return either a pointer to a class type or an object of a class type that defines its own operator arrow.
  • The set of candidate functions for an operator used in an expression can contain both nonmember and member functions.

Chapter 15

  • Dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class.
  • Base classes ordinarily should define a virtual destructor. Virtual destructors are needed even if they do no work.

Static type and Dynamic Type

  • The static type of an expression is always known at compile time, it is the type with which a variable is declared or that an expression yields.
  • The dynamic type is the type of the object in memory that the variable or expression represents. The dynamic type may not be known until run time.
  • Static type of a pointer or reference to a base class may differ from its dynamic type.
  • Dynamic binding happens only when a virtual function is called through a pointer or a reference.

Whether the derived-to-base conversion is accessible depends on which code is trying to use the conversion and may depend on the access specifier used in the derived class’ derivation. Assuming D inherits from B:

  • User code may use the derived-to-base conversion only if D inherits publicly from B. User code may not use the conversion if D inherits from B using either protected or private.
  • Member functions and friends of D can use the conversion to B regardless of how D inherits from B. The derived-to-base conversion to a direct base class is always accessible to members and friends of a derived class.
  • Member functions and friends of classes derived from D may use the derived-to- base conversion if D inherits from B using either public or protected. Such code may not use the conversion if D inherits privately from B.

Default Inheritance Protection Levels

  • By default, a derived class defined with the class keyword has private inheritance; a derived class defined with struct has public inheritance:

Virtual Destructors Turn Off Synthesized Move

  • If a class defines a destructor—even if it uses = default to use the synthesized version—the compiler will not synthesize a move operation for that class

  • Derived constructor or assignment operator can use its corresponding base class operation regardless of whether the base defined its own version of that operator or uses the synthesized version.

  • Dynamic binding applies only to functions declared as virtual and called through a reference or pointer.

Chapter 16

  • Template arguments used for nontype template parameters must be constant expressions.
  • Definitions of function templates and member functions of class templates are ordinarily put into header files.
  • A function parameter that is an rvalue reference to a template type parameter (i.e., T&&) preserves the constness and lvalue/rvalue property of its corresponding argument.

Question?

  • T && vs Const T& why can receive lvalue and rvalue
  • Top-level vs Low-level in C++
  • lvalue reference vs lvalue expression what's the different between them?
  • 862

Chapter 17

  • A tuple is most useful when we want to combine some data into a single object but do not want to bother to define a data structure to represent those data

  • The indexing conventions of strings and bitsets are inversely related: The character in the string with the highest subscript (the rightmost character) is used to initialize the low-order bit in the bitset (the bit with subscript 0). When you initialize a bitset from a string, it is essential to remember this difference

  • A given random-number generator always produces the same sequence of numbers. A function with a local random-number generator should make that generator (both the engine and distribution objects) static. Otherwise, the function will generate the identical sequence on each call.

Chapter 18

  • The type of the declaration determines what kinds of exceptions the handler can catch. The type must be a complete type. The type can be an lvalue reference but may not be an rvalue reference
  • In general, exceptions can occur at any point in the program’s execution. In particular, an exception might occur while processing a constructor initializer. Constructor initializers execute before the constructor body is entered. A catch inside the constructor body can’t handle an exception thrown by a constructor initializer because a try block inside the constructor body would not yet be in effect when the exception is thrown.
  • The only way for a constructor to handle an exception from a constructor initializer is to write the constructor as a function try block.
  • Virtual base classes are always constructed prior to nonvirtual base classes regardless of where they appear in the inheritance hierarchy.

Namespace

  • One place where using directives are useful is in the implementation files of the namespace itself.
  • Each file has its own unique unnamed namespace. Names in an unnamed namespace are not visible outside that file.

Multiple inheritance

  • As always, name lookup happens before type checking

Name Lookup

  • First, look for a declaration of the name in the block in which the name was used. Only names declared before the use are considered.
  • If the name isn’t found, look in the enclosing scope(s).
  • If no declaration is found, then the program is in error.

Class definitions are processed in two phases

  • First, the member declarations are compiled
  • Function bodies are compiled only after the entire class has been seen.

Chapter 19

  • The pointer that we pass to construct must point to space allocated by the same allocator object. The pointer that we pass to placement new need not point to memory allocated by operator new.
  • Whether typeid requires a run-time check determines whether the expression is evaluated. The compiler evaluates the expression only if the type has virtual functions. If the type has no virtuals, then typeid returns the static type of the expression; the compiler knows the static type without evaluating the expression.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment