函数总章C11
创始人
2024-02-01 21:12:11
0

函数

后置返回类型

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

#include 
using namespace std;
auto func(int a,int b)->int;int main()
{
cout<int
{return a+b ;
}

Ⅰ.常规函数

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

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

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

Ⅱ.内联函数

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

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;
}

类的成员函数实现内联

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

函数和const

函数前后const

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

const returnType functionName(param list)

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

returnType functionName(param list) const

说明:类中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);

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

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

使用尾置返回类型

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

使用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;
}

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(形参);

数组作为函数的参数

数组引用作为函数的参数

#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);}

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


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 };
编译会报错说明编译时将进行数组长度的检查

不存在引用的数组

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

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

函数重载

void func(int a){};
void func(const int a){};//重复定义

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

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

void func(int *a){};
void func( const int *  a){};
void func(int &a){};
void func( const int & a){};

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

//#include
//#include
//#include  								// 使用abi
//using namespace std;
//
//void print( int a[]){
//
//int b[100]={1,2,3};
//    cout<
//
//    int i[100]={1,2,3};
//    print(i);
//
//}

指针函数 和 函数指针

  • 函数指针

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

//定义普通函数,返回值为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));
}
  • 指针函数

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

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))
//再加⼀个*号,取地址。
}

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

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));
}

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

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函数
}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...