本篇文章将整理出关于参数缺省和引用的一些知识。
(一)参数缺省:参数缺省包括全缺省和半缺省。顾名思义,全缺省就是函数的所有参数都给出默认值,半缺省就是仅有函数的部分参数给出了默认值。看下边的一段代码:
#include<iostream> using namespace std; int Add(int x, int y) { return x + y; } int main() { int ret = Add(); system("pause"); return 0; }
要是写出上边的一段代码,低版本的vs会编译不通过,报错是Add函数不接收0个参数,高版本vs会有红色标注。要是对于函数调用的时候不想给出参数,我们可以使用默认参数,若将Add函数写成这样就好了:
int Add(int x = 0, int y = 0) { return x + y; }
这就是所谓的全缺省。那么半缺省呢??继续实例:
int Add(int x , int y = 0) { return x + y; } int main() { int ret = Add(10); system("pause"); return 0; }
这样子的话,最后会得到10和0相加。当然调用的时候,我们也可以给出两个参数。如果我们将x给出默认,y不默认,看可以不??
int Add(int x = 0 , int y) { return x + y; }
要是Add函数写成上边这样,高版本vs编译器会红线报错:默认参数不在参数列表末尾。想想这是为什么?其实,想一下就明白了,调用的时候(当然实参是只有一个)会把给出的实参赋值给函数参数列表的第一个形参,肯定就不对了嘛。要是用更专业的说法解释,本人认为:函数参数入栈的时候是实参进入Add函数的栈帧,直接就当作第一个参数,所以~总结:使用半缺省,缺省的参数只能在参数列表的最后边。缺省参数的用途:假如要传性别这个参数给函数,我们又约定缺省为男,所以,只需要在需要传的性别是女时,给出参数。这就是缺省参数的用途。
(二)引用
引用就是给一个变量起一个别名。比如:
int a = 10; int &ra = a;//ra是a的别名 int &refa = a;//refa也是a的别名
总结:
- 一个变量可以有多个别名。
- 定义引用的时候必须初始化(指明定义的引用是哪个变量的别名)。
- 引用只能在初始化的时候引用一次。
既然说,一个变量可以有多个别名,改变了别名的值,也就改变了变量的值,是不是感觉引用就很不安全了??其实并不是,如果不想改变,可以使用const修饰。
int a= 10; const int &ra = a; ra = 20;//报错!
关于const,在c中const修饰的变量是常变量,既有常量的属性,也有变量的属性。在c++中,const修饰的变量就是常量。不可修改。在学习指针的时候,如果一个函数的功能仅仅是打印或者是某个参数不可以被改变的时候,我们可以用const修饰。在使用引用的时候也是一样的。举例:
void print_a(const int &ra) { ra = 20;//报错(左值必须是可以修改的) cout << ra << endl; } int main() { int a = 10; int &ra = a; print_a(a); system("pause"); return 0; }
在cpp中,如果const修饰的是局部变量,则该局部变量是在栈里。不是不可以修改,可以使用指针修改。如果const修饰的是全局变量,不使用的时候是不会被分配空间的(&n是错误的),只有在使用的时候才有地址。
const int n = 10; int main() { int a = 10; int &ra = a; int arr[n] = { 0 };//这里并不算使用n,此时的n仍相当于宏标识符 const int *p = &n;//n才有空间,并且是只读区,不可修改的。 system("pause"); return 0; }
关于const引用的正确使用:看下边的几段代码:
const int n = 10; int &refa = n;
此时 refa是可以改变的,进而n的值可以改变。这就不对了嘛。(n是不可以改变的)。使得n从安全变成不安全。
int m = 10; const int &refm = m;
这个就是正确的。使得m从不安全变得安全。
const int &refd= 5;
这个是正确的。const修饰的refd是一个常量的引用。
double d = 6.15; int &refd = d;
这个是不对的。要是这样就对了:
double d = 6.15; const int &refd = d;
为什么呢??由于引用的类型是int,变量的类型是double,所以此时就会用double类型的d创建一个临时变量(也是d的隐式类型转换),refd引用的是临时变量。确切的说就是临时常量。此时refd =d,refd和d不在一个空间。引用作为函数参数:这里需要声明:引用的底层仍然是指针(这个可以通过汇编代码观到)。 下边我们来测试传引用和传值得效率问题:
#include<iostream> #include<windows.h> using namespace std; struct BigData { int arr[100]; }; int getData(BigData bd) { return bd.arr[0]; } int main() { BigData bd = { 0 }; int i = 0; int start = GetTickCount(); for (i = 0;i < 100000000;i++) { getData(bd); } int end = GetTickCount(); cout << end - start << endl; system("pause"); return 0; }
代码测试出的结果是6000毫秒左右。注意:每次运行的结果都不大一样,这根CPU的状态有关。当把上边代码改为传引用:效率可以提高大约一半,自己可以尝试。引用作为函数的返回值:看代码:
int& fun() { int num = 10; return num; } int main() { int &ret = fun(); cout << ret << endl; system("pause"); return 0; }
上边这段代码确实可以输出10.不过要是把上边的代码做以修改:
int& fun() { int num = 10; return num; } int main() { int &ret = fun(); printf("杨先生你好"); cout << ret << endl; system("pause"); return 0; }
看上边的代码,在高版本的vs下输出:杨先生你好10在低版本环境下ret的值会被改变。因为ret接收的是局部变量num,两者占用同一个空间当num消失了,ret也就消失了,变成随机值。高版本vs(比如vs2015)为什么ret的值一直是10,不知道做了什么优化。。如果将上边的代码做以修改:
int& fun() { int num = 10; return num; } int main() { int ret = fun(); printf("杨先生你好"); cout << ret << endl; system("pause"); return 0; }
这个ret的值不会改变。看原理:后者的ret就不是引用了,保存的仅仅是num的值,所以不会随着num的消失而消失。当引用作为函数的返回值的时候,有时会提高效率,有时并不会~~我们知道,当函数的返回值,int或者double等等内置类型时,返回值是用寄存器带回的。然而最大的寄存器也就32位,要是我们要返回一个比较大的结构体时,会使用引用,引用不会创建临时变量,直接把需要带回的值赋值给接收返回值的变量。总结:用函数的返回值的类型不是内置类型时,使用引用返回,会提高效率(减少创 建临时对象)。当返回的是内置类型时,会用寄存器带回,跟使用引用返回的效率差不多。引用和指针的区别:1.指针在定义的时候可以不初始化,但是引用不行。2.引用只能引用一个变量,而一般的指针变量并不是(除了const修饰的指针)3.指针的大小仅与品台有关,而引用的大小与引用的对象的类型有关。4.指针自加时,使得指针指向当前指向空间的下一个空间;引用自加时,会使自身和它所引用的对象的值加1.5.引用比指针更安全。(使用指针时,一定要检查指针的值是否是NULL)