• 函数总章C11


    函数

    后置返回类型

    前面放auto,表示还数返回类型放到参数列表之后,而放在参数列表之后的返回类型是通过->开始的。

    #include 
    using namespace std;
    auto func(int a,int b)->int;
    
    
    int main()
    {
    cout<int
    {
        return a+b ;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Ⅰ.常规函数

    常规函数调用时会使程序跳到另一个地址(函数的地址),并且在函数结束时返回。

    • 执行函数调用指令,立即存储该指令的地址,并将函数参数保存到的堆栈。
    • 跳到函数起点的内存单元,执行函数代码(将返回值保存到寄存器中。
    • 跳回被保存指令的地址处。

    这一过程和系统中的中断很类似。来回跳跃并记录跳跃位置意味着使用普通函数时,需要一定的开销。

    Ⅱ.内联函数

    内联函数就是 编译器将使用相应的函数代码代替了函数调用(没有读懂这句话可以直接跳到最后的思考) 。程序不用为了执行函数而来回跳动。
    所以 内联函数的运行速度比普通函数快,但代价是占用了更多的内存。

    1.语法
    1.在函数声明前加上关键字 inline。
    2.在函数定义前加上关键字 inline。

    但是通常的做法 省略原型,就是将整个定义放在原来声明函数的位置。

    2.注意
    程序员试图将函数作为内联函数的时候,编译器可能不会满足要求,原因:

    1.它可能认为函数过大。
    2.函数调用了自己(内联函数不能递归)

    Ⅲ.选择地使用内联

    • 如果执行函数的时间比处理函数调用机制的时间长,则节省的时间只占很小一部分。
    • 如果代码执行时间很短,这使得非内联调用的时间显得占比大。则内联调用就可以节省大部分时间。
    • 如果该函数多次使用,则就要看我们得需求是什们,要求速度,就用内联,要求内存,就用非内联。
    #include 
    using namespace std;
    inline int Max(int x, int y)
    {
    
        return (x > y)? x : y;
    }
    
    // 程序的主函数
    int main( )
    {
    
        cout << "Max (20,10): " << Max(20,10) << endl;
        cout << "Max (0,200): " << Max(0,200) << endl;
        cout << "Max (100,1010): " << Max(100,1010) << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    类的成员函数实现内联

    //一:在类定义中实现成员函数in1ine:类内的成员函数实现其实也叫类内的成员函数定义
    /这种直接在类的定义中实现的成员函数,会被当做in1ine内联函数来处理。
    
    • 1
    • 2

    函数和const

    函数前后const

    函数前const:普通函数或成员函数(非静态成员函数)前均可加const修饰,表示函数的返回值为const,不可修改。格式为:

    const returnType functionName(param list)
    
    • 1

    函数后加const:只有类的非静态成员函数后可以加const修饰,表示该类的this指针为const类型,不能改变类的成员变量的值,即成员变量为read only(例外情况见2),任何改变成员变量的行为均为非法。此类型的函数可称为只读成员函数,格式为:

    returnType functionName(param list) const
    
    • 1

    说明:类中const(函数后面加)与static不能同时修饰成员函数,原因有以下两点

    ①C++编译器在实现const的成员函数时,为了确保该函数不能修改类的实例状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的,也就是说此时const的用法和static是冲突的;

    ②两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系,因此不能同时用它们。

    返回数组指针的函数

    数组不能拷贝,所以函数不能返回数组,但函数可以返回数组的指针和引用

    使用类型别名可以简化

    typedef int arrt[10];或者 using arrt =int arrt[10];
    arrt*func(int i);
    
    • 1
    • 2

    如果不使用类型别名,则必须牢记被定义的名字后面数组的维度

    int (*func(int i))[10]
    
    • 1

    使用尾置返回类型

    auto func(int i) ->int(*)[10];
    
    • 1

    使用decltype

    int a[]={1,2,3,4,5,6};
    int b[]={2,3,4,2,5,0};
    decltype(a)*func(int i)
    {
        return (i%2) ? &a:&b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    a的类型是 int a[10]

    返回数组引用的函数也是类似的

    
    题目:编写一个函数的声明,使其返回包含10个string对象的数组的引用
     
    //不用类型别名
    string(&func(形参))[10];
     
    //类型别名
    using arr = string[10];
    arr& func(形参);
     
    typedef string(&arr)[10];
    arr func(形参);
     
    //尾置返回类型
    auto func(形参)->string(&)[10];
     
    //decltype关键字
    string ss[10];
    decltype(ss) &func(形参);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    数组作为函数的参数

    数组引用作为函数的参数

    #include
    using namespace std;
    //void func(const int *a)
    //{
    
    //}
    //void func(const int a[])
    //{
    //
    //}
    //void func(const int a[10])
    //{
    //
    //}
    
    //  ========================上面三个函数是等价的,函数形参都是const int * ================================
    
    
    // 当调用函数时,只会检查函数的参数是否是 const int * 类型。但是引用则会检查数组的维度
    //void func(const int (&a)[10])
    //{
    //
    //}
    
    int main()
    {
        int b[2]={};
        func(b);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    数组引用作为函数参数的作用

    
    2.数组的引用的作用(存在) 如:int(&arr)[n]
     
    (1).作为形参(防止数组退化为指针)
    下面三种方法是等价的:
    int func(int array[]);
    int func(int array[10]);
    int func(int* array);
    在func中是无法知道数组的大小,如果开发者必须提前知道数组的大小,就需要用到数组的引用做参数
    例如,int(&a)[2],可以有效的防止数组退化。也就是,数组作为函数参数传递过去后,仍然是一个数组。
    优点:节省内存消耗,不用拷贝一份数组,直接使用原数组(甚至可以修改原数组)
     
    #include 
    using namespace std;
     
    int func(int(&a)[6])
    {
    	int count = 0;
    	for (int i = 0; i < 6; i++)
    	{
    		count += a[i];
    	}
    	return count;
    }
     
    int main()
    {
    	int a1[6] = { 1,2,3,4,5,6 };
    	int count = func(a1);
    	cout << "count:" << count << endl;
    	return 0;
    }
     
    代码安全性提高:
    如果将 int a1[6] = { 1,2,3,4,5,6 }; 改为 int a1[5] = { 1,2,3,4,5 };
    编译会报错说明编译时将进行数组长度的检查
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    不存在引用的数组

    (2).c++中,引用可以说只是某个变量的别名,所谓别名,是和指针类型区分开的:指针类型也可以
    指向某个变量,但指针类型本身也是一个变量,而引用实际上不是一个变量。更本质来说,可以
    理解为引用没有自身的地址,不占用内存空间(这里为了简化问题可以这样考虑)。因此,
    声明引用数组没有办法分配空间,因为根本就没有空间可以分配给引用。所以不能声明和定义引用数组
     
    (3).C++不支持传统意义的复制:
    传统的复制为:int a = b;
    这里a和b在内存中分别占用不同的内存空间,但是内容一致。
    如果int& a = b; 这个时候,内存中a并不被分配内存,所以没有复制可言。
    所以对于数组元素是引用来说,没法完成元素的复制操作,没有给数组分配内存,所以数组中的元素不能是引用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    不仅仅对于数组是这样,对于容器而言,也不存在引用的 vector list 等。。。。

    函数重载

    void func(int a){};
    void func(const int a){};//重复定义
    
    
    • 1
    • 2
    • 3
    
    void func(int *a){};
    void func( int * const a){};//重复定义
    
    • 1
    • 2
    • 3

    一个拥有顶层const 的形参 无法和 另一个没有顶层const的 形参构成函数重载

    void func(int *a){};
    void func( const int *  a){};
    
    
    • 1
    • 2
    • 3
    void func(int &a){};
    void func( const int & a){};
    
    • 1
    • 2

    如果形参是某种类型的指针或引用,则通过区分其指向的是常量还是非常量可以实现函数重载

    //#include
    //#include
    //#include  								// 使用abi
    //using namespace std;
    //
    //void print( int a[]){
    //
    //int b[100]={1,2,3};
    //    cout<
    //    cout<
        int*
        int [3]
    https://www.zhihu.com/question/368331215
    //为何std::begin()的参数不能是一个指针?
    //
    //}
    //
    //int main(){
    //
    //    int i[100]={1,2,3};
    //    print(i);
    //
    //}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    指针函数 和 函数指针

    • 函数指针

    函数类型的指针,或者说是指向函数的指针

    //定义普通函数,返回值为int类型
    int add(int x,int y)
    {
    return (x+y);
    }
    //定义相乘的函数,返回值为int类型
    int multiply(int xx,int yy)
    {
    return (x*Y);
    }
    //定义函数指针,后⾯括号是两个参数,参数个数和类型取决于指针P指向的函数
    int (*P) (int a,int b); //指针的类型由P指向的函数决定,这⾥(*p)的前缀是int
    int main()
    {
    P=add;
    //P=&add;
    //让指针指向函数,以上两种写法都可以。因为函数名就代表地址,所以加不加取地址符号都⾏
    printf("%d",(*P)(3,5)); //(*P)就相当于是函数名
    P=&multiply; //改变指针指向
    printf("%d",(*p)(5,4));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 指针函数

    指针类型的函数,或者说函数的返回值类型是指针

    int c;//定义全局变量c
    int* add(int x,int y)
    {
    c=x+y;
    return &c;
    }
    int * (*p) (int x,int y); //前缀为 int*,说明P指针指向的是int*类型的函数
    int main()
    {
    p=add;
    printf("%d",(*p)(3,5));
    函数指针的作⽤可以是可以作为函数的参数
    我们也可以定义函数指针数组
    // 由于add函数的返回值是指针类型,所以返回的是地址
    printf("%d",*(*p)(3,5))
    //再加⼀个*号,取地址。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    函数指针的作⽤可以是可以作为函数的参数

    int add(int x,int y)
    {
    return (x+y);
    }
    int addPlus( int (*p) (int x,int y), int m,int n)
    {
    //其实就相当于是 int (*p)(int x,int y)=add;
    return (*p)(m,n);
    }
    int main()
    {
    printf("%d",addPlus(add,3,5));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们也可以定义函数指针数组

    int main()
    {
    int (*f[2]) (int x,int y)=(add,multiply);
    printf("%d",(*f[0]) (3,5) );//调⽤add函数
    printf("%d",(*f[1]) (5,4) );//调⽤multiply函数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    一文巩固Spring MVC的Bean加载机制
    小爱开直播了,欢迎大家微观
    命令行客户端-连接服务端&操作数据库
    Python读取PDF文字 去掉页眉页脚
    【kafka】-分区-消费端负载均衡
    ubuntu20.04安装FTP服务
    Java代码基础算法练习-求数据序列的最大值及最小值---2024.3.15
    Java中单体应用锁的局限性&分布式锁
    七大软件设置原则
    如何用 CMake 生成 C++ 库(支持 find_package 机制)
  • 原文地址:https://blog.csdn.net/qq_55125921/article/details/127944049