笔记会持续更新,有错误的地方欢迎指正,谢谢!
前言:先介绍函数的定义和声明,参数传递和函数返回;重载函数:几个不同的函数用同一个函数名,匹配调用:调用的是哪个;最后介绍函数指针。本章有意思,一起吃鸡吧!
函数基础
遇到一条return语句时,结束执行过程,return语句完成两项工作:
1.返回return语句中的值
2.将控制权还给主调函数
局部对象
局部静态对象:不同于只存在于块执行期间的自动对象,局部静态对象直到程序终止才被销毁;没有显式初始值时会执行值初始化。
static int cnt = 0;
函数声明
制造一次,使用多次。
函数的声明和函数的定义非常类似,唯一的区别就是函数声明无须函数体:
int fact(int val);
分离式编译
c++开发中广泛使用声明和实现分开的开发形式,其编译过程是分离式编译,就是说各个cpp文件完全分开编译,然后生成各自的obj目标文件,最后通过连接器link生成一个可执行的exe文件。
注意:定义函数的源文件还要把含有函数声明的头文件包含进来,目的是:编译器来验证函数的定义和声明是否匹配。
例子:
--test.h--
void fun();//这里的.h文件只是声明一个函数
--test.cpp--
#include ”test.h”
void fun()//这里给出了test.h中声明的f函数的具体实现
{
std::cout<<"分离式编译"<<std::endl;
}
--main.cpp--
#include ”test.h”
int main()
{
fun(); //调用fun
}
参数传递
形参的类型决定了形参和实参交互的方式:
1. 形参为值类型:形参和实参是两个独立的对象,只不过是实参的值拷贝给形参了。
2. 形参为引用:形参绑定到实参上,形参就是实参的绰号,是同一个。
传值参数
形参为值类型,形参的变化不会影响实参,因为经过了一次拷贝。若想要影响实参,就用返回值呗,不过要经过两次拷贝咯。
指针参数
对指针参数也同理:
void reset(int *p)
{
*p=0;//通过指针来改变值,对i有效!
p=0;//改变的是副本,对&i无效!要想有效,形参就用二级指针。
}
int main()
{
int i=666;
cout<<i<<endl;
cout<<&i<<endl;
reset(&i);
cout<<i<<endl;
cout<<&i<<endl;
return 0;
}
传引用参数
例子1:
void reset(int &i) //引用形参
i = 0;
例子2:
bool isShorter(const string &s1, const string &s2) //为什么用const,参数不用改变时,尽量都用它。
{
return s1.size() < s2.size();
}
总结:使用引用可避免拷贝操作。
const形参和实参
void f(const int i){//省略}
void f(int i){//省略}//错误,重复定义!
总结:实参初始化形参时会忽略顶层const,所以有无顶层const版本等价(上述两种的实参类型都可为int型或const int型,但第一个的形参不可被改变,第二个可被改变),都定义则属于重复定义!
指针形参或引用形参 与const
void reset(int *p){} //
void reset(int &p){} //重载函数
int main()
{
int i = 42;
const int ci = i;
reset(&i); //正确:调用第一个函数
reset(&ci); //错误:p不能指向常量const。实参不能初始化int*变量,可初始化const int*变量。
reset(i); //正确:调用第二个函数
reset(ci); //错误:不能把引用p绑定到常量const上。实参不能初始化int &变量,可初始化const int&变量。
}
尽量使用常量引用
对于引用来说,只要不需改变形参的值,都尽量用常量引用,理由有两点:
- 该用常量而不用常量,会让函数的调用者认为函数可以改变它的实参的值;
- 非常量引用会限制函数所能接受的实参类型。刚刚就学过,我们不能把const赋给非const,但可以把非const的给const。
数组形参
当函数不需要对数组元素执行写操作时,数组的形参应该是指向const的指针;
只有当函数确实要改变元素值得时候,才把形参定义成指向非const的指针。
数组引用形参
//这里的10形参,是数组类型的一部分,所以只接受10个元素的数组。
void fun(int (&arr)[10])
{
for(auto elem : arr)
{
cout << elem << endl;
}
}
含有可变参数的函数
有时候我们不清楚应向函数传递几个实参(不同类型/同类型),则可传递一个名为initializer_list的标准库类型。
PS:后面只是举例,看不下去的同志可以不往下看啦~
例子:我们要写一个输出错误的程序,我们不知道有多少错误,但这些错误的类型都是string。
void errorMsg(initializer_list<string> il)//同类型时
{
for(auto beg = il.begin(); beg != il.end(); ++beg)
{
cout << *beg << endl;
}
}
我们可以用不同的参数数量去调用:
errorMsg({"aaa","a","b"});
//向initializer_list传递值序列要用花括号
errorMsg({"a", "b"});
//不同数量的实参
还可以加其他类型的参数,加上就行了。
void errorMsg(int a, initializer_list<string> il){}
//不同类型时