不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也随之释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的区域。此时如果使用局部对象的引用或指针将发生未定义行为。
引用返回左值
函数的返回类型决定函数是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数的调用,特别是我们可以为返回类型是非常量引用的函数的结果赋值,如果返回值是常量引用,则不能赋值:
char &get_val(string &str, string::size_type ix){
return str[ix]; //下标运算符得到的是非常量引用类型
}
string s("hello");
get_val(s, 0) = 'H'; //将s[0]改成'H'
列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行初始化;否则,返回的值由函数的返回类型决定:
vector<string> process(){
string process1("process1");
string process1("process2");
string test1("test1");
string test1("test2");
//...
if(process1.empty()){
return {}; //返回空列表,此时vector是空的
}else if(process2.empty()){
return {test1, "test2"}; //返回列表初始化的vector对象
}else{
return {test1, test2, "test3"};
}
}
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占用的空间不应该大于目标类型的空间。如果函数返回的是类类型,由类本身定义初始化值确定列表初始化值。
返回数组的指针
因为数组不能被拷贝,所有函数不能返回数组。不过函数可以返回数组的指针或引用:
typedef int arr[10];
using arr = int[10]; //arr是类型别名,表示含有10个整数的数组
arr *func(int i); //func返回一个指向含有10个整数数组的指针
int (*func(int i))[10];//等价于arr *func(int i);
可以按下面的方法类逐层理解该声明的含义:
func(int i )表示调用func函数时需要一个int类型的实参。
(*func(int i))表示我们可以对函数调用的结果执行解引用。
(*func(int i))[10]表示解引用func调用将得到一个大小是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 even[] = {0,2,5,6,8};
//返回一个指针,该指针指向含有个整数的数组
decltype(odd) *arrPtr(int i){
return (i%2)? &odd:&even; //返回一个指向数组的指针
}
arrPtr使用关键字decltype表示它的返回类似是一个指针,并且该指针所指的对象与odd类型一致。因为odd是数组,所以arrPtr返回一个指向有5个整数的数组的指针。需要注意decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是一个数组,要想表示arrPtr返回指针还必须在函数声明中加一个*符号。