函数
- 函数三要素: 返回值类型, 函数名, 参数列表
int sum(int, int); //函数声明
int main()
{
//函数调用
int result = sum(5, 3);
}
//函数定义
int sum(int num1, int num2)
{
//函数实现的代码
}
- 函数声明与函数定义的头部类型, 最后以分号结尾
- 函数声明中的参数名称可以省略, 只写参数类型
- C++中返回值类型不能是数组, 但是可以是其他任何类型, 可以将数组作为结构或者对象组成部分返回
#include <iostream>
using namespace std;
double cuboidV(double, double, double);
int main()
{
cout << cuboidV(15.1, 12, 11.5);
return 0;
}
//长方体体积
double cuboidV(double length, double width, double height)
{
return length * width * height;
}
参数按值传递
-
给函数传递参数时, 参数不会直接传递给函数, 而是先制作参数的副本, 存储在栈上, 再使用这个副本用于函数, 而不是使用初始值.
数组作为函数参数
- 数组作为函数实参时, 只传递数组的地址(首地址), 并不传递整个数组的空间
- 当用数组名作为实参调用函数时, 数组首地址指针就被传递到函数中
一维数组作为参数
#include <iostream>
using namespace std;
//函数声明
void print(const int *, int);
int main(){
int values[]{100, 120, 80, 90};
//函数调用, 数组作为实参, 只传递地址
print(values, 4);
}
//函数实现, const修饰参数, 防止参数在函数内被修改
void print(const int values[], int len)
{
string value_names[]{"体力", "智力", "魅力", "气质"};
for(int i = 0; i < len; i++){
cout << value_names[i] << ":" << values[i] <<endl;
}
}
二维数组作为参数
#include <iostream>
using namespace std;
//函数声明
void show(const double (*)[5], int);
int main()
{
double powers[3][5]{
{45.5, 55.6, 88.9, 66.6, 78},
{98.2, 69.1, 33.7, 49.3, 58},
{78.2, 58.6, 12.8, 37.8, 43},
};
//函数调用
show(powers, 3);
}
//函数实现
void show(const double arrays[][5], int len)
//void show(const double (*arrays)[5], int len)
{
for(int i = 0; i < len; i++){
for (int j = 0; j < 5; j++){
cout << arrays[i][j] << ', ';
}
cout << endl;
}
}
函数指针
- 函数也有地址, 函数的地址是存储其机器语言代码的内存开始地址
- 好处: 可以在不同的时间使用不同的函数
//函数指针的声明
double (*ptr_sum)(double, double); //该语句声明了一个指针ptr_sum, 指向一个函数
double* ptr_sum(double, double); //不是函数指针, 而是声明了一个函数ptr_num, 返回值是double*类型
函数的进阶
内联函数
-
内联函数是C++为提高程序运行速度所做的一项改进
-
与常规函数的区别不在于编写方式, 而是编译器使用函数代码替换函数调用
-
如果执行函数代码的时间比处理函数调用机制的时间长, 则节省的时间将只占整个过程的很小一部分
-
如果代码执行时间很短, 内联调用就可以节省大部分时间
-
使用内联特性, 在函数声明前加关键字inline 或 在函数定义前加关键字inline
参数的引用传递
- 引用是给对象起的另外一个名字, 引用即别名
- 引用并对象, 只是为一个已经存在的对象起的别名
- 引用必须初始化
- 引用更接近与const, 一旦与某个变量关联起来, 就将一直效忠于它
- 将引用变量用作参数时, 函数将使用原始数据, 而不是副本
- 当数据所占内存比较大时, 建议使用引用参数
使用引用参数
函数返回引用类型
-
不要返回局部变量的引用, 局部变量的生存期只在函数中. 函数中的局部变量会被内存回收, 你程序中申请的这块内存已经不是你的了!
-
函数可以不返回值, 默认返回最后一个对象的引用
-
返回引用时, 要求函数参数中包含被返回的引用对象
-
避免在设计函数中存在模糊的情况, 返回类型为const int&, const类型为不可修改的左值
const int& sum(int & num){ } int num = 10; int& result = sum(num); sum(num) = 55; //加const之后不合法
小结
- 能够修改调用函数中的数据对象
- 数据对象较大时传递引用可以提高程序的运行效率
- 函数中不需要修改传递的参数
- 如果数据很小, 建议按值传递
- 传递数组只能使用指针, 并使用const关键字
- 较大的对象则使用const指针或引用
- 函数中需要修改传递的参数
- 数据对象是基本类型或结构时, 可以使用指针或引用
- 数据对象是数组时只能使用指针
- 数组对象是类对象时, 建议使用引用
默认参数
-
默认值可以在函数声明或定义中给出, 不能在两个位置同时出现
-
带参数列表的函数, 必须从右向左添加默认值
void test1(int a, int b = 5, int c = 10); void test2(int a, int b = 5, int c); //不合法, 默认参数后的参数也需有默认值 void test1(int a = 1, int b = 5, int c = 10);
函数重载
-
可以有多个同名的函数
-
函数名相同, 但是参数列表不同, 即特征标不同
//编译器在编译时, 根据参数列表对函数进行重命名 void eating(); void eating(string); void eating(string[]); void eating(int, int, int); eating_int_int_int void eating(string&); //和eating(string)的特征标识相同的, 编译器把类型引用和类型本身视为同一个特征标
函数模板
-
函数模板就是建议一个通用函数
-
函数定义时不指定具体的数据类型, 使用虚拟类型代替
-
函数被调用时编译器根据实参反推数据类型, 类型的参数化
//函数声明 template<typename T> void Swap(T&, T&); //模板头 template<typename T> void Swap(T& a, T& b) { T temp = a; a = b; b = temp; }