6.3 返回类型和return语句

本文深入探讨了C++中函数返回值的相关概念,包括无返回值函数和有返回值函数的使用,值如何返回,避免返回局部对象的引用或指针,以及引用返回值、列表初始化返回值、主函数返回值、递归函数和返回数组指针的注意事项。文章强调了返回类型的重要性以及在C++11新标准下的一些改进,如尾置返回类型和decltype的使用,旨在提供函数返回值处理的最佳实践。
摘要由CSDN通过智能技术生成


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

return;
return epression;

无返回值函数

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

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

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

有返回值函数

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

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

PS:值得注意的是:在含有return语句的循环后面应该也有一条return语句,如果没有的话该程序就是错误的。很多编译器都无法发现此类错误。

值是如何被返回的

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

同其他引用类型一样,如果函数返回引用,则该引用仅是它所引对象的一个别名。

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

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

const string & function()
{	
	string ret;
	if(!ret.empty())
	return ret;//1
	else
	return "empty";//2
}

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

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

引用返回左值

函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数的调用,特别是,我们能为返回类型是非常量引用的函数的结果赋值

样例:

#include<iostream>
#include<string>

using namespace std;

char &my_function(string& str,int num)
{
	return str[num];
}

int main()
{
	string s = "sello";
	my_function(s, 0) = 'h';
	cout << s;
    return 0;
}

输出结果:
在这里插入图片描述

但是如果返回的值是一个常量引用,那么就没办法进行修改了,这和我们所想的也差不多。

列表初始化返回值

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

#include<iostream>
#include<string>
#include<vector>

using namespace std;

vector<string> my_function(bool i)
{
	if (!i)
	{
		return {};
	}
	else
	{
		return { "hello","world" };
	}
}

int main()
{
	vector<string> v1 = my_function(0);
	vector<string> v2 = my_function(1);
	for (string i: v1)
	{
		cout << i<<" ";
	}
	cout << endl;
	for (string i : v2)
	{
		cout << i << " ";
	}
    return 0;
}

在这里插入图片描述

主函数main的返回值

一般来说都是返回0代表成功,返回非零的其他数据代表失败,具体失败与各自的编译器有关。

递归

如果一个函数调用了它自身,不管这种调用是直接的还是间接的,都称该函数为递归函数。

在递归函数中,一定有某条路径是不包含递归调用的;否则,函数将“永远"递归下去,换句话说,函数将不断地调用它自身直到程序栈空间耗尽为止。我们有时候会说这种函数含有递归循环。

举个例子:(阶乘)

int jiecheng(int num)
{	
	if(num>1)
		return jiecheng(num-1)*num;
	return 1;
}

对于上述的递归函数,num=1时的return 1即是停止。

返回数组指针

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

typedef int arrT[10];//arrT是一个类型声明,这种类型内部含有10个int类型的元素。
using arrT = int[10];//上一种的等价声明。
arrT* funcition()//返回的就是10个int类型的指针了

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

格式实际上是:数据的类型(*函数名(形式参数))[数组大小]

样例:

int ret[10];

int (*my_function())[10]
{
	ret[0] = 1;
	return &ret;//注意这个&
}

实际上此种方式过于麻烦,慎用,易错。

使用尾置返回类型

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

基本的格式:auto 函数名(形式参数)-> 需要转换成的类型

样例:

auto my_function() ->int (*)[10]
{
	ret[0] = 1;
	return &ret;//注意这个&
}

使用decltype

还有一种情况,如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。

int ret[10];

decltype(ret) *my_function() //注意得加上*
{
	ret[0] = 1;
	return &ret;//注意这个&
}

使用关键字decltype表示它的返回类型是个指针,并且该指针所指的对象与ret的类型一致。因为ret是数组,所以my_function返回一个指向含有10个整数的数组的指针。有一个地方需要注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值