函数探幽
c++内联函数
- 为了提高程序运行速度而做的改进
- 编译的最终产物是可执行程序——由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。然后计算机开始逐步执行指令。执行到函数调用的时候,程序将在调用函数后立即存储该指令的内存地址,并将函数参数赋值到堆栈,跳到标记函数起点的内存单元,执行函数代码,然后再跳回到地址被保存的指令处。在此过程中对计算器是有一定开销的
- 内联函数,使用相应的函数替换函数调用,让程序无需调到另一个位置处执行代码,再跳回来。运行速度会比普通函数调用要快,但缺点就是需要更多的内存
- 使用内联函数
- 在函数声明前加上关键字inline
- 在函数定义前加上关键字inline
引用变量
-
引用是已定义的变量的别名,主要用作函数的形参,通过将引用变量用作参数,函数将使用函数数据,而不是其副本
创建引用变量 -
&在c++里面不止是取址符,还能用来声明引用
int rat = 10;
int & test = rat;
cout << "rat的值是" << rat << endl;
cout << "test的值是" << test << endl;
rat++;
cout << "test的值是" << test << endl;
cout << &rat << endl;
cout << &test << endl;
//rat的值是10
//test的值是10
//test的值是11
//0x7ffc9b3dfe74
//0x7ffc9b3dfe74
- 需要在声明引用的时候进行初始化
int rat = 10;
int & test;
test = rat;
// 会报错
- 将引用用作函数参数,是函数中的变量名成为调用程序中的变量别名,此传递参数方法称为按引用传递
- 按引用传递允许被调用的函数能够访问调用函数中的变量
- 按引用传递和按指针传递函数参数的,都能够对原始数据进行修改。swapr中的变量a,b可以看作是wallet1和wallet2的别名,而swapv则是创建了新的变量a和b,其值和wallet1和wallet2相同
void swapr(int & a, int & b);
void swapp(int *p, int *q);
void swapv(int a, int b);
int main()
{
using namespace std;
int wallet1 = 300;
int wallet2 = 250;
// const int wallet1 = 300;
// const int wallet2 = 250;
cout << wallet1 << " " << wallet2 << endl;
swapr(wallet1, wallet2);
cout << wallet1 << " " << wallet2 << endl;
swapp(&wallet1, &wallet2);
cout << wallet1 << " " << wallet2 << endl;
swapv(wallet1, wallet2); //没有换过来
cout << wallet1 << " " << wallet2 << endl;
return 0;
}
void swapr(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swapp(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swapv(int a, int b)
{
int temp;
temp = a;
a = b;
b = a;
}
- 在函数引用参数中, 当参数为const引用时,c++会生成临时变量
- 实参类型不正确,但可以转换为正确类型
- 实参类型正确,但不是左值(左值参数是可被引用的数据对象,如变量、数组元素、结构成员、引用和解除引用的指针)
-
引用参数的声明尽可能地去使用const
- 可以避免无意中修改数据的编程错误
- 使用const是函数能够处理const和非const实参,否则只能接受非const数据
- 使用const引用使函数能够正确生成并使用临时变量
double refcube(const double &ra);
int main()
{
double side = 3.0;
double * pd = &side;
double &rb = side;
long edge = 5L;
double lens[4] = {1.0, 3.0, 5.0, 6.0};
double c1 = refcube(side);
double c2 = refcube(*pd);
double c3 = refcube(rb);
double c5 = refcube(edge);
double c4 = refcube(lens[2]);
double c6 = refcube(7.0);
double c7 = refcube(side + 10.0);
cout << c1 << endl;
cout << c2 << endl;
cout << c3 << endl;
cout << c4 << endl;
cout << c5 << endl;
cout << c6 << endl;
cout << c7 << endl;
return 0;
}
double refcube(const double &ra)
{
return ra*ra*ra;
}
- 使用引用参数的主要原因是
-
程序有可能会修改调用函数中的数据对象
-
通过传递引用而不是整个数据对象,可提高程序的运行速度
使用引用参数的情况:- 数据对象小(内置数据类型),可使用按值传递
- 数据对象是数组,使用指针
- 数组对象是较大的结构,使用const指针或const引用
- 数组对象是类对象,使用const引用
- 对于修改调用函数中的数据的函数
- 数据对象是内置数据类型,则使用指针
- 数据对象是数组,则使用指针
- 数据对象是结构,则使用引用或指针
- 数据对象是类对象,则使用引用
- 将引用用于结构,使用结构引用参数的方式与使用基本变量引用相同,只需在声明结构参数时使用引用运算符&即可
struct free_thorws
{
int made;
int attempts;
float percent;
}
void get_test(free_thorws & ft);
- 像下面free_throw &则该函数返回的是引用,free_throw的话,则是直接返回一个值
free_throw & accumulate(free_throws & target, const free_throws & source);
- 返回引用与返回值不同的区别
- 返回值将return的内容,赋值到一个临时的位置,然后程序调用的时候用的就是这个值
- 返回引用是直接使用该内存地址上面的值,相对来说效率会提高一点
- 返回引用是需要注意的是,要避免返回函数终止时不再存在的内存单元引用。下面的newguy在函数运行完毕之后就不再存在了,解决方法是可以用new来分配新的内存空间给newguy并返回指向该内存空间的指针
const free_thorw & clone2(free_throws & ft)
{
free_throws newguy;
newguy = ft;
return newguy;
}
默认参数
- 对参数列表的函数,必须从左到右添加默认值
int harpo(int n, int m = 6, int j = 7);
int chico(int n, int m = 6, int j);
函数重载
- 函数重载能够让程序使用多个同名的函数。若没有匹配的原型并不会自动通知使用其中的某个函数,c++会将其类型强制进行转换再匹配,要是转换后有多重匹配结构,则拒绝调用并报错
- 仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应采用重载
void print(const char * str, int width);
void print(double d, int width); //这里注释掉其中两个函数则能正常运行
void print(long l, int width);
void print(int i, int width);
void print(const char *str);
int main()
{
print("asdsad", 15);
print("Sdjosia");
print(199.0, 10);
print(1992, 12);
print(1993L, 15);
unsigned int year = 6;
print(year, 22); //这里会报错
return 0;
}
void print(const char * str, int width)
{
cout << str << " "<<width << endl;
}
void print(double d, int width)
{
cout << d << " " << width << endl;
}
void print(long l, int width)
{
cout << l << " " << width << endl;
}
void print(int i, int width)
{
cout << i << " " << width << endl;
}
void print(const char *str)
{
cout << str << endl;
}
- c++跟踪每个重载函数的原理是编译器根据函数原型中指定的形参类型对每个函数名进行加密
函数模板
- 使用泛型来定义函数,通过泛型来对具体类型进行替换
template <typename T> void Swap(T &a, T&b);
int main()
{
int i = 10;
int j = 20;
Swap(i, j);
cout << i << " " << j << endl;
float m = 10.3;
float n = 23.5;
Swap(m,n);
cout << m << " " << n << endl;
return 0;
}
template <typename T> void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
- 模板重载,与重载常规函数定义一样
template <typename T> void Swap(T &a, T&b);
template <typename T> void Swap(T *a, T *b, int n);
void show(int a[]);
const int Lim = 8;
int main()
{
int i = 10, j = 20;
Swap(i, j);
cout << i << " " << j<< endl;
int d1[Lim] = {0, 7, 0, 4, 1, 7, 7, 6};
int d2[Lim] = {0, 7, 2, 0, 1, 9, 6, 9};
show(d1);
show(d2);
Swap(d1, d2, Lim);
show(d1);
show(d2);
return 0;
}
template <typename T> void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template <typename T> void Swap(T a[], T b[], int n)
{
T temp;
for (int i = 0; i< n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void show(int a[])
{
cout << a[0] << a[1] << "/";
cout << a[2] << a[3] << "/";
for (int i = 4; i<Lim; i++)
{
cout << a[i];
}
cout << endl;
}