摘要: 本篇博客仅作为笔记,如有侵权,请联系,立即删除(网上找博客学习,然后手记笔记,因纸质笔记不便保存,所以保存到网络笔记)。
return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。return语句有两种形式:
return;
return expression;
1、无返回值函数
没有返回值的return语句只能在返回类型是void的函数中。返回void函数不要求非得有return语句,因为这类函数后面会隐含地执行return。
通常情况下,void函数如果想在它中间位置提前退出,可以使用return语句。
一个返回类型是void的函数也能使用return语句的第二种形式,不过此时return语句的expression必须是另一个返回void的函数。
2、有返回值函数
return语句的第二种形式提供了函数的结果。只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。
在含有return语句的循环后面也应该有一条return语句,如果没有的话该程序就是错误的。
3、值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时变量,该临时量就是函数调用的结果。
需要注意的是当函数返回局部变量时的初始化规则。例如我们写一个函数,给定计数值、单词和结束符之后,判断计数值是否大于1:如果是,返回单词的复数形式;如果不是,返回单词原型:
string make_plural(size_t ctr,const string %word,const string &ending){ return (ctr>0) ? word + ending : word; }
该函数的返回类型是string,意味着返回值将被拷贝到调用点。因此,该函数将返回world的副本或者一个未命名的临时string对象,该对象的内容是word和ending的和。
同其他引用类型一样,如果函数返回引用,则该引用仅是它所引对象的一个别名:
const string &shorterString(const string &s1,const string &s2){ return s1.size() <= s2.size() ? s1 : s2; }
其中形参和返回类型都是const string的引用,不管是调用函数还是返回结果都不会真正拷贝string对象。
4、不要返回局部对象的引用或指针
函数完成后,它所占的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。
const string &manip(){ string ret; //ret改变的过程 if(....) //某种条件下 return ret; else return "no"; }
这个函数时严重错误的,ret在函数生命周期结束时将一起结束,并不会返回。同样,第二条return语句中,字符串字面值转换成一个局部临时string对象,对于函数来说,该对象和ret一样都是局部的,当函数结束时临时对象占用的空间也就随之释放了,所以也不会返回正确的结果。
返回局部变量时错误的;同样,返回局部对象的指针也是错误的。一旦函数完成,局部对象被释放,指针将指向一个不存在的对象。
5、返回类类型的函数和调用运算符
和其他运算符一样,调用运算符也有优先级和结合律。调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律。因此,如果函数返回指针、引用或类的对象,我们就能使用调用的结果访问对象的成员。
例如,我们可以通过如下形式得到较短string对象的长度;
//调用string对象的size成员,该string对象是由shorterString函数返回的 auto sz = shorterString(s1,s2).size();
因为上面提到的运算符都满足左结合律,所以shorterString的结果是点运算符的左侧运算对象,点运算符可以得到该string对象的size成员,size又是第二个调用运算符的左侧运算对象。
6、引用返回左值
函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数调用,特别是,我们能为返回类型是非常量引用的函数的结果赋值:
char &get_val(string &str,string::size_type ix){ return str[ix]; } int main(){ stirng s("a value"); cout << s << endl; get_val(s,0) = 'A'; cout << s << endl; return 0; }
把函数调用放在赋值语句的左侧可能看起来有点奇怪,但是其实这没什么特别的,返回值是引用,因此调用是个左值,和其他左值一样他也能出现在赋值运算符的左侧。如果返回类型是常量引用,我们不能给调用的结果赋值:
shortString("hi","bye") = "X"; //错误,返回值是个常量
7、列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来标识函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
8、返回数组指针
因为数组不能被拷贝,所以函数不能返回数组,不过,函数可以返回数组的指针或引用。虽然从语法上来说,想要定义一个返回数组的指针或引用的函数比较繁琐,但是有一些方法可以简化这一任务,其中最直接的方法是使用类型别名:
typedef int arrT[10]; //arrT是一个类型别名,它表示类型是含有10个整数的数组 using arrT = int[10]; //arrT的等价声明 arrT *fun(int i); //func返回一个指向含有10个整数的数组的指针
其中arrT是含有10个整数的数组的别名。因为我们无法返回数组,所以讲返回类型定义成数组的指针,因此,func函数接受一个int实参,返回一个指向包含10个整数的数组指针。
我们也可以用STL中的vector进行定义。
9、声明一个返回数组指针的函数
要想在声明func时不使用类型别名,我们必须牢记被定义的名字后面数组的维度:
int arr[10]; //arr是一个含有10个整数的数组 int *p1[10]; //p1是一个含有10个指针的数组 int (*p2)[10] = &arr; //p2是一个指针,指向10个整数的数组
和这些声明一样,如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在函数名字后面,然而,函数的形参列表也跟在函数名字后面且形参列表应该先于数组的维度。因此,返回数组只针对额函数形式如下:
Type (*function(parameter_list))[dimension]
int (*func(int i))[10];
可以按照以下顺序来逐层理解该声明的含义:
·func(int i)表示调用func函数时需要一个int类型的实参;
·(*func(int i))表示我们队函数的调用结果执行解引用操作;
·(*func(int i))[10]表示解引用func的调用将得到一个大小是10的数组;
·int (*func(int i))[10]表示数组中的元素是int类型。
10、使用尾置返回类型
在C++11新标准中海油一种可以简化上诉func声明的方法,就是使用尾置返回类型。然后函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数正真的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:
auto func(int i)->int (*)[10]; //func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
因为我们把函数的返回类型放在了形参列表之后,所以可以清楚地看到func函数返回的是一个指针,并且该指针指向了含有10个整数的数组。
11、使用decltype
还有一种情况,如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。例如,下面的函数返回一个指针,该指针根据参数的不同指向两个已知数组中的某一个:
int odd[] = {1,3,5,7,9}; int even[] = {0,2,4,6,8}; //返回一个指针,该指针指向含有5个整数的数组 decltype(odd) *arrPtr(int i){ return (i % 2) ? &odd : &even; //返回一个指向数组的指针 }
arrPtr使用关键字decltype表示它的返回类型是指针,并且该指针所指的对象与odd的类型一致。因为odd是数组,所以arrPtr返回一个指向含有5个整数的数组的指针。有一个地方需要注意:decltype并不负责吧数组类型转换成对应的指针,是有decltype的结果是个数组,要想表示arrPtr返回指针还需要在函数声明时加一个*。