Notes on C + + primer Chapter 6 functions

  1. Any two formal parameters cannot have the same name, and the local variables in the outermost scope of the function cannot use the same name as the formal parameters of the function (the formal parameters are equivalent to the local variables of the function).
  2. Parameter names are optional, but since we cannot use unnamed parameters, they should generally have a name. A type of parameter is usually not named to indicate that it will not be used in the body of a function. In any case, setting an unnamed parameter does not affect the number of arguments provided when called. Even if a formal parameter is not used by a function, it must be provided with an argument.
  3. In C + + language, name has scope and object has life cycle.
    • The scope of the name is part of the program text in which the name is visible.
    • The life cycle of an object is a period of time that the object exists during the execution of a program.
  4. A statement block forms a new scope.
  5. Parameters and variables defined in the body of a function are collectively referred to aslocal variable, which is only visible within the scope of the function, and the local variable is also visiblehideAll other declarations with the same name in the outer scope.
  6. We refer to objects that exist only during execution as automatic objects. When the execution of the block is finished, the value of the automatic object created in the block becomes undefined.
  7. The local static object is initialized when the execution path of the program passes through the object definition statement for the first time, and it is not destroyed until the program is terminated. During this period, even if the function of the object ends, it will not be affected.
  8. If the local static variable does not have an explicit initial value, it performs value initialization, and the local static variable of the built-in type is initialized to 0.
  9. If the function does not need to change the value of the reference parameter, it is best to declare it as a constant reference.
  10. As with other initialization procedures, the top-level const (from a call point of view) is ignored when formal parameters are initialized with arguments. In other words, the top-level const of the formal parameter is ignored. When a formal parameter has a top-level const, it can be passed to a constant object or a non constant object. be careful:
    Void FCN (const int i) {/ * FCN can read I, but cannot write * /} to I
      Void FCN (int i) {/ *... * /} // error: duplicate definition of FCN (int)
      //In C + + language, we are allowed to define several functions with the same name, but the premise is that the parameter list of different functions should be obviously different. Because the top-level const is ignored, the parameters of the two FCN functions can be exactly the same in the above code. Therefore, the second FCN is wrong. Although it is different in form, its formal parameters are not different from those of the first FCN.
  11. Array parameter:
    //Although the forms are different, the three print functions are equivalent
      //Each function has a formal parameter of type const int *
      void print (const int*);
      void print (const int[]);
      void print (const int[10]); //  The dimensions here don't work.
  12. Specify the length of the array parameter:
    //1. Use the tag to specify the length of the array:
      Void print (const char * CP) // C-style string ends with '\ 0'
            if (cp)
                  while (cp)
                        cout << *cp++;
      //2. Standard library specification:
      Void print (const int * beg, const int * end) // passes pointers to the first and last elements of the array
            //Output all elements from beg to end (excluding end)
            while (beg != end)
                  cout << *beg++ << endl; //  Outputs the current element and moves the pointer forward one position
      //3. Explicitly pass a formal parameter representing the size of the array:
      void print (const int ia[], size_ T size) // define a formal parameter that represents the size of the array
            for (size_t i = 0; i != size; ++i)
                  cout << ia[i] << endl;
  13. When a function does not need to write to an array element, the array parameter should be a pointer to const. Only when the function really wants to change the value of the element, the formal parameter is defined as a pointer to the non constant.
  14. A formal parameter can also be a reference to an array.
    //True: a parameter is a reference to an array, and a dimension is part of a type
      Void print (int & arr [10]) // the dimension here is required // & the brackets at both ends of arr are necessary
            for (auto elem : arr)
                  cout << elem << endl;
  15. Parameters of multidimensional array:
    //The two are equivalent
      void print (int (*matrix)[10], int rowSize) { /*...*/ }
      void print (int matrix[][10], int rowSize) { /*...*/ }
  16. Main: handle command line optionsint main (int argc, char *argv[]) { ... }. The element value after the last pointer is guaranteed to be 0.
  17. If the number of arguments to a function is unknown, but all arguments are of the same type, we can useinitializer_listFormal parameter of type, initializer_ The elements in the list object are alwaysconstant value, we can’t change the initializer_ Value of the element in the list object:
    void error_msg (ErrCode e, initializer_list il)
            cout << e.msg() << ": ";
            For (const Auto & elem: IL) // because of initializer_ List contains begin and end members, so we can use the scope for loop to process the elements. It can also be written as for (auto beg = IL. Begin(); beg !=  il.end(); ++ beg)
                  cout << elem << " ";
            cout << endl;
      if (expected != actual)
            error_ msg (ErrCode(42), {"functionX", expected, actual}); //  Expected and actual are string objects
            error_msg (ErrCode(0), {"functionX", "okay"});
operation explain
initializer_list lst; Default initialization: empty list of T-type elements
initializer_list lst{a, b, c…}; Lst has the same number of elements as the initial value; The elements of LST are copies of the corresponding initial values; The element in the list is const
Lst2 (LST) or lst2 = LST Copy or assign an initializer_ The list object does not copy the elements of the list; After copying, the original list and the replica share elements (constant values after all)
lst.size() Number of elements in the list
lst.begin() Returns a pointer to the first element in LST
lst.end() Returns a pointer to the next position of the tail element in LST
  1. Ellipsis parameter (C language feature, non-C + +): most class type objects cannot be copied correctly when passed to ellipsis parameter.void foo (parm_list, ...);void foo (...);The first form specifies the types of some parameters of the foo function, and the arguments corresponding to these parameters will perform normal type checking. The argument corresponding to the ellipsis parameter does not need type checking. In the first form, the comma after the formal parameter declaration is optional.
  2. A function with a return type of void can also use the second form of the return statement(return expression;)In this case, the expression of the return statement must be another function that returns void. Forcing void functions to return other types of expressions will result in compilation errors.
  3. There should be a return statement after the loop containing the return statement. If not, the program is wrong. The control flow may have ended the execution of the function without returning any value. Many compilers are unable to detect such errors.
  4. const string &shorterString (const string &s1, const string &s2) { return s1.size() <= s2.size() ? s1 : s2; }The formal parameter and return type are all references to const string. No matter the function is called or the result is returned, the string object will not be copied.
  5. Do not return a reference or pointer to a local object (the local variable may be destroyed on return). To ensure that the return value is safe, we may ask: which object does the reference refer to that existed before the function?
  6. Call a function that returns the referenceLeft valueOther return types get right values. In particular, we can assign values to the result of a function whose return type is a non constant reference.
  7. List initialization return value: if the function returns a built-in type, the list surrounded by curly braces contains at most one value, and the space occupied by the value should not be greater than the space of the target type (soint a = {3.14};It’s wrong). If the function returns a class type, the class itself defines how to use the initial value (see curly braces to initialize a temporary variable to be returned). The variable type is the return value type.
    vector process()
            // ...
            //Expected and actual are string objects
            if (expected.empty())
                  return {}; //  Returns an empty vector object
            else if (expected == actual)
                  return {"functionX", "okay"}; //  Returns the vector object initialized by the list
                  return {"functionX", expected, actual};
      //Guess what the return value of func will be?
      vector func(void)
            return {10, "hi"};
  8. If the control reaches the end of the main function and there is no return statement, the compiler implicitly inserts a return statement that returns 0.
  9. Declare a function that returns an array pointer:
    //1. C Style
      int (*func(int i))[10];
      //2. Use the trailing return type
      auto func(int i) -> int(*)[10]
      //3. Using decltype
      int odd[] = {1,3,5,7,9};
      int even[] = {0,2,4,6,8};
      Decltype (odd) * arrptr (int i) // decltype is not responsible for converting the array type to the corresponding pointer, so the result of decltype is an array
            return (i % 2) ? & odd : &even; //  Returns a pointer to an array
  10. The main function cannot be overloaded
  11. For overloaded functions, they should be different in the number or type of parameters (omitting the parameter name or just changing the variable name). Two functions are not allowed to have the same elements except the return type. One hasTop level constThe formal parameter of cannot be compared with another parameter withoutTop level constThe underlying const can be distinguished.
  12. When we pass a non constant object or a pointer to a non constant object, the compiler will give priority to the non constant version of the function.
  13. const_ Cast and overload:
    const string &shorterString(const string &s1, const string &s2)
            return s1.size() <= s2.size() ? s1 : s2;
      string &shorterString(string &s1, string &s2)
            auto &r = shorterString(const_cast(s1),const_cast(s2));
            return const_cast(r);
  14. Function matchingA procedure in which we associate a function call with one of a set of overloaded functions. Function matching is also calledOverload determination
  15. There are three possible results when calling overloaded functions:
    • The compiler found an argument with theBest fitAnd generate the code to call the function.
    • Unable to find a function that matches the argument of the call, the compiler issues aNo matchError message for.
    • There are more than one function to match, but none of them is obviously the best choice. In this case, an error will also occur, which is calledAmbiguous call
  16. If we declare a name in the inner scope, it hides the entity with the same name (whether it’s a function name or a variable name) declared in the outer scope. Function names cannot be overloaded in different scopes (inner functions hide outer functions with the same name).
  17. In C + +, name lookup occurs before type checking.
  18. If the function declaration is placed in the same scope, it becomes another form of overloading.
  19. Default argument: once a parameter is given a default value, all parameters following it must have a default value. The order of formal parameters should be set reasonably, so that the formal parameters that do not use the default value appear in the front as much as possible, and those that often use the default value appear in the back.
    typedef string::size_type sz;
      string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');
  20. If we want to use the default argument, just omit it when calling the function. When a function is called, the arguments are resolved according to their positions, and the default arguments are responsible for filling the missing tail arguments (on the right side) of the function call.
  21. It is also legal to declare the same function more than once. A formal parameter can only be given a default argument once in a given scope. The subsequent declaration of a function can only add default arguments to those parameters that have no default values, and all parameters to the right of the parameter must have default values.
    typedef string::size_type sz;
      string screen(sz, sz, char = ' ');
      string screen(sz, sz, char = '*'); //  Error: duplicate declaration
      string screen(sz = 24, sz = 80, char); //  Correct: add default argument // the default argument is not at the end of the formal parameter list
      //It is wrong to change the order of the first sentence and the third sentence
      //If you add char to the third sentence, it's a redefinition
      //Vs2019 reports an error but can run
  22. A function definition and a function declaration cannot both specify default arguments. In general, you should specify the default argument in the function declaration and place the declaration in the appropriate header file.
  23. Local variables cannot be used as default arguments. In addition, as long as the type of the expression can be converted to the type required by the formal parameter, the expression can be used as the default argument.
    //Declarations of WD, def, and HT must appear outside the function
      typedef string::size_type sz;
      sz wd = 80;
      char def = ' ';
      sz ht();
      string screen(sz = ht(), sz = wd, char = def);
      string window = screen(); //  Call screen (ht(), 80, ')
  24. The name used as the default argument is in the function declarationIn the same scopeThese names are evaluated when the function is called.
    void f2()
            def = '*'; //  Change the value of the default argument
            sz wd = 100; //  The WD defined by the outer layer is hidden, but the default value is not changed
            window = screen(); //  Call screen (ht(), 80, '*')
  25. Inline is only issued to the compilerA request, the compiler can choose to ignore this request. Generally speaking, inline mechanism is used to optimize functions with small scale, direct process and frequent calls. Many compilers do not support inline recursive functions, and a 75 line function is unlikely to be expanded inline at the call point.
  26. Constexpr functionSeveral conventions of definition: the return type of function and the type of all formal parameters must be literal type; There must be only one return statement in the function body.
    constexpr int new_sz() { return 42; }
      constexpr int foo = new_ sz(); //  Correct: foo is a constant expression
  27. In order to expand at any time during compilation, constexpr function is implicitly specified as inline function.
  28. Constexpr function can also contain other statements, as long as these statements do not perform any operation at run time. For example, the constexpr function can have empty statements, type aliases, and using declarations. We allow the return value of constexpr function not to be a constant. When the argument of scale is a constant expression, its return value is also a constant expression; On the contrary, it is not( Remember that constexpr evaluates at compile time.) constexpr functions do not necessarily return constant expressions
    //If arg is a constant expression, scale (ARG) is also a constant expression
      constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
      int arr[scale(2)]; //  Correct: Scale (2) is a constant expression
      int i=2; //  I is not a constant expression
      int a2[scale(i)]; //  Error: Scale (I) is not a constant expression
  29. Inline function and constexpr function can be defined many times in the program, but their definitions must be identical. For this reason, inline and constexpr functions are usually defined in header files.
  30. Assert is a preprocessing macroassert(expr);. First, evaluate the expert. If the expression is false (i.e. 0), assert outputs information and terminates the execution of the program. If the expression is true (that is, not 0), assert does nothing. The assert macro is defined in the cast header file.
  31. We can use a define statement to define ndebug#includeTo turn off debugging( Command line:CC -D NDEBUG main.CIt is equivalent to writing at the beginning of the main. C file#define NDEBUG
  32. Debugging help:
    #ifndef NDEBUG
            // __ func__ Is a local static variable defined by the compiler to store the name of the function
            cerr << __func__ << ": array size is " << size << endl;
name explain
__func__ Output the name of the function that is currently debugged.
__FILE__ The literal value of a string that holds the file name
__LINE__ Holds the integer literal value of the current line number
__TIME__ The literal value of a string that holds the compile time of a file
__DATE__ The literal value of a string that holds the compilation date of a file
  1. The first step of function matching is to select the overloaded function set corresponding to this call. The functions in the set are calledcandidate functions . The candidate function has two characteristics: one is that it has the same name as the called function, the other is that its declaration is visible at the call point.
  2. The second step is to examine the arguments provided by this call, and then select the functions that can be called by this group of arguments from the candidate functions. These newly selected functions are calledFeasible function. Feasible function also has two characteristics: one is that the number of formal parameters is equal to the number of actual parameters provided by this call; the other is that the type of each actual parameter is the same as the corresponding formal parameter type, or can be converted to the type of formal parameter( If the function contains default arguments, the number of arguments we pass in when calling the function may be less than the number of arguments it actually uses.)
  3. Next, the compiler checks each argument in turn to determine which function isBest fit. If only one function satisfies the following conditions, the matching is successful:
    • The matching of each argument of this function is not inferior to that of other feasible functions.
    • At least one argument matches better than other feasible functions.
  4. When calling overloaded functions, you should try to avoid casting. If it is necessary to cast in practice, the formal parameter set we designed is not reasonable.
  5. Argument type conversion:
    • Exact matching
      • The argument type is the same as the formal parameter type
      • An argument is converted from an array type or a function type to a corresponding pointer type
      • Add or remove a top-level const from an argument
    • Matching by const transformation
    • Matching by type promotion
    • Matching by arithmetic type conversion or pointer conversion
    • Matching through class type conversion.
  6. Before analyzing function calls, we should know that small integers are generally promoted to int or larger integers. Sometimes, even if the argument is a small integer value, it will be promoted to int directly.
    void ff(int);
      void ff(short);
      ff('a'); //  Char is promoted to int; Call FF (int);
  7. All arithmetic type conversions have the same level. For example, the conversion from int to unsigned int is no higher than the conversion from int to double. For example:
    void manip(long);
      void manip(float);
      manip(3.14); //  Error: ambiguous call
      //The type of literal 3.14 is double, which can be converted to both long and float. Because there are two possible arithmetic type conversions, the call is ambiguous.
  8. The type of a function is determined by itsReturn typeandparameter types It is determined jointly, regardless of the function name.
  9. When we use the function name as a value, the function is automatically converted to a pointer. In addition, we can directly use the pointer to the function to call the function without dereferencing the pointer in advance. Note that there are no conversion rules between pointers to different function types. But as always, we can assign a nullptr or an integer constant expression with a value of 0 to the function pointer, indicating that the pointer does not point to any function.
    //Pf points to a function whose parameters are references to two const strings and whose return value is of bool type.
      bool (*pf)(const string &, const string &);
      pf = lengthCompare; //  Pf points to a function called lengthcompare
      pf = &lengthCompare; //  Equivalent assignment statement: the address character is optional
      bool b1 = pf("hello", "goodbye"); //  Call the function of lengthcompare
      bool b2 = (*pf)("hello", "goodbye"); //  An equivalent call
      bool b3 = lengthCompare("hello", "goodbye"); //  Another equivalent call
      string::size_type sumLength(const string &, const string &);
      bool cstringCompare(const char *, const char *);
      pf = 0; //  True: pf does not point to any function
      pf = sumLength; //  Error: return type mismatch
  10. The compiler decides which function to choose by pointer type, which must be the same as one of overloaded functionsExact matching
  11. Similar to arrays, although you cannot define a parameter of a function type, a parameter can be a pointer to a function. At this point, the formal parameter appears to be a function type, but it is actually used as a pointer. We can directly use the function as an argument, and it will be automatically converted to a pointer.
    //The third parameter is the function type, which is automatically converted to a pointer to the function
      void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
      //Equivalent declaration: explicitly define a formal parameter as a pointer to a function
      void useBigger(const string &s1, const string &s2, bool(*pf)(const string &, const string &));
      //Automatically converts the function lengthcompare to a pointer to the function
      useBigger(s1, s2, lengthCompare);
  12. Type aliases and decltype allow us to simplify code that uses function pointers. Decltype returns the function type, which is not automatically converted to a pointer type.
    //Func and func2 are function types
      typedef bool Func(const string&, const string&);
      typedef decltype(lengthCompare) Func2; //  Equivalent types
      //Funcp and funcp2 are pointers to functions
      typedef bool(*FuncP)(const string&, const string&);
      typedef decltype(lengthCompare) *FuncP2; //  Equivalent types
      //The equivalent declaration of usebigger, where the type alias is used. These two statements declare the same function.
      void useBigger(const string&, const string&, Func); //  The compiler automatically converts the function type represented by func to a pointer
      void useBigger(const string&, const string&, FuncP2);
  13. Like arrays, although you cannot return a function, you can return a pointer to the function type. However, we must write the return type as a pointer, and the compiler will not automatically treat the function return type as the corresponding pointer type.
    using F = int(int*, int); //  F is a function type, not a pointer
      using PF = int(*)(int*, int); //  PF is a pointer type
      //1. Declare F1 with type alias
      PF f1(int); //  Correct: PF is the pointer to the function, F1 returns the pointer to the function
      F f1(int); //  Error: F is a function type, F1 cannot return a function
      F *f1(int); //  Correct: explicitly specifies that the return type is a pointer to the function
      //2. Direct declaration
      int (*f1(int))(int*, int); 
      //3. Declare F1 by using the trailing return type
      auto f1(int) -> int (*)(int*, int); 
      //4. Use auto and decltype for function pointer type
      string::size_type sumLength(const string&, const string&);
      string::size_type largerLength(const string&, const string&);
      //Remember that when we apply decltype to a function, it returns the function type instead of the pointer type.
      decltype(sumLength) *getFcn(const string &); //  Using decltype (sumlength) is the same as decltype (lagerlength)
  14. Argument, parameter
  15. Hidden name: the name declared in a scope will hide the entity with the same name declared in the outer scope (not limited to the name of the variable).
  16. Inline functions: request that the compilerWhere possibleExpand the function at the call point. Inline functions can avoid the common overhead of function calls.