美文网首页
C++ Primer: Functions

C++ Primer: Functions

作者: algebra2k | 来源:发表于2020-05-13 16:59 被阅读0次

    1. 函数基础

    形参和实参
    实参是形参的初始值. 形参和实参类型要一致, 顺序要对应. (但编译器具体按什么顺序求值则不确定)

    局部对象

    形参和函数内部的变量是局部变量, 仅在函数作用域可见.
    所有函数体之外的对象存在于整个程序的生命周期, 直到程序退出.

    自动对象
    对于在函数执行时创建对象, 函数退出时销毁的对象称为 自动对象.
    形参也是自动对象, 函数开始时为其申请空间, 函数退出时形参也被销毁.

    局部静态对象
    使用static声明的局部对象称为 局部静态对象. 这种对象在函数执行时创建, 但函数退出时却不销毁, 直到程序退出时才销毁**, 生命周期贯穿整个程序.

    函数声明

    函数必须在使用前声明
    函数可以有多次声明, 但只能有一次定义. 函数声明也称为 函数原型.

    2. 函数参数

    参数传递

    • 引用传递, 形参相当于实参的别名
    • 值传递, 形参和实参是独立的, 实参拷贝给形参
    • 指针传递, 拷贝的指针的地址, 可以通过指针修改对象, 但形参和实参的指针地址是不同的

    const形参和实参
    形参有顶层const时, 实参传递常量对象或非常量对象都可以

    // 变量初始化和形参初始化对比
    int i = 0;
    const int *cp = &i; // 正确, 但cp不能改变i
    const int &r = i; // 正确, 但r不能改变i
    const int &r2 = 42; // 正确
    int *p = cp; // 错误, p的类型和cp类型不匹配
    int &r3 = r; // 错误, r的类型和r3类型不匹配
    int &r4 = 42; // 错误, 不能用字面值初始化一个引用
    
    int i = 0;
    const int ci = i; 
    string::size_type ctr = 0;
    reset(&i); // 调用参数是int*的reset
    reset(&ci); // 错误, 不能用指向const int的对象初始化int *
    reset(i); // 调用参数时int&的reset
    reset(ci); // 错误, 不能把普通引用绑定到const上
    reset(42); // 错误, 不能把普通引用绑定到字面值上
    reset(ctr); // 错误, 类型不匹配, size_t是无符号类型
    

    尽量使用常量引用

    数组形参
    数组两个特殊的性质

    1. 数组不允许拷贝, 意味着不能通过值传递一个数组实参
    2. 数组会被转化为指针, 意味着传递一个数组实参时, 实际上传递的首元素指针

    传递方式

    1. 使用标准库规范
    void print(const  int *beg, const int *end)
    {
         while(beg != end)
            cout << *beg++ << endl;
    }
    
    int j[2] = {0, 1};
    print(begin(j), end(j));
    
    1. 显式传递一个数组大小的实参数
    void print(const  int ia[], size_t size)
    {
        for (size_t i = 0; i < size; i++)
            cout << ia[i] << endl;
    }
    
    int j[2] = {0, 1};
    print(j, end(j) - begin(j));
    

    当函数不需要写操作, 数组的形参应该是const类型的指针.

    1. 数组引用形参
    void print(int (&arr)[10])
    {
        for (auto elem: arr)
            cout << elem << endl;
    }
    

    &arr 括号不能少, 因为 f(int &arr[10]) 表示arr是引用的数组, f(int (&arr)[10]) 是具有10个整数的数组引用.

    传递多维数组
    c++没有真正的多维数组, 多维数组在c++中是数组的数组, 因此和数组一样, 多维数组指向数组的首元素

    void print(int (*metrix)[10), int rowSize) { /**...**/};
    

    main函数参数

    • argc 表示元素数量
    • **argv[] 表示多个c风格字符串的数组, 具体有多少个由argc决定

    可变形参函数

    initializer_list 是标准库类型, 表示特定类型值的数组

    void err_msg(int err_code, initializer_list<string> errors) {
         cout << "code " << err_code;
         for (auto beg = erros.begin(); beg != errors.end(); beg++)
           cout << *beg << " ";
         cout << endl;
    }
    
    // initializer_list形参在传递实参时, 需要传递一个列表值
    err_msg(500, {"e1", "e2", "e3"})
    

    c++兼容了c的省略符号形参和varargs void f(param_list, ...);

    3. 函数返回值

    引用返回左值

    char &get_val(string &str, string::size_type ix)
    {
          return str[ix];
    }
    
    int main() 
    {
          string a("value");
          cout << a << endl;
          // get_val返回左值引用
          get_val(a, 0) = 'V';
          cout << a << endl;
    }
    

    列表初始化返回值

    vector<string> process() 
    {
          //...
          return {"a", "b"};
    }
    

    返回数组指针
    由于数组不能拷贝, 因此返回数组返回的是数组指针, 这种返回类型函数的声明方式可以有以下几种

    1. 使用typedef或using
    typedef int arrT[10];
    
    using arrT = int[10]; // 和typedef等价
    arrT *func(int i); // 返回一个指向10个整数的数组的指针
    
    1. 不使用类型别名的声明
      语法 Type (*function (parameter_list))[dimension]
      上面的可以声明为 int (*func (int i))[10]

    分解声明的每个部分的含义:

    • func (int i), 表示函数调用需要一个int类型的实参
    • (func(int i)), 表示对调用函数可以执行解引用*操作
    • (*func (int i))[10], 表示调用func 解引用 得到一个大小是10的数组
    • int (*func (int i))[10], 表示数组的类型是int
    1. 使用尾置返回类型(c++11)
    auto func(int i);
    
    1. 使用decltype (c++11)

    ** 前提是知道了返回的数组指针指向哪个数组**

    int odd[] = {1, 3, 5};
    int even[] = {2, 4, 6};
    decltype(odd) *arrPtr(int i)
    {
      return (i % 2) ? odd : event;
    }
    

    4. 函数重载

    main函数不能重载

    重载函数是名字相同, 但形参类型不同的一组函数集合

    Record lookup(const Account&);
    Record lookup(const Phone&);
    Record lookup(const Name&);
    

    const影响重载的识别吗
    顶层const形参不会认为是不同的参数

    Record lookup(Phone);
    Record lookup(const Phone); // 重复声明
    
    Record lookup(Phone *);
    Record lookup(Phone * const); // 重复声明
    

    底层const实现函数重载

    Record lookup(Account &);
    Record lookup(const Phone&); // 新函数, 常量引用
    
    Record lookup(Account *);
    Record lookup(const Account *); // 新函数, 常量指针
    

    const_cast和重载

    const string &shorterString(const string &s1, const string &s2)
    {
          return s1.size() < s2.size()? s1 : s2;
    }
    
    // 重载函数, 非常量引用
    string &shorterString(string &s1, string &s2)
    {
        // s1和s2都是非常量引用, 通过const_cast转为常量引用, 调用了shorterString重载函数
        auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
        // 重载函数返回的类型是常量引用, 通过const_cast转为非常量引用返回
        return const_cast<string&>(r);
    }
    

    作用域和重载

    • 作用域内部不能重载函数名, 否则会造成未定义的行为
    • 作用域外重载函数, 作用域内不受影响, 正常使用

    5. 函数特殊特性

    默认实参

    应该在函数声明中就声明默认实参, 并放在合适的头文件中

    默认实参行为: 调用函数没有传递参数则使用默认实参

    默认实参声明

    string screen(sz = 24, sz = 80, char);
    

    默认实参声明一个参数只能声明一次

    string screen(sz, sz, char = '');
    string screen(sz, sz, char = '*'); // 错误, 重复声明
    

    constexpr函数
    constexpr变量定义时如果用到了函数, 则函数必须也是constexpr函数

    char *s = "hello";
    constexpr size_t size = strlen(s) + 1; // 错误, strlen不是constexpr函数
    
    constexpr int new_size() { return 42; }
    constexpr int foo = new_size() + 1; // 正确
    constexpr size_t scale(size_t cnt) { return new_sz() * cnt };
    
    int arr[scale(2)]; // 正确scale是常量表达式, 传递的cnt实参也属于常量
    int i = 2;
    int a2[scale(i)]; // 错误, i不是constexpr
    

    内联函数
    内联函数声明

    inline const string &shorterString(const string &s1, const string &s2)
    {
          return s1.size() < s2.size()? s1 : s2;
    }
    

    声明内联函数, 编译器会尝试展开函数, 但不包装声明inline的函数100%做成内联的.

    内联函数适合优化规模较小、流程直接、频繁调用的函数

    调试帮助

    1. assert预处理宏
      assert(expr) 如果expr为真, 输出信息并终止程序执行, 如果为假什么也不做
      assert宏常用于检测不能发生的条件

    2. 预处理宏

    • __func__, 函数名
    • __FILE__, 文件名
    • __LINE__, 行号
    • __TIME__, 文件编译时间
    • __DATE__, 文件编译日期

    6. 函数指针

    函数指针声明

    // 原型函数
    bool lengthCompare(const string &, const string &);
    bool (*pf)(const string &, const string &);
    

    使用函数指针

    // 原型函数
    pf = lengthCompare; 
    pf = &lengthCompare; // 取地址符是可选的
    

    重载函数指针
    重载函数可以有对应的函数指针, 编译器会决定选择哪个函数

    指针作为函数形参

    void userBigger(const string &s1, const string &s2,
                              bool pf(const string&, const string &);
    void userBigger(const string &s1, const string &s2,
                              bool (*pf)(const string&, const string &); // 等价声明, 函数名会自动转换
    

    返回函数指针

    1. 使用typedef或using
    using F = (int *, int); // F是函数类型
    using PF = int(*)(int *, int); // PF是函数指针类型
    
    1. 直接声明
      int(*f1(int))(int *, int);

    2. 使用auto
      auto f1(int);

    3. 使用decltype

    string::size_type sumLength(const string&, const string &);
    decltype(sumLength) *getFn(const string &); // 返回类型是一个函数指针
    

    相关文章

      网友评论

          本文标题:C++ Primer: Functions

          本文链接:https://www.haomeiwen.com/subject/ttsnnhtx.html