C++ Primer 第六章 函数
6.1 函数基础
函数是一个命名了的代码块,通过函数执行相应的代码。可以有 0 个或多个参数,可重载
/* 编写一个求数阶乘的程序 */ int fact(int val){ int ret =1; //局部变量,用于保存计算结果 while(val > 1){ ret *= val --; } return ret; } /* 函数调用: 用实参初始化函数对应的形参(类型,个数需要匹配) 主函数暂时中断,背调函数开始执行 */ int main(){ int j = fact(5); //j等于120 }
局部对象
局部对象:生命周期从变量声明开始,到函数块末尾结束
局部静态对象:从变量声明开始,至程序结束时销毁
size_t count_calls(){ static size_t ctr =0; //调用结束后,这个值仍然有效 return ++ctr; }
参数传递
传值参数,传引用参数
//该函数接受一个指针,然后将指针所指的位置置为 0void reset(int *p){ *p =0; //改变指针p所指对象的值 p =0; //改变p的局部拷贝,实参并未改变 } //该函数接受一个int对象的引用,然后将对象所指的值置为 0void reset(int& i){ i =0; //改变了 i 所引用对象的值 }
使用引用避免拷贝
如果函数无需改变引用形参的值,最好将其声明为常量引用
//比较两个 string 对象的长度 bool isShorter(const string& s1,const string& s2){ return s1.size() < s2.size(); }
使用引用形参返回额外信息
//返回s中第一次出现的位置索引,引用参数occurs赋值统计c出现的总次数 string::size_type find_char(const string& s,char c,string::size_type& occurs){ auto ret = s.size(); occurs =0; for(decltype(ret) i=0;i!=ret;;++i){ if(s[i]==c){ //只有第一次出现的时候,才会被记录 if(ret==s.size()) ret = i; ++ occurs; } } return ret; }
const 形参和实参
形参的顶层 const 会被忽略掉
void fcn(const int i){/*fcn能读i,但不能修改*/} void fcn(int i){/*错误,重复定义*/}
指针或引用形参与 const
void reset(int* i){*i =0; i=0;} void reset(int& i){i =0;} //返回s中第一次出现的位置索引,引用参数occurs赋值统计c出现的总次数 string::size_type find_char(const string& s,char c,string::size_type& occurs){ auto ret = s.size(); occurs =0; for(decltype(ret) i=0;i!=ret;;++i){ if(s[i]==c){ //只有第一次出现的时候,才会被记录 if(ret==s.size()) ret = i; ++ occurs; } } return ret; } int main(){ 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); //错误,类型不匹配,ctr是无符号类型 //正确,find_char第一个形参是对常量的引用 find_char("Hello",'o',ctr); }
数组形参
//尽管形式不同,但三个print函数是等价的 //每个函数都有一个 const int* 类型的形参 void print(const int*); void print(const int[]); void print(const int[10]); //这里的10只是一个期望 /* 以数组未形参的函数必须确保使用数组时不会越界 */ //1、使用标记指定数组长度 void print(const char* cp){ if(cp){ //如果不是空指针 while(*cp){ //取出不是空字符 cout << *p++; //输出当前字符,并移动到下一个位置 } } } //2、使用标准库规范 void print(const int* beg,const int* end){ //输出之前的所有元素 while(beg != end){ cout << *beg++; } } //3、显式传递一个表示数组大小的形参 void print(const int[] ia,size_t size){ for(size_t i=0;i!=size;++i){ cout << ia[i]; } }//可使用 print(j,end(j)-begin(j))调用
C++ 允许将变量定义成数组的引用
//正确:形参是数组的引用,维度是类型的一部分 void print(const (&arr)[3]){ //()不能少 for(auto elem : arr){ cout << elem << endl; } } int i=0,j[2] = {0,1},k[3] ={0,1,2}; print(&i); //错误,实参不是含有3个整数的数组 print(&j); //错误 print(&k); //正确 /* 传递多维数组 : 在C++中没有实际的多维数组 */ void print(int (*matrix)[10],int rowsize){}; void print(int matrix[][10],int rowsize){}; //实际上是指向 10 个整数的数组的指针
main:命令行选项
int main(int argc,char* argv[]){} int main(int argc,char** argv){} //如果运行 prog -d-o ofile data0 //argc =5//argv[0] ="prog" ... argv[4] ="data0",argv[5] =0//最后一个指针之后的元素保证为 0
含有可变形参的函数:参数个数不固定
如果所有的实参的类型相同,可以提供一个名为 initializer_list 的标准库类型
//和vector一样,initializer_list 也是一种模板类型 initializer_list<string> ls; //元素类型是string initializer_list<int> li; //和vector不一样的是,initializer_list对象中的元素永远是常量 void error_msg(initializer_list<string> ls){ for(auto beg=ls.begin();beg!=ls.end;++beg){ cout << *beg << endl; } } if(expected != actual){ error_msg("functionX",expected,actual); }else{ error_msg("functionX","OK"); }
6.2 函数返回值
无返回值函数 & 有返回值函数
值是如何被返回的
//返回word对象的副本(拷贝),或是一个没命名的临时string对象 string make_plural(size_t ctr,const string& word,const string& ending){ return (ctr>1) ? word+ending : word; } //返回其引用,不会拷贝 string 对象 string& shorterString(const string& s1,const string& s2){ return s1.size()<=s2.size() ? s1 : s2; }
不要返回局部对象的引用或指针
const string& manip(){ string ret; if(!ret.empty()){ return ret; //错误,返回的是局部对象的引用,除了该函数,对象即被销毁 }else{ return "empty"; //错误,"empty"是局部临时变量 } }
返回类类型的函数和调用运算符
//调用string对象的size成员函数,该string对象是由函数返回的 auto sz = shorterString(s1,s2).size();
引用返回左值
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'; cout << s << endl; return 0; }
列表初始化返回值:C++ 11
vector<string> process(){ //... if(expected != actual){ return("functionX",expected,actual); }else{ return("functionX","OK"); } }
主函数 main 的返回值
int main(){ if(some_failure){ return EXIT_FAILURE; //定义在cstdlib头文件中 }else{ return EXIT_SUCCESS; } //编译器将隐式插入一条返回0的return语句 }
递归:函数调用了自己
main函数不能自己调用自己
//计算val的阶乘 int fact(int val){ if(val>1){ return fact(val -1)*val; }else{ return 1; } }
返回数组指针
数组不能拷贝,所以函数不能返回数组
typedef int arrT[10]; //arrT是一个类型别名,using arrT = int[10] arrT* func(int i); //func返回一个指向含有10个整数的数组的指针 //声明一个返回数组指针的函数 //返回数组指针的函数形式为 : Type (*function(parm)[dimension]) /* ()表示其实一个函数,参数为int *代表返回值的类型为一个指针 int[10] 代表返回的指针指向一个 int 类型的数组 即这是一个返回数组指针的函数 */ int (*func(int))[10]; //使用尾置返回类型,c++11//->符号开始,在本应该出现类型的地方使用 auto //func返回数组指针的函数 auto func(int i)->int(*)[10]; //使用 decltype ,当已知函数返回的数组 int odd[] = {1,3,5}; int even[] = {2,4,6}; //返回一个指针,该指针指向含有五个整数的数组 decltype(odd) *arrPtr(int i){ return (i%2)?&odd:&even; //返回一个指向数组的指针 }