今天学习的内容时C++ Primer函数这一章的第三节内容,本章内容围绕函数的返回类型展开,主要有以下内容
返回类型和return语句
1.无返回值函数
2.有返回值函数
-
值是如何被返回的
-
引用返回左值
-
列表初始化返回值
-
主函数main的返回值
3. 返回数组指针
-
声明一个返回数组指针的函数
-
使用尾置返回类型
-
使用 decltype
返回类型和return语句
return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。
return语句有两种形式:
return;
return expression;
1.无返回值函数
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数
不要求非得有return语句,因为在这类函数的最后一句后面会隐式地执行return。
2.有返回值函数
return 语句的第二种形式提供了函数的结果。只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。
值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式完全一样;返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。
不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也随之被释放掉。因此函数终止意味着局部变量的引用将指向不再有效的内存区域。
//严重错误:这个函数试图返回局部对象的引用
const string &manip ()
{
string ret;
//以某种方式改变一下ret
if (!ret.empty ())
{
return ret; //错误:返回局部对象的引用!
}else
{
return "Empty"; // 错误: "Empty"是一个局部临时量
}
}
Tip
要想确保返回值安全,我们不妨提问:引用所引的是在函数之前已经存在的哪个对象?
返回局部对象的引用是错误的;同样,返回局部对象的指针也是错误的。一旦函数完成,局部对象被释放,指针将指向一个不存在的对象。
引用返回左值
函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数的调用,特别是,我们能为返回类型是非常量引用的函数的结果赋值:
char &get_val (string &str, string: :size_type ix)
{
return str [ix] ;
}
// get_val假定索引值是有效的
int main ()
{
string s("a value");
cout << s << endl; // 输出a value
get_val (s, 0) = 'A';//将s[0]的值改为A
cout << s << endl; // 输出A value
return 0;
}
列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此,处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
vector process ()
{
//...
// expected actual string 对象
if (expected. empty ())
return {}; //返回一个空vector对象
else if (expected == actual)
return {"functionx", "okay"};//返回列表初始化的vector对象
else
return {"functionx", expected, actual};
}
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果函数返回的是类类型,,由类本身定义初始值如何使用。
主函数main的返回值
如果函数的返回类型不是void,那么它必须返回一个值。但是这条规则有个例外:我们允许main函数没有return语句直接结束。如果控制到达了main函数的结尾处而且没有return语句,编译器将隐式地插入一条返回0的return语句。
3.返回数组指针
因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用 。虽然从语法上来说,要想定义一个返回数组的指针或引用的函数比较烦琐,但是有一些方法可以简化这一任务,其中最直接的方法是使用类型别名:
typedef int arrT [10]; // arrT是一个类型别名,它表示的类型是含有10个整数的数组
using arrT = int [10]; // arrT的等价声明
arrt* func (int i);// func返回一个指向含有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声明的方法,就是使用尾置返回类型(trailing return type)。任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。
尾置返回类型跟在形参列表后面并以一个->符号开头。
为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:
// func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func (int i) -> int (*) [10] ;
因为我们把函数的返回类型放在了形参列表之后,所以可以清楚地看到func函数返回的是一个指针,并且该指针指向了含有10个整数的数组。
使用 decltype
还有一种情况,如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。例如,下面的函数返回一个指针,该指针根据参数i的不同指向两个已知数组中的某一个: i
nt odd [] = (1,3,5,7,9);
int even [] = [0,2,4,6,8);
//返回一个指针,该指针指向含有5个整数的数组
decltype (odd) *arrPtr(int i) {
return (i 2) ? &odd: &even; //返回一个指向数组的指针
}