一、数组形参
数组都是以指针的形式传递给函数的,所以一开始并不知道数组的确切尺寸,调用者应该为此提供一些额外的信息,管理指针形参有三种常用的技术。
1、使用标记指定数组长度
要求数组本身包含一个结束标记,例如c风格字符串存储在字符数组中,并且在最后一个字符后面跟着一个空字符。
void pri(const char* cp)
{
if (cp)//若cp不是空指针
while(*cp)//若指针所指的字符不是空字符
cout<< *(cp++);//输出当前字符并将指针向前移一位
}
这种方法适用于有明显结束标记且该标记不会与普通数据混淆的情况,但是对于像int这样所有取值都是合法值得数据就不太有效了
2、使用标准库规范
传递指向数组首元素和尾后元素的指针
void pri(const int*beg, const int *end)
{
while (beg!=end)//输出beg到end之间(不含end)的所有元素
cout<< *(beg++) <<endl;//输出当前元素并将指针向前移动一个位置
}
3 、显示传递一个表示数组大小的形参
专门定义一个表示数组大小的形参
void pri(const int ia[],size_t size)
{
for(size_t i=0;i!=size;++i)
cout<< ia[i]<<endl;
}
二、多维数组的传入
将多维数组传递给函数时,真正传递的是指向数组首元素的指针。因为我们处理的是数组的数组,所以首元素本身就是一个数组,指针就是指向数组的指针。数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能忽略。
void d3arr(int(*arr)[10], int rowsize)
{
for (int i = 0; i < rowsize; ++i)
{
for (int j = 0; j < 10; ++j)
arr[i][j] *= 2;
}
}
上述函数是一个二维数组,有10列,rowsize行。
注意传入参数int(*arr)[10]
它表示传入的是一个指向整个数组的指针,这个数组含有10个整数元素,
注意括号不能省,否则意思就改变成它是一个数组,数组里有10个整型指针。
当然如下定义也是可以的:void d3arr(int arr [][10], int rowsize);
三、main:预处理命令行选项
int main(int argc, char argv[]){/ …*/}
(或定义成int main(int argc, char ** argv
)也行)
第二个形参argv是一个数组,它的元素是指向c风格字符串的指针,第一个形参argc表示数组中字符串的数量。
举个例子,有个程序prog.exe,可在命令行运行
prog.exe plaintext.txt
argv[0]为”prog.exe”;
argv[1]为”plaintext.txt”;
即他会读入plaintext.txt这么一个文件路径或是其它参数
(参数、选项什么的是从1开始,而0是程序名)
四、含可变形参的函数
C++11新标准提供了两种主要的方法:
如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;如果实参类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。
如果函数实参数量未知,但是全部实参的类型相同,我们可以使用initializer_list类型的形参。
void error_msg(initializer_list i1)
{
for (auto beg = i1.begin(); beg != i1.end(); ++beg)
cout << *beg << " ";
cout << endl;
}
如上函数可以接收若干条string,并输出出来。
但要记住,调用该函数的时候,相应的部分参数要写在{}里
如:
string s1=”sss”;
error_msg({“aaa”,s1});
五、引用返回左值
一般情况下,函数完成后,它所占用的存储空间也随之被释放。因此,函数种子意味着局部变量的引用将指向不再有效的内存空间。但是当你返回的并不是函数内部的局部变量的引用时,就相当于返回了一个可赋值的左值。
char &get_val(string &str, string::size_type ix)
{
return str[ix];
}
string s=”a value”;
get_val(s,0)=’A’;//正确,相当于s[0]=’A’;
六、函数重载
C++允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应有明显区别。比如说数据类型不同或者参数数量不同。但形参的的顶层const的有无并不是算是明显区别,如下:
void fcn (const int i){/* … /}//定义了一个函数,能读i,但不能写i
void fcn (int i){/ …*/}//错误,重复定义
(注意如果是底层const的话,还是算一个新函数的)
函数重载:
如果同一作用域内的,几个函数名字相同但形参列表不同,我们称之为重载函数。函数名字仅仅是让编译器知道它调用的是哪个函数,而函数重载可以在一定程度上减轻程序员起名字、记名字的负担。
函数匹配:
把本次调用的函数和和一组重载函数中的某一个匹配起来,也称重载确定。
函数匹配的三种结果:
①能找到一个与实参的最佳匹配,然后成功调用该函数
②找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息
③有多于一个函数可以匹配,但是每一个都不是明显的最佳选择。此时也将发生错误(二义性调用)
函数匹配的过程:
1、找出同名的
2、找出可行的
3、选出最佳的(最佳匹配只能有一个,否则就报错)
寻找最佳匹配的基本思想就是:实参类型与形参类型越接近,转换的力度越小,他们匹配得越好
当存在实参类型转换时,从转换的力度从小到大排序如下:
1、精确匹配,含以下情况:
①实参类型和形参类型相同。
②实参从数组或函数类型转换成相应的指针类型
③向实参添加顶层const或者从实参删除顶层const
2、通过const转换实现的匹配(针对底层const)
3、通过类型提升实现的匹配
4、通过算术类型转换实现的匹配
5、通过类类型转换实现的匹配