函数的返回值
无返回值的函数
没有返回值的return语句只能用于返回类型是void的函数中。返回void的函数可以没有return语句,这类函数的最后一句后面会隐式地执行return。而如果void函数需要在函数中间退出,可以使用return语句。例如一个值交换的函数:
void swap(int &v1, int &v2) {
if(v1 == v2)
return;
// 程序中间退出
int temp = v2;
v2 = v1;
v1 = temp;
// 无显式return语句
}
有返回值的函数
只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。且return语句的类型必须与函数的返回类型相同,或者能够隐式转换成函数的类型。
值是如何返回的
返回一个值的方式和初始化变量或形参的方式一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。
分别举一个返回值和返回引用的例子:
// 如果ctr的值大于1则返货复数形式,否则返回原型
string makePlural(size_t ctr, const string &word, const string &ending) {
return (ctr > 1) ? word + ending : word;
}
该函数的返回类型是string,意味着返回值将被拷贝到调用点。因此,该函数将返回一个未命名的临时string对象或者一个word的副本。
如果函数返回引用,则该引用只是它所引对象的一个别名。
// 返回两个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;
if(!ret.empty())
return ret; // 错误:返回的是局部对象的引用
else
return "Empty"; // 错误:"Empty"是一个局部临时量
}
上面两条return语句都将返回未定义的值,也就是说,试图使用manip函数的返回值将发生未定义的行为。
返回类类型的函数和调用运算符
如果函数返回指针、引用或类的对象,我们就能使用函数调用的结果访问结果对象的成员。例如:
// 调用string对象的size成员
auto sz = shorterString(s1, s2).size();
引用返回左值
调用一个返回引用的函数得到左值,其他返回类型得到右值。特别地,我们能够为返回类型是非常量引用的函数的结果赋值:
char &getVal(string &str, string::size_type ix) {
return str[ix];
}
int main() {
string s("a value");
cout << s << endl;
getVal(s, 0) = 'A'; // 将A[0]的值改为A
cout << s << endl;
return 0;
}
返回值是一个引用,因此调用的是左值,和其他左值一样它也可以出现在赋值运算符的左侧。
而如果返回类型是常量引用,则不能对调用的结果赋值,例如之前的shorterString函数:
shorterString("hi", "bye") = "X"; // 错误:返回值是常量引用
列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表,此列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回值由函数的返回类型决定。例如:
vector<string> process(const srting &expected) {
if(expected.empty())
return {};
else
return {"okay", "function"};
}
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,并且该值所占的空间不能大于目标类型的空间。如果函数返回的是类类型,由类本身定义初始值如何使用。