参数传递
1.值传递:把实参的值拷贝给形参,形参和实参是两个相互独立的对象。
2.引用传递:形参是引用类型,它将绑定到对应的实参上。
3.指针形参:指针的行为和其它非引用类型一样。将形参传递给实参时,是拷贝指针的值(也就是地址)。拷贝之后,两个指针是不同的指针,但是他们指向的地址相同,
所以通过指针可以修改它所指的对象的值。
总结:尽量使用引用形参,在拷贝大的类类型对象或者容器对象时,这样做比较高效,有的类类型甚至就不支持拷贝操作。同时,如果不希望改变实参的值,就将形参声明为常 量引用。此外,使用引用形参的好处还包括使得函数变相返回多个值。
注意:
void fun(int i) {}
void fun(const int i) {}//错误,不属于函数重载,因为实参初始化形参时会忽略顶层const,因为实参是被
//值传递,所以对实参没有影响。所以就不认为是函数重载了
数组形参
1.一维数组形参
下面这三个函数是等价的
void print(const int*);
void print(const int[]);
void print(const int[8]);//虽然给出了维度大小,但对实际传进来的数组维度没有影响
注意:如果形参是数组的引用,则形参中的维度就控制了实参的维度
void print1(int(&arr1)[]);//错误,若形参是数组的引用,则必须给出维度。
void print2(int (&arr2)[8]);//正确,arr2是具有8个整数的整形数组的引用,实参数组维度的大小也必须为8。
2.二维数组形参
C++中,多维数组就是数组中元素的内容还是数组。
void print(int(*arr)[10]);//arr指向数组的首元素,该元素是由10个整数构成的数组
//下面是等价定义.要注意的是,不能省略数组第二维的大小,否则会报错。
void print(int arr[][10]);//即使形参写成这样,形参本质上也还是指向含有10个整数的数组的指针。
3.initializer_list形参
如果函数的实参数量未知但是全部实参的类型都相同,就可以使用initializer_list类型的形参。这是一种标准库类型,使用时需要添加同名的头文件。
函数返回值
1.返回类型是void的函数可以return另外一个返回void的函数。
void f1()
{
}
void f()
{
int i;
return f1();
}
2.return语句返回值的类型必须与函数的返回类型相同,或者能隐式转换成函数的返回类型。
3.不要返回局部对象的引用或指针。因为函数完成之后,它所占用的内存空间也随之被注释掉。
string &manip()
{
string s1 = "cd";
if (1)
{
return s1;//注意:编辑器在此处不会报错。但实际上在函数调用结束的时候s1就会被析构掉
//所以是不安全的。编译是可以通过的,但是对于返回的结果,因为已经析构,所以是不应该使用的。
}
else
return "aaa";//错误,并且编译器能够检测出这种错误。因为“aaa”这个字符串被转换成
//局部临时string对象了
}
4.其实标准的C++,主函数的返回类型根本就没有void这么一说,只不过是某些编译器支持void main(){}罢了。具体原因,经过查阅如下:
<1>Linux下的进程运行完毕都会有一个返回值,范围是[0~255],int main()就是为了对应这个返回值。
<2>使用int main()主要是可以给操作系统返回一个值,让操作系统明白这个程序执行的状态,比如执行这个程序后下一步可能要根据这个返回值做分支处理,如果是void的话
返回值不会确定,异出退出和正常退出无法区别。
注意:允许main()函数没有return语句直接结束。如果控制到达了main()函数的结尾处而且没有return语句,编译器将隐式地插入一条返回0的return语句。
5.main()函数不能调用自己本身。
6.返回数组指针的几种方式:
<1>使用类型别名。
using arr=int[10];//arr是一个类型别名
arr* fun(int i);//返回一个数组指针的函数
<2>使用尾置返回类型。(C++新标准提出的)任何函数的定义都能使用尾置返回,经常用在返回类型比较复杂的地方,比如返回类型是数组的指针或者数组的引用。尾置返 回类型跟在形参初始化列表后面并以->符号开头。在本应该放置返回类型的地方用auto代替。
auto fun(int i)->int(*)[10];
<3>使用declype。这种方式使用情况比较有限。需要纸袋函数返回的指针将指向哪个数组,也就是知道数组的类型和维度。
int odd[] = { 1,3,5,7,9 };
int even[] = { 2,3,4,5,6 };//注意:enen数组的类型和维度必须与odd数组一直,否则错误。
decltype(odd) *arr(int i)
{
return (i % 2) ? &odd : &even;
}
7.函数的返回类型不作为函数重载的依据。
此外,在函数重载中,拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。
int fun(int i)
{
return 0;
}
int fun(const int i)//错误,函数“int fun(int)”已有主体
{
return 0;
}
如果函数形参是某种类型的指针或者引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载。此时的const是底层的。
int fun(int *p)
{
return 0;
}
int fun(const int p)
{
return 0;
}
8.重载与作用域,请看下面实例(需要说明的是,很少会出现下面这种情况,即将函数声明置于局部作用域内):
void print(double);
void print(const string &);
int main()
{
void print(int);
print("value:");//错误
system("pause");
return 0;
}
注意:在main()函数中声明的print(int)隐藏了前面两个print函数。
9.默认实参初始值
局部变量不能作为默认实参。
char c = 'a';
int wd = 80;
int fun();
string screen(int=fun(), int = wd, char = c);
void print()
{
c = 'b';//改变了默认实参的值
int wd = 100;//注意,在函数内部定义了同名局部变量,全局变量wd将被隐藏,
//但是该局部变量与传递给screen的默认实参没有任何关系。
string s = screen();//调用的是screen(fun(),80,'a')
}
10.将函数指定为内联函数,也就是将它在每个调用点上“内联地”展开。可以避免函数调用的开销。不过只适用于规模较小、流程简单直接、频繁调用的函数。定义函数时在返回 类型前面加上关键字inline即可。
函数指针
1.函数指针指向的是函数而非对象。
bool lengthCompare(const string &, const string &);
bool (*pf)(const string &, const string &);//pf首先是一个指针,说明pf指向一个函数,
//该函数的参数是两个const string的引用,返回值是bool类型
bool *pf(const string &, const string &);//如果不加括号,则pf是一个函数名,返回值为bool指针
2.使用函数指针。当把函数名作为一个值使用的时候,该函数自动地转换成指针。
bool lengthCompare(const string &, const string &);
bool(*pf)(const string &, const string &);
int main()
{
pf = lengthCompare;//此时pf指向函数lengthCompare
pf = &lengthCompare;//同上,等价语句
system("pause");
return 0;
}
而且,能直接使用指向函数的指针调用该函数,无需解引用指针。
bool lengthCompare(const string &, const string &);
bool(*pf)(const string &, const string &);
int main()
{
bool b1 = pf("hello", "world");//调用lengthCompare函数
bool b2 = (*pf)("hello", "world");同上
bool b3 = lengthCompare("hello", "world");
system("pause");
return 0;
}
<3>函数指针形参
和数组类似,不能定义函数类型的形参,但是形参可以是指向函数的指针。虽然看起来是函数类型,实际上是当成指针在使用。
void useBigger1(const string &, const string &, bool pf(const string &, const string &));
void useBigger2(const string &, const string &, bool (*qf)(const string &, const string &));//上面函数的等价形式
<4>返回指向函数的指针
和数组类似,同样函数的返回值不能是函数,但是能返回指向函数类型的指针。
using u1= int(*)(int, int);//u1是指针类型
using u2 = int(int, int);u2是函数类型
u1 f1(int);//f1这个函数返回指向函数的指针
u2 *f2(int);//同上,显示指出返回类型是指向函数的指针
auto f1(int)->int(*)(int, int);//尾置返回类型的方法