函数:一个函数的定义通常包含:返回值类型,函数名字,形参列表以及函数体组成。
一、函数的调用
函数的调用主要完成两个工作:1、用实参初始化函数对应的形参。2、将控制权交给被调函数。
【注意】:实参和形参的类型一定要相同。
二、函数的返回类型
大多数类型都能用作函数的返回类型,但是数组不能作为函数的返回类型,还有一种特殊的返回类型过就是void类型,它表示函数不返回任何值。
三、局部对象
在C++中,名字有作用域,对象有生命周期。
名字的作用域是程序文本的一部分,名字在其中可见。
对象的生命周期是程序执行过程中该对象存在的一段时间。
局部变量
形参和函数体内部定义的变量称为局部变量,它们对函数而言是局部的,仅在函数的作用域内可见,局部变量还回隐藏在外层作用域中的同名的其他所有的声明。
自动对象
对于普通局部变量的对象来说,当函数的控制路径经过变量的定义语句时会创建该对象,当到达定义所在块的末尾时销毁该对象,只存在于块执行期间的对象,称为自动对象。
【注意】:形参是一种自动对象。
局部静态对象
在定义局部变量的时候,给其定义成static类型的,这样就成了一个局部静态对象。局部静态对象的声明周期贯穿函数调用及之后的时间。
局部静态对象在程序的执行路径第一次经过对象的定义语句的时候初始化该对象,并且直到程序终止才被销毁,在此期间及时对象所在的函数结束的执行也不会对它有影响。
【注意】:如果局部静态对象没有显示的初始化,将会被默认初始化为0。
我们来看一个例子:
#include <iostream>
using namespace std;
int fun();//fun函数声明
int main()
{
for (int i = 0; i < 3; ++i)
{
cout << fun() << " ";//循环调用fun函数
}
cout << endl;
system("pause");
return 0;
}
int fun()
{
static int a = 0;//定义一个局部静态对象
return ++a;
}
最后程序输出 1 2 3 ,正如上面所说,局部静态对象只在其所在函数第一次经过它的定义语句时才会对其初始化。
再来对比一下如果没有给a定义成一个局部静态对象的例子。
#include <iostream>
using namespace std;
int fun();//fun函数声明
int main()
{
for (int i = 0; i < 3; ++i)
{
cout << fun() << " ";//循环调用fun函数
}
cout << endl;
system("pause");
return 0;
}
int fun()
{
int a = 0;//定义一个普通的局部对象。
return ++a;
}
最后结果是 1 1 1, 每次fun函数被调用的时候,都会对a进行初始化,因为它是一个普通的局部对象,所以每次函数执行完之后都会被销毁。
四、函数声明
函数的名字也必须在调用之前声明。函数声明和函数定义类似,但是函数声明不需要函数体,同时也可以不用写形参的名字,只要在函数名字之后加上一个分号就可以了。
例如:
int fun(); //这就是一个函数声明。
五、参数传递
传值参数
1、普通对象
函数对形参的所有操作都不会形象实参,因为这样的传参是将实参的值拷贝给形参的,实参和形参是两个相互独立的对象。
2、指针形参
当我们将指针作为参数的时候,这个时候拷贝的是指针的值,拷贝之后,两个指针是不同的,但是它们都是可以间接地访问它们所指的对象,它们所指的对象是同一个,所以我们可以通过指针来修改它所指向的对象的值。
传引用参数
我们可以使用引用形参来改变实参的值,因为对引用的操作实际上就是对被引用的对象的操作,引用只是给对象起了另外一个名字。
我们来看看这三种参数传递有何不同
1、普通的对象传递
#include <iostream>
using namespace std;
void swap(int x, int y); //普通参数传递函数的声明
int main()
{
int a = 3, b = 5;
cout << "交换之前的值:" << " a = " << a << " b = " << b << endl;
swap(a, b); //函数调用
cout << "交换之后的值:" << " a = " << a << " b = " << b << endl;
system("pause");
return 0;
}
void swap(int x, int y) //函数定义
{
int t = y; //定义一个中间变量用来交换两个对象。
y = x;
x = t;
}
最后输出的结果是:
交换之前的值:a = 3 b = 5
交换之后的值:a = 3 b = 5
2、指针形参
#include <iostream>
using namespace std;
void swap(int *x, int *y); //指针参数传递函数的声明
int main()
{
int a = 3, b = 5;
cout << "交换之前的值:" << " a = " << a << " b = " << b << endl;
swap(&a, &b); //实参要取地址
cout << "交换之后的值:" << " a = " << a << " b = " << b << endl;
system("pause");
return 0;
}
void swap(int *x, int *y) //函数定义
{
int t = *x; //定义一个中间变量用来交换两个对象。
*x = *y;
*y = t;
}
最后输出的结果是:
交换之前的值:a = 3 b = 5
交换之后的值:a = 5 b = 3
3、传递引用参数
#include <iostream>
using namespace std;
void swap(int &x, int &y); //传递引用参数传递函数的声明
int main()
{
int a = 3, b = 5;
cout << "交换之前的值:" << " a = " << a << " b = " << b << endl;
swap(a, b);
cout << "交换之后的值:" << " a = " << a << " b = " << b << endl;
system("pause");
return 0;
}
void swap(int &x, int &y) //函数定义
{
int t = x; //定义一个中间变量用来交换两个对象。
x = y;
y = t;
}
最后输出的结果是:
交换之前的值:a = 3 b = 5
交换之后的值:a = 5 b = 3
通过上面的例子,我们可以看到,普通的对象的调用不会改变实参的值,但是我们通过指针或者引用是可以改变实参的值的。
【注意】:普通的参数和指针的调用都会把实参拷贝给形参,普通的参数拷贝的是参数的值, 而指针参数拷贝的是指针,这两个指针是不一样的。引用不需要拷贝,因为引用就是直接对实参的操作。
建议:因为引用不需要拷贝,所以在传递大型的结构的时候最好要用引用。
六、函数重载
在同一个作用域内的几个函数名字相同,但是形参列表不同,这样的函数是重载函数。
例如:
void fun(int a, int b);
void fun(char a);
void fun(int *a, const int *b);
以上的几个函数就是重载函数。
【注意】:main函数不能够重载。
七、默认实参
调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
例如:
void fun(int a, int b = 5);//b是默认实参
//在面函数中可以这样调用
int main()
{
int x = 6, y = 9;
fun(x);
}
如果我们在fun函数中打印a和b的值,会输出,6和5;我们在调用fun函数的时候没有传递y的值,这样就会用到默认实参。但是如果我们调用的时候这样写fun(x, y),在fun函数中打印的结果就是6和9。
【注意】:一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
八、内联函数
只要在函数返回类型前面加上inline就成为了一个内联函数。
例:
inline int sum(int a, int b)
{
int sum = a + b;
return sum;
}
上面就是一个内联函数。
内联函数的作用:将它在每个调用点上“内联地”展开。相当于在函数调用的地方把函数体的内容展开,放到调用的这个地方来。
【注意】:内联函数适用于规模小,流程直接、频繁调用的函数。