参数传递:
-
形参(parameter)和实参(argument):
int num (int a, int b); int num (int a, int b){return a+b; }//在函数的声明或者定义里,由由0个或多个形参组成的列表。int main(){int n=1,m=1;cout<<num(n,m);//我们用调用运算符来执行函数,调用运算符形式是个圆括号,圆括号内是由逗号隔开的实参列表。我们用实参初始化函数的形参。 }
-
形参的类型决定了形参实参交互的方式,如形参是引用类型,他将绑定到对应的实参上,引用形参是它对应实参的别名。否则,将实参的值拷贝后赋给形参。
当形参是引用类型,我们说,它对应的实参被引用传递(passed by reference)或者函数被传引用调用(called by reference)。
当实参的值被拷贝给形参时,我们说,这样的实参被值传递(passed by value)或者函数被传值调用(called by value)。
-
传值参数:
初始化一个非引用类型变量,初始值被拷贝给变量,对变量的改动不会影响初始值。
int n = 0; int i = n; i =1; cout << n;//输出0,i的值改变,n的值不变。
指针形参:指针的行为和其他非引用类型一样,当执行指针拷贝操作,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。因为通过指针,我们可以间接访问它所指对象,所以通过指针可以修改它所指对象的值。
void reset(int *ip){*ip = 0;//只改变了指针所指对象的值。ip = 0;//这里改变的只是ip的局部拷贝,实参并未改变 } int i = 1; reset(&i);//改变i的值而非i的地址 cout << "i = " << i <<endl;//输出i=0。 —————————————————————————————————————— //实参类似于下面:(q) int i = 1; int *q = &i; //形参类似于下面:(p) int n = 0; int *p = &n; //实参调用形参类似与下面: p = q;//p指向了i *p = 2;//通过p改变了i,但是此时q并未改变。
-
传引用参数:
对于引用的操作,实际上是作用在引用所引的对象上。通过引用形参,允许函数改变一个或多个实参的值。
void reset (int &i){//形参i仅仅是实参j的又一个名字,在reset内部对i的使用即是对实参j的使用。i = 0;//改变了i所引对象的值。 } int j=1; reset(j);//j采用传引用方式,因此他的值被改变。 cout<<j<<endl;//输出j=0。
-
传引用参数:当形参是顶层const时,传给他常量对象或者非常量对象都是可以的。
const int ci = 1; int i = ci;//正确,当拷贝ci时,忽略了它的顶层const。 int *const p = &i;//const是顶层的,不能给p赋值。 *p = 0;//正确,通过p改变对象内容是允许的。//在c++中,允许定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该具有明显区别。 void fcn(const int i){}//调用fcn函数时,既可以传入const int也可以传入int。 void fcn(int i){}//此时再写一个函数定义,会报错误,因为重复定义了fcn(int)。
-
传引用参数:我们可以用非常量初始化一个底层const对象,但无法用一个底层const对象初始化一个非常量。如果形参是一个非常量,那么实参不能是一个底层const对象
int i = 1;const int *cp = &i;//正确,但是cp不能改变iconst int &r = i;//正确,但是r不能改变iconst int &r2 = 1;//正确int *p = cp;//错误,p类型和cp类型不匹配int &r3 = r;//错误,r3类型和r类型不匹配int &r4 = 1;//错误,不能用字面值初始化一个非常量引用
同样的初始化规则应用到参数传递上:
void reset(int *ip){...}void reset(int &i){...}string::size_type find_char(const string &s,char c,string::size_type &occurs){...}int i = 0;const int ci = i;string::size_type ctr = 0;reset(&i);//正确,调用形参类型是int*的reset函数。reset(&ci);//错误,不能用指向const int对象的指针初始化int*。reset(i);//正确,调用形参类型是int&的reset函数。reset(ci);//错误,不能把普通引用绑定到const对象ci上。reset(42);//错误,不能把普通引用绑定到字面值上。reset(ctr);//错误,类型不匹配find_char("hello world!",'o',ctr);//正确,find_char第一个形参是对常量的引用。
-
尽量使用常量引用:
把函数不会改变的形参定义成普通的引用会给函数调用者一种误导,即函数可以修改它实参的值。此外,使用引用而非常量引用也会极大限制函数所能接受实参类型。不能把const对象、字面值、或者需要类型转换的对象传递给普通的引用形参。
string::size_type find_char(string &s,char c,string::size_type &occurs){...} find_char("hello world!",'o',ctr);//编译时发生错误,因为不能把普通引用绑定到const对象上。应该把string &s修改为const string &sbool is_sentence(const string &s){string ::size_type ctr = 0;return find_char(s,'.',ctr) == s.size() - 1 && ctr == 1;//编译时发生错误,因为s是常量引用,但find_char被定义成只接受普通引用。修改的话,需要改正find_char的形参,应该把find_char形参的string &s修改为const string &s }