C++ study notes (6) - functions, overloading and pointers

C++ study notes (6) - functions, overloading and pointers

Function Basics

The function definition includes the following parts

return type, function name, list of zero or more parameters, and function body

local object

In the C++ language, names have scope and objects have life cycles

  • The scope of the name is the part of the program text in which the name is visible
  • The lifetime of an object is the period of time during which the object exists during program execution

automatic object

Objects that exist during the execution of the block become automatic objects, that is, the object is created when the execution reaches the variable definition, and it is destroyed at the end. such as formal parameters.

local static object

Initialized when the execution path of the program passes through the object definition statement for the first time, and is destroyed until the program terminates, even if the execution of the function in which it is located ends.

size_t count_calls()
{
    static size_t ctr = 0;  // 调用结束后,这个值仍然有效
    return ++ctr;
}
int main()
{
    for(size_t i = 0; i != 10; ++i)
        cout << count_calls() << endl;
    return 0;
}

function declaration

  • Function prototype : that is, the function declaration, including the return type, function name and formal parameter types.

  • The declaration of a function is similar to the definition of a function, the difference is that the declaration does not need a function body, it can be replaced by a semicolon

  • The source file that defines the function should include the header file containing the function declaration, and the compiler is responsible for verifying that the function definition and declaration match

Parameter passing

  • Parameters are initialized in the same way as variables are initialized

  • Pass by reference : The formal parameter is a reference type, also known as call by reference, that is, the reference parameter is an alias for the corresponding actual parameter

  • Pass -by-value: When the value of the actual parameter is copied to the formal parameter, the formal parameter and the actual parameter are two separate objects, also known as call-by-value

pass-by-value parameters

  • Pointer parameters: pointers are the same as non-reference types. When a copy operation is performed, the value of the pointer is copied. The two pointers are different pointers, but since the pointers can indirectly access the object they refer to, they point to the same content

    void reset(int *ip)
    {
      *ip = 0; // 改变指针 ip 所指对象的值
        ip = 0;  // 只改变了 ip 的局部拷贝,实参未被改变
    }

 pass-by-reference parameter

  • The operation on the reference actually acts on the object to which the reference refers

    void reset(int &i)
    {
      i = 0;  // 改变了 i 所引对象的值
    }
  • Use references to avoid copying

  • If the function does not need to change the value of the reference parameter, it is better to declare it as a constant reference

const formal and actual parameters

  • The top-level const is ignored when the actual parameter initializes the formal parameter, that is, the top-level const of the formal parameter is ignored

    void fcn(const int i) { /* fcn 能够读取 i, 但是不能向 i 写值 */ }
    void fcn(int i) {/* ... */}  // 错误:重复定义 fcn(int)
  • The C++ language allows the function names to be the same, but the premise is that the formal parameter list should be significantly different. Since the top-level const is ignored, the parameters passed into the two functions in the above code can be exactly the same, so it is a redefinition.

  • Non-constants can initialize the underlying const object, but not vice versa

  • Literals can initialize constant references

  • Use constant references as far as possible for function parameters

    • Constant: Avoid modifying the value in the function, and can pass in ordinary parameters and constant parameters at the same time
    • Reference: avoid function copying of parameters to improve efficiency

知识点

数组形参

数组的两个性质

  • 不允许拷贝数组, 故无法值传递
  • 使用数组时通常会将其转换成指针,故实际上传递给函数的是指向数组首元素的指针
// 下面三个函数等价,每个函数形参都是 const int*
void print(const int*);
void print(const int[]);  // 可以看出,函数的意图是作用于一个数组
void print(const int[10]);  // 维度表示期望数组含有多少元素,但实际不一定

含有可变形参的函数

C++11新标准提供了两个方法处理不同数量实参的函数

  • 若所有实参类型相同,可以传递一个名为initializer_list的标准库类型
  • 若实参不同,则可以拜年可变参数模板(16.4节介绍)

initializer_list

若函数的实参数量未知但全部实参类型相同,则可以使用initializer_list类型的形参, initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。其定义在同名头文件中

initializer_list_operations_methods

  • vector类似,initializer_list也是模板类型,定义时,需要说明类型

  • initializer_list对象中元素必须是常量值,无法改变, 并且放在花括号中

    void error_msg(initializer_list<string> il)
    {
      for(auto beg = il.begin(); beg != il.end(); ++beg)
            cout << *beg << " ";
        cout << endl;
    }
    // 调用语句
    // expected, actual 是 string 对象
    if (expected != actual)
        error_msg({"functionX", expected, actual});
    else
        error_msg({"functionX", "okay"});

省略符形参

省略符形参是为了便于C++程序访问某些特殊的C代码设置的,这些代码使用了varargs的C标准库功能。

  • 省略符形参只能出现在形参列表的最后一个位置,形式有两种

    // 第一种指定了部分形参的类型,对于这部分参数将进行正常的类型检查,省略符形参所对应的实参无需类型检查
    void foo(parm_list, ...);  
    void foo(...);
进阶

返回类型和return语句

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方,return语句有两种形式

return;
return expression;

无返回值函数

无返回值的return语句用在返回类型是void的函数中,此类函数最后会隐式执行return

有返回值函数

return语句返回值的类型必须与函数的返回类型相同,或能隐式转换成函数的额返回类型

  • 返回值的方式和初始化一个变量或形参的方式完全一样

  • 不要反悔局部对象的引用或指针:因为函数完成后,其存储空间被释放,局部变量的引用指向的内存无效

  • 调用返回引用的函数得到左值,其他返回类型得到右值

  • 列表初始化返回值

    vector<string> process() { return {"return", "example"}};

返回数组指针

函数不能返回数组,所以可以返回数组指针或引用

  • Type (*function (parameter_list)) [dimension]

    // 例子,使用类型别名
    typedef int arrT[10];  // arrT为类型别名,表示类型是含有 10 个整数的数组
    using arrT = int[10];  // arrT的等价声明
    arrT* func(int i); // func 返回一个指向含有 10 个整数的数组的指针
    
    // 例子,没有使用类型别名
    int (*func(int i))[10];
    // 准曾理解该声明含义
    func(int i);  // 表示调用func函数时需要一个int类型的实参
    (*func(int i));  // 意味着我们可以对函数调用的结果执行解引用操作
    (*func(int i))[10];  // 表示解引用func的调用将得到一个大小是10的数组
    int (*func(int i))[10];  // 表示数组中的元素是int 类型
  • 使用尾置返回类型

    // func 接受一个 int 类型的实参,返回一个指针,该指针指向含有 10 个整数的数组
    auto func(int i) -> int(*)[10];
  • 使用 decltype

    int odd[] = {1, 3, 5, 7, 9};
    decltype(odd) *arrPtr(int i);  /* 返回一个指向含有 5 个整数的数组的指针,注意 decltype 的结果是数组,不会把数组类型转换成对应的指针,故需要加 * 符号 */

函数重载

If several functions in the same scope have the same name but different parameter lists, they are overloaded functions

  • top const-level parameter is equivalent to no top const-level parameter

    int lookup(int i);
    int lookup(const int i);  // 重复声明 int lookup(int); 顶层const
    
    int lookup(int *pi);
    int lookup(int * const pi);  // 重复声明 int lookup(int *); 顶层const
  • The bottom layer constcan implement function overloading

    int lookup(int &);  // 函数作用域 int 引用
    int lookup(const int&);  // 新函数,作用域常量引用
    
    int lookup(int *);  // 新函数,作用于指向 int 的指针
    int lookup(const int *);  // 新函数,作用于指向常量的指针
  • const_castand overloading

    // 参数和返回类型都是 const string 的引用
    const string &shorterString(const string &s1, const string &s2)
    {
      return s1.size() <= s2.size() ? s1 : s2;
    }
    // 返回类型是普通引用, 参数为普通变量
    string &shorterString(string &s1, string &s2)
    {
        // 将普通引用转换成对对const的引用,然后调用const版本,返回对const的引用,如果不转换成const,则会根据非常量参数调用非const版本,直至报错
      auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
        // 将const引用转成普通引用返回
        return const_cast<string&>(r);
    }

special-purpose language features

default arguments

  • A parameter is assigned a default value, then all parameters following it must have a default value

  • A formal parameter can only be assigned a default argument once in a given scope

    typedef string::size_type sz;
    string screen(sz, sz, char = ' ');
    string screen(sz, sz, char = '*'); // 错误:重复声明
    string screen(sz = 24, sz = 80, char); // 正确:添加默认实参
  • In general, default arguments should be specified in a function declaration and placed in an appropriate header file.

  • Local variables cannot be used as default arguments. In addition, expressions can be used as default arguments as long as the type of the expression can be converted to the parameter type.

Inline functions are sum constexprfunctions

  • Inline functions inline, i.e. functions are expanded inline at the call site

  • An inline specification is just a request to the compiler, which the compiler can choose to ignore

  • Many compilers do not support inlining recursive functions

  • constexprFunction: A function that can be used in constant expressions. The return type of the function and the types of all formal parameters are literal types, and there must be one and only one returnstatement

    constexpr int new_sz() { return 42; }  // new_sz() 为无参数的 constexpr 函数
  • constexprfunction is implicitly specified as an inline function

  • constexprThe body of a function can contain other statements as long as they do nothing at runtime, such as null statements, type aliases, and usingdeclarations

  • constexprFunctions do not necessarily return constant expressions

    // 若参数 arg 为常量表达式,则 scale(arg) 也是常量表达式
    constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
  • Inline functions and constexprfunctions can be defined multiple times in a program. In order to keep multiple definitions completely consistent, they are usually defined in header files.

debugging help

  • assertpreprocessor macro: a preprocessor variable, similar to an inline function, managed by the preprocessor rather than the compiler

    assert(expr);  // 对 exp r求值,若表达式为假(即 0 ),assert输出信息并终止运行;若为真,则什么也不做
  • NDEBUG: Preprocessing variables, assertthe behavior depends on the state of the secondary variables, if defined NDEBUG, assertnothing is good, equivalent to turning off debugging. It is not defined by default, and asserta runtime check is performed at this time

    void print(const int ia[], size_t size)
    {
    
    # ifndef NDEBUG
    
        cerr << __func__ << endl  // 当前调试函数名
            << __FILE__ << endl   // 存放文件名的字符串字面值
            << __LINE__ << endl   // 存放当前行号的整型字面值
            << __TIME__ << endl   // 存放文件编译时间的字符串字面值
            << __DATE__ << endl;  // 存放文件编译时期的字符串字面值
    }

function matching

  • Candidate function : call the corresponding overloaded function set, there are two characteristics
    • same name as the called function
    • Its declaration is visible at the call site
  • Feasible function : By examining the arguments provided by the call, the function that can be called by this set of arguments is selected from the candidate functions. There are also two characteristics
    • Its formal parameters are equal to the actual parameters provided by this call
    • Each actual parameter type is the same as the corresponding formal parameter type, or can be converted to the parameter type
  • The closer the actual parameter type and the formal parameter type can be, the better the match
  • If more than one parameter matches, the compiler checks each argument at once to determine the best matching function, and reports an ambiguity error if none is found
    • The matching of each argument of the function is no worse than the matching required by other feasible functions
    • A match with at least one argument is due to a match provided by other feasible functions

Argument type conversion

To determine the best match, the compiler classifies the conversion of actual parameter types to formal parameter types as follows

function_real_arguments_cast

Matching that requires type promotion and arithmetic type conversion

  • Small integers are generally promoted to inttypes or larger integer types

    void ff(int);
    void ff(short);
    ff('a');  // char 提升成 int; 调用 f(int)
  • All arithmetic type conversions have the same level

    void manip(long);
    void manip(float);
    manip(3.14);  // 错误:二义性调用,因为double可以转换为long 和 float

Function matching and constformal parameters

If the difference of the overloaded function is whether the formal parameter of the used type is referenced const, or whether the formal parameter of the pointer type is executed const, the compiler decides the function selection by whether the actual parameter is a constant or not.

int lookup(int &);  // 参数为 int 引用
int lookup(const int &);  // 参数为常量引用
const int a;
int b;
lookup(a);  // 调用 lookup(const int &)
lookup(b);  // 调用 lookup(int &)

function pointer

A function pointer is a pointer to a function, and a function pointer also points to a specific type. The type of a function is determined by its return type and parameter types, regardless of the function name.

// 函数类型是 bool (const string&, const string &)
bool lengthCompare(const string &, const string &); 
// 声明指向该函数的指针,只需要用指针替换函数名即可
// pf 指向一个函数,该函数的参数为两个 const string引用,返回值是 bool 类型
bool (*pf)(const string &, const string &);  // 未初始化
  • When the function name is used as a value, it is automatically converted to a pointer

    pf = lengthCompare;  // 等价于 pf = &lengthCompare; 取地址符可选
  • Functions can be called directly using function pointers without dereferencing

    bool b1 = pf("hello", "goodbye");  // 调用 lengthCompare 函数
    bool b2 = (*pf)("hello", "goodbye");  // 等价于上个调用语句
  • There is no type conversion for function pointers, and function pointers can be assigned nullptror 0, indicating that function pointers do not point to any function

  • Function pointers can be used as formal parameters, and type aliases can be used to simplify formal parameters

    // 第三个形参是函数类型,它会自动转换成指向函数的指针
    void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
    // 等价声明,显示地将形参定义成指向函数的指针
    void useBigger(const string &s1, const string &s2, book (*pf)(const string &, const string &));
    // 自动将函数 lengthCompare 转换成指向该函数的指针
    useBigger(s1, s2, lengthCompare);
  • Functions can return function pointers. Unlike formal parameters, the compiler does not automatically convert the function return type to the corresponding pointer type.

    using F = int(int *, int);  // F 是函数类型,不是指针
    using PF = int(*)(int *, int);  // PF 是指针类型
    PF f1(int);  // 正确:PF为函数指针,f1 返回函数指针
    F f1(int);  // 错误:F是函数类型,f1不能返回一个函数
    int (*f1(int))(int *, int);  // 等价于PF f1(int);
    auto f1(int) -> int(*)(int *, int);  // 使用尾置返回类型方式声明返回函数指针的函数

Epilogue

  • Functions are named units of computation, each including a return type, a name, a (possibly empty) parameter list, and a function body
  • In C++, functions can be overloaded: the same sensitive sub can be used to define multiple functions, as long as the parameter quantities or parameter types are different.

The basic knowledge of functions has been understood, and the knowledge of functions will be further studied in the future.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325629165&siteId=291194637