C++单元学习小结之函数(续)
C++单元学习小结
函数(续)
函数的传递:
参数传递是指用函数调用的实参来初始化函数形参存储区的过程。
函数的形参是局部对象,仅在函数的作用域内可见,每次调用函数时,会创建形参变量,并用传入的实参初始化形参,其中形参的类型决定了实参初始化形参的方式。
参数传递方式分为以下及种类型:
一、传值调用函数(按值传递)
当实参的值被复制给形参时,形参和实参是两个独立的对象,实参被称为按值传递,或传值调用函数。
按值传递实参时,函数不会访问当前调用的实参,函数中处理的只是实参的副本,这些副本在运行栈中存储,改变这些值不会影响实参。
这是C++的默认参数传递方式。
二、传址调用函数(传指针)
使用指针参数是传地址值。
按值传递参数不适合的情况(缺陷):
大型的类对象或结构体变量作为实参时,在运行栈中分配空间并复制对象,时间和空间开销过大。
另外使用传址调用函数可能会导致指向错误,修改系统值造成无法想象的错误,这可以参考指针的使用注意情况。
三、传引用,按引用传递参数
当形参是引用类型时,对应的实参被称为按引用传递,或者传引用调用函数,此时,引用形参绑定到实参,是实参对象的别名。
引用参数的用途:
可以通过传递引用在函数内修改实参的值
可以向主调函数返回额外的结果
可以向函数传递大型的结构体变量或类对象,减少了复制,提高效率,同时可以将参数声明为const引用,避免在函数内部修改引用参数。
参数传递方式的选择:
特殊要求:
数组和函数作参数时必须传指针,数组作参数时,将传递数组第一个元素的地址。因此被调函数内对参数数组的改变将应用到数组实参上,如果将形参数组声明为const表明不希望改变数组元素。
拷贝构造函数的参数必须传引用。
函数调用和返回:
函数调用会使程序的控制权传递给被调函数,而当前活动函数的执行被挂起。
当被调函数执行完成时,主调函数从调用语句之后的语句恢复执行。
函数的执行结果由return语句返回:
return语句有两种形式:
return;
//用在返回类型为void的函数中,不是必需的
return 表达式;
非void的函数必须返回一个与声明类型匹配的值,否则会引起编译错误。
函数的返回类型决定了函数调用表达式的类型,以及是否是左值。
调用一个返回引用的函数得到左值,其他返回类型得到右值。
返回引用与返回列表:
返回引用:
不能返回自动局部对象的指针或引用,函数执行结束后,函数占用的栈存储空间被释放,原本位于这段存储空间中的局部对象和临时对象都被释放,返回的局部对象引用或指针将指向不再有效的内存区域。
返回列表:
C++11的函数可以返回花括号包围的值的列表,用来初始化表示函数返回结果的临时量。
如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
如果函数返回内置类型,则花括号括起来的列表最多包含一个值,而且这个值所占的空间不应该大于返回值类型的空间。
如果函数返回类类型,由类本身定义初始值如何使用。
例:
vector<string> func()
{
if(condition) return {};
else return {"Okay", str1, str2};
}
特殊返回:
main函数的返回值:
main函数的返回类型为int,作为程序返回给系统的值。
main()函数返回0表示执行成功,返回非0值表示执行失败,其中非0值的具体含义依机器而定。
允许main函数没有return语句直接结束。
如果控制到达了main函数的结尾处而没有return语句,编译器隐式地插入一条表示返回0的return语句。
尾置返回类型语法:
尾置返回类型的语法形式:
auto 函数名(形参列表) -> 返回类型;
这种语法形式对返回类型比较复杂的函数最有效。
例:
auto fac(int n) -> int;
auto shorter(string& s1, string& s2) -> string&;
// func带一个int参数、返回指向大小为10的int数组的指针
int (*func(int i))[10];
// 使用尾置返回类型语法,返回类型更清晰
auto func(int i) -> int(*)[10];
函数的活动记录:
函数在运行时使用的程序运行栈中分配的存储区,一直与该函数相关联,直到函数结束时被自动释放。函数执行结束后,函数的活动记录将从栈中弹出,这些局部值就消失了。
函数重载:
如果要定义一组函数,对不同类型的数据执行同样的一般性动作,表达相同的概念,可以使用函数重载。这些函数是同一种操作,用户不需要了解其细节。
如果同一个作用域内的几个函数名字相同但形参列表不同,则它们是重载函数。调用重载函数时,编译器会根据实参的类型推断出要调用的是哪个函数。
注意事项:
重载函数的参数表必须不同,或参数个数不同,或参数类型不同。
返回类型不能区分两个重载函数,因为调用函数时可以忽略函数的返回值。
非指针和引用上的const限定词不能区分重载函数。
const限定指针或引用时,可以实现函数重载。
例:
int foo(const string& str);
int foo(const string&);
//同一函数:省略了形参名
typedef A B; //A是类型,B是A的别名
//同一个函数的重复声明
void print( const string& s1);
void print( const string& s2);
//错误的重复声明:只有返回类型不同
int print( int val);
void print( int val);
// 同一函数的重复声明
int foo(int); //内置类型
int foo(const int); //同一函数的重复声明
//重载函数
void foo(string& str); //非const引用
void foo(const string& str); //const引用
int goo(int*); //非const指针
int goo(const int*); //const指针