◆
gcd("hello","world");
实参的类型都是const char *,这种类型无法转换为int型,因此该调用不合法。
◆使用引用形参返回额外的值
函数只能返回单个值,但有些时候,函数有不止一个的内容需要返回。例如,定义一个find_val函数,在一个整型vector对象的
元素中搜素某个特定值。如果找到满足要求的元素,则返回指向该元素的迭代器;否则返回一个迭代器,指向该vector对象的
end操作返回的元素。此外,如果该值出现了不止一次,我们还希望函数可以返回其出现的次数。在这种情况下,返回的迭代器
应该指向具有要寻找的值的第一个元素。
如何定义既返回一个迭代器又返回出现次数的函数?我们可以定义一种包含一个迭代器和一个计数器的新类型。而更简单的
解决方案是给find_val传递一个额外的引用实参,用于返回出现次数的统计结果:
//return an iterator that refers to the first occurrence of value
//the reference parameter occurs contains a second return value
vector<int>::const_iterator find_val(
vector<int>::const_iterator beg,
vector<int>::const_iterator end,
int value,
vector<int>::size_type &occurs)
{
//res_iter will hold first occurrence,if any
vector<int>::const_iterator res_iter = end;
occurs=0;//set occurrence count parameter
for(;beg!=end;++beg)
if(*beg==value)
{
//remember first occurrence of value
if(res_iter==end)
res_iter=beg;
++occurs;//increment occurrence count
}
return res_iter;//count returned impalicitly in occurs
}
调用find_val时,需要传递四个实参:一对标志vector对象中要搜素的元素范围的迭代器,所查找的值,以及用于存储出现次数的size_type类型对象。
假设ivec是vector<int>类型的对象,it是一个适当类型的迭代器,而ctr则是size_type类型的变量,则可如此调用该函数:
it = find_val(ivec.begin(),ivec.end(),42,ctr);
调用后,ctr的值将是42出项的次数,如果42在ivec中出现了,则it将指向其第一次出现的位置;否则,it的值为ivec.end(),而ctr则为0。
◆更灵活的指向const的引用
//function takesa non_const reference parameter
int incr(int &val)
{
return ++val;
}
int main()
{
short v1=0;
const int v2=42;
int v3=incr(v1); //error: v1 is not an int
v3=incr(v2); //error: v2 is const
v3=incr(0); //error:literals are not lvalues
v3=incr(v1+v2); //error:addition doesn't yield an lvalue
int v4=incr(v3); //ok: v3 is a non const object type int
}
问题的关键是非const引用形参只能与完全同类型的非const对象关联。
◆传递指向指针的引用
//swap values of two pointers to int
void ptrswap(int *&v1,*&v2)
{
int *tmp=v2;
v2=v1;
v1=tmp;
}
形参
int *&v1
的定义应从右至左理解:v1是一个引用,与指向int型对象的指针相关联。也就是说,v1只是传递近ptrswap函数的任意指针的别名。
◆数组形参
数组有两个特殊的性质,影响我们定义和使用在数组上的函数:一是不能复制数组;二是使用数组名字时,数组名自动转化为指向
其第一个元素的指针。因为数组不能复制,所以无法编写使用数组类型形参的函数。因为数组会自动转化为指针,所以处理数组的
函数通常通过操纵指向数组中的元素的指针来处理数组。
数组形参的定义
//three equivalent definitions of printValues
void printValues(int*){/**//**/}
void printValues(int []){/**//**/}
void printValues(int [10]){/**//**/}
上面的三种定义是等价的,形参类型都是int*。
函数操纵的是指向数组元素的指针,而不是数组本身。
形参的长度会引起误解
//parameter treated as const int*,size of array is ignored
void printValues(const int ia[10])
{
// this code assumes array has 10 elements;
//disaster if argument has fewer than 10 elements!
for(size_t i=0;i!=10;++i)
{
cout<<ia[i]<<endl;
}
}
尽管上述代码假定所传递的数组至少含有10个元素,但C++语言没有任何机制强制实现这个假设。下面的调用都是合法的:
int main()
{
int i=0,j[2]={0,1};
printValues(&i);
printValues(j);
return 0;
}
虽然编译没有问题,但是这两个调用都是错误的,可能导致运行错误。在这两个调用中,由于函数printValues假设传递进来的数组至少含有10个
元素,因此造成数组内存的越界访问。程序的执行可能产生错误的输出,也可能崩溃,这取决于越界访问的内存中恰好存储的数值是什么。
当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。
通过引用传递数组
和其他类型一样,数组形参可声明为数组的引用。如果形参是数组的引用,编译器不会将数组形参转化为指针,而是传递数组的引用本身。
在这种情况下,数组大小成为形参和实参类型的一部分。编译器检查数组实参的大小与形参的大小是否匹配:
void printValues(int (&arr)[10]){/**//**/}
int main()
{
int i=0,j[2]={0,1};
int k[10]={0,1,2,3,4,5,6,7,8,9};
printValues(&i); //error: argument is not an array of 10 ints
printValues(j); //error: argument is not an array of 10 ints
printValues(k); //ok: argument is an array of 10 ints
return 0;
}
&arr 两边的圆括号是必需的,因为下标操作符具有更高的优先级。
◆没有返回值的函数
一般情况下,返回类型是void的函数使用return语句是为了引起函数的强制结束,这种return的用法类似于循环结构中的break语句的作用。
◆不要返回局部对象的引用
//disaster:Function returns a reference to a local object
const string &manip(const string& s)
{
string ret=s;
return ret;
}
这个函数会在运行时出错,因为它返回了局部对象的引用。当函数执行完毕,字符串ret占用的储存空间被释放,函数返回值指向了对于这个
程序来说不再有效的内存空间。
◆引用返回左值
char &get_val(string &str,string::size_type ix)
{
return str[ix];
}
int main()
{
string s("a value");
cout<<s<<endl;
get_val(s,0)='A'; // changes s[0] to A
cout<<s<<endl; //print A value
return 0;
}
给函数返回值赋值可能让人惊讶,由于函数返回的是一个引用,因此这是正确的,该引用时被返回元素的同义词。
◆默认实参
默认实参是通过给形参提供明确的初始值来制定的。程序员可为一个或多个形参定义默认值。但是,如果有一个形参具有默认实参,那么,
它后面所有的形参都必须有默认实参。
例如,下面的函数创建并初始化了一个string对象,用于模拟窗口屏幕。此函数为窗口屏幕的高、宽和背景字符提供了默认实参:
string screenInit(string::size_type height=24,
string::size_type width=80,
char background=' ');
调用包含默认实参的函数时,可以为该形参提供实参,也可以不提供。如果提供了实参,则它将覆盖默认的实参值;否则,函数将
使用默认实参值。下面的函数screenInit的调用都是正确的:
string screen;
screen = screenInit(); //equivalent to screenInit(24,80,' ');
screen = screenInit(66); //equivalent to screenInit(66,80,' ');
screen = screenInit(66,256); //screenInit(66,256,' ')
screen = screenInit(66,256,'#');
◆指定默认实参的约束
既可以再函数声明也可以在函数定义中指定默认实参。但是,在一个文件中,只能为一个形参指定默认实参一次。下面的例子是错误的:
//ff.h
int ff(int = 0);
//ff.c
#include "ff.h"
int ff(int i=0){/**//**/} //error