return
语句将终止当前正在执行的函数并将控制权返回到调用该函数的地方,两种形式:
return;
return expression;
无返回值的函数
没有返回值的函数只能用在返回类型是 void
的函数中,返回 void
的函数不要求非得有 return
语句,因为这类函数的最后一句后面会隐式的执行 return
。
只要函数的返回类型不是 void
,则该函数内的每条 return
语句必须返回一个值, return
语句返回值的类型必须与函数的返回类型一致,或者能够隐式转换成函数返回类型。
在含有 return
语句的循环后面应该也有一条 return
语句,如果没有的话,程序就是错误的。
值是如何被返回的
返回一个值的方式和初始化一个变量或者形参的方式是完全一样的:返回的值用于初始化调用点的一个临时两,该临时两就是函数调用的结果。
string make_plural(size_t ctr, const string &word, const string &ending)
{
return (ctr > 1) ? word + ending : word;
}
该函数的返回类型是 string
,意味着返回值将被拷贝到调用点,因此函数返回 word
的副本或者一个未命名的临时 string
对象,该对象的内容是 word + ending
。
如果函数返回引用:
const string &shorterString(const string &str1, const string &str2)
{
return str1.size() <= str2.size() ? str1 : str2;
}
其中形参和返回类型都是 const string
的引用,无论是在函数调用还是返回结果都不会真正拷贝 string
对象。
不要返回局部对象的引用或指针
函数执行完成之后,它所占用的存储空间会被释放,因此函数终止意味着局部变量的引用指向的不再是有效的内存区域。
const string &manip()
{
string ret;
if (ret.empty())
return ret; //错误,返回的是局部对象的引用
else
return "empty"; //错误,"empty"是一个局部临时变量
}
返回局部对象的引用是错误的,同样返回局部对象的指针也是错误的。
引用返回左值
调用一个返回引用的函数得到左值,其它返回类型都是右值。
可以为返回类型是非常量引用的函数赋值,返回常量引用则不可以赋值。
char &get_val(string &str,string::size_type ix)
{
return str[ix];
}
int main()
{
string s("a value");
get_val(s,0) = 'A'; //返回非常量引用可以赋值
}
列表初始化返回值
C++ 11规定,函数可以返回花括号包围起来的列表。
- 类似于其他类型的返回值,此处的列表也用来对表示函数返回的临时量进行初始化。
- 如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
- 如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,并且该值所占用的空间不应该大于目标类型的空间;如果函数返回的是类类型,由类本身定义初始值如何使用。
vector<string> process(const string &expected)
{
if (expected.empty())
{
return{}; //返回空列表
}
else if (expected == "actual")
{
return{"functionX","okay"};
}
else
{
return{ "functionX","expected","actual"};
}
}
主函数main 的返回值
上面说到如果函数的返回值不是 void
,那么它必须返回一个值,但是这条规定有一个意外:允许 main
函数没有 return
语句直接结束。
- 如果到达了
main
的末尾并且没有遇到return
语句,编译器会隐式地插入一条返回 0 的return
语句。 -
main
的返回值可以看作是状态指示器,返回 0 表示执行成功,返回其他值表示执行失败,其中非0返回值的具体定义由机器定义。 - 为了使函数的返回值与机器无关,在
cstdlib
中 定义了两个预处理变量:EXIT_SUCCESS
,EXIT_FAILURE
,使用这两个变量分别表示成功与失败。
因为数组不能拷贝,所以函数无法返回数组,但是,函数可以返回数组的指针或引用。
// 使用类型别名
typedef int arrT[10]; //arrT 是一个类型别名,它表示的类型是含有10个整型元素的数组
using arrT = int[10]; //与上面的声明形式是等价的
arrT* func(int i); //定义一个返回指向10个整数数组的指针
注意:
int arr[10]; //arr是一个含有10个整数的数组
int *p1[10]; //p1是一个含有10个int* 的数组
int (*p2)[10] = &arr; //p2是一个指针,它指向的是包含10个int类型的数组
如果不使用类型别名,定义一个返回数组指针的函数,通用形式是:
Type (*function(parameter_list))[dimension]
-
Type
表示元素的类型; -
dimension
表示数组的大小; -
(*function(parameter_list))
两端的括号必须存在,如果没有括号,则函数返回的类型是指针的数组。
int (*func(int i))[10];
理解上面表达式的含义:
-
func(int i)
表示调用函数时需要一个int
类型的实参。 -
(*func(int i))
意味着可以对函数调用的结果执行解引用操作。 -
(*func(int i))[10]
意味着解引用函数调用的结果将是大小是10的数组。 -
int (*func(int i))[10]
意味着数组中的元素都是int
类型。
使用尾置返回类型
C++ 11中规定了一种可以简化上面 func
声明的方法,就是使用 尾置返回类型。
任何函数的定义都可以使用尾置返回类型,但是这种形式对于返回类型比较复杂的函数最有效。
比如返回类型是数组的指针或者数组的引用,尾置返回类型会在函数的形参列表后面加一个->
符号开头,为了表示函数真正的返回类型跟在形参列表之后,本应该放置返回类型的地方放一个 auto
:
auto func(int i)->int(*)[10];//func接受一个int类型的实参返回一个指针,该指针指向含有10个整数的数组
使用decltype
还有一种情况,如果我们知道函数返回的指针指向哪个数组,就可以使用 decltype
关键字声明返回类型。
int odd[] = {1,3,5,7,9};
int ecen[] = {0,2,4,6,8};
// 返回一个指针,该指针指向含有5个整数的数组
decltype(odd) * arrPtr(int i)
{
return (i % 2) ? &odd : &even;
}
arrPtr
使用关键字 decltype
表示它返回的类型是指针,并且该指针所指向的对象与 odd
的类型一致。
需要注意的是 decltype
并不负责把数组类型转换成对应的指针,所以 decltype
的结果是一个数组,要想表示 arrPtr
返回指针,还必须在函数声明时加上*
。