c++函数

  1. 函数

    1. 定义包括
    • 返回类型
    • 函数名字
    • 0个或多个形参列表,列表位于一对圆括号中,形参之间以逗号分隔开
    • 函数体,位于语句块中
    1. 不允许嵌套定义
  2. 函数通过调用运算符()来执行函数

  3. 函数的执行过程

    • (隐式的)定义并且初始化它的形参
  4. 编译器能以任意可行的顺序对实参求值

  5. 没有形参的列表

    void f1(){};
    // 和C语言兼容
    void f2(void){};
    
  6. 函数的返回类型不能是数组或者函数类型。但是可以是指向数组或者函数的指针。

  7. 自动对象

    1. 普通局部变量,控制流经过变量定义语句时创建该对象。到达定义所在块末尾时销毁它。只存在于块执行期间的对象称为自动对象
    2. 形参时自动对象,函数开始为形参申请存储空间,因为形参定义在函数体作用域之内,一旦函数终止,形参被销毁
    3. 局部静态对象,有必要令局部变量的声明周期贯穿函数调用以及之后的时间。可以把局部变量定义为static类型。控制流第一次经过对象定义语句时初始化,直到程序终止才被销毁。内置类型的局部静态变量初始化为0
  8. 引用类型的参数可以用来充当多值返回函数

  9. const形参和实参。形参的初始化过程基本上就等于const变量被初始化,符合一样的规则

  10. 尽量用const引用的形参,因为const引用形参的可接受范围比普通引用形参的可接受参数的范围大,因为底层const的限制

  11. 不能以值传递的方式传递数组,但是并不妨碍出现形参位数组的形式

    1. 定义接收数组的函数
    void print(const int*);
    void print(const int[]);
    // 这里的维度表示我们期望数组含有多少元素,实际上不一定
    void print(const int[10]);
    
    1. 接收数组的函数如何知道数组的长度
      • c风格的字符串就是使用\0来表示。其他的int数组就没有什么明显有效的特殊不合法数字。
      • 所以标准库规范是传递首尾指针。
      • 另外可以传入表示数组大小的参数
    2. c++允许使用数组的引用作为形参。因为变量允许。
    void print(int (&arr)[10]);
    int j[2]={0,1};
    // 错误,实参不是含有10个整数的数组
    print(j);
    
    1. 编译器处理数组参数的时候一定会忽略第一维数组的大小,所以没有必要写上去
    2. 可变形参,省略符形参是为了便于c++程序访问某些特殊的c代码而设置的,这些代码使用了varargsc标准库功能。省略符形参只能出现在形参列表的最后一个位置。形式只能为以下两种
    // 第一种,对于这种指定部分形参的部分会进行正常的类型检查。省略符形参对应的实参无须类型检查。此处的逗号是可选的
    void foo(param_list,...);
    void foo(...);
    
  12. 调用约定

    cdel标准调用约定,同时也是默认约定
    如果函数A调用函数B,那么称函数A为调用者(caller),函数B称为被调用者(callee)。caller把向callee传递的参数存放在栈中,并且压栈顺序按参数列表中从右向左的顺序;callee不负责清理栈,而是由caller清理

    stdcall

    cdecl

    fastcall

    thiscal

    naked call

  13. c语言可变参

    1. 引入相应的库
    #include <stdarg.h>
    
    1. 栈对齐
      函数参数是有对齐的,每个参数不管sizeof是多少,均以系统栈对齐参数做对齐。公式如下
    #define SYSTEM_ALIGN
    #define CAL_ALIGN = SYSTEM_ALIGN - 1
    #define CAL_SIZE(type) (sizeof(type)+CAL_ALIGN)&(~CAL_ALIGN)
    
  14. 函数可以返回引用值(函数左侧需要加上&引用符号)。返回引用值的函数得到左值,其他类型得到右值。函数返回的左值可以赋值,常量左值无法赋值。不能返回局部变量的引用和指针

  15. 返回数组的引用和数组的指针

    1. 使用类型别名来返回数组指针
    using intArr=int[10];
    intArr* func(){};
    
    1. 不使用类型别名来返回数组的指针的函数
    // 如果没有此括号,则语义变成了返回指针的数组,这样就不合法并且会报错了
    int (*func(int i))[10];
    
  16. 重载

    1. main函数不能被
    2. 顶层const不能用来重载
    3. 底层const可以用来重载。并且具有const的可以传入const或者非const的对象,编译器会优先选择非常量版本的函数
    4. 重载确定overload resolution,编译器首先将调用的实参与重载集合中的每一个函数形参进行比较,然后根据比较的结果决定到底调用哪个函数
      1. 编译器找到一个与实参最佳匹配best match的函数
      2. 找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误
      3. 有多个函数可以用于匹配,但是每一个都不是明显的最佳选择此时也将发生错误,称为二义调用ambiguous call
    5. 重载和作用域,只有再相同的作用域中才能重载函数名。另外c++允许在局部作用域中声明函数。还有个值得注意的是,如果在局部作用域中声明了标志符,那么不管是变量还是函数,编译器都只会找到这一个而不会再去看外层的作用域的符号了。
    6. c++的名字查找发生在类型检查之前
    7. 函数匹配过程
      1. 第一步时确定候选函数candidate function。具有于被调用的函数同名,且声明在调用点可见
      2. 考察实参,从候选函数中选出能被这组实参调用的函数,选出的函数称为可行函数viable function。具有形参数量和调用提供的实参数量相复合,实参的类型于对应的形参类型相同或者能转换成形参的类型
      3. 从可行函数中选择最佳匹配。有且只有一个函数满足下列条件则匹配成功
        • 该函数的每个实参的匹配都不劣于其他可行函数需要的匹配
        • 至少有一个实参的匹配优于其他可行函数提供的匹配
      4. 如果上述检查之后没有一个函数脱颖而出,则调用错误,将报告二义性调用。
    8. 使用强制类型转换可以避免二义性调用,但是这样说明设计的形参不合理
    9. 实参形参转换的精确性等级划分
      1. 精确匹配
      • 实参的类型和形参的类型相同
      • 实参从数组类型或者函数类型转换成指针类型
      • 向实参添加顶层const或者从实参中删除顶层const
      1. 通过const转换实现的匹配(底层const转换,比如char*转变成const char*,如果重载函数的区别在于它们引用类型的形参是否引用了const,或者指针类型的形参是否指向const,则当调用发生时编译器通过实参是否为常量来决定哪个函数)
      2. 通过类型提升实现的匹配(整型提升)
      3. 通过算术类型转换或者指针转换实现的匹配
      4. 通过类类型转换实现的匹配
    10. 类型提升和算术类型转换的匹配
      void ff(int);
      void ff(short);
      ff('a');//char提升为int,调用ff(int)
      
      void aa(long);
      void aa(float);
      aa(3.14);// double可以转换成上述两种,二义调用
      
  17. 默认实参,一旦一个形参被赋予了默认值,则它后面的所有形参都必须有默认值。原因是为了避免歧义,c++中的默认形参调用方法是直接省略逗号和实参值。而在vb中可以用,,来表示中间的是一个默认实参。

    // 具有默认实参
    void test(int a=1,int b=2);
    
    // 具有默认实参的错误形式,这样会编译错误。
    void test(int a=1,int b=2,int c);
    // 此时无法区分是绑定到哪个形参
    test(1,2);
    
    • c++函数支持多次声明,但是声明只能修改原来函数的默认实参。后续声明只能为那些没有默认值的形参添加默认实参,而不能为一个形参赋予多次默认实参
    string scree(sz,sz,char = ' ' );
    // 错误,重复声明
    string scree(sz,sz,char = '*' );
    // 正确,添加一个默认实参,原来的char类型不变
    string scree(sz,sz = 80,char);
    
    #include <iostream>
    using namespace std;
    int b(int c,int d=1,int e=2);
    // 下面的重复定义默认值会报错
    int b(int c, int d, int e=3);
    int b(int c,int d,int e);
    int b(int c, int d, int e) {
        cout << d <<"\n"<< e << endl;
        return 2;
    }
    int main() {
        // 输出2 3
        b(1, 2, 3);
        // 输出1 2
        b(1);
        _fgetchar();
    }
    
    • 默认实参初始值可以是任意一个表达式,只要改表达式的类型能转化成形参所需的类型。。并且用作默认实参的名字在声明所在的作用域内解析。另外默认实参求值是发生在函数调用的时候,也就是意味着默认实参的值并不是一个constexpr
    sz wd=80;
    char def=' ';
    sz ht();
    string scree(sz = ht(),sz = wd,char=def);
    void f2(){
        def = '*';//改变了默认实参的值
        sz wd=100;//隐藏了外层定义的wd,但是没有改变默认实参的值
        window=scree();
    }
    
  18. 内联函数和constexpr函数,使用inline关键字。只是像编译器发出一个请求,编译器可以选择忽略这个请求。递归函数和比较长的函数一般不可能内被内联

    1. constexpr函数值的是能用于常量表达式的函数,定义constexpr函数的方法与其他函数类似。函数的返回类型以及形参类型必须都必须的是字面值类型,而且函数体中必须有且只有一条return语句。
    2. constexpr函数被隐式的指定为inline函数。constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行,比如空语句,类型别名以及using声明
    3. constexpr函数也可以返回非常量值,编译器会将确实返回常量值的地方变成常量值进行替换,而非常量值的地方则不会变成常量进行替换。而constexpr函数如果作用在常量表达式上下文中,编译器会检查函数的结果是否为常量,如果不是,则编译器发出错误信息。
    4. inline函数和constexpr函数可以在程序中多次进行定义,不过定义必须完全一致,所以一般放在头文件中
  19. 函数指针

    1. 函数的类型由返回类型和形参类型共同决定,和函数名无关
    2. 申明函数类型只需要用指针替换函数名即可
    // 函数a
    bool a(const string b);
    // 函数指针a
    bool (*a)(const string b);
    
    1. 函数指针的赋值必须精确匹配
    2. 函数名可以转换成函数指针
    3. 函数类型和函数指针并不同
    // test是函数类型
    typedef bool test(const string &);
    // test是函数指针类型
    typedef bool (*test) (const string &);
    
    1. decltype函数类型得到的结果是函数类型而不是函数指针
    typedef bool test(const string &);
    decltype(test) *testFunc; // testFunc是函数指针
    
    1. 直接声明返回函数指针的函数,从里向外看,f1有列表,f1是个函数,f1前面有*所以f1返回指针。再看括号外面,返回的指针也有形参列表,所以返回值是指针函数,该函数返回int类型
    int (*f1(int))(int *,int);
    
    1. 使用尾置类型简化上述返回函数指针的函数
    auto f1(int)->int (*)(int *,int);
    
  20. const修饰返回结果的函数,避免返回结果被赋值,并且接受其的变量也必须为const变量

    const int test();
    
  21. const修饰成员函数,即常量成员函数,表示函数不会修改对象的内容,并且const成员函数不能调用非const成员函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值