一个典型的函数定义包括:返回类型、函数名字、0个或多个参数以及函数体。
举例计算5的阶乘
int fact(int val){
int ret = 1;
while(val>0){
ret *=val--;
}
return ret;
}
第一个int就是返回类型是int,fact是函数名,int val是参数,大括号中是函数体。
//调用
int j = fact(5);
如果没有返回值就写void,返回类型不能是数组或函数,但可以是指向数组或函数的指针。
函数在使用之前必须声明,函数只能定义一次,但可以声明多次。函数声明与定义的差别是声明没有函数体。建议在头文件中写函数声明。
当对函数参数传递非引用类型的变量时,初始值被拷贝给变量,对变量的改动不会影响初始值。
void add(int a){
a = a + 1;
}
int a = 1;
add(a);
这里a还是1。
参数是指针时和其他非引用类型一样,当执行指针拷贝操作时,拷贝的是指针的值,但是可以通过指针修改它所指对象的值。
void reset(int *ip){
*ip = 0; //改变实参所指对象的值
ip = 0; //只是改变拷贝的ip,实参未被改变
}
int i = 1;
reset(&i); //0
参数是引用时实际上作用在引用所引的对象上。
void reset(int &i){
i = 0; //改变了实参的值
}
int i = 1;
reset(i);
使用引用可以避免拷贝
拷贝大的对象比较低效或者有些对象根本就不支持拷贝,函数只能通过引用形式访问。举例编写一个函数比较string对象的长度,由于string可能非常长,应该尽量避免拷贝,使用引用会比较好,如果无需改变string对象的内容,可以定义成常量引用。
boolean isShorter(const string &s1,const string &s2){
return s1.size() < s2.size();
}
一个函数只能返回一个值,然而有时函数需要同时返回多个值,引用参数为我们一次返回多个结果提供了有效的途径。定义一个函数find_char,返回string对象中某个指定字符第一次出现的位置,同时也希望函数能返回该字符出现的总次数。
一种方法是定义一个新类型包含位置和数量两个成员,还有一个方法是给函数传入一个额外的引用参数来保存次数。
string::size_type find_char(const string &s,char c,string::size_type &occurs){
auto ret = s.size();
occurs = 0; //次数
if (decltype(ret) i = 0; i!=s.size(); ++i){
if(s[i] == c){
if(ret == s.size()){
ret = i;
}
++occurs;
}
}
return ret;
}
const类型的参数可以传入常量对象,也可以传入非常量对象。
void fcn(const int i) //能够传入const int也可以传入int
把函数不会改变的形参定义成引用是一个常见错误,这样容易误导函数可以修改他的实参值,使用引用而非常量引用也会限制函数所能接受的实参类型
bool is_sent(const string &s){
string::size_type ctr = 0;
return find_char(s,'.',ctr) == s.size() - 1 && ctr == 1;
}
find_char中的s参数不是const类型,所以这样是不能编译通过的。
数组当做形参无法以值传递方式使用,函数传递一个数组时,实际上传递的是指向数组首元素的指针。
void print(const int*);
void print(const int[]);
int i = 1;
print(&i);
上面是等价的。
如果函数的实参数量未知,但是全部实参的类型都相同,可以使用initializer_list类型的形参,initializer_list在同名的头文件中
void error_msg(initializer_list<string> li){
for(auto beg = li.begin();beg!=li.end();++beg){
cout << *beg << endl;
}
}
error_msg({"aa","bb"});
error_msg({"aa","bb","cc"});
initializer_list和其他参数一起使用
void error_msg(int a,initializer_list<string> li)