一个典型的函数包含三个基础部分:返回类型,函数名字和形参。
而这个三个部分可以说构成了函数的一个接口,当我们使用函数的时候,三个基础部分的不同就代表了代表函数的能够做的实现的不同。
为了清除函数的具体作用,首先应引入声明周期和局部对象的两个概念,每个对象在程序运行中都有自己的声明周期。
而名字的作用域也就是这个名字作为某一个对象的生存空间是程序文本的一部分,名字在这个部分,是可见的。
而对象的生存周期是程序执行的过程中,该对象存在的一段时间。
形参和函数体内定义的变量统称为局部变量,他们仅在函数的作用域内可见。但当他们可见的时候,就会隐藏外层作用域中的同名对象,知道推出局部变量的作用域时,外层作用的同名对象才得以显示和恢复。
局部对象中,如果一个对象在函数执行路径经过时创建,在该定义到达所在的代码块的末尾时销毁,那么我们称这个对象是自动对象。
形参时一种自动对象,如果我们对于自动变量的定义的本身含有初始值,那么就用这个初始值进行初始化;如果我们对于变量本身的定义不含初始值,那么将执行默认的初始化,而因为自动变量本身是局部变量,如果是内置类型的自动变量,其初始化的值本身是没有意义的。
有时,我们需要在内部定义的变量在整个外部作用域存在,那么就需要使用局部静态变量,局部静态变量的设定是在变量声明的前面加上关键字static,此时该对象将在函数退出时继续存在,并且只在第一次函数执行路径这个声明的时候进行初始化。
有时,我们需要额外返回除了返回对象的另一个参数,一个方法是使用隐式返回形参变量,方法是在进行函数声明的时候,多加一个引用形参。
注意:当用实参初始化形参的时候,将自动忽略顶层const,这在使用函数重载的时候要格外小心。
指针或引用形参和const☆
形参的初始化和变量的初始化其实是一样的方式,我们可以通过一个非常量来初始化一个底层const的对象,但是反过来说是不行的,即我们不能用一个有底层const的对象来初始化一个非常量。
同时,一个普通的引用必须用同类型的对象进行初始化。
1.不能用const引用来初始化一个普通的引用。
2.不能用字面值来初始化一个普通的引用。
在重载中,有时我们可能需要用一个const引用来初始化一个普通引用,或者用一个具有底层const的对象来初始化一个非常量(指针或引用),那么此时我们可以选择使用const_cast函数来进行(去const)转化。
而一个成熟的程序中,应当尽可能少使用类型转换,我们要确定的规则是,尽可能的多使用const reference即长量引用。
数组
对于函数的形参,因为数组本身的特性,会使我们在使用和定义的时候遇到麻烦;
因为:
1.不允许拷贝数组
2.在使用数组的时候,通常会将其转化为指针。
尽管,数组在作为形参的时候会被转化为指针,我们依然可以用数组的形式进行函数的声明。
如:1. void print(const int*);
2. void print(const int[]);
3. void print(const int[10]);
这三者的声明是完全等价的,其实都等同于具有唯一的形参const int*。因此,我们可以看出 3 中的数组的大小并没有实际的作用。
正因为数组的大小在作为形参的使用没有意义,我们在处理数组的时候又需要知道准确的数组大小的信息。就需要我们额外添加关于数组大小的信息。
一般来讲,管理指针形参有三种方式:
一、使用标记指定数组的长度
这个方法需要数组本身含有一个结束标记,如C风格的字符串,其最后一个元素为\0,那么我们在处理信息的时候,就可以使用while(*cp)判断是否达到末尾。
二、使用标准库的规范
使用传递指向数组首元素和尾后元素指针的方法,如使用begin()和end()函数。
三、显式地传递一个表示数组大小的形参
略
关于数组,因为C++允许将变量定义为数组的一个引用,所以,形参也可以是数组的引用。
如:void print(int (&arr1) [10]){
for (auto elem:arr1)
cout<
}
而此时的数组的大小在形参中确实具有意义,这也表明我们使用这种形式的时候,必须注意对数组的操作不能越界。
可变形参的函数
有时候,我们需要对一个函数指定不定个数的形参,一般来讲,这些形参很可能具有相同的类型,此时,我们就需要用到C++11提供的标准库类型:initializer_list类型。
initializer_list
函数声明: void error_msg(initializer_list
函数调用:error_msg({"function x",expected,actual}); (务必记得大括号的列表化初始方法)
返回值类型
仍然需要我们再提一遍,函数返回一个局部变量或者局部变量的引用是错误的,因为在函数执行结束的时候,局部变量已经被销毁,我们需要知道的是在我们返回时,所用或者所引的对象是在函数之前已经存在还是在函数中创建的?
如果函数返回了一个指针、引用或者类类型,我们就可以通过这些类型的相关操作,来调用函数的返回结果。
如:auto sz=short(s1,s2).size();
上面的运算是调用了short返回的类类型的成员运算符,获得返回值的长度大小信息。
注意:我们有时候要知道一个函数返回的是左值还是右值?那么怎么判断呢?其实很简单,当一个函数返回一个对象的引用的时候,这个返回的值就是左值,其余的是右值。
当返回左值的时候,我们可以像使用左值一样来使用函数的返回结果,特别的是,当返回一个非常量的左值的时候,我们甚至可以为其赋值。
如: get_val(s , 0) ='A'; get_val(s , 0)返回 s[0]的引用。