函数的返回值不能是数组或函数类型,但可以是指向数组或函数的指针
函数体是一个块,构成一个新的作用域,其中定义的变量和形参都是局部变量
在函数体外部定义的对象存在于程序的整个执行过程中
只存在于块执行期间的对象,例如形参,函数终止,形参会被销毁,局部变量如果本身不带初始值,则会执行默认初始化,意味内置类型的未初始化局部变量将产生未定义的值
定义为static类型,如果没有显式的初始值,则将初始化为0
又叫函数原型,不包含函数体,无需形参名(也可以有)
将实参的值拷贝后赋值给形参,对形参的操作不会影响实参
传递指针,拷贝的是指针的值,拷贝后的两个指针是不同的指针,但是由于指针可以间接的访问所指的对象,所以可以通过指针修改它所指的对象值
void reset(int *p){ *p = 0;//改变了指向的对象的值 p = 0;//只改变了p的局部拷贝,实参未改变 }
C++建议用引用类型的形参代替指针
void reset(int &p){}
改变引用形参,就是改变初始化形参的实参
尽量使用引用来避免拷贝,而且有些类不支持拷贝(比如IO类)
顶层const作用于对象本身,形参有顶层const时,传递常量或非常量对象都可以(实参初始化形参时会忽略顶层const)
void func(const int n){} const int i = 1; int j = 2; func(i); func(j); //上面两种调用方法都正确 //由于这种忽略,导致下面两种函数其实是一样的,所以不是重载 void func(const int i); void func(int i);
可以使用非常量初始化一个底层const,但是不能反过来,一个普通的引用必须用同类型的对象初始化
int i = 0; const int ci = i; string::size_type ctr = 0; reset(&i);//形参类型为int* reset(ci);//错误,不能使用const int对象的指针去初始化int* reset(i);//形参为int&时 reset(ci);//不能将普通引用绑定到const对象ci上 reset(42);//不能绑定字面值 reset(ctr);//类型不匹配,ctr是无符号类型
可以使用字面值初始化常量引用
尽量使用常量引用,避免限制函数能接收的实参类型(字面值)
数组的两个特殊性质:
数组的大小对函数调用没影响
一种方法是使用标记指定长度,只适用于C语言中类似C风格字符串,以空字符结束
所以更实用的是另外两种方法:
标准库函数begin,end
void print(const int *beg,const int *end){ while(beg!=end){ cout<<*beg++<<endl; } } int j[2] = {0,1}; print(begin(j),end(j));
显式传递一个数组大小形参
print(j,end(j)-begin(j));
不需要改变数组元素值时,最好加上const
//数组的引用,维度是类型的一部分 void print(int (&arr)[10]){} //必须加(),否则arr就是引用的数组,而不是具有10个整数的整型数组的引用 //传递参数时,只能将函数作用域大小为10的数组 int i[2] = {2,1}; print(i);//错误
C++的多维数组就是数组的数组
数组的数组,首元素本身就是一个数组,第二维不能省略
void print(int (*matrix)[10],int rowsize); //如果没有(),则是十个指针构成的数组,而不是指向含有10个整数的数组的指针 void print(int matrix[][10],int rowsize);
处理不同数量的实参的函数:
如果所有实参的类型相同,可以传递标准库类型:initializer_list
不同则写可变参数模板
省略符(一般只用于与C函数交互的接口程序)
形式:void fun(parm_list,...)
void fun(...)
仅用于C和C++通用的类型
initializer_list对象中的元素永远是常量值
拷贝,赋值一个initializer_list对象不会拷贝列表中的元素,拷贝后原始列表和副本共享元素
list2(list1);//拷贝 list2 = list1;//赋值 void error_msg(initializer_list<string> il){ //initializer_list有begin,end,所以可以使用指针,范围for循环遍历列表 } //调用:expected,actuall为string对象 error_msg({"functionX",expected,actuall});
无返回值的return后可以什么都没有,也可以跟另外一个返回void的函数
不要返回局部对象的引用或指针
函数结束后,局部变量的内存空间会被释放
const string&manip(){//下面两个都错 string ret; if(){ return ret;//局部对象 } else{ return "empty";//临时局部变量 } }
调用运算符优先级和点,箭头相同
//符合左结合律,返回指针,引用,类的对象: auto s = func(s1,s2).size();
调用返回引用的函数会得到左值,其他类型为右值
因此这种情况下函数的返回值可以像使用其他左值一样:
char &get_val(string &str,string::size_type ix){ return str[ix]; } int main() { string s("a value"); get_val(s,0) = 'A'; //s[0]被改为A return 0; } //返回值是个常量引用就不能赋值了
vector<string>pro(){ return {"one","two"}; }
如果返回内置类型则{}中最多包含一个值
数组不能拷贝,所以函数不能返回数组
使用类型别名简化返回数组的指针和引用
//下面两行代码是等价的 typedef int arr[10]; using arr = int[10]; //返回一个指向含有十个整数的数组的指针 arr* func(int i); //如果不使用类型别名: Type (*function(parameter_list))[dimension] int (*func(parameter_list))[10]; //必须有*之外的(),否则声明的函数返回类型就会是指针的数组,而不是数组的指针
函数真正的返回类型在形参列表之后,格式:
auto func(int i)->int(*)[10]; //任何函数定义都能使用尾置返回
如果知道函数返回的指针指向哪个数组,则可以使用decltype
int o[] = {12,3}; int o2[] = {1,2}; decltype(o)* arrptr(int i){}
decltype的结果是数组,所以还要加上*