return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。return语句有两种形式:

return;

return expression;

无返回值函数

没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐含地执行return。

通常情况下,void函数如果想在它的中间位置提前退出,可以使用return语句。return的这种用法有点类似于我们用break语句退出循环。

一个返回类型是void的函数也能使用return语句的第二种形式,不过此时return语句的expression必须是另一个返回void的函数。强行令void函数返回其他类型的表达式将产生编译错误。

有返回值的函数

return语句的第二种形式提供了函数的结果。只要函数的返回类型不是void,则该函数内的每天return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。

尽管C++无法确保结果的正确性,但是可以保证每个return语句的结果类型正确。也许无法顾及所有情况,但是编译器仍然尽量确保具有返回值的函数只能通过一条有效的return语句退出。

在含有return语句的循环后面应该也有一条return语句,如果没有的话该程序就是错误的。

值是如何被返回的

返回一个值得方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

必须注意当函数返回局部变时的初始化规则。例如我们写一个函数,给定计数值、单词和结束符之后,判断计数值是否大于1:如果是,返回单词的复数形式;如果不是,返回单词原型:

//如果ctr的值大于1,返回word的复数形式

string make_plural(size_t ctr,const string &word,const string &ending)

{

  return (ctr>)?word+ending:word;

}

该函数的返回类型是string,意味着返回值将被拷贝到调用点。因此,该函数将返回word的副本或者一个未命名的临时string对象,该对象的内容是word和ending的和。

同其他引用类型一样,如果函数返回引用,则该引用仅是它所引对象的一个别名。举个例子来说明,假定某函数跳出两个string形参中较短的那个并返回其引用:

//挑出两个string对象中较短的那个,返回其引用

const string &shorterString(const string &s1,const string &s2)

{

  return s1.size()<=s2.size()?s1:s2;

}

其中形参和返回类型都是const string的引用,不管是调用函数还是返回结果都不会真正拷贝string对象。

不要返回局部对象的引用或指针

函数完成后,它所占的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域:

//严重错误:这个函数试图返回局部对象的引用

const string &manip()

{

  string ret;

  //以某种方式改变一下ret

  if(!ret.empty())

    return ret;  //错误:返回局部对象的引用

  else

    return "Empty";  //错误:“Empty”是一个局部临时量

}

上面的两条return语句都将返回未定义的值,也就是说试图使用manip函数的返回值将引发未定义的行为。对于第一天return语句来说,显然它返回的是局部对象的引用。在第二条return语句中,字符串字面值转换成一个局部临时string对象,对于manip来说,该对象和ret一样都是局部的,当函数结束时临时对象占用的空间也就随之是否释放了,所以两条return语句都指向了不再可用的内存空间。

如前所述,返回局部对象的引用是错误的;同样,返回局部对象的指针也是错误的。一旦函数完成,局部对象被释放,指针将指向一个不存在的对象。

返回类类型的函数和调用运算符

和其他运算符一样,调用运算符也有优先级和结合律。调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律。因此,如果函数返回指针、引用或类的对象,我们就能使用调用的结果访问结果对象的成员。

例如,我们可以通过如下形式得到较短string对象的长度:

//调用string对象的size成员,该string对象是由shorterString函数返回的

auto sz=shorterString(s1,s2).size();

因为上面提到的运算符都满足左结合律,所以shorterString的结果是点运算符的左侧运算对象,点运算符可以得到该string对象的size成员,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;

}

把函数调用放在赋值语句的左侧可能看起来有点奇怪,但其实这没什么特别的。返回值是引用,因此调用是个左值,和其他左值一样它也能出现在赋值运算符的左侧。

如果返回类型是常量引用,我们不能给调用的结果赋值,这一点和我们熟悉的情况是一样的:

shorterString("hi","bye")="X";   //错误:返回值是个常量

列表初始化返回值

C++11新标准规定,函数可以返回花括号包围的值得列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。

返回数组指针

因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。虽然从语法上来说,要想定义一个返回数组的指针或引用的函数比较繁琐,但是有一些方法可以简化这一任务,其中最直接的方法是使用类型别名:

typedef int arrT[10];   //arrT是一个类型别名,它表示的类型是含有10个整数的数组

using arrT=int [10];   //arrT的等价声明

arrT * func(int i);    //func返回一个指向含有10个整数的数组的指针

其中arrT是含有10个整数的数组的别名。因为我们无法返回数组,所以将返回类型定义成数组的指针。因此,func函数接受一个int实参,返回一个指向包含10个整数的数组的指针。

声明一个返回数组指针的函数

要想在声明func时不使用类型别名,我们必须牢记被定义的名字后面数组的维度:

int arr[10];   //arr是一个含有10个整数的数组

int *p1[10];  //p1是一个含有10个指针的数组

int (*p2)[10]=&arr;   //p2是一个指针,它指向含有10个整数的数组

和这些声明一样,如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后。然而,函数的形参列表也跟在函数名字后面且形参列表应该先于数组的维度。因此,返回数组指针的函数形式如下所示:

Type (*function (parameter_list)) [dimension]

类似于其他数组的声明,Type表示元素的类型,dimension表示数组的大小。(*function(parameter_list))两端的括号必须存在,就像我们定义p2是两端必须有括号一样。如果没有这对括号,函数的返回类型将是指针的数组。

举个具体点的例子,下面这个func函数的声明没有使用类型别名:

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 类型

使用尾置返回类型

在C++11新标准中还有一种可以简化上述func声明的方法,就是使用尾置返回类型。然后函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:

//func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组

auto func(int i) ->int (*)[10];

因为我们把函数的返回类型放在了形参列表之后,所以可以清楚地看到func函数返回的是一个指针,并且该指针指向了含有10个整数的数组。

使用decltype

还有一种情况,如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。例如,下面的函数返回一个指针,该指针根据参数i的不同指向两个已知数组中的某一个:

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返回指针还必须在函数声明时加一个*符号。

返回类型和return语句的更多相关文章

  1. 函数----基础,参数传递,返回类型和return语句

    一.函数基础1.形参和实参 实参是形参的初始值.第一个实参初始化第一个形参,第二个实参初始化第二个形参,以此类推.尽管实参与形参存在对应关系,但是并没有规定实参的求值顺序.编译器能以任意可行的顺序对实 ...

  2. 【c++ primer, 5e】返回类型和return语句

    [无返回值函数] 1.在c++的void函数中,可以显式地使用return;语句来提前结束函数的调用. [有返回值函数] 1.值是如何被返回的:返回一个值的方式和初始化一个变量或者形参的方式完全一样. ...

  3. 返回类型和 return 语句

    return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方.return 语句有两种形式: return; return expression; 不要返回局部对象的引用或指针: 函数完成 ...

  4. 关于类中的参数类型和return返回值

    基础有些忘了,现在重新巩固一下 先定义一个Person类 class Person(): def __init__(self,name,age,height): self.name=name, sel ...

  5. try {}里有一个return语句 finally执行顺序

    先看例子 package example; class Demo{ public static void main(String args[]) { int x=1; System.out.print ...

  6. GO学习笔记 - 没有参数的 return 语句返回各个返回变量的当前值,这种用法被称作“裸”返回。

    Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用. 返回值的名称应当具有一定的意义,可以作为文档使用. 没有参数的 return 语句返回各个返回变量的当前值.这种用法被称作“裸”返回 ...

  7. Python return语句 函数返回值

    return语句是从python 函数返回一个值,在讲到定义函数的时候有讲过,每个函数都要有一个返回值.Python中的return语句有什么作用,今天就来仔细的讲解一下. python 函数返回值 ...

  8. java try中包含return语句,finally中的return语句返回顺序

    //结论: finally 中的代码比 return 和 break 语句后执行 public static void main(String[] args) { int x=new Test.tes ...

  9. 高程(3):操作符、for、for...in循环、break/continue/return语句、函数等

    1.关系操作符 注意点:1)比较操作数是两个字符串,是比较字符串的字符编码值. 如:"a" > "b"  返回 false:"a" & ...

随机推荐

  1. Java集群优化——dubbo+zookeeper构建高可用分布式集群

    不久前,我们讨论过Nginx+tomcat组成的集群,这已经是非常灵活的集群技术,但是当我们的系统遇到更大的瓶颈,全部应用的单点服务器已经不能满足我们的需求,这时,我们要考虑另外一种,我们熟悉的内容, ...

  2. mysql中php生成唯一ID

    <?php //uniqid官方手册 function create_guid($namespace = '') { static $guid = ''; $uid = uniqid(" ...

  3. 【转】读取android根目录下的文件或文件夹

    原文网址:http://my.oschina.net/Ccx371161810/blog/287823 读取android根目录下的文件或文件夹 SDK的操作 读取android根目录下的文件或文件夹 ...

  4. 如何编写Linux设备驱动程序

    一.Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看 ...

  5. memcache 存储单个KEY,数据量过大的时候性能慢!以及简单的memcache不适合用到的场景

    今天有人问到我:memcache存储大数据量,10K,100K,1M的时候,效果怎么样??我回答:不好,效果非常慢.对方问:为什么啊??我回答不上来...于是就找了点资料. memcached使用需要 ...

  6. 设计模式_Memento_备忘录模式

    形象例子: 同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦 ...

  7. MySQL在线备份与恢复工具 --> Xtrabackup

    1 Xtrabackup原理简介 xtrabackup是一个对InnoDB做数据备份的工具,支持在线热备份(备份时不影响数据读写),是商业备份工具InnoDB Hotbackup的一个很好的替代品.  ...

  8. POJ1038 - Bugs Integrated, Inc.(状态压缩DP)

    题目大意 要求你在N*M大小的主板上嵌入2*3大小的芯片,不能够在损坏的格子放置,问最多能够嵌入多少块芯片? 题解 妈蛋,这道题折腾了好久,黑书上的讲解看了好几遍才稍微有点眉目(智商捉急),接着看了网 ...

  9. Java网络编程(UDP协议-聊天程序)

    接收端: package WebProgramingDemo; import java.net.DatagramPacket; import java.net.DatagramSocket; publ ...

  10. python 安装第三方模块

    在Python中,安装第三方模块,是通过setuptools这个工具完成的. 如果你正在使用Mac或Linux,安装setuptools本身这个步骤就可以跳过了. 如果你正在使用Windows,请首先 ...